fix: UART bug (retransmit) + auto-start + LED heartbeat + sim verification

Blitter6502OISC_small.v:
- Added uart_sent flag to prevent UART retransmission
- Verified via Verilator: UART sends exactly one byte on halt
- Test byte: 0xAA (recognizable pattern)

research_stack_top.v:
- Auto-start logic (100ms after reset, no button needed)
- LED shows heartbeat when CPU running, register values on halt

research_stack_tangnano9k.cst:
- uart_tx=17, uart_rx=18 (matches Sparkle reference design)

Simulation results (Verilator):
- uart_test.v: 115 bytes in 300K cycles (continuous TX verified)
- research_stack_top: UART fires after Blitter halt, 0xAA byte sent
- LED pattern changes from IDLE to RUNNING to HALTED
This commit is contained in:
Brandon Schneider 2026-05-28 21:42:11 -05:00
parent dbf67ab130
commit 270ef14e21
4 changed files with 188 additions and 11 deletions

View file

@ -227,11 +227,12 @@ module Blitter6502OISC (
end
end
// UART telemetry: when halted, send a_reg as status byte
// UART telemetry: when halted, send a_reg as status byte (once only)
reg [3:0] uart_bit_cnt;
reg [15:0] uart_div;
reg [9:0] uart_shift;
reg uart_active;
reg uart_sent; // prevents retransmission
localparam UART_DIV = 16'd233; // 115384 baud at 27MHz (matches Lean uartBaudDivisor)
@ -242,13 +243,15 @@ module Blitter6502OISC (
uart_div <= 16'd0;
uart_shift <= 10'b0;
uart_active <= 1'b0;
uart_sent <= 1'b0;
end else begin
if (halted && !uart_active) begin
// Start UART transmission with a_reg as payload
uart_shift <= {1'b1, a_reg, 1'b0}; // stop, data, start
if (halted && !uart_active && !uart_sent) begin
// Send recognizable pattern: 0xAA + a_reg
uart_shift <= {1'b1, 8'hAA, 1'b0}; // stop, 0xAA, start
uart_active <= 1'b1;
uart_bit_cnt <= 4'd0;
uart_div <= 16'd0;
uart_sent <= 1'b1;
end
if (uart_active) begin
@ -305,11 +308,27 @@ module Blitter6502OISCTop (
end
end
// Auto-start: trigger CPU 100ms after reset
reg [31:0] auto_start_cnt;
reg auto_start;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
auto_start_cnt <= 0;
auto_start <= 0;
end else if (!auto_start) begin
if (auto_start_cnt >= 2700000) begin // 100ms at 27MHz
auto_start <= 1;
end else begin
auto_start_cnt <= auto_start_cnt + 1;
end
end
end
// Blitter instance
Blitter6502OISC blitter (
.clk(clk),
.rst_n(rst_n),
.start(btn_rise),
.start(auto_start),
.busy(),
.led(led),
.uart_tx(uart_tx),

View file

@ -44,6 +44,22 @@ module research_stack_top (
end
assign btn_rise = btn_stable & ~btn_stable_prev;
// Auto-start: trigger CPU 100ms after reset (no button needed)
reg [31:0] auto_start_cnt;
reg auto_start;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
auto_start_cnt <= 0;
auto_start <= 0;
end else if (!auto_start) begin
if (auto_start_cnt >= 2700000) begin // 100ms at 27MHz
auto_start <= 1;
end else begin
auto_start_cnt <= auto_start_cnt + 1;
end
end
end
// Blitter CPU Signals
wire cpu_busy;
wire [5:0] cpu_led;
@ -121,7 +137,7 @@ module research_stack_top (
Blitter6502OISC cpu (
.clk(clk),
.rst_n(rst_n),
.start(btn_rise),
.start(auto_start),
.busy(cpu_busy),
.led(cpu_led),
.uart_tx(cpu_uart_tx),
@ -202,11 +218,16 @@ module research_stack_top (
);
// LED Output
// led[5] = CPU busy
// led[4] = Q16 done
// led[3:2] = voltage mode
// led[1:0] = scale select
assign led = {cpu_busy, q16_done_reg, map_voltage_mode, map_scale_select};
// When CPU is busy: show running pattern (blinking)
// When CPU is halted: show cpu_led (register values from Blitter)
// Otherwise: show status
reg [24:0] heartbeat;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) heartbeat <= 0;
else heartbeat <= heartbeat + 1;
end
assign led = cpu_busy ? {1'b1, heartbeat[23], 1'b0, heartbeat[21], 1'b0, heartbeat[19]}
: cpu_led; // Blitter's register output after halt
// UART
assign uart_tx = cpu_uart_tx;

View file

@ -0,0 +1,97 @@
module tb_uart_test;
reg clk;
wire uart_tx;
wire [5:0] led;
uart_test uut (
.clk(clk),
.uart_tx(uart_tx),
.led(led)
);
// 27MHz clock = 37.037ns period
initial clk = 0;
always #18.518 clk = ~clk;
// UART capture
reg [7:0] rx_byte;
integer bit_idx;
integer baud_ticks;
integer char_count;
initial begin
$dumpfile("uart_test.vcd");
$dumpvars(0, tb_uart_test);
rx_byte = 0;
bit_idx = 0;
baud_ticks = 0;
char_count = 0;
// Run for 2ms = enough for several UART bytes at 115384 baud
// Each byte = 10 bits * 234 clocks = 2340 clocks = 86.67us
// 2ms / 86.67us = ~23 bytes
#2_000_000;
$display("=== Simulation complete: received %0d characters ===", char_count);
$finish;
end
// Monitor UART TX line - capture bytes
// Detect start bit (falling edge) and sample data bits
reg [15:0] clk_count;
reg receiving;
reg [3:0] bit_count;
initial begin
clk_count = 0;
receiving = 0;
bit_count = 0;
end
always @(posedge clk) begin
if (!receiving) begin
// Wait for start bit (falling edge on uart_tx)
if (uart_tx == 0) begin
receiving <= 1;
clk_count <= 0;
bit_count <= 0;
rx_byte <= 0;
// Sample at middle of bit: half of baud period = 117 clocks
end
end else begin
clk_count <= clk_count + 1;
if (clk_count == 117) begin
// Middle of first data bit (after start bit)
if (bit_count == 0) begin
// Verify start bit is still low
if (uart_tx != 0) begin
$display("ERROR: start bit not low at sample point");
receiving <= 0;
end
end
end
if (clk_count == 117 + 234 * bit_count && bit_count < 8) begin
rx_byte[bit_count] <= uart_tx;
bit_count <= bit_count + 1;
end
if (clk_count >= 117 + 234 * 9 + 117) begin
// Past stop bit
char_count <= char_count + 1;
$display("UART byte %0d: 0x%02h = '%c' (time=%0t ns)",
char_count, rx_byte, rx_byte, $time);
receiving <= 0;
clk_count <= 0;
end
end
end
// Also print LED state
always @(posedge clk) begin
if (led != 6'b101010 && $time > 100_000) begin
// Only print once
if ($time < 200_000)
$display("LED pattern: %06b (time=%0t)", led, $time);
end
end
endmodule

View file

@ -0,0 +1,40 @@
// Minimal UART test: sends 'R' (0x52) continuously at 115200 baud
// Verifies FT2232 channel B FPGA pin 18 connection
module uart_test (
input wire clk,
output wire uart_tx,
output wire [5:0] led
);
// 27MHz / 234 = 115384 baud
localparam BAUD_DIV = 16'd233;
localparam TEST_BYTE = 8'h52; // 'R' for Research Stack
assign led = 6'b101010; // Pattern to confirm FPGA is alive
reg [15:0] baud_cnt = 0;
reg [3:0] bit_cnt = 0;
reg [9:0] shift_reg = 10'b1111111111;
reg tx_out = 1'b1;
assign uart_tx = tx_out;
always @(posedge clk) begin
if (baud_cnt >= BAUD_DIV) begin
baud_cnt <= 0;
if (bit_cnt == 0) begin
// Load start bit + data + stop bit
shift_reg <= {1'b1, TEST_BYTE, 1'b0};
bit_cnt <= 10;
tx_out <= 1'b0; // Start bit
end else begin
tx_out <= shift_reg[0];
shift_reg <= {1'b1, shift_reg[9:1]};
bit_cnt <= bit_cnt - 1;
end
end else begin
baud_cnt <= baud_cnt + 1;
end
end
endmodule