Initial commit: LVDS Protocol Analyser v1.0.0
This commit is contained in:
615
top.v
Normal file
615
top.v
Normal file
@@ -0,0 +1,615 @@
|
||||
// top.v
|
||||
//
|
||||
// LVDS FlatLink monitor top level.
|
||||
//
|
||||
// Inputs:
|
||||
// clk_50mhz - onboard oscillator
|
||||
// rx_clk - DS90CF386 RxCLKOUT (~72 MHz pixel clock)
|
||||
// de - DS90CF386 RxOUT24
|
||||
// vsync - DS90CF386 RxOUT25 (currently unused, brought in for future use)
|
||||
// hsync - DS90CF386 RxOUT26 (currently unused, brought in for future use)
|
||||
// rst_n_pin - optional external reset (tie high if not used)
|
||||
//
|
||||
// Output:
|
||||
// uart_tx_pin - to CH340 RX
|
||||
//
|
||||
// Frame reports:
|
||||
// - On a clean frame, sends "OK\n" once every HEARTBEAT_FRAMES frames.
|
||||
// - On an anomalous frame, immediately sends
|
||||
// "ERR lines=NNNN width=NNNN\n"
|
||||
// where NNNN are zero-padded 4-digit decimals.
|
||||
|
||||
module top (
|
||||
input wire clk_50mhz,
|
||||
input wire rx_clk,
|
||||
input wire de,
|
||||
input wire vsync,
|
||||
input wire hsync,
|
||||
input wire rst_n_pin,
|
||||
output wire uart_tx_pin
|
||||
);
|
||||
|
||||
// unused for now — keep ports alive so pin assignments stick
|
||||
wire _unused = &{1'b0, vsync, hsync, 1'b0};
|
||||
|
||||
localparam integer HEARTBEAT_FRAMES = 60;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Clocking: UART domain runs directly off the 50 MHz oscillator.
|
||||
// ----------------------------------------------------------------
|
||||
wire clk_uart = clk_50mhz;
|
||||
wire pll_locked = 1'b1;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Reset synchronisers
|
||||
// ----------------------------------------------------------------
|
||||
reg [2:0] rst_sync_uart;
|
||||
reg [2:0] rst_sync_pix;
|
||||
|
||||
always @(posedge clk_uart or negedge rst_n_pin) begin
|
||||
if (!rst_n_pin) rst_sync_uart <= 3'b000;
|
||||
else rst_sync_uart <= {rst_sync_uart[1:0], pll_locked};
|
||||
end
|
||||
wire rst_n_uart = rst_sync_uart[2];
|
||||
|
||||
always @(posedge rx_clk or negedge rst_n_pin) begin
|
||||
if (!rst_n_pin) rst_sync_pix <= 3'b000;
|
||||
else rst_sync_pix <= {rst_sync_pix[1:0], 1'b1};
|
||||
end
|
||||
wire rst_n_pix = rst_sync_pix[2];
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// DE monitor in pixel-clock domain
|
||||
// ----------------------------------------------------------------
|
||||
wire frame_done_pix;
|
||||
wire [15:0] lines_pix;
|
||||
wire [15:0] width_pix;
|
||||
wire anomaly_pix;
|
||||
|
||||
de_monitor #(
|
||||
.EXPECTED_LINES (16'd800),
|
||||
.EXPECTED_WIDTH (16'd1280),
|
||||
.FRAME_GAP_PIX (16'd2048)
|
||||
) u_mon (
|
||||
.pix_clk (rx_clk),
|
||||
.rst_n (rst_n_pix),
|
||||
.de (de),
|
||||
.frame_done (frame_done_pix),
|
||||
.lines_o (lines_pix),
|
||||
.width_o (width_pix),
|
||||
.anomaly_o (anomaly_pix)
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Pixel-domain: latch report fields + heartbeat, toggle req
|
||||
// ----------------------------------------------------------------
|
||||
reg [15:0] lines_lat;
|
||||
reg [15:0] width_lat;
|
||||
reg anomaly_lat;
|
||||
reg heartbeat_lat;
|
||||
reg req_tog;
|
||||
reg [7:0] hb_count; // up to 255 — plenty for HEARTBEAT_FRAMES=60
|
||||
|
||||
always @(posedge rx_clk or negedge rst_n_pix) begin
|
||||
if (!rst_n_pix) begin
|
||||
lines_lat <= 16'd0;
|
||||
width_lat <= 16'd0;
|
||||
anomaly_lat <= 1'b0;
|
||||
heartbeat_lat <= 1'b0;
|
||||
req_tog <= 1'b0;
|
||||
hb_count <= 8'd0;
|
||||
end else if (frame_done_pix) begin
|
||||
lines_lat <= lines_pix;
|
||||
width_lat <= width_pix;
|
||||
anomaly_lat <= anomaly_pix;
|
||||
|
||||
if (hb_count == HEARTBEAT_FRAMES - 1) begin
|
||||
hb_count <= 8'd0;
|
||||
heartbeat_lat <= 1'b1;
|
||||
end else begin
|
||||
hb_count <= hb_count + 8'd1;
|
||||
heartbeat_lat <= 1'b0;
|
||||
end
|
||||
req_tog <= ~req_tog;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// CDC: sync req toggle into UART domain, edge-detect
|
||||
// ----------------------------------------------------------------
|
||||
reg [2:0] req_sync;
|
||||
always @(posedge clk_uart or negedge rst_n_uart) begin
|
||||
if (!rst_n_uart) req_sync <= 3'b000;
|
||||
else req_sync <= {req_sync[1:0], req_tog};
|
||||
end
|
||||
wire req_edge = req_sync[2] ^ req_sync[1];
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// UART-domain: decimal conversion + report FSM
|
||||
// ----------------------------------------------------------------
|
||||
reg [15:0] lines_u;
|
||||
reg [15:0] width_u;
|
||||
reg anomaly_u;
|
||||
reg heartbeat_u;
|
||||
|
||||
// BCD digit registers — computed in F_CONVERT before transmission
|
||||
reg [3:0] L0_r, L1_r, L2_r, L3_r;
|
||||
reg [3:0] W0_r, W1_r, W2_r, W3_r;
|
||||
reg [1:0] conv_step;
|
||||
reg [15:0] l_rem, w_rem;
|
||||
|
||||
// Sample latched fields when req_edge fires. Data is stable in
|
||||
// pix domain until the next frame_done (~16 ms), far longer than we
|
||||
// need for the handful of µs of UART setup.
|
||||
always @(posedge clk_uart or negedge rst_n_uart) begin
|
||||
if (!rst_n_uart) begin
|
||||
lines_u <= 16'd0;
|
||||
width_u <= 16'd0;
|
||||
anomaly_u <= 1'b0;
|
||||
heartbeat_u <= 1'b0;
|
||||
end else if (req_edge) begin
|
||||
lines_u <= lines_lat;
|
||||
width_u <= width_lat;
|
||||
anomaly_u <= anomaly_lat;
|
||||
heartbeat_u <= heartbeat_lat;
|
||||
end
|
||||
end
|
||||
|
||||
// FSM that emits the message byte-by-byte.
|
||||
localparam [2:0] F_IDLE = 3'd0,
|
||||
F_CONVERT = 3'd1,
|
||||
F_LOAD = 3'd2,
|
||||
F_WAIT = 3'd3,
|
||||
F_BANNER = 3'd4;
|
||||
|
||||
// Banner: ESC[2J ESC[H ESC[33m + 2 separator lines + 6 info lines + ESC[0m
|
||||
localparam [8:0] BANNER_LEN = 9'd288;
|
||||
|
||||
reg [2:0] fstate;
|
||||
reg [8:0] idx; // byte index within message
|
||||
reg [8:0] msg_len;
|
||||
reg is_banner;
|
||||
reg is_err_msg;
|
||||
reg tx_start;
|
||||
reg [7:0] tx_byte;
|
||||
wire tx_busy;
|
||||
|
||||
// ERR layout: ESC[31m + "ERR lines=LLLL width=WWWW\n" + ESC[0m (35 bytes)
|
||||
function [7:0] err_char(input [7:0] i);
|
||||
case (i)
|
||||
8'd0: err_char = 8'h1B;
|
||||
8'd1: err_char = "[";
|
||||
8'd2: err_char = "3";
|
||||
8'd3: err_char = "1";
|
||||
8'd4: err_char = "m";
|
||||
8'd5: err_char = "E";
|
||||
8'd6: err_char = "R";
|
||||
8'd7: err_char = "R";
|
||||
8'd8: err_char = " ";
|
||||
8'd9: err_char = "l";
|
||||
8'd10: err_char = "i";
|
||||
8'd11: err_char = "n";
|
||||
8'd12: err_char = "e";
|
||||
8'd13: err_char = "s";
|
||||
8'd14: err_char = "=";
|
||||
8'd15: err_char = "0" + L0_r;
|
||||
8'd16: err_char = "0" + L1_r;
|
||||
8'd17: err_char = "0" + L2_r;
|
||||
8'd18: err_char = "0" + L3_r;
|
||||
8'd19: err_char = " ";
|
||||
8'd20: err_char = "w";
|
||||
8'd21: err_char = "i";
|
||||
8'd22: err_char = "d";
|
||||
8'd23: err_char = "t";
|
||||
8'd24: err_char = "h";
|
||||
8'd25: err_char = "=";
|
||||
8'd26: err_char = "0" + W0_r;
|
||||
8'd27: err_char = "0" + W1_r;
|
||||
8'd28: err_char = "0" + W2_r;
|
||||
8'd29: err_char = "0" + W3_r;
|
||||
8'd30: err_char = 8'h0A;
|
||||
8'd31: err_char = 8'h1B;
|
||||
8'd32: err_char = "[";
|
||||
8'd33: err_char = "0";
|
||||
8'd34: err_char = "m";
|
||||
default: err_char = 8'h00;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
// OK layout: ESC[32m + "OK\n" + ESC[0m (12 bytes)
|
||||
function [7:0] ok_char(input [7:0] i);
|
||||
case (i)
|
||||
8'd0: ok_char = 8'h1B;
|
||||
8'd1: ok_char = "[";
|
||||
8'd2: ok_char = "3";
|
||||
8'd3: ok_char = "2";
|
||||
8'd4: ok_char = "m";
|
||||
8'd5: ok_char = "O";
|
||||
8'd6: ok_char = "K";
|
||||
8'd7: ok_char = 8'h0A;
|
||||
8'd8: ok_char = 8'h1B;
|
||||
8'd9: ok_char = "[";
|
||||
8'd10: ok_char = "0";
|
||||
8'd11: ok_char = "m";
|
||||
default: ok_char = 8'h00;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
// Banner: ESC[2J ESC[H ESC[33m + separator + info lines + separator + ESC[0m
|
||||
// Indices 12-55 and 239-282 are '-' (handled by range check).
|
||||
// Total: 288 bytes (indices 0-287).
|
||||
function [7:0] banner_char(input [8:0] i);
|
||||
if ((i >= 9'd12 && i <= 9'd55) || (i >= 9'd239 && i <= 9'd282))
|
||||
banner_char = "-";
|
||||
else begin
|
||||
case (i)
|
||||
// ESC[2J
|
||||
9'd0: banner_char = 8'h1B;
|
||||
9'd1: banner_char = "[";
|
||||
9'd2: banner_char = "2";
|
||||
9'd3: banner_char = "J";
|
||||
// ESC[H
|
||||
9'd4: banner_char = 8'h1B;
|
||||
9'd5: banner_char = "[";
|
||||
9'd6: banner_char = "H";
|
||||
// ESC[33m
|
||||
9'd7: banner_char = 8'h1B;
|
||||
9'd8: banner_char = "[";
|
||||
9'd9: banner_char = "3";
|
||||
9'd10: banner_char = "3";
|
||||
9'd11: banner_char = "m";
|
||||
// first separator \n
|
||||
9'd56: banner_char = 8'h0A;
|
||||
// "Arrive LVDS Protocol Analyser\n"
|
||||
9'd57: banner_char = "A";
|
||||
9'd58: banner_char = "r";
|
||||
9'd59: banner_char = "r";
|
||||
9'd60: banner_char = "i";
|
||||
9'd61: banner_char = "v";
|
||||
9'd62: banner_char = "e";
|
||||
9'd63: banner_char = " ";
|
||||
9'd64: banner_char = "L";
|
||||
9'd65: banner_char = "V";
|
||||
9'd66: banner_char = "D";
|
||||
9'd67: banner_char = "S";
|
||||
9'd68: banner_char = " ";
|
||||
9'd69: banner_char = "P";
|
||||
9'd70: banner_char = "r";
|
||||
9'd71: banner_char = "o";
|
||||
9'd72: banner_char = "t";
|
||||
9'd73: banner_char = "o";
|
||||
9'd74: banner_char = "c";
|
||||
9'd75: banner_char = "o";
|
||||
9'd76: banner_char = "l";
|
||||
9'd77: banner_char = " ";
|
||||
9'd78: banner_char = "A";
|
||||
9'd79: banner_char = "n";
|
||||
9'd80: banner_char = "a";
|
||||
9'd81: banner_char = "l";
|
||||
9'd82: banner_char = "y";
|
||||
9'd83: banner_char = "s";
|
||||
9'd84: banner_char = "e";
|
||||
9'd85: banner_char = "r";
|
||||
9'd86: banner_char = 8'h0A;
|
||||
// "Board: DS90CF386 Interface S/N: 1000052088 Rev A\n"
|
||||
9'd87: banner_char = "B";
|
||||
9'd88: banner_char = "o";
|
||||
9'd89: banner_char = "a";
|
||||
9'd90: banner_char = "r";
|
||||
9'd91: banner_char = "d";
|
||||
9'd92: banner_char = ":";
|
||||
9'd93: banner_char = " ";
|
||||
9'd94: banner_char = "D";
|
||||
9'd95: banner_char = "S";
|
||||
9'd96: banner_char = "9";
|
||||
9'd97: banner_char = "0";
|
||||
9'd98: banner_char = "C";
|
||||
9'd99: banner_char = "F";
|
||||
9'd100: banner_char = "3";
|
||||
9'd101: banner_char = "8";
|
||||
9'd102: banner_char = "6";
|
||||
9'd103: banner_char = " ";
|
||||
9'd104: banner_char = "I";
|
||||
9'd105: banner_char = "n";
|
||||
9'd106: banner_char = "t";
|
||||
9'd107: banner_char = "e";
|
||||
9'd108: banner_char = "r";
|
||||
9'd109: banner_char = "f";
|
||||
9'd110: banner_char = "a";
|
||||
9'd111: banner_char = "c";
|
||||
9'd112: banner_char = "e";
|
||||
9'd113: banner_char = " ";
|
||||
9'd114: banner_char = " ";
|
||||
9'd115: banner_char = "S";
|
||||
9'd116: banner_char = "/";
|
||||
9'd117: banner_char = "N";
|
||||
9'd118: banner_char = ":";
|
||||
9'd119: banner_char = " ";
|
||||
9'd120: banner_char = "1";
|
||||
9'd121: banner_char = "0";
|
||||
9'd122: banner_char = "0";
|
||||
9'd123: banner_char = "0";
|
||||
9'd124: banner_char = "0";
|
||||
9'd125: banner_char = "5";
|
||||
9'd126: banner_char = "2";
|
||||
9'd127: banner_char = "0";
|
||||
9'd128: banner_char = "8";
|
||||
9'd129: banner_char = "8";
|
||||
9'd130: banner_char = " ";
|
||||
9'd131: banner_char = "R";
|
||||
9'd132: banner_char = "e";
|
||||
9'd133: banner_char = "v";
|
||||
9'd134: banner_char = " ";
|
||||
9'd135: banner_char = "A";
|
||||
9'd136: banner_char = 8'h0A;
|
||||
// "Author: David Rice\n"
|
||||
9'd137: banner_char = "A";
|
||||
9'd138: banner_char = "u";
|
||||
9'd139: banner_char = "t";
|
||||
9'd140: banner_char = "h";
|
||||
9'd141: banner_char = "o";
|
||||
9'd142: banner_char = "r";
|
||||
9'd143: banner_char = ":";
|
||||
9'd144: banner_char = " ";
|
||||
9'd145: banner_char = "D";
|
||||
9'd146: banner_char = "a";
|
||||
9'd147: banner_char = "v";
|
||||
9'd148: banner_char = "i";
|
||||
9'd149: banner_char = "d";
|
||||
9'd150: banner_char = " ";
|
||||
9'd151: banner_char = "R";
|
||||
9'd152: banner_char = "i";
|
||||
9'd153: banner_char = "c";
|
||||
9'd154: banner_char = "e";
|
||||
9'd155: banner_char = 8'h0A;
|
||||
// "Role: Senior Electronics Engineer\n"
|
||||
9'd156: banner_char = "R";
|
||||
9'd157: banner_char = "o";
|
||||
9'd158: banner_char = "l";
|
||||
9'd159: banner_char = "e";
|
||||
9'd160: banner_char = ":";
|
||||
9'd161: banner_char = " ";
|
||||
9'd162: banner_char = "S";
|
||||
9'd163: banner_char = "e";
|
||||
9'd164: banner_char = "n";
|
||||
9'd165: banner_char = "i";
|
||||
9'd166: banner_char = "o";
|
||||
9'd167: banner_char = "r";
|
||||
9'd168: banner_char = " ";
|
||||
9'd169: banner_char = "E";
|
||||
9'd170: banner_char = "l";
|
||||
9'd171: banner_char = "e";
|
||||
9'd172: banner_char = "c";
|
||||
9'd173: banner_char = "t";
|
||||
9'd174: banner_char = "r";
|
||||
9'd175: banner_char = "o";
|
||||
9'd176: banner_char = "n";
|
||||
9'd177: banner_char = "i";
|
||||
9'd178: banner_char = "c";
|
||||
9'd179: banner_char = "s";
|
||||
9'd180: banner_char = " ";
|
||||
9'd181: banner_char = "E";
|
||||
9'd182: banner_char = "n";
|
||||
9'd183: banner_char = "g";
|
||||
9'd184: banner_char = "i";
|
||||
9'd185: banner_char = "n";
|
||||
9'd186: banner_char = "e";
|
||||
9'd187: banner_char = "e";
|
||||
9'd188: banner_char = "r";
|
||||
9'd189: banner_char = 8'h0A;
|
||||
// "Email: david.rice@arrive.com\n"
|
||||
9'd190: banner_char = "E";
|
||||
9'd191: banner_char = "m";
|
||||
9'd192: banner_char = "a";
|
||||
9'd193: banner_char = "i";
|
||||
9'd194: banner_char = "l";
|
||||
9'd195: banner_char = ":";
|
||||
9'd196: banner_char = " ";
|
||||
9'd197: banner_char = "d";
|
||||
9'd198: banner_char = "a";
|
||||
9'd199: banner_char = "v";
|
||||
9'd200: banner_char = "i";
|
||||
9'd201: banner_char = "d";
|
||||
9'd202: banner_char = ".";
|
||||
9'd203: banner_char = "r";
|
||||
9'd204: banner_char = "i";
|
||||
9'd205: banner_char = "c";
|
||||
9'd206: banner_char = "e";
|
||||
9'd207: banner_char = "@";
|
||||
9'd208: banner_char = "a";
|
||||
9'd209: banner_char = "r";
|
||||
9'd210: banner_char = "r";
|
||||
9'd211: banner_char = "i";
|
||||
9'd212: banner_char = "v";
|
||||
9'd213: banner_char = "e";
|
||||
9'd214: banner_char = ".";
|
||||
9'd215: banner_char = "c";
|
||||
9'd216: banner_char = "o";
|
||||
9'd217: banner_char = "m";
|
||||
9'd218: banner_char = 8'h0A;
|
||||
// "Software Rev: 1.0.0\n"
|
||||
9'd219: banner_char = "S";
|
||||
9'd220: banner_char = "o";
|
||||
9'd221: banner_char = "f";
|
||||
9'd222: banner_char = "t";
|
||||
9'd223: banner_char = "w";
|
||||
9'd224: banner_char = "a";
|
||||
9'd225: banner_char = "r";
|
||||
9'd226: banner_char = "e";
|
||||
9'd227: banner_char = " ";
|
||||
9'd228: banner_char = "R";
|
||||
9'd229: banner_char = "e";
|
||||
9'd230: banner_char = "v";
|
||||
9'd231: banner_char = ":";
|
||||
9'd232: banner_char = " ";
|
||||
9'd233: banner_char = "1";
|
||||
9'd234: banner_char = ".";
|
||||
9'd235: banner_char = "0";
|
||||
9'd236: banner_char = ".";
|
||||
9'd237: banner_char = "0";
|
||||
9'd238: banner_char = 8'h0A;
|
||||
// second separator \n
|
||||
9'd283: banner_char = 8'h0A;
|
||||
// ESC[0m
|
||||
9'd284: banner_char = 8'h1B;
|
||||
9'd285: banner_char = "[";
|
||||
9'd286: banner_char = "0";
|
||||
9'd287: banner_char = "m";
|
||||
default: banner_char = 8'h00;
|
||||
endcase
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Trigger: one cycle after req_edge (so lines_u/width_u are updated).
|
||||
reg req_edge_q;
|
||||
always @(posedge clk_uart or negedge rst_n_uart) begin
|
||||
if (!rst_n_uart) req_edge_q <= 1'b0;
|
||||
else req_edge_q <= req_edge;
|
||||
end
|
||||
|
||||
always @(posedge clk_uart or negedge rst_n_uart) begin
|
||||
if (!rst_n_uart) begin
|
||||
fstate <= F_BANNER;
|
||||
idx <= 9'd0;
|
||||
msg_len <= BANNER_LEN;
|
||||
is_banner <= 1'b1;
|
||||
is_err_msg <= 1'b0;
|
||||
tx_start <= 1'b0;
|
||||
tx_byte <= 8'd0;
|
||||
conv_step <= 2'd0;
|
||||
l_rem <= 16'd0;
|
||||
w_rem <= 16'd0;
|
||||
L0_r <= 4'd0; L1_r <= 4'd0; L2_r <= 4'd0; L3_r <= 4'd0;
|
||||
W0_r <= 4'd0; W1_r <= 4'd0; W2_r <= 4'd0; W3_r <= 4'd0;
|
||||
end else begin
|
||||
tx_start <= 1'b0;
|
||||
case (fstate)
|
||||
F_BANNER: begin
|
||||
// idx/msg_len/is_banner already set in reset block; kick off load
|
||||
fstate <= F_LOAD;
|
||||
end
|
||||
F_IDLE: begin
|
||||
is_banner <= 1'b0;
|
||||
if (req_edge_q && (anomaly_u || heartbeat_u)) begin
|
||||
idx <= 9'd0;
|
||||
is_err_msg <= anomaly_u;
|
||||
msg_len <= anomaly_u ? 9'd35 : 9'd12;
|
||||
l_rem <= lines_u;
|
||||
w_rem <= width_u;
|
||||
conv_step <= 2'd0;
|
||||
fstate <= F_CONVERT;
|
||||
end
|
||||
end
|
||||
F_CONVERT: begin
|
||||
conv_step <= conv_step + 2'd1;
|
||||
case (conv_step)
|
||||
2'd0: begin // thousands
|
||||
if (l_rem >= 16'd9000) begin L0_r <= 4'd9; l_rem <= l_rem - 16'd9000; end
|
||||
else if (l_rem >= 16'd8000) begin L0_r <= 4'd8; l_rem <= l_rem - 16'd8000; end
|
||||
else if (l_rem >= 16'd7000) begin L0_r <= 4'd7; l_rem <= l_rem - 16'd7000; end
|
||||
else if (l_rem >= 16'd6000) begin L0_r <= 4'd6; l_rem <= l_rem - 16'd6000; end
|
||||
else if (l_rem >= 16'd5000) begin L0_r <= 4'd5; l_rem <= l_rem - 16'd5000; end
|
||||
else if (l_rem >= 16'd4000) begin L0_r <= 4'd4; l_rem <= l_rem - 16'd4000; end
|
||||
else if (l_rem >= 16'd3000) begin L0_r <= 4'd3; l_rem <= l_rem - 16'd3000; end
|
||||
else if (l_rem >= 16'd2000) begin L0_r <= 4'd2; l_rem <= l_rem - 16'd2000; end
|
||||
else if (l_rem >= 16'd1000) begin L0_r <= 4'd1; l_rem <= l_rem - 16'd1000; end
|
||||
else L0_r <= 4'd0;
|
||||
if (w_rem >= 16'd9000) begin W0_r <= 4'd9; w_rem <= w_rem - 16'd9000; end
|
||||
else if (w_rem >= 16'd8000) begin W0_r <= 4'd8; w_rem <= w_rem - 16'd8000; end
|
||||
else if (w_rem >= 16'd7000) begin W0_r <= 4'd7; w_rem <= w_rem - 16'd7000; end
|
||||
else if (w_rem >= 16'd6000) begin W0_r <= 4'd6; w_rem <= w_rem - 16'd6000; end
|
||||
else if (w_rem >= 16'd5000) begin W0_r <= 4'd5; w_rem <= w_rem - 16'd5000; end
|
||||
else if (w_rem >= 16'd4000) begin W0_r <= 4'd4; w_rem <= w_rem - 16'd4000; end
|
||||
else if (w_rem >= 16'd3000) begin W0_r <= 4'd3; w_rem <= w_rem - 16'd3000; end
|
||||
else if (w_rem >= 16'd2000) begin W0_r <= 4'd2; w_rem <= w_rem - 16'd2000; end
|
||||
else if (w_rem >= 16'd1000) begin W0_r <= 4'd1; w_rem <= w_rem - 16'd1000; end
|
||||
else W0_r <= 4'd0;
|
||||
end
|
||||
2'd1: begin // hundreds
|
||||
if (l_rem >= 16'd900) begin L1_r <= 4'd9; l_rem <= l_rem - 16'd900; end
|
||||
else if (l_rem >= 16'd800) begin L1_r <= 4'd8; l_rem <= l_rem - 16'd800; end
|
||||
else if (l_rem >= 16'd700) begin L1_r <= 4'd7; l_rem <= l_rem - 16'd700; end
|
||||
else if (l_rem >= 16'd600) begin L1_r <= 4'd6; l_rem <= l_rem - 16'd600; end
|
||||
else if (l_rem >= 16'd500) begin L1_r <= 4'd5; l_rem <= l_rem - 16'd500; end
|
||||
else if (l_rem >= 16'd400) begin L1_r <= 4'd4; l_rem <= l_rem - 16'd400; end
|
||||
else if (l_rem >= 16'd300) begin L1_r <= 4'd3; l_rem <= l_rem - 16'd300; end
|
||||
else if (l_rem >= 16'd200) begin L1_r <= 4'd2; l_rem <= l_rem - 16'd200; end
|
||||
else if (l_rem >= 16'd100) begin L1_r <= 4'd1; l_rem <= l_rem - 16'd100; end
|
||||
else L1_r <= 4'd0;
|
||||
if (w_rem >= 16'd900) begin W1_r <= 4'd9; w_rem <= w_rem - 16'd900; end
|
||||
else if (w_rem >= 16'd800) begin W1_r <= 4'd8; w_rem <= w_rem - 16'd800; end
|
||||
else if (w_rem >= 16'd700) begin W1_r <= 4'd7; w_rem <= w_rem - 16'd700; end
|
||||
else if (w_rem >= 16'd600) begin W1_r <= 4'd6; w_rem <= w_rem - 16'd600; end
|
||||
else if (w_rem >= 16'd500) begin W1_r <= 4'd5; w_rem <= w_rem - 16'd500; end
|
||||
else if (w_rem >= 16'd400) begin W1_r <= 4'd4; w_rem <= w_rem - 16'd400; end
|
||||
else if (w_rem >= 16'd300) begin W1_r <= 4'd3; w_rem <= w_rem - 16'd300; end
|
||||
else if (w_rem >= 16'd200) begin W1_r <= 4'd2; w_rem <= w_rem - 16'd200; end
|
||||
else if (w_rem >= 16'd100) begin W1_r <= 4'd1; w_rem <= w_rem - 16'd100; end
|
||||
else W1_r <= 4'd0;
|
||||
end
|
||||
2'd2: begin // tens
|
||||
if (l_rem >= 16'd90) begin L2_r <= 4'd9; l_rem <= l_rem - 16'd90; end
|
||||
else if (l_rem >= 16'd80) begin L2_r <= 4'd8; l_rem <= l_rem - 16'd80; end
|
||||
else if (l_rem >= 16'd70) begin L2_r <= 4'd7; l_rem <= l_rem - 16'd70; end
|
||||
else if (l_rem >= 16'd60) begin L2_r <= 4'd6; l_rem <= l_rem - 16'd60; end
|
||||
else if (l_rem >= 16'd50) begin L2_r <= 4'd5; l_rem <= l_rem - 16'd50; end
|
||||
else if (l_rem >= 16'd40) begin L2_r <= 4'd4; l_rem <= l_rem - 16'd40; end
|
||||
else if (l_rem >= 16'd30) begin L2_r <= 4'd3; l_rem <= l_rem - 16'd30; end
|
||||
else if (l_rem >= 16'd20) begin L2_r <= 4'd2; l_rem <= l_rem - 16'd20; end
|
||||
else if (l_rem >= 16'd10) begin L2_r <= 4'd1; l_rem <= l_rem - 16'd10; end
|
||||
else L2_r <= 4'd0;
|
||||
if (w_rem >= 16'd90) begin W2_r <= 4'd9; w_rem <= w_rem - 16'd90; end
|
||||
else if (w_rem >= 16'd80) begin W2_r <= 4'd8; w_rem <= w_rem - 16'd80; end
|
||||
else if (w_rem >= 16'd70) begin W2_r <= 4'd7; w_rem <= w_rem - 16'd70; end
|
||||
else if (w_rem >= 16'd60) begin W2_r <= 4'd6; w_rem <= w_rem - 16'd60; end
|
||||
else if (w_rem >= 16'd50) begin W2_r <= 4'd5; w_rem <= w_rem - 16'd50; end
|
||||
else if (w_rem >= 16'd40) begin W2_r <= 4'd4; w_rem <= w_rem - 16'd40; end
|
||||
else if (w_rem >= 16'd30) begin W2_r <= 4'd3; w_rem <= w_rem - 16'd30; end
|
||||
else if (w_rem >= 16'd20) begin W2_r <= 4'd2; w_rem <= w_rem - 16'd20; end
|
||||
else if (w_rem >= 16'd10) begin W2_r <= 4'd1; w_rem <= w_rem - 16'd10; end
|
||||
else W2_r <= 4'd0;
|
||||
end
|
||||
2'd3: begin // units — remainder is 0-9 by construction
|
||||
L3_r <= l_rem[3:0];
|
||||
W3_r <= w_rem[3:0];
|
||||
fstate <= F_LOAD;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
F_LOAD: begin
|
||||
if (!tx_busy) begin
|
||||
tx_byte <= is_banner ? banner_char(idx) :
|
||||
is_err_msg ? err_char(idx) : ok_char(idx);
|
||||
tx_start <= 1'b1;
|
||||
fstate <= F_WAIT;
|
||||
end
|
||||
end
|
||||
F_WAIT: begin
|
||||
if (tx_busy == 1'b0 && tx_start == 1'b0) begin
|
||||
// byte fully sent (uart drops busy at stop-bit end)
|
||||
if (idx == msg_len - 9'd1) begin
|
||||
fstate <= F_IDLE;
|
||||
end else begin
|
||||
idx <= idx + 9'd1;
|
||||
fstate <= F_LOAD;
|
||||
end
|
||||
end
|
||||
end
|
||||
default: fstate <= F_IDLE;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
uart_tx #(
|
||||
.CLK_HZ (50_000_000),
|
||||
.BAUD (115_200)
|
||||
) u_uart (
|
||||
.clk (clk_uart),
|
||||
.rst_n (rst_n_uart),
|
||||
.start (tx_start),
|
||||
.data (tx_byte),
|
||||
.tx (uart_tx_pin),
|
||||
.busy (tx_busy)
|
||||
);
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user