blob: fbffcbf6e648eae82bd6119c2cbbf73e9eced7b3 [file] [log] [blame]
`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