mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
- 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.
372 lines
11 KiB
Systemverilog
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
|