msg_printer¶
Source: Verilog/fpga/basys3/mem-test/msg_printer.v
Hierarchy: not instantiated by any of the 9 build tops (elaborated by yosys).
Module hierarchy - All modules

Schematic¶
Drawn from the Verilog: no build top uses this module, so it was elaborated from its own file with no defines and default parameters. Sub-modules are boxes (click the picture to open it full size; there every sub-module box links to its page, and every wire shows its Verilog name).
Description¶
UART message printer for the SDRAM test Formats fixed messages and read/write trace lines like "W 200000=3E" (write: address=data, hex) "R 200000=3E OK" / "R 200000=7F ERR" and streams them out through uart_tx, one byte at a time. Last reviewed: 8-JUL-2026 Ronny Hansen
Parameters¶
| Parameter | Default |
|---|---|
DELAY_FRAMES |
2812 // passed on to uart_tx |
Ports¶
| Direction | Width | Name | Description |
|---|---|---|---|
| input | 1 |
clk |
|
| input | 1 |
rst_n (active low) |
|
| input | 1 |
start |
1-cycle pulse; only when busy=0 |
| input | [3:0] |
msg |
MSG_* selector, latched at start |
| input | [22:0] |
addr |
address field for W/R messages |
| input | [7:0] |
data |
data field for W/R messages |
| output | 1 |
busy |
high until the whole message has left the UART |
| output | 1 |
txd |
Verilog source¶
Verilog/fpga/basys3/mem-test/msg_printer.v on GitHub.
Show the Verilog of msg_printer (187 lines)
/****************************************************************************
** UART message printer for the SDRAM test **
** **
** Formats fixed messages and read/write trace lines like **
** "W 200000=3E" (write: address=data, hex) **
** "R 200000=3E OK" / "R 200000=7F ERR" **
** and streams them out through uart_tx, one byte at a time. **
** **
** Last reviewed: 8-JUL-2026 **
** Ronny Hansen **
*****************************************************************************/
module msg_printer #(
parameter DELAY_FRAMES = 2812 // passed on to uart_tx
) (
input clk,
input rst_n,
input start, // 1-cycle pulse; only when busy=0
input [3:0] msg, // MSG_* selector, latched at start
input [22:0] addr, // address field for W/R messages
input [7:0] data, // data field for W/R messages
output busy, // high until the whole message has left the UART
output txd
);
// Message selectors
localparam MSG_BANNER = 4'd0;
localparam MSG_PROMPT = 4'd1;
localparam MSG_WRITE = 4'd2;
localparam MSG_READ_OK = 4'd3;
localparam MSG_READ_ERR = 4'd4;
localparam MSG_BLOCK = 4'd5;
localparam MSG_VERIFY = 4'd6;
localparam MSG_DOT = 4'd7;
localparam MSG_PASS = 4'd8;
localparam MSG_FAIL = 4'd9;
localparam CR = 8'h0D;
localparam LF = 8'h0A;
// Fixed strings (widths derived from the literals via $bits).
// NOTE: \r is not a legal Verilog string escape - CR must be octal \015.
localparam S_BANNER = "\015\012ND120 BASYS3 MEM TEST 9600-8N1\015\012";
localparam S_PROMPT = "PRESS S1 OR ANY KEY\015\012";
localparam S_BLOCK = "WRITE BLOCK\015\012";
localparam S_VERIFY = "VERIFY BLOCK\015\012";
localparam S_PASS = "\015\012PASS\015\012";
localparam S_FAIL = "\015\012FAIL\015\012";
function [7:0] hexd(input [3:0] n);
hexd = (n < 4'd10) ? (8'h30 + {4'b0, n}) : (8'h37 + {4'b0, n}); // '0'-'9','A'-'F'
endfunction
// pick character idx (0-based) out of a string literal; 0 terminates
function [7:0] strchar(input [8*40-1:0] s, input integer len, input integer i);
strchar = (i < len) ? s[8*(len-1-i)+:8] : 8'h00;
endfunction
reg [ 3:0] msg_r;
reg [22:0] addr_r;
reg [ 7:0] data_r;
reg [ 5:0] idx;
wire [23:0] a = {1'b0, addr_r};
// Character at position idx of the latched message; 8'h00 = end of message
reg [7:0] ch;
always @(*) begin
case (msg_r)
MSG_BANNER: ch = strchar({8'b0, S_BANNER}, $bits(S_BANNER) / 8, idx);
MSG_PROMPT: ch = strchar({8'b0, S_PROMPT}, $bits(S_PROMPT) / 8, idx);
MSG_BLOCK: ch = strchar({8'b0, S_BLOCK}, $bits(S_BLOCK) / 8, idx);
MSG_VERIFY: ch = strchar({8'b0, S_VERIFY}, $bits(S_VERIFY) / 8, idx);
MSG_PASS: ch = strchar({8'b0, S_PASS}, $bits(S_PASS) / 8, idx);
MSG_FAIL: ch = strchar({8'b0, S_FAIL}, $bits(S_FAIL) / 8, idx);
MSG_DOT: ch = (idx == 0) ? "." : 8'h00;
MSG_WRITE, MSG_READ_OK, MSG_READ_ERR: begin
case (idx)
6'd0: ch = (msg_r == MSG_WRITE) ? "W" : "R";
6'd1: ch = " ";
6'd2: ch = hexd(a[23:20]);
6'd3: ch = hexd(a[19:16]);
6'd4: ch = hexd(a[15:12]);
6'd5: ch = hexd(a[11:8]);
6'd6: ch = hexd(a[7:4]);
6'd7: ch = hexd(a[3:0]);
6'd8: ch = "=";
6'd9: ch = hexd(data_r[7:4]);
6'd10: ch = hexd(data_r[3:0]);
default: begin
case (msg_r)
MSG_WRITE:
case (idx)
6'd11: ch = CR;
6'd12: ch = LF;
default: ch = 8'h00;
endcase
MSG_READ_OK:
case (idx)
6'd11: ch = " ";
6'd12: ch = "O";
6'd13: ch = "K";
6'd14: ch = CR;
6'd15: ch = LF;
default: ch = 8'h00;
endcase
default: // MSG_READ_ERR
case (idx)
6'd11: ch = " ";
6'd12: ch = "E";
6'd13: ch = "R";
6'd14: ch = "R";
6'd15: ch = CR;
6'd16: ch = LF;
default: ch = 8'h00;
endcase
endcase
end
endcase
end
default: ch = 8'h00;
endcase
end
// Byte pump: hand ch to uart_tx, one handshake per character
localparam P_IDLE = 2'd0;
localparam P_SEND = 2'd1;
localparam P_GAP = 2'd2; // one cycle for tx_busy to assert
reg [1:0] pstate;
reg tx_valid;
reg [7:0] tx_byte;
wire tx_busy;
assign busy = (pstate != P_IDLE);
always @(posedge clk) begin
if (!rst_n) begin
pstate <= P_IDLE;
tx_valid <= 1'b0;
idx <= 0;
msg_r <= MSG_BANNER;
addr_r <= 0;
data_r <= 0;
tx_byte <= 0;
end else begin
tx_valid <= 1'b0;
case (pstate)
P_IDLE:
if (start) begin
msg_r <= msg;
addr_r <= addr;
data_r <= data;
idx <= 0;
pstate <= P_SEND;
end
P_SEND:
if (ch == 8'h00) begin
pstate <= P_IDLE;
end else if (!tx_busy) begin
tx_valid <= 1'b1;
tx_byte <= ch;
idx <= idx + 1;
pstate <= P_GAP;
end
P_GAP: pstate <= P_SEND;
default: pstate <= P_IDLE;
endcase
end
end
uart_tx #(
.DELAY_FRAMES(DELAY_FRAMES)
) u_tx (
.clk(clk),
.rst_n(rst_n),
.tx_data(tx_byte),
.tx_valid(tx_valid),
.tx_busy(tx_busy),
.txd(txd)
);
endmodule