| `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 |