Files
retroSoC/tests/rtl/ws2812_tb.sv
T
maksyuki 6eedacd2f7 refactor: refactor tests to replace RIBP references with APB4 equivalents
- Updated test files to change instances of ribp_if to apb4_if in various test cases, ensuring consistency with the new APB4 interface.
- Modified assertions and checks in tests to reflect the new APB4 naming conventions, including changes in expected output and input files.
- Adjusted the test descriptions to accurately represent the APB4 functionality being tested, particularly in timer, UART, and GPIO tests.
- Ensured that all relevant paths and file names in the tests are updated to point to the new APB4 implementations, maintaining the integrity of the test suite.
2026-08-16 21:19:20 +08:00

307 lines
9.8 KiB
Systemverilog

`timescale 1ns / 1ps
`include "ws2812_define.svh"
module ws2812_tb;
localparam int FIFO_DEPTH = 4;
localparam int BIT_CYCLES = 4;
localparam int T0H_CYCLES = 1;
localparam int T1H_CYCLES = 3;
localparam int RESET_CYCLES = 5;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
apb4_if apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
ws2812_if ws2812 ();
logic monitor_enable = 1'b0;
logic [23:0] expected_pixels [0:7];
integer expected_pixel_count = 0;
integer observed_pixel = 0;
integer observed_bit = 0;
integer observed_cycle = 0;
integer expected_bit_cycles = BIT_CYCLES;
integer expected_t0h_cycles = T0H_CYCLES;
integer expected_t1h_cycles = T1H_CYCLES;
integer reset_low_cycles = 0;
integer test_stage = 0;
always #5 clk_i = ~clk_i;
initial begin
repeat (10000) @(posedge clk_i);
$fatal(1, "WS2812 test timeout at stage %0d state=%0d valid=%b ready=%b", test_stage,
u_dut.u_ws2812_core.s_state_q, apb4.psel, apb4.pready);
end
apb4_ws2812 #(
.TxFifoDepth(FIFO_DEPTH)
) u_dut (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.apb4 (apb4),
.ws2812 (ws2812)
);
task automatic apb4_write(input logic [31:0] address, input logic [31:0] data,
input logic [3:0] strobe, input logic expected_error,
output integer wait_cycles);
begin
wait_cycles = 0;
@(negedge clk_i);
apb4.paddr = address;
apb4.pwdata = data;
apb4.pstrb = strobe;
apb4.pwrite = 1'b1;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
@(negedge clk_i);
apb4.penable = 1'b1;
while (!apb4.pready) begin
@(negedge clk_i);
wait_cycles = wait_cycles + 1;
end
if (apb4.pslverr !== expected_error) begin
$fatal(1, "write %h error=%b expected=%b", address, apb4.pslverr, expected_error);
end
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
end
endtask
task automatic apb4_read(input logic [31:0] address, input logic expected_error,
output logic [31:0] data);
begin
@(negedge clk_i);
apb4.paddr = address;
apb4.pwdata = '0;
apb4.pstrb = '0;
apb4.pwrite = 1'b0;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
@(negedge clk_i);
apb4.penable = 1'b1;
while (!apb4.pready) @(negedge clk_i);
if (apb4.pslverr !== expected_error) begin
$fatal(1, "read %h error=%b expected=%b", address, apb4.pslverr, expected_error);
end
data = apb4.prdata;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
task automatic write_ok(input logic [31:0] address, input logic [31:0] data);
integer ignored_wait;
begin
apb4_write(address, data, 4'hF, 1'b0, ignored_wait);
end
endtask
task automatic wait_until_idle;
logic [31:0] status;
begin
status = 32'h1;
while (status[`WS2812_STATUS_BUSY]) begin
apb4_read(`APB4_WS2812_STATUS, 1'b0, status);
end
end
endtask
task automatic configure_timing;
begin
write_ok(`APB4_WS2812_BIT_CYCLES, BIT_CYCLES);
write_ok(`APB4_WS2812_T0H_CYCLES, T0H_CYCLES);
write_ok(`APB4_WS2812_T1H_CYCLES, T1H_CYCLES);
write_ok(`APB4_WS2812_RESET_CYCLES, RESET_CYCLES);
write_ok(`APB4_WS2812_FIFO_WATERMARK, 3);
write_ok(`APB4_WS2812_INTR_ENABLE, 4'hF);
end
endtask
always @(posedge clk_i) begin
#1;
if (!rst_n_i && ws2812.dat_o !== 1'b0) begin
$fatal(1, "WS2812 output is not low during reset");
end
if (monitor_enable && u_dut.s_busy && !u_dut.s_reset_active) begin
integer high_cycles;
logic expected_data;
high_cycles = expected_pixels[observed_pixel][23-observed_bit] ? expected_t1h_cycles :
expected_t0h_cycles;
expected_data = observed_cycle < high_cycles;
if (ws2812.dat_o !== expected_data) begin
$fatal(1, "waveform mismatch pixel=%0d bit=%0d cycle=%0d", observed_pixel, observed_bit,
observed_cycle);
end
observed_cycle = observed_cycle + 1;
if (observed_cycle == expected_bit_cycles) begin
observed_cycle = 0;
observed_bit = observed_bit + 1;
if (observed_bit == 24) begin
observed_bit = 0;
observed_pixel = observed_pixel + 1;
end
end
end
if (monitor_enable && u_dut.s_reset_active) begin
if (ws2812.dat_o !== 1'b0) begin
$fatal(1, "WS2812 output is high during latch/reset");
end
reset_low_cycles = reset_low_cycles + 1;
end
end
task automatic begin_monitor(input integer pixel_count);
begin
observed_pixel = 0;
observed_bit = 0;
observed_cycle = 0;
reset_low_cycles = 0;
expected_pixel_count = pixel_count;
monitor_enable = 1'b1;
end
endtask
task automatic end_monitor(input logic expect_complete_frame);
begin
monitor_enable = 1'b0;
if (expect_complete_frame && observed_pixel != expected_pixel_count) begin
$fatal(1, "observed %0d pixels, expected %0d", observed_pixel, expected_pixel_count);
end
if (reset_low_cycles < RESET_CYCLES) begin
$fatal(1, "latch/reset low interval was only %0d cycles", reset_low_cycles);
end
end
endtask
initial begin
logic [31:0] value;
integer wait_cycles;
apb4.psel = 1'b0;
apb4.paddr = '0;
apb4.pwdata = '0;
apb4.pstrb = '0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
test_stage = 1;
apb4_read(`APB4_WS2812_IP_INFO, 1'b0, value);
if (value !== 32'h1804_0100) begin
$fatal(1, "unexpected IP_INFO %h", value);
end
apb4_read(`APB4_WS2812_STATUS, 1'b0, value);
if (value[`WS2812_STATUS_CONFIG_VALID] || !value[`WS2812_STATUS_FIFO_EMPTY]) begin
$fatal(1, "unexpected reset status %h", value);
end
apb4_read(`APB4_WS2812_CTRL, 1'b1, value);
apb4_write(8'h02, 32'd1, 4'hF, 1'b1, wait_cycles);
apb4_read(32'h00000100, 1'b1, value);
apb4_write(`APB4_WS2812_TXDATA, 24'h112233, 4'h7, 1'b1, wait_cycles);
write_ok(`APB4_WS2812_TXDATA, 24'h112233);
write_ok(`APB4_WS2812_FRAME_WORDS, 1);
apb4_write(`APB4_WS2812_CTRL, 32'h1, 4'hF, 1'b1, wait_cycles);
apb4_read(`APB4_WS2812_ERROR_STATUS, 1'b0, value);
if (!value[`WS2812_ERROR_CONFIG]) begin
$fatal(1, "invalid timing did not set CONFIG error");
end
write_ok(`APB4_WS2812_ERROR_STATUS, 32'h7);
write_ok(`APB4_WS2812_CTRL, 32'h4);
configure_timing();
apb4_read(`APB4_WS2812_STATUS, 1'b0, value);
if (!value[`WS2812_STATUS_CONFIG_VALID]) begin
$fatal(1, "legal timing was not accepted");
end
test_stage = 2;
for (integer index = 0; index < FIFO_DEPTH; index = index + 1) begin
write_ok(`APB4_WS2812_TXDATA, index);
end
apb4_write(`APB4_WS2812_TXDATA, 32'h55, 4'hF, 1'b1, wait_cycles);
write_ok(`APB4_WS2812_CTRL, 32'h4);
write_ok(`APB4_WS2812_ERROR_STATUS, 32'h7);
write_ok(`APB4_WS2812_INTR_STATE, 32'hF);
test_stage = 3;
expected_pixels[0] = 24'h800001;
expected_pixels[1] = 24'h010203;
expected_pixels[2] = 24'hA5A5A5;
expected_pixels[3] = 24'h5A5A5A;
expected_pixels[4] = 24'hFFFFFF;
expected_pixels[5] = 24'h000000;
for (integer index = 0; index < FIFO_DEPTH; index = index + 1) begin
write_ok(`APB4_WS2812_TXDATA, expected_pixels[index]);
end
write_ok(`APB4_WS2812_FRAME_WORDS, 6);
begin_monitor(6);
write_ok(`APB4_WS2812_CTRL, 32'h1);
write_ok(`APB4_WS2812_TXDATA, expected_pixels[4]);
apb4_write(`APB4_WS2812_TXDATA, expected_pixels[5], 4'hF, 1'b0, wait_cycles);
if (wait_cycles < (BIT_CYCLES * 20)) begin
$fatal(1, "full FIFO write was not backpressured: %0d cycles", wait_cycles);
end
wait_until_idle();
end_monitor(1'b1);
apb4_read(`APB4_WS2812_INTR_STATE, 1'b0, value);
if ((value & 32'h3) != 32'h3 || !ws2812.irq_o) begin
$fatal(1, "done/low-watermark interrupt missing: %h", value);
end
write_ok(`APB4_WS2812_INTR_STATE, 32'hF);
if (ws2812.irq_o) begin
$fatal(1, "IRQ did not clear");
end
test_stage = 4;
expected_pixels[0] = 24'h123456;
write_ok(`APB4_WS2812_TXDATA, expected_pixels[0]);
write_ok(`APB4_WS2812_FRAME_WORDS, 2);
begin_monitor(1);
write_ok(`APB4_WS2812_CTRL, 32'h1);
wait_until_idle();
end_monitor(1'b1);
apb4_read(`APB4_WS2812_ERROR_STATUS, 1'b0, value);
if (!value[`WS2812_ERROR_UNDERFLOW]) begin
$fatal(1, "underflow was not reported");
end
apb4_read(`APB4_WS2812_FIFO_LEVEL, 1'b0, value);
if (value != 0) begin
$fatal(1, "underflow did not flush FIFO");
end
write_ok(`APB4_WS2812_ERROR_STATUS, 32'h7);
write_ok(`APB4_WS2812_INTR_STATE, 32'hF);
test_stage = 5;
expected_pixels[0] = 24'hABCDEF;
expected_pixels[1] = 24'h765432;
write_ok(`APB4_WS2812_TXDATA, expected_pixels[0]);
write_ok(`APB4_WS2812_TXDATA, expected_pixels[1]);
write_ok(`APB4_WS2812_FRAME_WORDS, 2);
begin_monitor(2);
write_ok(`APB4_WS2812_CTRL, 32'h1);
repeat (8) @(posedge clk_i);
write_ok(`APB4_WS2812_CTRL, 32'h2);
wait_until_idle();
end_monitor(1'b0);
apb4_read(`APB4_WS2812_INTR_STATE, 1'b0, value);
if (!value[`WS2812_INTR_ABORTED]) begin
$fatal(1, "abort completion was not reported");
end
write_ok(`APB4_WS2812_INTR_TEST, 32'h1);
if (!ws2812.irq_o) begin
$fatal(1, "interrupt test did not assert IRQ");
end
write_ok(`APB4_WS2812_INTR_STATE, 32'hF);
$display("WS2812 register, waveform, streaming, and error test passed");
$finish;
end
endmodule