`default_nettype none // ============================================================================ module message_formatter # ( parameter WIDTH = 24, // Word length in bits. MUST be a multiply of 4 parameter COUNT = 2, // Word count parameter TX_INTERVAL = 4 // Character transmission interval ) ( // Clock and reset input wire CLK, input wire RST, // Data input input wire I_STB, input wire [(WIDTH*COUNT)-1:0] I_DAT, // ASCII output output wire O_STB, output wire [7:0] O_DAT ); // ============================================================================ // Total input data word width localparam TOTAL_WIDTH = WIDTH * COUNT; // ============================================================================ // FSM states integer fsm; localparam FSM_IDLE = 'h00; localparam FSM_TX_HEX = 'h11; localparam FSM_TX_CR = 'h21; localparam FSM_TX_LF = 'h22; localparam FSM_TX_SEP = 'h31; // ============================================================================ // TX interval counter reg [24:0] tx_dly_cnt; reg tx_req; wire tx_rdy; always @(posedge CLK) if (RST) tx_dly_cnt <= -1; else if (!tx_rdy) tx_dly_cnt <= tx_dly_cnt - 1; else if ( tx_rdy && tx_req) tx_dly_cnt <= TX_INTERVAL - 2; assign tx_rdy = tx_dly_cnt[24]; always @(posedge CLK) if (RST) tx_req <= 1'b0; else case (fsm) FSM_TX_HEX: tx_req <= 1'b1; FSM_TX_SEP: tx_req <= 1'b1; FSM_TX_CR: tx_req <= 1'b1; FSM_TX_LF: tx_req <= 1'b1; default: tx_req <= 1'b0; endcase // ============================================================================ // Word and char counter reg [7:0] char_cnt; reg [7:0] word_cnt; always @(posedge CLK) if (fsm == FSM_IDLE || fsm == FSM_TX_SEP) char_cnt <= (WIDTH/4) - 1; else if (tx_rdy && fsm == FSM_TX_HEX) char_cnt <= char_cnt - 1; always @(posedge CLK) if (fsm == FSM_IDLE) word_cnt <= COUNT - 1; else if (tx_rdy && fsm == FSM_TX_SEP) word_cnt <= word_cnt - 1; // ============================================================================ // Data shift register reg [TOTAL_WIDTH-1:0] sr_reg; wire [3:0] sr_dat; always @(posedge CLK) if (fsm == FSM_IDLE && I_STB) sr_reg <= I_DAT; else if (fsm == FSM_TX_HEX && tx_rdy) sr_reg <= sr_reg << 4; assign sr_dat = sr_reg[TOTAL_WIDTH-1:TOTAL_WIDTH-4]; // ============================================================================ // Control FSM always @(posedge CLK) if (RST) fsm <= FSM_IDLE; else case (fsm) FSM_IDLE: if (I_STB) fsm <= FSM_TX_HEX; FSM_TX_HEX: if (tx_rdy && (char_cnt == 0) && (word_cnt == 0)) fsm <= FSM_TX_CR; else if (tx_rdy && (char_cnt == 0)) fsm <= FSM_TX_SEP; else if (tx_rdy && (char_cnt != 0)) fsm <= FSM_TX_HEX; FSM_TX_SEP: if (tx_rdy) fsm <= FSM_TX_HEX; FSM_TX_CR: if (tx_rdy) fsm <= FSM_TX_LF; FSM_TX_LF: if (tx_rdy) fsm <= FSM_IDLE; endcase // ============================================================================ // Data to ASCII converter reg o_stb; reg [7:0] o_dat; always @(posedge CLK or posedge RST) if (RST) o_stb <= 1'd0; else o_stb <= tx_req & tx_rdy; always @(posedge CLK) if (fsm == FSM_TX_CR) o_dat <= 8'h0D; else if (fsm == FSM_TX_LF) o_dat <= 8'h0A; else if (fsm == FSM_TX_SEP) o_dat <= "_"; else if (fsm == FSM_TX_HEX) case (sr_dat) 4'h0: o_dat <= "0"; 4'h1: o_dat <= "1"; 4'h2: o_dat <= "2"; 4'h3: o_dat <= "3"; 4'h4: o_dat <= "4"; 4'h5: o_dat <= "5"; 4'h6: o_dat <= "6"; 4'h7: o_dat <= "7"; 4'h8: o_dat <= "8"; 4'h9: o_dat <= "9"; 4'hA: o_dat <= "A"; 4'hB: o_dat <= "B"; 4'hC: o_dat <= "C"; 4'hD: o_dat <= "D"; 4'hE: o_dat <= "E"; 4'hF: o_dat <= "F"; endcase assign O_STB = o_stb; assign O_DAT = o_dat; endmodule