mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
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:
57 files changed
+3239
-727
No files matched your search
+56
-32
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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"),
|
||||
"",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user