Files
retroSoC/tests/rtl/sdram_data_tb.sv
T
maksyuki a32d9af957 feat: enhance SDRAM Controller and Testbench Functionality
- Introduced burst length handling in the SDRAM Verilator model, allowing for more flexible read/write operations.
- Added new SDRAM-related source files to the project structure, including sdram_pkg.sv and sdram_axi4.sv.
- Updated software makefile to include new SDRAM-related C source files for HAL.
- Enhanced C test suite with new tests for SDRAM timing and functionality, ensuring proper integration with the HAL.
- Improved RTL testbench for SDRAM, adding support for burst writes and reads, and verifying data integrity.
- Updated Python test scripts to reflect changes in SDRAM handling and ensure comprehensive coverage of SDRAM functionality.
- Added checks to ensure register offsets between RTL and HAL definitions match, maintaining consistency across the codebase.
2026-08-18 09:06:19 +08:00

372 lines
11 KiB
Systemverilog

`timescale 1ns / 1ps
`include "axi4_define.svh"
`include "sdram_define.svh"
module sdram_data_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
wire [15:0] s_dq;
logic [31:0] s_read_data;
logic [31:0] s_apb_data;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
apb4_if cfg_apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
sdram_if sdram ();
always #5 clk_i = ~clk_i;
assign s_dq = sdram.oe_o ? sdram.dq_o : 'z;
assign sdram.dq_i = s_dq;
axi4_sdram u_axi4_sdram (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.axi4 (axi4),
.cfg_apb4(cfg_apb4),
.sdram (sdram)
);
`ifdef SDRAM_TIMING_MODEL
sdr u_sdr (
.Clk (sdram.clk_o),
.Cke (sdram.cke_o),
.Cs_n (sdram.cs_n_o),
.Ras_n(sdram.ras_n_o),
.Cas_n(sdram.cas_n_o),
.We_n (sdram.we_n_o),
.Addr (sdram.addr_o),
.Ba (sdram.ba_o),
.Dq (s_dq),
.Dqm (sdram.dqm_o)
);
`else
sdram_verilator_model u_sdram_verilator_model (
.clk_i (sdram.clk_o),
.cke_i (sdram.cke_o),
.cs_n_i (sdram.cs_n_o),
.ras_n_i(sdram.ras_n_o),
.cas_n_i(sdram.cas_n_o),
.we_n_i (sdram.we_n_o),
.ba_i (sdram.ba_o),
.addr_i (sdram.addr_o),
.dqm_i (sdram.dqm_o),
.dq_io (s_dq)
);
`endif
task automatic init_axi4;
begin
axi4.awid = '0;
axi4.awaddr = '0;
axi4.awlen = '0;
axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
axi4.awburst = `AXI4_BURST_TYPE_INCR;
axi4.awlock = `AXI4_LOCK_NORM;
axi4.awcache = `AXI4_CACHE_NO_BUF;
axi4.awprot = `AXI4_PROT_DATA;
axi4.awqos = `AXI4_QOS_NORMAL;
axi4.awregion = `AXI4_REGION_NORMAL;
axi4.awuser = '0;
axi4.awvalid = 1'b0;
axi4.wdata = '0;
axi4.wstrb = '0;
axi4.wlast = 1'b0;
axi4.wuser = '0;
axi4.wvalid = 1'b0;
axi4.bready = 1'b0;
axi4.arid = '0;
axi4.araddr = '0;
axi4.arlen = '0;
axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
axi4.arburst = `AXI4_BURST_TYPE_INCR;
axi4.arlock = `AXI4_LOCK_NORM;
axi4.arcache = `AXI4_CACHE_NO_BUF;
axi4.arprot = `AXI4_PROT_DATA;
axi4.arqos = `AXI4_QOS_NORMAL;
axi4.arregion = `AXI4_REGION_NORMAL;
axi4.aruser = '0;
axi4.arvalid = 1'b0;
axi4.rready = 1'b0;
cfg_apb4.psel = 1'b0;
cfg_apb4.penable = 1'b0;
cfg_apb4.pwrite = 1'b0;
cfg_apb4.paddr = '0;
cfg_apb4.pwdata = '0;
cfg_apb4.pstrb = '0;
end
endtask
task automatic apb_write(input logic [11:0] offset, input logic [31:0] data);
begin
@(negedge clk_i);
cfg_apb4.psel = 1'b1;
cfg_apb4.penable = 1'b0;
cfg_apb4.pwrite = 1'b1;
cfg_apb4.paddr = {20'd0, offset};
cfg_apb4.pwdata = data;
cfg_apb4.pstrb = 4'hF;
@(posedge clk_i);
@(negedge clk_i);
cfg_apb4.penable = 1'b1;
do @(posedge clk_i); while (!cfg_apb4.pready);
if (cfg_apb4.pslverr) begin
$fatal(1, "APB write SLVERR at %03x", offset);
end
@(negedge clk_i);
cfg_apb4.psel = 1'b0;
cfg_apb4.penable = 1'b0;
cfg_apb4.pwrite = 1'b0;
end
endtask
task automatic apb_read(input logic [11:0] offset);
begin
@(negedge clk_i);
cfg_apb4.psel = 1'b1;
cfg_apb4.penable = 1'b0;
cfg_apb4.pwrite = 1'b0;
cfg_apb4.paddr = {20'd0, offset};
cfg_apb4.pstrb = 4'h0;
@(posedge clk_i);
@(negedge clk_i);
cfg_apb4.penable = 1'b1;
do @(posedge clk_i); while (!cfg_apb4.pready);
if (cfg_apb4.pslverr) begin
$fatal(1, "APB read SLVERR at %03x", offset);
end
s_apb_data = cfg_apb4.prdata;
@(negedge clk_i);
cfg_apb4.psel = 1'b0;
cfg_apb4.penable = 1'b0;
end
endtask
task automatic write_word(input logic [31:0] address, input logic [31:0] data,
input logic [3:0] strobe);
begin
@(negedge clk_i);
axi4.awaddr = address;
axi4.awlen = 8'd0;
axi4.awburst = `AXI4_BURST_TYPE_INCR;
axi4.awlock = `AXI4_LOCK_NORM;
axi4.awvalid = 1'b1;
do @(posedge clk_i); while (!axi4.awready);
@(negedge clk_i);
axi4.awvalid = 1'b0;
axi4.wdata = data;
axi4.wstrb = strobe;
axi4.wlast = 1'b1;
axi4.wvalid = 1'b1;
do @(posedge clk_i); while (!axi4.wready);
@(negedge clk_i);
axi4.wvalid = 1'b0;
axi4.wlast = 1'b0;
axi4.bready = 1'b1;
do @(posedge clk_i); while (!axi4.bvalid);
if (axi4.bresp != `AXI4_RESP_OKAY) begin
$fatal(1, "AXI4 SDRAM write failed at %08x", address);
end
@(negedge clk_i);
axi4.bready = 1'b0;
end
endtask
task automatic read_word(input logic [31:0] address);
begin
@(negedge clk_i);
axi4.araddr = address;
axi4.arlen = 8'd0;
axi4.arburst = `AXI4_BURST_TYPE_INCR;
axi4.arlock = `AXI4_LOCK_NORM;
axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!axi4.arready);
@(negedge clk_i);
axi4.arvalid = 1'b0;
axi4.rready = 1'b1;
do @(posedge clk_i); while (!axi4.rvalid);
s_read_data = axi4.rdata;
if (axi4.rresp != `AXI4_RESP_OKAY || !axi4.rlast) begin
$fatal(1, "AXI4 SDRAM read failed at %08x", address);
end
@(negedge clk_i);
axi4.rready = 1'b0;
end
endtask
task automatic write_incr(input logic [31:0] address, input logic [7:0] length);
integer beat;
begin
@(negedge clk_i);
axi4.awaddr = address;
axi4.awlen = length;
axi4.awburst = `AXI4_BURST_TYPE_INCR;
axi4.awvalid = 1'b1;
do @(posedge clk_i); while (!axi4.awready);
@(negedge clk_i);
axi4.awvalid = 1'b0;
for (beat = 0; beat <= length; beat++) begin
axi4.wdata = 32'h1000_0000 + address + (beat * 4);
axi4.wstrb = 4'hF;
axi4.wlast = (beat == length);
axi4.wvalid = 1'b1;
do @(posedge clk_i); while (!axi4.wready);
@(negedge clk_i);
end
axi4.wvalid = 1'b0;
axi4.wlast = 1'b0;
axi4.bready = 1'b1;
do @(posedge clk_i); while (!axi4.bvalid);
if (axi4.bresp != `AXI4_RESP_OKAY) begin
$fatal(1, "AXI4 SDRAM INCR write failed at %08x", address);
end
@(negedge clk_i);
axi4.bready = 1'b0;
end
endtask
task automatic read_incr(input logic [31:0] address, input logic [7:0] length);
integer beat;
begin
@(negedge clk_i);
axi4.araddr = address;
axi4.arlen = length;
axi4.arburst = `AXI4_BURST_TYPE_INCR;
axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!axi4.arready);
@(negedge clk_i);
axi4.arvalid = 1'b0;
axi4.rready = 1'b1;
for (beat = 0; beat <= length; beat++) begin
do @(posedge clk_i); while (!axi4.rvalid);
if (axi4.rdata !== (32'h1000_0000 + address + (beat * 4))) begin
$fatal(1, "INCR read mismatch beat %0d got %08x", beat, axi4.rdata);
end
if (axi4.rresp != `AXI4_RESP_OKAY) begin
$fatal(1, "INCR read SLVERR beat %0d", beat);
end
if (axi4.rlast !== (beat == length)) begin
$fatal(1, "INCR RLAST mismatch beat %0d", beat);
end
@(negedge clk_i);
end
axi4.rready = 1'b0;
end
endtask
initial begin
#2_000_000;
$fatal(1, "simulation timeout");
end
initial begin
init_axi4();
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
$display("TB reset released");
write_word(`SOC_ADDR_SDRAM_BASE + 32'h40, 32'h1357_9BDF, 4'b1111);
read_word(`SOC_ADDR_SDRAM_BASE + 32'h40);
if (s_read_data !== 32'h1357_9BDF) begin
$fatal(1, "post-reset stalled SDRAM readback was %08x", s_read_data);
end
// Auto-init is already complete if the post-reset access returned OKAY.
repeat (8000) @(posedge clk_i);
$display("TB init wait done");
apb_read(`APB4_SDRAM__STATUS);
$display("TB STATUS=%08x", s_apb_data);
if (!s_apb_data[`APB4_SDRAM__STATUS_READY]) begin
$fatal(1, "SDRAM STATUS.READY was not set after auto-init");
end
apb_read(`APB4_SDRAM__IP_VERSION);
if (s_apb_data !== `APB4_SDRAM__IP_VERSION_VALUE) begin
$fatal(1, "SDRAM IP_VERSION mismatch %08x", s_apb_data);
end
apb_read(`APB4_SDRAM__CAPABILITY);
if (s_apb_data !== `APB4_SDRAM__CAPABILITY_VALUE) begin
$fatal(1, "SDRAM CAPABILITY mismatch %08x", s_apb_data);
end
write_word(`SOC_ADDR_SDRAM_BASE, 32'h1234_5678, 4'b1111);
read_word(`SOC_ADDR_SDRAM_BASE);
if (s_read_data !== 32'h1234_5678) begin
$fatal(1, "full-word SDRAM readback was %08x", s_read_data);
end
write_word(`SOC_ADDR_SDRAM_BASE, 32'hA5A5_0000, 4'b1100);
read_word(`SOC_ADDR_SDRAM_BASE);
if (s_read_data !== 32'hA5A5_5678) begin
$fatal(1, "masked SDRAM readback was %08x", s_read_data);
end
write_word(`SOC_ADDR_SDRAM_BASE + 32'h03FF_FFFC, 32'hCAFE_BABE, 4'b1111);
read_word(`SOC_ADDR_SDRAM_BASE + 32'h03FF_FFFC);
if (s_read_data !== 32'hCAFE_BABE) begin
$fatal(1, "high-address SDRAM readback was %08x", s_read_data);
end
write_incr(`SOC_ADDR_SDRAM_BASE + 32'h100, 8'd3);
read_incr(`SOC_ADDR_SDRAM_BASE + 32'h100, 8'd3);
write_incr(`SOC_ADDR_SDRAM_BASE + 32'h200, 8'd7);
read_incr(`SOC_ADDR_SDRAM_BASE + 32'h200, 8'd7);
write_incr(`SOC_ADDR_SDRAM_BASE + 32'h300, 8'd15);
read_incr(`SOC_ADDR_SDRAM_BASE + 32'h300, 8'd15);
@(negedge clk_i);
axi4.araddr = `SOC_ADDR_SDRAM_BASE;
axi4.arlen = 8'd0;
axi4.arlock = 1'b1;
axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!axi4.arready);
@(negedge clk_i);
axi4.arvalid = 1'b0;
axi4.arlock = `AXI4_LOCK_NORM;
axi4.rready = 1'b1;
do @(posedge clk_i); while (!axi4.rvalid);
if (axi4.rresp != `AXI4_RESP_SLAVE_ERROR) begin
$fatal(1, "exclusive read did not return SLVERR");
end
@(negedge clk_i);
axi4.rready = 1'b0;
apb_write(`APB4_SDRAM__COMMAND, 32'h1 << `APB4_SDRAM__COMMAND_PRECHARGE_ALL);
read_word(`SOC_ADDR_SDRAM_BASE);
if (s_read_data !== 32'hA5A5_5678) begin
$fatal(1, "post-precharge SDRAM readback was %08x", s_read_data);
end
apb_write(`APB4_SDRAM__CTRL, 32'h1 << `APB4_SDRAM__CTRL_ENABLE);
@(negedge clk_i);
axi4.araddr = `SOC_ADDR_SDRAM_BASE;
axi4.arlen = 8'd0;
axi4.arburst = `AXI4_BURST_TYPE_INCR;
axi4.arlock = `AXI4_LOCK_NORM;
axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!axi4.arready);
@(negedge clk_i);
axi4.arvalid = 1'b0;
axi4.rready = 1'b1;
do @(posedge clk_i); while (!axi4.rvalid);
if (axi4.rresp != `AXI4_RESP_SLAVE_ERROR) begin
$fatal(1, "disabled-window read did not return SLVERR");
end
@(negedge clk_i);
axi4.rready = 1'b0;
$display("sdram data integrity test passed");
$finish;
end
endmodule