status_printer¶
Source: Verilog/fpga/tang-nano-20k/sd-fat-test/src/status_printer.v
Hierarchy: not instantiated by any of the 9 build tops (elaborated by yosys).
Module hierarchy - All modules

Schematic¶
Schematic not generated: netlistsvg failed: RangeError: Maximum call stack size exceeded.
Description¶
Status message printer for the SD-FAT test Streams one of a set of fixed ASCII status lines through an external uart_tx (shared with hex_dumper, muxed by the top level). Same byte pump pattern as sdram-test/src/msg_printer.v. NOTE: \r is not a legal Verilog string escape - CR must be octal \015. Every message must stay within 64 characters: strchar's window is [8*64-1:0] and idx is 6 bits, so a longer string is silently truncated and then wraps. That is why the menu is TWO messages. Last reviewed: 10-AUG-2026 Ronny Hansen
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 | [5:0] |
msg |
MSG_* selector, latched at start |
| output | 1 |
busy |
high until the whole message has left the UART |
| output | [7:0] |
tx_data |
|
| output | 1 |
tx_valid |
|
| input | 1 |
tx_busy |
Verilog source¶
Verilog/fpga/tang-nano-20k/sd-fat-test/src/status_printer.v on GitHub.
Show the Verilog of status_printer (304 lines)
/****************************************************************************
** Status message printer for the SD-FAT test **
** **
** Streams one of a set of fixed ASCII status lines through an external **
** uart_tx (shared with hex_dumper, muxed by the top level). Same byte **
** pump pattern as sdram-test/src/msg_printer.v. **
** **
** NOTE: \r is not a legal Verilog string escape - CR must be octal \015. **
** **
** Every message must stay within 64 characters: strchar's window is **
** [8*64-1:0] and idx is 6 bits, so a longer string is silently truncated **
** and then wraps. That is why the menu is TWO messages. **
** **
** Last reviewed: 10-AUG-2026 **
** Ronny Hansen **
*****************************************************************************/
`include "sd_fat_test_config.vh"
module status_printer (
input clk,
input rst_n,
input start, // 1-cycle pulse; only when busy=0
input [5:0] msg, // MSG_* selector, latched at start
output busy, // high until the whole message has left the UART
// handshake to the shared uart_tx (top level muxes this with hex_dumper)
output reg [7:0] tx_data,
output reg tx_valid,
input tx_busy
);
// Message selectors (top level uses the same values)
localparam MSG_BANNER= 6'd0;
localparam MSG_CARD_SDV1= 6'd1;
localparam MSG_CARD_SDV2= 6'd2;
localparam MSG_CARD_SDHC= 6'd3;
localparam MSG_FS_FAT16= 6'd4;
localparam MSG_FS_FAT32= 6'd5;
localparam MSG_FS_UNKNOWN= 6'd6;
localparam MSG_FILE_FOUND= 6'd7;
localparam MSG_FILE_NOTFND= 6'd8;
localparam MSG_ERR_CARD_TO= 6'd9;
localparam MSG_ERR_SCAN_TO= 6'd10;
localparam MSG_ERR_TRUNC= 6'd11;
localparam MSG_MENU= 6'd12;
localparam MSG_NOTIMPL= 6'd13;
localparam MSG_SD_NOTCHK= 6'd14; // menu SD status line (14 + status code)
localparam MSG_SD_NOCARD= 6'd15;
localparam MSG_SD_ERROR= 6'd16;
localparam MSG_SD_OK= 6'd17;
localparam MSG_COPYING= 6'd18;
localparam MSG_COPY_DONE= 6'd19;
localparam MSG_ERR_NOTGT= 6'd20;
localparam MSG_ERR_SMALL= 6'd21;
localparam MSG_ERR_WRITE= 6'd22;
localparam MSG_BLK_WRITING= 6'd23;
localparam MSG_BLK_DONE= 6'd24;
localparam MSG_ERR_RANGE= 6'd25;
localparam MSG_HELP1= 6'd26;
localparam MSG_HELP2= 6'd27;
localparam MSG_HELP3= 6'd28;
localparam MSG_HELP4= 6'd29;
localparam MSG_HELP5= 6'd30;
localparam MSG_HELP6= 6'd31;
localparam MSG_HELP7= 6'd32;
localparam MSG_ERR_IOSZ= 6'd33;
localparam MSG_IOW_RUN= 6'd34;
localparam MSG_IOR_RUN= 6'd35;
localparam MSG_SCANNING= 6'd36;
localparam MSG_ERR_READ= 6'd37;
localparam MSG_ERR_FATRD= 6'd38;
localparam MSG_ERR_FATCOR= 6'd39;
localparam MSG_MENU2= 6'd40; // second menu line (the streaming commands)
localparam MSG_ASK_BLK= 6'd41;
localparam MSG_ASK_SEC= 6'd42;
localparam MSG_ASK_NAME= 6'd43;
localparam MSG_ERR_NUM= 6'd44;
localparam MSG_AT_SEC= 6'd45; // followed by 8 hex digits from the top level
localparam MSG_NAME_SET= 6'd46;
localparam MSG_HELP8= 6'd47; // help continues here: 33..39 were taken
localparam MSG_HELP9= 6'd48; // before the streaming commands existed
localparam MSG_HELP10= 6'd49;
localparam MSG_HELP11= 6'd50;
localparam MSG_HELP12= 6'd51;
localparam MSG_HELP13= 6'd52;
// multi-block range read (menu R): prompts, progress and the summary
localparam MSG_ASK_STA= 6'd53;
localparam MSG_ASK_CNT= 6'd54;
localparam MSG_R_RUN= 6'd55;
localparam MSG_R_AT= 6'd56; // + 8 hex digits: block reached
localparam MSG_R_BLOCKS= 6'd57; // + 8 hex digits
localparam MSG_R_SECS= 6'd58; // + 8 hex digits
localparam MSG_R_CHK= 6'd59; // + 8 hex digits
localparam MSG_R_CYC= 6'd60; // + 8 hex digits
localparam MSG_R_PASS= 6'd61;
localparam MSG_R_FAIL= 6'd62;
// Fixed strings (longest is 55 chars; strchar window is 64)
localparam S_BANNER = "\015\012SD-FAT TEST 10-JUL-2026\015\012";
localparam S_CARD_SDV1 = "CARD: SDV1\015\012";
localparam S_CARD_SDV2 = "CARD: SDV2\015\012";
localparam S_CARD_SDHC = "CARD: SDHC\015\012";
localparam S_FS_FAT16 = "FS: FAT16\015\012";
localparam S_FS_FAT32 = "FS: FAT32\015\012";
localparam S_FS_UNKNOWN = "FS: UNKNOWN\015\012";
localparam S_FILE_FOUND = "FILE: FOUND\015\012";
localparam S_FILE_NOTFND = "FILE: NOT FOUND\015\012";
localparam S_ERR_CARD_TO = "ERROR: CARD INIT TIMEOUT\015\012";
localparam S_ERR_SCAN_TO = "ERROR: FS SCAN TIMEOUT\015\012";
localparam S_ERR_TRUNC = "ERROR: FILE TRUNCATED TO BUFFER\015\012";
// The menu is printed as two messages (64-char limit, see the header).
// The read-only build must not OFFER a command it does not contain, so
// the writing keys are absent from the text as well as from the FSM.
`ifdef SDFAT_TEST_READONLY
localparam S_MENU = "\015\0121=LIST 2=DUMP 5=CHECK 8=BLOCK 9=SECTOR\015\012";
localparam S_MENU2 = "R=RANGE N=NAME H=HELP\015\012# ";
`else
localparam S_MENU = "\015\0121=LIST 2=DUMP 3=COPY 4=WRBLK1 5=CHECK 6=WS 7=RS\015\012";
localparam S_MENU2 = "8=BLOCK 9=SECTOR R=RANGE N=NAME H=HELP\015\012# ";
`endif
localparam S_NOTIMPL = "NOT IMPLEMENTED\015\012";
localparam S_SD_NOTCHK = "SD: NOT CHECKED\015\012";
localparam S_SD_NOCARD = "SD: NO CARD\015\012";
localparam S_SD_ERROR = "SD: ERROR\015\012";
localparam S_SD_OK = "SD: OK\015\012";
localparam S_COPYING = "COPYING BOOT.BPUN TO TEST.TXT\015\012";
localparam S_COPY_DONE = "COPY DONE\015\012";
localparam S_ERR_NOTGT = "ERROR: TARGET FILE NOT ON CARD\015\012";
localparam S_ERR_SMALL = "ERROR: NO CONTIGUOUS FREE SPACE\015\012";
localparam S_ERR_WRITE = "ERROR: SD WRITE FAILED\015\012";
localparam S_BLK_WRITING = "WRITING PATTERN TO 1KW BLOCK 1\015\012";
localparam S_BLK_DONE = "BLOCK 1 UPDATED\015\012";
localparam S_ERR_RANGE = "ERROR: BLOCK OUT OF RANGE\015\012";
localparam S_HELP1 = "1 = LIST ROOT DIR + CARD/VOL/FREE MB INFO LINE\015\012";
localparam S_HELP2 = "2 = DUMP WHOLE FILE VIA 64 KB BUFFER, HEX + OCTAL\015\012";
`ifdef SDFAT_TEST_READONLY
// 3/4/6/7 are not in this build: an empty string prints nothing, so the
// help sequence below needs no second form
localparam S_HELP3 = 8'h00;
localparam S_HELP4 = 8'h00;
localparam S_HELP6 = 8'h00;
localparam S_HELP7 = 8'h00;
`else
localparam S_HELP3 = "3 = COPY THE FILE OVER TEST.TXT (IN PLACE)\015\012";
localparam S_HELP4 = "4 = WRITE WORDS 0-1023 TO 1KW BLOCK 1 OF THE FILE\015\012";
localparam S_HELP6 = "6 = WRITE SPEED: REWRITE IO.DAT, 1000 X 1KW BLOCKS\015\012";
localparam S_HELP7 = "7 = READ SPEED: READ IO.DAT BACK (RUN 6 FIRST)\015\012";
`endif
localparam S_HELP5 = "5 = VALIDATE EVERY FILE CLUSTER CHAIN (FSCK-LITE)\015\012";
localparam S_HELP8 = "8 = DUMP 1KW BLOCK N OF THE FILE, ANY FILE SIZE\015\012";
localparam S_HELP9 = "9 = DUMP ABSOLUTE SD SECTOR N, FAT NOT CONSULTED\015\012";
localparam S_HELP10 = "N = SET FILE NAME: ROOT DIR ONLY, 8.3 LENGTH EXACT\015\012";
localparam S_HELP11 = "LIST COLUMN 4 = FIRST ABSOLUTE SECTOR OF THE FILE\015\012";
localparam S_HELP12 = "BLOCK N IS AT FIRST SECTOR + 4*N (FILE CONTIGUOUS)\015\012";
localparam S_HELP13 = "R = READ A BLOCK RANGE, CHECKSUM ONLY, NO DUMP\015\012";
localparam S_ASK_BLK = "BLOCK NUMBER, DECIMAL, THEN CR: ";
localparam S_ASK_STA = "START BLOCK, DECIMAL, THEN CR: ";
localparam S_ASK_CNT = "BLOCK COUNT, DECIMAL, THEN CR: ";
// The range summary is printed in HEX throughout (each label is followed
// by 8 hex digits from the top level's hex printer): one number format
// for every field keeps the console reader honest, and CYCLES is a raw
// 27 MHz count anyway - seconds = CYCLES / 27000000.
localparam S_R_RUN = "READING BLOCK RANGE AT SECTOR ";
localparam S_R_AT = "AT BLOCK ";
localparam S_R_BLOCKS = "BLOCKS READ ";
localparam S_R_SECS = "SECTORS READ ";
localparam S_R_CHK = "CHECKSUM ";
localparam S_R_CYC = "CYCLES ";
localparam S_R_PASS = "RESULT: PASS\015\012";
localparam S_R_FAIL = "RESULT: FAIL\015\012";
localparam S_ASK_SEC = "ABSOLUTE SECTOR, DECIMAL, THEN CR: ";
localparam S_ASK_NAME = "FILE NAME, THEN CR: ";
localparam S_ERR_NUM = "ERROR: NO DIGITS ENTERED\015\012";
localparam S_AT_SEC = "AT SECTOR ";
localparam S_NAME_SET = "FILE NAME SET\015\012";
localparam S_ERR_IOSZ = "ERROR: IO.DAT WRONG SIZE - RUN 6 FIRST\015\012";
localparam S_IOW_RUN = "WRITING IO.DAT\015\012";
localparam S_IOR_RUN = "READING IO.DAT\015\012";
localparam S_SCANNING = "SCANNING FAT FOR FREE SPACE...\015\012";
localparam S_ERR_READ = "ERROR: SD READ FAILED\015\012";
localparam S_ERR_FATRD = "ERROR: FAT READ FAILED\015\012";
localparam S_ERR_FATCOR = "ERROR: FAT CHAIN CORRUPT\015\012";
// pick character idx (0-based) out of a string literal; 0 terminates
function [7:0] strchar(input [8*64-1:0] s, input integer len, input integer i);
strchar = (i < len) ? s[8*(len-1-i)+:8] : 8'h00;
endfunction
reg [5:0] msg_r;
reg [5:0] idx;
reg [7:0] ch;
always @(*) begin
case (msg_r)
MSG_BANNER: ch = strchar({8'b0, S_BANNER}, $bits(S_BANNER)/8, idx);
MSG_CARD_SDV1: ch = strchar({8'b0, S_CARD_SDV1}, $bits(S_CARD_SDV1)/8, idx);
MSG_CARD_SDV2: ch = strchar({8'b0, S_CARD_SDV2}, $bits(S_CARD_SDV2)/8, idx);
MSG_CARD_SDHC: ch = strchar({8'b0, S_CARD_SDHC}, $bits(S_CARD_SDHC)/8, idx);
MSG_FS_FAT16: ch = strchar({8'b0, S_FS_FAT16}, $bits(S_FS_FAT16)/8, idx);
MSG_FS_FAT32: ch = strchar({8'b0, S_FS_FAT32}, $bits(S_FS_FAT32)/8, idx);
MSG_FS_UNKNOWN: ch = strchar({8'b0, S_FS_UNKNOWN}, $bits(S_FS_UNKNOWN)/8, idx);
MSG_FILE_FOUND: ch = strchar({8'b0, S_FILE_FOUND}, $bits(S_FILE_FOUND)/8, idx);
MSG_FILE_NOTFND: ch = strchar({8'b0, S_FILE_NOTFND}, $bits(S_FILE_NOTFND)/8, idx);
MSG_ERR_CARD_TO: ch = strchar({8'b0, S_ERR_CARD_TO}, $bits(S_ERR_CARD_TO)/8, idx);
MSG_ERR_SCAN_TO: ch = strchar({8'b0, S_ERR_SCAN_TO}, $bits(S_ERR_SCAN_TO)/8, idx);
MSG_ERR_TRUNC: ch = strchar({8'b0, S_ERR_TRUNC}, $bits(S_ERR_TRUNC)/8, idx);
MSG_MENU: ch = strchar({8'b0, S_MENU}, $bits(S_MENU)/8, idx);
MSG_NOTIMPL: ch = strchar({8'b0, S_NOTIMPL}, $bits(S_NOTIMPL)/8, idx);
MSG_SD_NOTCHK: ch = strchar({8'b0, S_SD_NOTCHK}, $bits(S_SD_NOTCHK)/8, idx);
MSG_SD_NOCARD: ch = strchar({8'b0, S_SD_NOCARD}, $bits(S_SD_NOCARD)/8, idx);
MSG_SD_ERROR: ch = strchar({8'b0, S_SD_ERROR}, $bits(S_SD_ERROR)/8, idx);
MSG_SD_OK: ch = strchar({8'b0, S_SD_OK}, $bits(S_SD_OK)/8, idx);
MSG_COPYING: ch = strchar({8'b0, S_COPYING}, $bits(S_COPYING)/8, idx);
MSG_COPY_DONE: ch = strchar({8'b0, S_COPY_DONE}, $bits(S_COPY_DONE)/8, idx);
MSG_ERR_NOTGT: ch = strchar({8'b0, S_ERR_NOTGT}, $bits(S_ERR_NOTGT)/8, idx);
MSG_ERR_SMALL: ch = strchar({8'b0, S_ERR_SMALL}, $bits(S_ERR_SMALL)/8, idx);
MSG_ERR_WRITE: ch = strchar({8'b0, S_ERR_WRITE}, $bits(S_ERR_WRITE)/8, idx);
MSG_BLK_WRITING: ch = strchar({8'b0, S_BLK_WRITING}, $bits(S_BLK_WRITING)/8, idx);
MSG_BLK_DONE: ch = strchar({8'b0, S_BLK_DONE}, $bits(S_BLK_DONE)/8, idx);
MSG_ERR_RANGE: ch = strchar({8'b0, S_ERR_RANGE}, $bits(S_ERR_RANGE)/8, idx);
MSG_HELP1: ch = strchar({8'b0, S_HELP1}, $bits(S_HELP1)/8, idx);
MSG_HELP2: ch = strchar({8'b0, S_HELP2}, $bits(S_HELP2)/8, idx);
MSG_HELP3: ch = strchar({8'b0, S_HELP3}, $bits(S_HELP3)/8, idx);
MSG_HELP4: ch = strchar({8'b0, S_HELP4}, $bits(S_HELP4)/8, idx);
MSG_HELP5: ch = strchar({8'b0, S_HELP5}, $bits(S_HELP5)/8, idx);
MSG_HELP6: ch = strchar({8'b0, S_HELP6}, $bits(S_HELP6)/8, idx);
MSG_HELP7: ch = strchar({8'b0, S_HELP7}, $bits(S_HELP7)/8, idx);
MSG_ERR_IOSZ: ch = strchar({8'b0, S_ERR_IOSZ}, $bits(S_ERR_IOSZ)/8, idx);
MSG_IOW_RUN: ch = strchar({8'b0, S_IOW_RUN}, $bits(S_IOW_RUN)/8, idx);
MSG_IOR_RUN: ch = strchar({8'b0, S_IOR_RUN}, $bits(S_IOR_RUN)/8, idx);
MSG_SCANNING: ch = strchar({8'b0, S_SCANNING}, $bits(S_SCANNING)/8, idx);
MSG_ERR_READ: ch = strchar({8'b0, S_ERR_READ}, $bits(S_ERR_READ)/8, idx);
MSG_ERR_FATRD: ch = strchar({8'b0, S_ERR_FATRD}, $bits(S_ERR_FATRD)/8, idx);
MSG_ERR_FATCOR: ch = strchar({8'b0, S_ERR_FATCOR}, $bits(S_ERR_FATCOR)/8, idx);
MSG_MENU2: ch = strchar({8'b0, S_MENU2}, $bits(S_MENU2)/8, idx);
MSG_ASK_BLK: ch = strchar({8'b0, S_ASK_BLK}, $bits(S_ASK_BLK)/8, idx);
MSG_ASK_SEC: ch = strchar({8'b0, S_ASK_SEC}, $bits(S_ASK_SEC)/8, idx);
MSG_ASK_NAME: ch = strchar({8'b0, S_ASK_NAME}, $bits(S_ASK_NAME)/8, idx);
MSG_ERR_NUM: ch = strchar({8'b0, S_ERR_NUM}, $bits(S_ERR_NUM)/8, idx);
MSG_AT_SEC: ch = strchar({8'b0, S_AT_SEC}, $bits(S_AT_SEC)/8, idx);
MSG_NAME_SET: ch = strchar({8'b0, S_NAME_SET}, $bits(S_NAME_SET)/8, idx);
MSG_HELP8: ch = strchar({8'b0, S_HELP8}, $bits(S_HELP8)/8, idx);
MSG_HELP9: ch = strchar({8'b0, S_HELP9}, $bits(S_HELP9)/8, idx);
MSG_HELP10: ch = strchar({8'b0, S_HELP10}, $bits(S_HELP10)/8, idx);
MSG_HELP11: ch = strchar({8'b0, S_HELP11}, $bits(S_HELP11)/8, idx);
MSG_HELP12: ch = strchar({8'b0, S_HELP12}, $bits(S_HELP12)/8, idx);
MSG_HELP13: ch = strchar({8'b0, S_HELP13}, $bits(S_HELP13)/8, idx);
MSG_ASK_STA: ch = strchar({8'b0, S_ASK_STA}, $bits(S_ASK_STA)/8, idx);
MSG_ASK_CNT: ch = strchar({8'b0, S_ASK_CNT}, $bits(S_ASK_CNT)/8, idx);
MSG_R_RUN: ch = strchar({8'b0, S_R_RUN}, $bits(S_R_RUN)/8, idx);
MSG_R_AT: ch = strchar({8'b0, S_R_AT}, $bits(S_R_AT)/8, idx);
MSG_R_BLOCKS: ch = strchar({8'b0, S_R_BLOCKS}, $bits(S_R_BLOCKS)/8, idx);
MSG_R_SECS: ch = strchar({8'b0, S_R_SECS}, $bits(S_R_SECS)/8, idx);
MSG_R_CHK: ch = strchar({8'b0, S_R_CHK}, $bits(S_R_CHK)/8, idx);
MSG_R_CYC: ch = strchar({8'b0, S_R_CYC}, $bits(S_R_CYC)/8, idx);
MSG_R_PASS: ch = strchar({8'b0, S_R_PASS}, $bits(S_R_PASS)/8, idx);
MSG_R_FAIL: ch = strchar({8'b0, S_R_FAIL}, $bits(S_R_FAIL)/8, idx);
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;
assign busy = (pstate != P_IDLE);
always @(posedge clk) begin
if (!rst_n) begin
pstate <= P_IDLE;
tx_valid <= 1'b0;
tx_data <= 0;
idx <= 0;
msg_r <= MSG_BANNER;
end else begin
tx_valid <= 1'b0;
case (pstate)
P_IDLE:
if (start) begin
msg_r <= msg;
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_data <= ch;
idx <= idx + 1;
pstate <= P_GAP;
end
P_GAP: pstate <= P_SEND;
default: pstate <= P_IDLE;
endcase
end
end
endmodule