feat: enhance DMA and RIB functionality with burst support and performance tests

- Updated dma_error_tb to implement burst transfer handling and performance measurement for INCR4 transfers.
- Introduced rib_burst_bus_tb to validate RIB burst arbitration and protocol compliance.
- Added soc_rib_burst_ram_tb to test SRAM behavior with burst commands.
- Created spill_register_tb to verify the functionality of the spill register under various conditions.
- Modified test_dma.py to reflect changes in DMA error handling and performance reporting.
- Updated test_memory_map.py to include new RIB burst interfaces and spill register utilities.
- Enhanced test_rib_burst.py with directed tests for RIB burst building blocks and spill register behavior.
- Adjusted test_soc_topology.py to ensure correct instantiation counts for RIB interfaces.
- Improved test_user_extensions.py to validate the generation of isolated scalar bindings for burst interfaces.
This commit is contained in:
maksyuki committed 2026-08-03 22:23:37 +08:00
1 parent 9dc32ab9cc
commit bef5881149
57 files changed
+3239 -727

No files matched your search

+56 -32
View File
@@ -12,17 +12,23 @@ module bus_fault_tb;
logic [ 2:0] fault_code_o;
logic user_bus_idle_o;
soc_rib_if mgmt_rib ();
soc_rib_if user_rib ();
soc_rib_if dma_rib ();
rib_if rib ();
soc_rib_burst_if user_rib ();
soc_rib_burst_if dma_rib ();
soc_rib_burst_if rib ();
rib_if apb_rib ();
always #5 clk_i = ~clk_i;
assign rib.ready = 1'b1;
assign rib.rdata = 32'hCAFE_BABE;
assign apb_rib.ready = 1'b1;
assign apb_rib.rdata = 32'h1234_5678;
assign rib.cmd_ready = 1'b1;
assign rib.w_ready = 1'b1;
assign rib.rsp_valid = 1'b0;
assign rib.rdata = 32'hCAFE_BABE;
assign rib.resp_err = 1'b0;
assign rib.resp_code = `SOC_RIB_RESP_OK;
assign rib.rsp_beat = '0;
assign rib.rsp_last = 1'b0;
assign apb_rib.ready = 1'b1;
assign apb_rib.rdata = 32'h1234_5678;
bus u_bus (
.clk_i (clk_i),
@@ -32,7 +38,7 @@ module bus_fault_tb;
.dma_rib (dma_rib),
.user_bus_enable_i(1'b1),
.user_bus_idle_o (user_bus_idle_o),
.rib (rib),
.rib (rib),
.apb_rib (apb_rib),
.apb_resp_err_i (1'b0),
.perf_enable_i (1'b0),
@@ -57,12 +63,11 @@ module bus_fault_tb;
while (!fault_valid_o) @(posedge clk_i);
if (fault_addr_o !== address || fault_wstrb_o !== write_strobes ||
fault_reserved_o !== reserved || fault_access_o || fault_code_o !== expected_code ||
rib.valid || apb_rib.valid) begin
rib.cmd_valid || apb_rib.valid) begin
$fatal(1, "unexpected fault response for address %h", address);
end
while (!mgmt_rib.ready) @(posedge clk_i);
if (mgmt_rib.rdata !== 32'd0 || !mgmt_rib.resp_err ||
mgmt_rib.resp_code !== expected_code) begin
if (mgmt_rib.rdata !== 32'd0 || !mgmt_rib.resp_err) begin
$fatal(1, "fault response data must be zero");
end
@(negedge clk_i);
@@ -74,38 +79,57 @@ module bus_fault_tb;
task automatic expect_user_denied(input logic [31:0] address, input logic [3:0] write_strobes);
begin
@(negedge clk_i);
user_rib.addr = address;
user_rib.wdata = 32'h5A5A_A5A5;
user_rib.wstrb = write_strobes;
user_rib.valid = 1'b1;
user_rib.cmd_addr = address;
user_rib.cmd_write = |write_strobes;
user_rib.cmd_len = `SOC_RIB_BURST_INCR1;
user_rib.cmd_valid = 1'b1;
while (!user_rib.cmd_ready) @(posedge clk_i);
@(negedge clk_i);
user_rib.cmd_valid = 1'b0;
if (|write_strobes) begin
user_rib.wdata = 32'h5A5A_A5A5;
user_rib.wstrb = write_strobes;
user_rib.wlast = 1'b1;
user_rib.w_valid = 1'b1;
while (!user_rib.w_ready) @(posedge clk_i);
@(negedge clk_i);
user_rib.w_valid = 1'b0;
end
while (!fault_valid_o) @(posedge clk_i);
if (!fault_access_o || fault_master_o != 2'd1 || rib.valid || apb_rib.valid) begin
if (!fault_access_o || fault_master_o != 2'd1 || rib.cmd_valid || apb_rib.valid) begin
$fatal(1, "user access was not denied locally");
end
while (!user_rib.ready) @(posedge clk_i);
if (user_rib.rdata !== 32'd0 || !user_rib.resp_err ||
if (user_rib.rdata !== 32'd0 || !user_rib.resp_err || !user_rib.rsp_last ||
user_rib.resp_code !== `SOC_RIB_RESP_PROTERR) begin
$fatal(1, "denied user response data must be zero");
end
@(negedge clk_i);
user_rib.valid = 1'b0;
while (fault_valid_o) @(posedge clk_i);
end
endtask
initial begin
mgmt_rib.valid = 1'b0;
mgmt_rib.addr = '0;
mgmt_rib.wdata = '0;
mgmt_rib.wstrb = '0;
user_rib.valid = 1'b0;
user_rib.addr = '0;
user_rib.wdata = '0;
user_rib.wstrb = '0;
dma_rib.valid = 1'b0;
dma_rib.addr = '0;
dma_rib.wdata = '0;
dma_rib.wstrb = '0;
mgmt_rib.valid = 1'b0;
mgmt_rib.addr = '0;
mgmt_rib.wdata = '0;
mgmt_rib.wstrb = '0;
user_rib.cmd_valid = 1'b0;
user_rib.cmd_addr = '0;
user_rib.cmd_write = 1'b0;
user_rib.cmd_len = `SOC_RIB_BURST_INCR1;
user_rib.w_valid = 1'b0;
user_rib.wdata = '0;
user_rib.wstrb = '0;
user_rib.wlast = 1'b0;
user_rib.rsp_ready = 1'b1;
dma_rib.cmd_valid = 1'b0;
dma_rib.cmd_addr = '0;
dma_rib.cmd_write = 1'b0;
dma_rib.cmd_len = `SOC_RIB_BURST_INCR1;
dma_rib.w_valid = 1'b0;
dma_rib.wdata = '0;
dma_rib.wstrb = '0;
dma_rib.wlast = 1'b0;
dma_rib.rsp_ready = 1'b1;
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
+105 -9
View File
@@ -1,6 +1,10 @@
`timescale 1ns / 1ps
module dma_error_tb;
localparam logic [1:0] MODEL_IDLE = 2'd0;
localparam logic [1:0] MODEL_WDATA = 2'd1;
localparam logic [1:0] MODEL_RESP = 2'd2;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic [ 2:0] mode_i = 3'd0;
@@ -19,19 +23,76 @@ module dma_error_tb;
logic [ 1:0] fsm_o;
logic error_mode = 1'b0;
integer write_count = 0;
integer incr4_read_count = 0;
integer incr4_write_count = 0;
integer legacy_cycles;
integer burst_cycles;
logic [ 1:0] model_state_q = MODEL_IDLE;
logic [ 1:0] model_len_q = '0;
logic [ 1:0] model_beat_q = '0;
logic model_write_q = 1'b0;
logic model_error_q = 1'b0;
dma_hw_trg_if hw_trg ();
soc_rib_if rib ();
soc_rib_burst_if rib ();
always #5 clk_i = ~clk_i;
assign rib.ready = rib.valid;
assign rib.rdata = 32'hA5A5_5A5A;
assign rib.resp_err = error_mode && rib.valid;
assign rib.resp_code = error_mode ? `SOC_RIB_RESP_DECERR : `SOC_RIB_RESP_OK;
assign rib.cmd_ready = model_state_q == MODEL_IDLE;
assign rib.w_ready = model_state_q == MODEL_WDATA;
assign rib.rsp_valid = model_state_q == MODEL_RESP;
assign rib.rdata = 32'hA5A5_5A5A + model_beat_q;
assign rib.resp_err = model_error_q;
assign rib.resp_code = model_error_q ? `SOC_RIB_RESP_DECERR : `SOC_RIB_RESP_OK;
assign rib.rsp_beat = model_beat_q;
assign rib.rsp_last = model_write_q || model_error_q || (model_beat_q == model_len_q);
always @(posedge clk_i) begin
if (rib.valid && rib.ready && (|rib.wstrb)) begin
write_count <= write_count + 1;
always @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
model_state_q <= MODEL_IDLE;
model_len_q <= '0;
model_beat_q <= '0;
model_write_q <= 1'b0;
model_error_q <= 1'b0;
write_count <= 0;
end else begin
unique case (model_state_q)
MODEL_IDLE: begin
if (rib.cmd_valid && rib.cmd_ready) begin
model_len_q <= rib.cmd_len;
model_beat_q <= '0;
model_write_q <= rib.cmd_write;
model_error_q <= error_mode && !rib.cmd_write;
model_state_q <= rib.cmd_write ? MODEL_WDATA : MODEL_RESP;
if (rib.cmd_len == `SOC_RIB_BURST_INCR4 && rib.cmd_write) begin
incr4_write_count <= incr4_write_count + 1;
end
if (rib.cmd_len == `SOC_RIB_BURST_INCR4 && !rib.cmd_write) begin
incr4_read_count <= incr4_read_count + 1;
end
end
end
MODEL_WDATA: begin
if (rib.w_valid && rib.w_ready) begin
write_count <= write_count + 1;
if (rib.wlast) begin
model_beat_q <= model_len_q;
model_state_q <= MODEL_RESP;
end else begin
model_beat_q <= model_beat_q + 1'b1;
end
end
end
MODEL_RESP: begin
if (rib.rsp_valid && rib.rsp_ready) begin
if (rib.rsp_last) begin
model_state_q <= MODEL_IDLE;
end else begin
model_beat_q <= model_beat_q + 1'b1;
end
end
end
default: model_state_q <= MODEL_IDLE;
endcase
end
end
@@ -65,6 +126,18 @@ module dma_error_tb;
end
endtask
task automatic measure_transfer(output integer elapsed);
begin
start_transfer();
elapsed = 0;
while (!done_o) begin
@(posedge clk_i);
elapsed = elapsed + 1;
end
@(negedge clk_i);
end
endtask
initial begin
hw_trg.i2s_tx_proc = 1'b0;
hw_trg.i2s_rx_proc = 1'b0;
@@ -91,7 +164,30 @@ module dma_error_tb;
$fatal(1, "DMA issued a write after a read error");
end
$display("dma error responder test passed");
error_mode = 1'b0;
xferlen_i = 32'd8;
srcincr_i = 1'b0;
dstincr_i = 1'b1;
measure_transfer(legacy_cycles);
srcincr_i = 1'b1;
measure_transfer(burst_cycles);
if (incr4_read_count != 2 || incr4_write_count != 2) begin
$fatal(1, "aligned eight-word transfer did not use two INCR4 chunks");
end
if ((burst_cycles * 3) > (legacy_cycles * 2)) begin
$fatal(1, "INCR4 DMA speedup is below 1.5x: legacy=%0d burst=%0d", legacy_cycles,
burst_cycles);
end
srcaddr_i = 32'h1000_0000;
dstaddr_i = 32'h1000_1000;
measure_transfer(legacy_cycles);
if (incr4_read_count != 2 || incr4_write_count != 2) begin
$fatal(1, "DMA did not fall back to INCR1 for a register window");
end
$display("dma error and burst performance test passed");
$finish;
end
endmodule
+175
View File
@@ -0,0 +1,175 @@
`timescale 1ns / 1ps
module rib_burst_bus_tb;
localparam logic TARGET_IDLE = 1'b0;
localparam logic TARGET_RESP = 1'b1;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic target_state_q = TARGET_IDLE;
logic [1:0] target_len_q = '0;
logic [1:0] target_beat_q = '0;
integer rib_command_count = 0;
integer cycle_count = 0;
integer last_response_cycle;
integer response_count;
logic fault_valid_o;
logic [2:0] fault_code_o;
logic user_bus_idle_o;
soc_rib_if mgmt_rib ();
soc_rib_burst_if user_rib ();
soc_rib_burst_if dma_rib ();
soc_rib_burst_if rib ();
rib_if apb_rib ();
always #5 clk_i = ~clk_i;
always @(posedge clk_i) cycle_count <= cycle_count + 1;
assign rib.cmd_ready = target_state_q == TARGET_IDLE;
assign rib.w_ready = 1'b0;
assign rib.rsp_valid = target_state_q == TARGET_RESP;
assign rib.rdata = 32'hCAFE_0000 + target_beat_q;
assign rib.resp_err = 1'b0;
assign rib.resp_code = `SOC_RIB_RESP_OK;
assign rib.rsp_beat = target_beat_q;
assign rib.rsp_last = target_beat_q == target_len_q;
always @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
target_state_q <= TARGET_IDLE;
target_len_q <= '0;
target_beat_q <= '0;
rib_command_count <= 0;
end else if (target_state_q == TARGET_IDLE) begin
if (rib.cmd_valid && rib.cmd_ready) begin
if (rib.cmd_write) $fatal(1, "read-only target received write command");
target_len_q <= rib.cmd_len;
target_beat_q <= '0;
target_state_q <= TARGET_RESP;
rib_command_count <= rib_command_count + 1;
end
end else if (rib.rsp_valid && rib.rsp_ready) begin
if (rib.rsp_last) begin
target_state_q <= TARGET_IDLE;
end else begin
target_beat_q <= target_beat_q + 1'b1;
end
end
end
assign apb_rib.ready = apb_rib.valid;
assign apb_rib.rdata = 32'h1234_5678;
bus u_bus (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.mgmt_rib (mgmt_rib),
.user_rib (user_rib),
.dma_rib (dma_rib),
.user_bus_enable_i(1'b1),
.user_bus_idle_o (user_bus_idle_o),
.rib (rib),
.apb_rib (apb_rib),
.apb_resp_err_i (1'b0),
.perf_enable_i (1'b0),
.perf_clear_i (1'b0),
.fault_valid_o (fault_valid_o),
.fault_addr_o (),
.fault_wstrb_o (),
.fault_reserved_o (),
.fault_access_o (),
.fault_master_o (),
.fault_code_o (fault_code_o),
.perf_mgmt_wait_o (),
.perf_user_wait_o (),
.perf_dma_wait_o (),
.perf_rib_wait_o (),
.perf_apb_wait_o (),
.perf_sdram_wait_o(),
.perf_psram_wait_o(),
.perf_flash_wait_o()
);
task automatic issue_read(input logic [31:0] address, input logic [1:0] length,
input logic expect_error);
begin
@(negedge clk_i);
dma_rib.cmd_addr = address;
dma_rib.cmd_write = 1'b0;
dma_rib.cmd_len = length;
dma_rib.cmd_valid = 1'b1;
while (!dma_rib.cmd_ready) @(posedge clk_i);
@(negedge clk_i);
dma_rib.cmd_valid = 1'b0;
response_count = 0;
last_response_cycle = -1;
while (response_count <= length) begin
while (!dma_rib.rsp_valid) @(posedge clk_i);
if (dma_rib.resp_err !== expect_error) begin
$fatal(1, "unexpected response status for %h", address);
end
if (expect_error) begin
if (dma_rib.resp_code !== `SOC_RIB_RESP_BURSTERR || !dma_rib.rsp_last) begin
$fatal(1, "illegal burst did not return terminal BURSTERR");
end
response_count = length + 1;
end else begin
if (dma_rib.rsp_beat !== response_count[1:0]) begin
$fatal(1, "response beat index mismatch");
end
if ((last_response_cycle >= 0) && (cycle_count != last_response_cycle + 1)) begin
$fatal(1, "bubble detected between INCR4 response beats");
end
last_response_cycle = cycle_count;
response_count = response_count + 1;
end
@(posedge clk_i);
@(negedge clk_i);
end
end
endtask
initial begin
mgmt_rib.valid = 1'b0;
mgmt_rib.addr = '0;
mgmt_rib.wdata = '0;
mgmt_rib.wstrb = '0;
user_rib.cmd_valid = 1'b0;
user_rib.cmd_addr = '0;
user_rib.cmd_write = 1'b0;
user_rib.cmd_len = '0;
user_rib.w_valid = 1'b0;
user_rib.wdata = '0;
user_rib.wstrb = '0;
user_rib.wlast = 1'b0;
user_rib.rsp_ready = 1'b1;
dma_rib.cmd_valid = 1'b0;
dma_rib.cmd_addr = '0;
dma_rib.cmd_write = 1'b0;
dma_rib.cmd_len = '0;
dma_rib.w_valid = 1'b0;
dma_rib.wdata = '0;
dma_rib.wstrb = '0;
dma_rib.wlast = 1'b0;
dma_rib.rsp_ready = 1'b1;
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
issue_read(32'h3800_1000, `SOC_RIB_BURST_INCR4, 1'b0);
if (rib_command_count != 1) begin
$fatal(1, "INCR4 was split before reaching the burst RIB target");
end
issue_read(32'h3800_1004, `SOC_RIB_BURST_INCR4, 1'b1);
issue_read(32'h1000_0000, `SOC_RIB_BURST_INCR4, 1'b1);
issue_read(32'h2000_0000, `SOC_RIB_BURST_INCR4, 1'b1);
issue_read(32'h3800_1000, 2'd2, 1'b1);
if (rib_command_count != 1 || apb_rib.valid) begin
$fatal(1, "illegal burst reached a downstream target");
end
$display("RIB burst arbitration and protocol test passed");
$finish;
end
endmodule
+119
View File
@@ -0,0 +1,119 @@
`timescale 1ns / 1ps
module soc_rib_burst_ram_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic [31:0] memory [0:31];
logic [31:0] expected [ 0:3];
integer index;
soc_rib_burst_if burst ();
ram_if ram ();
always #5 clk_i = ~clk_i;
always_ff @(posedge clk_i) begin
ram.rdata <= memory[ram.addr];
if (ram.wstrb[0]) memory[ram.addr][7:0] <= ram.wdata[7:0];
if (ram.wstrb[1]) memory[ram.addr][15:8] <= ram.wdata[15:8];
if (ram.wstrb[2]) memory[ram.addr][23:16] <= ram.wdata[23:16];
if (ram.wstrb[3]) memory[ram.addr][31:24] <= ram.wdata[31:24];
end
soc_rib_burst_ram u_dut (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.burst (burst),
.ram (ram)
);
task automatic issue_command(input logic write);
begin
@(negedge clk_i);
burst.cmd_addr = 32'h3000_0000;
burst.cmd_write = write;
burst.cmd_len = `SOC_RIB_BURST_INCR4;
burst.cmd_valid = 1'b1;
while (!burst.cmd_ready) @(posedge clk_i);
@(negedge clk_i);
burst.cmd_valid = 1'b0;
end
endtask
initial begin
for (index = 0; index < 32; index = index + 1) memory[index] = '0;
for (index = 0; index < 4; index = index + 1) begin
expected[index] = 32'hA500_1000 + index;
end
burst.cmd_valid = 1'b0;
burst.cmd_addr = '0;
burst.cmd_write = 1'b0;
burst.cmd_len = '0;
burst.w_valid = 1'b0;
burst.wdata = '0;
burst.wstrb = '0;
burst.wlast = 1'b0;
burst.rsp_ready = 1'b1;
repeat (2) @(posedge clk_i);
@(negedge clk_i);
rst_n_i = 1'b1;
issue_command(1'b1);
for (index = 0; index < 4; index = index + 1) begin
burst.w_valid = 1'b1;
burst.wdata = expected[index];
burst.wstrb = 4'hF;
burst.wlast = index == 3;
while (!burst.w_ready) @(posedge clk_i);
@(negedge clk_i);
end
burst.w_valid = 1'b0;
burst.wlast = 1'b0;
while (!burst.rsp_valid) @(posedge clk_i);
if (burst.resp_err || !burst.rsp_last || burst.rsp_beat != 2'd3) begin
$fatal(1, "invalid SRAM write response");
end
@(negedge clk_i);
burst.rsp_ready = 1'b0;
issue_command(1'b0);
while (!burst.rsp_valid) @(negedge clk_i);
burst.rsp_ready = 1'b1;
for (index = 0; index < 4; index = index + 1) begin
if (burst.rdata !== expected[index] || burst.rsp_beat !== index[1:0] ||
burst.rsp_last !== (index == 3) || burst.resp_err) begin
$fatal(1, "invalid SRAM read response beat=%0d data=%h expected=%h rsp_beat=%0d last=%0b",
index, burst.rdata, expected[index], burst.rsp_beat, burst.rsp_last);
end
@(posedge clk_i);
@(negedge clk_i);
if ((index != 3) && !burst.rsp_valid) begin
$fatal(1, "SRAM inserted a response bubble at beat %0d", index + 1);
end
end
burst.rsp_ready = 1'b0;
issue_command(1'b0);
while (!burst.rsp_valid) @(negedge clk_i);
repeat (4) begin
@(posedge clk_i);
if (!burst.rsp_valid || burst.rdata !== expected[0] || burst.rsp_beat != 2'd0) begin
$fatal(1, "SRAM response changed while stalled");
end
end
@(negedge clk_i);
burst.rsp_ready = 1'b1;
for (index = 0; index < 4; index = index + 1) begin
if (burst.rdata !== expected[index] || burst.rsp_beat !== index[1:0]) begin
$fatal(1, "SRAM lost data after backpressure beat=%0d data=%h rsp_beat=%0d", index,
burst.rdata, burst.rsp_beat);
end
@(posedge clk_i);
@(negedge clk_i);
while ((index != 3) && !burst.rsp_valid) @(negedge clk_i);
end
$display("RIB burst SRAM pipeline test passed");
$finish;
end
endmodule
+113
View File
@@ -0,0 +1,113 @@
`timescale 1ns / 1ps
module spill_register_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic flush_i = 1'b0;
logic valid_i = 1'b0;
logic ready_o;
logic [31:0] data_i = '0;
logic valid_o;
logic ready_i = 1'b0;
logic [31:0] data_o;
logic [31:0] expected [0:511];
integer head = 0;
integer tail = 0;
integer cycle;
logic held_valid = 1'b0;
logic [31:0] held_data = '0;
logic [31:0] lfsr = 32'h1ACE_B00C;
always #5 clk_i = ~clk_i;
spill_register #(
.DATA_WIDTH(32),
.BYPASS (1'b0)
) u_dut (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(flush_i),
.valid_i(valid_i),
.ready_o(ready_o),
.data_i (data_i),
.valid_o(valid_o),
.ready_i(ready_i),
.data_o (data_o)
);
task automatic check_cycle;
begin
@(posedge clk_i);
if (held_valid && (!valid_o || (data_o !== held_data))) begin
$fatal(1, "spill register changed output while stalled");
end
if (valid_o && ready_i) begin
if (head == tail || data_o !== expected[head]) begin
$fatal(1, "spill register output ordering failure");
end
head = head + 1;
end
if (valid_i && ready_o) begin
expected[tail] = data_i;
tail = tail + 1;
end
if (flush_i) begin
head = tail;
end
held_valid = valid_o && !ready_i && !flush_i;
held_data = data_o;
end
endtask
initial begin
repeat (2) @(posedge clk_i);
@(negedge clk_i);
rst_n_i = 1'b1;
// Sustained one-word-per-cycle traffic exercises simultaneous push/pop.
for (cycle = 0; cycle < 16; cycle = cycle + 1) begin
valid_i = 1'b1;
ready_i = 1'b1;
data_i = 32'h1000_0000 + cycle;
check_cycle();
@(negedge clk_i);
end
// Fill both entries, hold the output, and check stability under pressure.
ready_i = 1'b0;
for (cycle = 0; cycle < 8; cycle = cycle + 1) begin
valid_i = 1'b1;
data_i = 32'h2000_0000 + cycle;
check_cycle();
@(negedge clk_i);
end
valid_i = 1'b0;
flush_i = 1'b1;
check_cycle();
@(negedge clk_i);
flush_i = 1'b0;
// Deterministic randomized backpressure covers all occupancy transitions.
for (cycle = 0; cycle < 256; cycle = cycle + 1) begin
lfsr = {lfsr[30:0], lfsr[31] ^ lfsr[21] ^ lfsr[1] ^ lfsr[0]};
valid_i = lfsr[0] | lfsr[3];
ready_i = lfsr[1] | lfsr[5];
data_i = 32'h3000_0000 + cycle;
check_cycle();
@(negedge clk_i);
end
valid_i = 1'b0;
ready_i = 1'b1;
while (head != tail || valid_o) begin
check_cycle();
@(negedge clk_i);
end
$display("spill register ready/valid test passed");
$finish;
end
endmodule
+18 -1
View File
@@ -17,16 +17,33 @@ def test_dma_reports_bus_errors_and_transfers_exact_word_count(tmp_path: Path) -
if iverilog is None or vvp is None:
return
memory_map = tmp_path / "memory_map"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/address_map/generate_memory_map.py"),
"--map",
str(ROOT / "rtl/mini/address_map/memory_map.json"),
"--output-dir",
str(memory_map),
"--have-sram-if",
"NO",
],
check=True,
)
source_list = tmp_path / "dma_error.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{memory_map / 'rtl'}",
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
f"+incdir+{ROOT / 'rtl/mini/top'}",
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/rib_if.sv"),
str(ROOT / "rtl/mini/top/soc_rib_if.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/fifo.sv"),
str(ROOT / "rtl/ip/rib/peripheral/dma.sv"),
str(ROOT / "rtl/ip/rib/peripheral/dma_core.sv"),
str(ROOT / "tests/rtl/dma_error_tb.sv"),
@@ -53,4 +70,4 @@ def test_dma_reports_bus_errors_and_transfers_exact_word_count(tmp_path: Path) -
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "dma error responder test passed" in result.stdout
assert "dma error and burst performance test passed" in result.stdout
+7 -1
View File
@@ -113,9 +113,15 @@ def test_bus_fault_responder_handles_reserved_and_unmapped_addresses(tmp_path: P
f"+incdir+{tmp_path / 'user_extensions' / 'rtl'}",
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/rib_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/ram_if.sv"),
str(ROOT / "rtl/mini/top/soc_rib_if.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/mini/top/soc_rib_regslice.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/spill_register.sv"),
str(ROOT / "rtl/mini/top/soc_rib_legacy_to_burst.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_to_legacy.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_error_slave.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_ram.sv"),
str(ROOT / "rtl/mini/top/bus.sv"),
str(ROOT / "tests/rtl/bus_fault_tb.sv"),
"",
+4 -3
View File
@@ -7,7 +7,7 @@ from scripts.parse_performance_log import parse_log
def test_parse_log_requires_terminal_marker_and_complete_samples() -> None:
report = parse_log(
"PERF region=sdram op=read words=1024 checksum=0x1234 mgmt_wait=10 "
"PERF region=sdram op=read words=1024 checksum=0x1234 cycles=123 mgmt_wait=10 "
"rib_wait=9 sdram_wait=8 psram_wait=0 flash_wait=0 dma_wait=0\n"
"PERF_BENCHMARK_PASS\n"
)
@@ -18,6 +18,7 @@ def test_parse_log_requires_terminal_marker_and_complete_samples() -> None:
assert report["samples"] == [
{
"checksum": 0x1234,
"cycles": 123,
"dma_wait": 0,
"flash_wait": 0,
"mgmt_wait": 10,
@@ -33,7 +34,7 @@ def test_parse_log_requires_terminal_marker_and_complete_samples() -> None:
def test_parse_log_rejects_missing_benchmark_marker() -> None:
report = parse_log(
"PERF region=flash op=read words=1024 checksum=0 mgmt_wait=1 rib_wait=1 "
"PERF region=flash op=read words=1024 checksum=0 cycles=10 mgmt_wait=1 rib_wait=1 "
"sdram_wait=0 psram_wait=0 flash_wait=1 dma_wait=0\n"
)
@@ -42,7 +43,7 @@ def test_parse_log_rejects_missing_benchmark_marker() -> None:
def test_parse_log_rejects_report_with_performance_failure() -> None:
report = parse_log(
"PERF region=sdram op=read words=1024 checksum=0x1234 mgmt_wait=10 "
"PERF region=sdram op=read words=1024 checksum=0x1234 cycles=123 mgmt_wait=10 "
"rib_wait=9 sdram_wait=8 psram_wait=0 flash_wait=0 dma_wait=0\n"
"PERF_FAIL region=sdram op=read reason=data expected=1234 actual=0\n"
"PERF_BENCHMARK_PASS\n"
+146
View File
@@ -0,0 +1,146 @@
"""Directed tests for the SoC-owned RIB burst building blocks."""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_bus_preserves_incr4_and_rejects_illegal_bursts(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
memory_map = tmp_path / "memory_map"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/address_map/generate_memory_map.py"),
"--map",
str(ROOT / "rtl/mini/address_map/memory_map.json"),
"--output-dir",
str(memory_map),
"--have-sram-if",
"NO",
],
check=True,
)
source_list = tmp_path / "rib_burst_bus.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{memory_map / 'rtl'}",
f"+incdir+{ROOT / 'rtl/mini/top'}",
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/rib_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/ram_if.sv"),
str(ROOT / "rtl/mini/top/soc_rib_if.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/spill_register.sv"),
str(ROOT / "rtl/mini/top/soc_rib_legacy_to_burst.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_to_legacy.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_error_slave.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_ram.sv"),
str(ROOT / "rtl/mini/top/bus.sv"),
str(ROOT / "tests/rtl/rib_burst_bus_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "rib_burst_bus_tb.v"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/script/convt_sv2v.py"),
"-f",
str(source_list),
"--output",
str(converted),
],
check=True,
)
simulation = tmp_path / "rib_burst_bus_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "rib_burst_bus_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "RIB burst arbitration and protocol test passed" in result.stdout
def test_common_spill_register_preserves_ready_valid_data(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
simulation = tmp_path / "spill_register_tb"
subprocess.run(
[
iverilog,
"-g2012",
"-s",
"spill_register_tb",
"-o",
str(simulation),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/spill_register.sv"),
str(ROOT / "tests/rtl/spill_register_tb.sv"),
],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "spill register ready/valid test passed" in result.stdout
def test_burst_sram_preserves_data_and_backpressure(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
source_list = tmp_path / "soc_rib_burst_ram.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{ROOT / 'rtl/mini/top'}",
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/ram_if.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/spill_register.sv"),
str(ROOT / "rtl/mini/top/soc_rib_burst_ram.sv"),
str(ROOT / "tests/rtl/soc_rib_burst_ram_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "soc_rib_burst_ram_tb.v"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/script/convt_sv2v.py"),
"-f",
str(source_list),
"--output",
str(converted),
],
check=True,
)
simulation = tmp_path / "soc_rib_burst_ram_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "soc_rib_burst_ram_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "RIB burst SRAM pipeline test passed" in result.stdout
+64 -2
View File
@@ -179,11 +179,19 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
(user_extensions / "rtl").mkdir(parents=True)
bus_filelist = tmp_path / "bus.fl"
rib_adapter_filelist = tmp_path / "rib_adapter.fl"
spill_register_filelist = tmp_path / "spill_register.fl"
rib2apb_filelist = tmp_path / "rib2apb.fl"
sysctrl_filelist = tmp_path / "sysctrl.fl"
pll_rcu_filelist = tmp_path / "pll_rcu.fl"
gpio_user_filelist = tmp_path / "gpio_user.fl"
assert generate_formal_filelist("bus", bus_filelist, memory_map, topology, user_extensions)
assert generate_formal_filelist(
"rib_adapter", rib_adapter_filelist, memory_map, topology, user_extensions
)
assert generate_formal_filelist(
"spill_register", spill_register_filelist, memory_map, topology, user_extensions
)
assert generate_formal_filelist(
"rib2apb", rib2apb_filelist, memory_map, topology, user_extensions
)
@@ -198,14 +206,25 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
)
bus = parse_filelists([bus_filelist], require_files=False)
rib_adapter = parse_filelists([rib_adapter_filelist], require_files=False)
spill_register = parse_filelists([spill_register_filelist], require_files=False)
rib2apb = parse_filelists([rib2apb_filelist], require_files=False)
sysctrl = parse_filelists([sysctrl_filelist], require_files=False)
pll_rcu = parse_filelists([pll_rcu_filelist], require_files=False)
gpio_user = parse_filelists([gpio_user_filelist], require_files=False)
assert "+define+SV_ASSRT_DISABLE" in bus.defines
assert ROOT / "rtl/mini/top/bus.sv" in bus.files
assert ROOT / "rtl/mini/top/soc_rib_regslice.sv" in bus.files
assert ROOT / "rtl/mini/top/soc_rib_burst_if.sv" in bus.files
assert ROOT / "rtl/mini/top/soc_rib_legacy_to_burst.sv" in bus.files
assert ROOT / "rtl/mini/top/soc_rib_burst_error_slave.sv" in bus.files
assert ROOT / "rtl/mini/formal/bus_formal.sv" in bus.files
assert ROOT / "rtl/mini/top/soc_rib_burst_to_legacy.sv" in rib_adapter.files
assert ROOT / "rtl/mini/formal/rib_adapter_formal.sv" in rib_adapter.files
assert (
ROOT / "rtl/managed/clusterip/common/rtl/utils/spill_register.sv"
in spill_register.files
)
assert ROOT / "rtl/mini/formal/spill_register_formal.sv" in spill_register.files
assert ROOT / "rtl/ip/rib/interconnect/rib2apb.sv" in rib2apb.files
assert ROOT / "rtl/mini/formal/rib2apb_formal.sv" in rib2apb.files
assert ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_pure_if.sv" in rib2apb.files
@@ -629,7 +648,7 @@ def test_benchmark_profile_uses_functional_sram_and_reserved_data() -> None:
profile = (ROOT / "configs/benchmark/ihp130-hazard3.mk").read_text(encoding="utf-8")
benchmark = (ROOT / "app/apps/benchmark/main.c").read_text(encoding="utf-8")
assert "PDK_BEHAV ?= NO" in makefile
assert re.search(r"^PDK_BEHAV\s+\?= NO$", makefile, re.MULTILINE)
assert "PDK_BEHAV HAVE_SVA" in makefile
assert "PDK_BEHAV=YES is for functional simulation" in makefile
assert "HAVE_SRAM_MACRO := YES" in profile
@@ -824,6 +843,49 @@ def test_run_flow_writes_structured_result(tmp_path: Path) -> None:
assert log.read_text(encoding="utf-8") == "flow output\n"
def test_run_flow_can_terminate_an_opt_in_local_flow_at_success_marker(tmp_path: Path) -> None:
log = tmp_path / "marker.log"
result = tmp_path / "marker.json"
completed = subprocess.run(
[
sys.executable,
str(ROOT / "scripts/run_flow.py"),
"--tool",
"test",
"--log",
str(log),
"--result",
str(result),
"--success-marker",
"Hello retroSoC!",
"--terminate-on-success-marker",
"--",
sys.executable,
"-c",
"import time; print('Hello retroSoC!', flush=True); time.sleep(30)",
],
text=True,
capture_output=True,
timeout=10,
)
report = json.loads(result.read_text(encoding="utf-8"))
assert completed.returncode == 0
assert report["status"] == "passed"
assert report["completion_mode"] == "success_marker"
assert report["success_marker_seen"] is True
assert "Hello retroSoC!" in log.read_text(encoding="utf-8")
def test_regression_boot_only_mode_replaces_only_netsim_command() -> None:
transformed = regress.with_netsim_boot_only(PR_COMMANDS)
flattened = [value for _, values in transformed for value in values]
assert "netsim" not in flattened
assert "netsim-boot" in flattened
assert "SIM_FIRMWARE_NAME=retrosoc_asm" in flattened
assert not any(value.startswith("SIM_SUCCESS_MARKER=") for value in flattened)
assert any("sta" in values for _, values in transformed)
def test_run_flow_adds_carriage_return_when_terminal_disables_onlcr(monkeypatch) -> None:
class Terminal(io.StringIO):
def isatty(self) -> bool:
+3 -1
View File
@@ -87,7 +87,9 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign tmr.paddr = rib.addr;" in apb_routes
assert "({32{s_psel_q[8]}} & tmr.prdata)" in apb_response
assert "localparam int NSLV = 10;" in apb_declarations
assert fabric.count("rib_if u_") == 5
assert fabric.count("soc_rib_if u_") == 1
assert fabric.count("soc_rib_burst_if u_") == 3
assert fabric.count("rib_if u_") == 2
assert ".mgmt_rib(u_mgmt_rib_if)" in bus_fabric
assert ".user_rib(u_user_rib_if)" in bus_fabric
assert ".apb_rib(u_apb_rib_if)" in bus_fabric
+14 -1
View File
@@ -48,12 +48,17 @@ def test_extensions_generate_isolated_scalar_bindings(tmp_path: Path) -> None:
ip = (tmp_path / "rtl/user_ip_bindings.svh").read_text(encoding="utf-8")
filelist = (tmp_path / "user_extensions.fl").read_text(encoding="utf-8")
assert core.count("soc_rib_burst_if u_user_") == 6
assert core.count("rib_if u_user_") == 6
assert "rib.valid = '0;" in core
assert core.count("rib_legacy_to_burst #(") == 6
assert core.count(".SYNC_RESET(1'b1)") == 2
assert core.count(".SYNC_RESET(1'b0)") == 4
assert "rib.cmd_valid = '0;" in core
assert "5'd0: begin" in core
assert "5'd5: begin" in core
assert "core_reset_i[0]" in core
assert "user_core_design_username1 #(" in core
assert "User core 0 uses the legacy RIB contract" in core
assert ip.count("user_gpio_if #(`USER_GPIO_NUM)") == 2
assert "gpio.do_o = '0;" in ip
assert "8'd2: begin" in ip
@@ -73,3 +78,11 @@ def test_extensions_reject_noncontiguous_slots_and_invalid_modules(tmp_path: Pat
result = validate(write_invalid_extensions(tmp_path, document))
assert result.returncode != 0
assert "must be a SystemVerilog identifier" in result.stderr
def test_extensions_reject_invalid_core_reset_style(tmp_path: Path) -> None:
document = json.loads(EXTENSIONS.read_text(encoding="utf-8"))
document["core_targets"][0]["reset"] = "mixed"
result = validate(write_invalid_extensions(tmp_path, document))
assert result.returncode != 0
assert ".reset must be sync or async" in result.stderr