mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
feat: improve the performance of the onchip sram ctrl
This commit is contained in:
53 files changed
+2881
-151
No files matched your search
@@ -0,0 +1,26 @@
|
||||
// Functional test model for the site-supplied ICS55 1024 x 32 SRAM macro.
|
||||
module S55NLLG1PH_X256Y4D32_BW (
|
||||
input logic [31:0] D,
|
||||
input logic CLK,
|
||||
input logic CEN,
|
||||
input logic WEN,
|
||||
input logic [31:0] BWEN,
|
||||
input logic [ 9:0] A,
|
||||
output logic [31:0] Q
|
||||
);
|
||||
logic [31:0] s_memory[0:1023];
|
||||
|
||||
always_ff @(posedge CLK) begin
|
||||
if (!CEN) begin
|
||||
if (!WEN) begin
|
||||
for (int bit_index = 0; bit_index < 32; bit_index++) begin
|
||||
if (!BWEN[bit_index]) begin
|
||||
s_memory[A][bit_index] <= D[bit_index];
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
Q <= s_memory[A];
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,146 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
`include "axi4_define.svh"
|
||||
`include "onchip_ram_define.svh"
|
||||
|
||||
module onchip_ram_absent_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) mem_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
apb4_if cfg_apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
onchip_ram #(
|
||||
.Present (1'b0),
|
||||
.CapacityKiB(128)
|
||||
) u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.perf_enable_i(1'b1),
|
||||
.perf_clear_i (1'b0),
|
||||
.mem_axi4 (mem_axi4),
|
||||
.cfg_apb4 (cfg_apb4)
|
||||
);
|
||||
|
||||
task automatic init_interfaces;
|
||||
begin
|
||||
mem_axi4.awid = '0;
|
||||
mem_axi4.awaddr = '0;
|
||||
mem_axi4.awlen = '0;
|
||||
mem_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
|
||||
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
|
||||
mem_axi4.awlock = `AXI4_LOCK_NORM;
|
||||
mem_axi4.awcache = `AXI4_CACHE_NO_BUF;
|
||||
mem_axi4.awprot = `AXI4_PROT_DATA;
|
||||
mem_axi4.awqos = `AXI4_QOS_NORMAL;
|
||||
mem_axi4.awregion = `AXI4_REGION_NORMAL;
|
||||
mem_axi4.awuser = '0;
|
||||
mem_axi4.awvalid = 1'b0;
|
||||
mem_axi4.wdata = '0;
|
||||
mem_axi4.wstrb = '0;
|
||||
mem_axi4.wlast = 1'b0;
|
||||
mem_axi4.wuser = '0;
|
||||
mem_axi4.wvalid = 1'b0;
|
||||
mem_axi4.bready = 1'b0;
|
||||
mem_axi4.arid = '0;
|
||||
mem_axi4.araddr = '0;
|
||||
mem_axi4.arlen = '0;
|
||||
mem_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
|
||||
mem_axi4.arburst = `AXI4_BURST_TYPE_INCR;
|
||||
mem_axi4.arlock = `AXI4_LOCK_NORM;
|
||||
mem_axi4.arcache = `AXI4_CACHE_NO_BUF;
|
||||
mem_axi4.arprot = `AXI4_PROT_DATA;
|
||||
mem_axi4.arqos = `AXI4_QOS_NORMAL;
|
||||
mem_axi4.arregion = `AXI4_REGION_NORMAL;
|
||||
mem_axi4.aruser = '0;
|
||||
mem_axi4.arvalid = 1'b0;
|
||||
mem_axi4.rready = 1'b0;
|
||||
cfg_apb4.paddr = '0;
|
||||
cfg_apb4.pprot = '0;
|
||||
cfg_apb4.psel = 1'b0;
|
||||
cfg_apb4.penable = 1'b0;
|
||||
cfg_apb4.pwrite = 1'b0;
|
||||
cfg_apb4.pwdata = '0;
|
||||
cfg_apb4.pstrb = '0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_read(input logic [11:0] offset, input logic [31:0] expected);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
cfg_apb4.paddr = {20'd0, offset};
|
||||
cfg_apb4.psel = 1'b1;
|
||||
cfg_apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
cfg_apb4.penable = 1'b1;
|
||||
do @(posedge clk_i); while (!cfg_apb4.pready);
|
||||
if (cfg_apb4.pslverr || (cfg_apb4.prdata !== expected)) begin
|
||||
$fatal(1, "absent SRAM APB read mismatch offset=%03x", offset);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
cfg_apb4.psel = 1'b0;
|
||||
cfg_apb4.penable = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
init_interfaces();
|
||||
repeat (3) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
apb_read(`APB4_ONCHIP_RAM__CAPABILITY, 32'h0004_107E);
|
||||
apb_read(`APB4_ONCHIP_RAM__MEMORY_BYTES, 32'd0);
|
||||
apb_read(`APB4_ONCHIP_RAM__BANK_COUNT, 32'd0);
|
||||
|
||||
@(negedge clk_i);
|
||||
mem_axi4.araddr = `SOC_ADDR_SRAM_BASE;
|
||||
mem_axi4.arvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.arready);
|
||||
@(negedge clk_i);
|
||||
mem_axi4.arvalid = 1'b0;
|
||||
mem_axi4.rready = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.rvalid);
|
||||
if ((mem_axi4.rresp != `AXI4_RESP_DECODE_ERROR) || !mem_axi4.rlast) begin
|
||||
$fatal(1, "absent SRAM read did not return DECERR");
|
||||
end
|
||||
@(negedge clk_i);
|
||||
mem_axi4.rready = 1'b0;
|
||||
|
||||
@(negedge clk_i);
|
||||
mem_axi4.awaddr = `SOC_ADDR_SRAM_BASE;
|
||||
mem_axi4.awvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.awready);
|
||||
@(negedge clk_i);
|
||||
mem_axi4.awvalid = 1'b0;
|
||||
mem_axi4.wdata = 32'hDEAD_BEEF;
|
||||
mem_axi4.wstrb = 4'hF;
|
||||
mem_axi4.wlast = 1'b1;
|
||||
mem_axi4.wvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.wready);
|
||||
@(negedge clk_i);
|
||||
mem_axi4.wvalid = 1'b0;
|
||||
mem_axi4.wlast = 1'b0;
|
||||
mem_axi4.bready = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.bvalid);
|
||||
if (mem_axi4.bresp != `AXI4_RESP_DECODE_ERROR) begin
|
||||
$fatal(1, "absent SRAM write did not return DECERR");
|
||||
end
|
||||
|
||||
$display("absent on-chip SRAM test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,192 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
`include "axi4_define.svh"
|
||||
|
||||
module onchip_ram_perf_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic [31:0] s_legacy_memory [0:1023];
|
||||
integer s_native_cycles;
|
||||
integer s_legacy_cycles;
|
||||
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) native_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) legacy_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
apb4_if cfg_apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
ram_if legacy_ram ();
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
legacy_ram.rdata <= s_legacy_memory[legacy_ram.addr[9:0]];
|
||||
for (int lane = 0; lane < 4; lane++) begin
|
||||
if (legacy_ram.wstrb[lane]) begin
|
||||
s_legacy_memory[legacy_ram.addr[9:0]][lane*8+:8] <= legacy_ram.wdata[lane*8+:8];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
onchip_ram #(
|
||||
.Present (1'b1),
|
||||
.CapacityKiB(4)
|
||||
) u_native (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.perf_enable_i(1'b0),
|
||||
.perf_clear_i (1'b0),
|
||||
.mem_axi4 (native_axi4),
|
||||
.cfg_apb4 (cfg_apb4)
|
||||
);
|
||||
axi42ram u_legacy (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.axi4 (legacy_axi4),
|
||||
.ram (legacy_ram)
|
||||
);
|
||||
|
||||
task automatic init_axi4;
|
||||
begin
|
||||
native_axi4.awid = '0;
|
||||
native_axi4.awaddr = '0;
|
||||
native_axi4.awlen = '0;
|
||||
native_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
|
||||
native_axi4.awburst = `AXI4_BURST_TYPE_INCR;
|
||||
native_axi4.awlock = 1'b0;
|
||||
native_axi4.awcache = '0;
|
||||
native_axi4.awprot = '0;
|
||||
native_axi4.awqos = '0;
|
||||
native_axi4.awregion = '0;
|
||||
native_axi4.awuser = '0;
|
||||
native_axi4.awvalid = 1'b0;
|
||||
native_axi4.wdata = '0;
|
||||
native_axi4.wstrb = '0;
|
||||
native_axi4.wlast = 1'b0;
|
||||
native_axi4.wuser = '0;
|
||||
native_axi4.wvalid = 1'b0;
|
||||
native_axi4.bready = 1'b0;
|
||||
native_axi4.arid = '0;
|
||||
native_axi4.araddr = '0;
|
||||
native_axi4.arlen = '0;
|
||||
native_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
|
||||
native_axi4.arburst = `AXI4_BURST_TYPE_INCR;
|
||||
native_axi4.arlock = 1'b0;
|
||||
native_axi4.arcache = '0;
|
||||
native_axi4.arprot = '0;
|
||||
native_axi4.arqos = '0;
|
||||
native_axi4.arregion = '0;
|
||||
native_axi4.aruser = '0;
|
||||
native_axi4.arvalid = 1'b0;
|
||||
native_axi4.rready = 1'b1;
|
||||
legacy_axi4.awid = '0;
|
||||
legacy_axi4.awaddr = '0;
|
||||
legacy_axi4.awlen = '0;
|
||||
legacy_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
|
||||
legacy_axi4.awburst = `AXI4_BURST_TYPE_INCR;
|
||||
legacy_axi4.awlock = 1'b0;
|
||||
legacy_axi4.awcache = '0;
|
||||
legacy_axi4.awprot = '0;
|
||||
legacy_axi4.awqos = '0;
|
||||
legacy_axi4.awregion = '0;
|
||||
legacy_axi4.awuser = '0;
|
||||
legacy_axi4.awvalid = 1'b0;
|
||||
legacy_axi4.wdata = '0;
|
||||
legacy_axi4.wstrb = '0;
|
||||
legacy_axi4.wlast = 1'b0;
|
||||
legacy_axi4.wuser = '0;
|
||||
legacy_axi4.wvalid = 1'b0;
|
||||
legacy_axi4.bready = 1'b0;
|
||||
legacy_axi4.arid = '0;
|
||||
legacy_axi4.araddr = '0;
|
||||
legacy_axi4.arlen = '0;
|
||||
legacy_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
|
||||
legacy_axi4.arburst = `AXI4_BURST_TYPE_INCR;
|
||||
legacy_axi4.arlock = 1'b0;
|
||||
legacy_axi4.arcache = '0;
|
||||
legacy_axi4.arprot = '0;
|
||||
legacy_axi4.arqos = '0;
|
||||
legacy_axi4.arregion = '0;
|
||||
legacy_axi4.aruser = '0;
|
||||
legacy_axi4.arvalid = 1'b0;
|
||||
legacy_axi4.rready = 1'b1;
|
||||
cfg_apb4.paddr = '0;
|
||||
cfg_apb4.pprot = '0;
|
||||
cfg_apb4.psel = 1'b0;
|
||||
cfg_apb4.penable = 1'b0;
|
||||
cfg_apb4.pwrite = 1'b0;
|
||||
cfg_apb4.pwdata = '0;
|
||||
cfg_apb4.pstrb = '0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic measure_native;
|
||||
integer beats;
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
native_axi4.araddr = `SOC_ADDR_SRAM_BASE;
|
||||
native_axi4.arlen = 8'd15;
|
||||
native_axi4.arvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!native_axi4.arready);
|
||||
@(negedge clk_i);
|
||||
native_axi4.arvalid = 1'b0;
|
||||
s_native_cycles = 0;
|
||||
beats = 0;
|
||||
while (beats < 16) begin
|
||||
@(posedge clk_i);
|
||||
s_native_cycles = s_native_cycles + 1;
|
||||
if (native_axi4.rvalid) beats = beats + 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic measure_legacy;
|
||||
integer beats;
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
legacy_axi4.araddr = `SOC_ADDR_SRAM_BASE;
|
||||
legacy_axi4.arlen = 8'd15;
|
||||
legacy_axi4.arvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!legacy_axi4.arready);
|
||||
@(negedge clk_i);
|
||||
legacy_axi4.arvalid = 1'b0;
|
||||
s_legacy_cycles = 0;
|
||||
beats = 0;
|
||||
while (beats < 16) begin
|
||||
@(posedge clk_i);
|
||||
s_legacy_cycles = s_legacy_cycles + 1;
|
||||
if (legacy_axi4.rvalid) beats = beats + 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
init_axi4();
|
||||
repeat (3) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
measure_native();
|
||||
measure_legacy();
|
||||
$display("on-chip SRAM burst cycles native=%0d legacy=%0d", s_native_cycles, s_legacy_cycles);
|
||||
if ((s_native_cycles != 16) || (s_legacy_cycles < (s_native_cycles + 2))) begin
|
||||
$fatal(1, "native SRAM did not reach one beat per cycle or remove bridge overhead");
|
||||
end
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,387 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
`include "axi4_define.svh"
|
||||
`include "mmap_define.svh"
|
||||
`include "onchip_ram_define.svh"
|
||||
|
||||
module onchip_ram_tb #(
|
||||
parameter int unsigned CapacityKiB = 128
|
||||
);
|
||||
localparam logic [31:0] SramBase = `SOC_ADDR_SRAM_BASE;
|
||||
localparam logic [31:0] SramEnd = `SOC_ADDR_SRAM_END;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic perf_enable_i = 1'b0;
|
||||
logic perf_clear_i = 1'b0;
|
||||
logic [31:0] s_apb_read_data;
|
||||
logic [31:0] s_expected;
|
||||
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) mem_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
apb4_if cfg_apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
onchip_ram #(
|
||||
.Present (1'b1),
|
||||
.CapacityKiB(CapacityKiB)
|
||||
) u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.perf_enable_i(perf_enable_i),
|
||||
.perf_clear_i (perf_clear_i),
|
||||
.mem_axi4 (mem_axi4),
|
||||
.cfg_apb4 (cfg_apb4)
|
||||
);
|
||||
|
||||
task automatic init_interfaces;
|
||||
begin
|
||||
mem_axi4.awid = '0;
|
||||
mem_axi4.awaddr = '0;
|
||||
mem_axi4.awlen = '0;
|
||||
mem_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
|
||||
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
|
||||
mem_axi4.awlock = `AXI4_LOCK_NORM;
|
||||
mem_axi4.awcache = `AXI4_CACHE_NO_BUF;
|
||||
mem_axi4.awprot = `AXI4_PROT_DATA;
|
||||
mem_axi4.awqos = `AXI4_QOS_NORMAL;
|
||||
mem_axi4.awregion = `AXI4_REGION_NORMAL;
|
||||
mem_axi4.awuser = '0;
|
||||
mem_axi4.awvalid = 1'b0;
|
||||
mem_axi4.wdata = '0;
|
||||
mem_axi4.wstrb = '0;
|
||||
mem_axi4.wlast = 1'b0;
|
||||
mem_axi4.wuser = '0;
|
||||
mem_axi4.wvalid = 1'b0;
|
||||
mem_axi4.bready = 1'b0;
|
||||
mem_axi4.arid = '0;
|
||||
mem_axi4.araddr = '0;
|
||||
mem_axi4.arlen = '0;
|
||||
mem_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
|
||||
mem_axi4.arburst = `AXI4_BURST_TYPE_INCR;
|
||||
mem_axi4.arlock = `AXI4_LOCK_NORM;
|
||||
mem_axi4.arcache = `AXI4_CACHE_NO_BUF;
|
||||
mem_axi4.arprot = `AXI4_PROT_DATA;
|
||||
mem_axi4.arqos = `AXI4_QOS_NORMAL;
|
||||
mem_axi4.arregion = `AXI4_REGION_NORMAL;
|
||||
mem_axi4.aruser = '0;
|
||||
mem_axi4.arvalid = 1'b0;
|
||||
mem_axi4.rready = 1'b0;
|
||||
cfg_apb4.paddr = '0;
|
||||
cfg_apb4.pprot = '0;
|
||||
cfg_apb4.psel = 1'b0;
|
||||
cfg_apb4.penable = 1'b0;
|
||||
cfg_apb4.pwrite = 1'b0;
|
||||
cfg_apb4.pwdata = '0;
|
||||
cfg_apb4.pstrb = '0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic write_burst(input logic [31:0] address, input logic [7:0] length,
|
||||
input logic [2:0] size, input logic [1:0] burst,
|
||||
input logic [31:0] seed, input logic [1:0] response);
|
||||
integer previous_cycle;
|
||||
integer cycle_count;
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
mem_axi4.awaddr = address;
|
||||
mem_axi4.awlen = length;
|
||||
mem_axi4.awsize = size;
|
||||
mem_axi4.awburst = burst;
|
||||
mem_axi4.awvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.awready);
|
||||
@(negedge clk_i);
|
||||
mem_axi4.awvalid = 1'b0;
|
||||
previous_cycle = -1;
|
||||
cycle_count = 0;
|
||||
for (int beat = 0; beat <= int'(length); beat++) begin
|
||||
mem_axi4.wdata = seed + beat;
|
||||
mem_axi4.wstrb = 4'hF;
|
||||
mem_axi4.wlast = beat == int'(length);
|
||||
mem_axi4.wvalid = 1'b1;
|
||||
do begin
|
||||
@(posedge clk_i);
|
||||
cycle_count = cycle_count + 1;
|
||||
end while (!mem_axi4.wready);
|
||||
if ((previous_cycle >= 0) && (cycle_count != previous_cycle + 1)) begin
|
||||
$fatal(1, "write burst inserted a bubble at beat %0d", beat);
|
||||
end
|
||||
previous_cycle = cycle_count;
|
||||
@(negedge clk_i);
|
||||
end
|
||||
mem_axi4.wvalid = 1'b0;
|
||||
mem_axi4.wlast = 1'b0;
|
||||
mem_axi4.bready = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.bvalid);
|
||||
if (mem_axi4.bresp != response) begin
|
||||
$fatal(1, "write response=%0d expected=%0d", mem_axi4.bresp, response);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
mem_axi4.bready = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic read_burst(input logic [31:0] address, input logic [7:0] length,
|
||||
input logic [2:0] size, input logic [1:0] burst,
|
||||
input logic [31:0] seed, input logic [1:0] response,
|
||||
input logic check_data, input logic add_backpressure);
|
||||
logic [31:0] held_data;
|
||||
logic [ 1:0] held_resp;
|
||||
logic held_last;
|
||||
integer previous_cycle;
|
||||
integer cycle_count;
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
mem_axi4.araddr = address;
|
||||
mem_axi4.arlen = length;
|
||||
mem_axi4.arsize = size;
|
||||
mem_axi4.arburst = burst;
|
||||
mem_axi4.arvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.arready);
|
||||
@(negedge clk_i);
|
||||
mem_axi4.arvalid = 1'b0;
|
||||
mem_axi4.rready = 1'b1;
|
||||
previous_cycle = -1;
|
||||
cycle_count = 0;
|
||||
for (int beat = 0; beat <= int'(length); beat++) begin
|
||||
if (add_backpressure && (beat == 1)) begin
|
||||
@(negedge clk_i);
|
||||
mem_axi4.rready = 1'b0;
|
||||
while (!mem_axi4.rvalid) @(negedge clk_i);
|
||||
held_data = mem_axi4.rdata;
|
||||
held_resp = mem_axi4.rresp;
|
||||
held_last = mem_axi4.rlast;
|
||||
repeat (3) begin
|
||||
@(negedge clk_i);
|
||||
if (!mem_axi4.rvalid || (mem_axi4.rdata !== held_data) ||
|
||||
(mem_axi4.rresp !== held_resp) || (mem_axi4.rlast !== held_last)) begin
|
||||
$fatal(1, "read response changed under backpressure");
|
||||
end
|
||||
end
|
||||
mem_axi4.rready = 1'b1;
|
||||
previous_cycle = -1;
|
||||
end
|
||||
do begin
|
||||
@(posedge clk_i);
|
||||
cycle_count = cycle_count + 1;
|
||||
end while (!mem_axi4.rvalid);
|
||||
if (mem_axi4.rresp != response || mem_axi4.rlast != (beat == int'(length))) begin
|
||||
$fatal(1, "read response mismatch at beat %0d", beat);
|
||||
end
|
||||
s_expected = seed + beat;
|
||||
if (check_data && (mem_axi4.rdata !== s_expected)) begin
|
||||
$fatal(1, "read data=%08x expected=%08x at beat %0d", mem_axi4.rdata, s_expected, beat);
|
||||
end
|
||||
if ((previous_cycle >= 0) && (cycle_count != previous_cycle + 1)) begin
|
||||
$fatal(1, "read burst inserted a bubble at beat %0d", beat);
|
||||
end
|
||||
previous_cycle = cycle_count;
|
||||
end
|
||||
@(negedge clk_i);
|
||||
mem_axi4.rready = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic write_single(input logic [31:0] address, input logic [2:0] size,
|
||||
input logic [31:0] data, input logic [3:0] strobe,
|
||||
input logic [1:0] response);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
mem_axi4.awaddr = address;
|
||||
mem_axi4.awlen = 8'd0;
|
||||
mem_axi4.awsize = size;
|
||||
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
|
||||
mem_axi4.awvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.awready);
|
||||
@(negedge clk_i);
|
||||
mem_axi4.awvalid = 1'b0;
|
||||
mem_axi4.wdata = data;
|
||||
mem_axi4.wstrb = strobe;
|
||||
mem_axi4.wlast = 1'b1;
|
||||
mem_axi4.wvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.wready);
|
||||
@(negedge clk_i);
|
||||
mem_axi4.wvalid = 1'b0;
|
||||
mem_axi4.wlast = 1'b0;
|
||||
mem_axi4.bready = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.bvalid);
|
||||
if (mem_axi4.bresp != response) begin
|
||||
$fatal(1, "single write response=%0d expected=%0d", mem_axi4.bresp, response);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
mem_axi4.bready = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic write_missing_last(input logic [31:0] address, input logic [31:0] data);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
mem_axi4.awaddr = address;
|
||||
mem_axi4.awlen = 8'd0;
|
||||
mem_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
|
||||
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
|
||||
mem_axi4.awvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.awready);
|
||||
@(negedge clk_i);
|
||||
mem_axi4.awvalid = 1'b0;
|
||||
mem_axi4.wdata = data;
|
||||
mem_axi4.wstrb = 4'hF;
|
||||
mem_axi4.wlast = 1'b0;
|
||||
mem_axi4.wvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.wready);
|
||||
@(negedge clk_i);
|
||||
mem_axi4.wvalid = 1'b0;
|
||||
mem_axi4.bready = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.bvalid);
|
||||
if (mem_axi4.bresp != `AXI4_RESP_SLAVE_ERROR) begin
|
||||
$fatal(1, "missing WLAST did not return SLVERR");
|
||||
end
|
||||
@(negedge clk_i);
|
||||
mem_axi4.bready = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic read_single(input logic [31:0] address, input logic [2:0] size,
|
||||
input logic [31:0] expected, input logic [1:0] response);
|
||||
begin
|
||||
read_burst(address, 8'd0, size, `AXI4_BURST_TYPE_INCR, expected, response,
|
||||
response == `AXI4_RESP_OKAY, 1'b0);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_read(input logic [11:0] offset, input logic [31:0] expected);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
cfg_apb4.paddr = {20'd0, offset};
|
||||
cfg_apb4.psel = 1'b1;
|
||||
cfg_apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
cfg_apb4.penable = 1'b1;
|
||||
do @(posedge clk_i); while (!cfg_apb4.pready);
|
||||
s_apb_read_data = cfg_apb4.prdata;
|
||||
if (cfg_apb4.pslverr || (s_apb_read_data !== expected)) begin
|
||||
$fatal(1, "APB offset=%03x data=%08x expected=%08x error=%0b", offset, s_apb_read_data,
|
||||
expected, cfg_apb4.pslverr);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
cfg_apb4.psel = 1'b0;
|
||||
cfg_apb4.penable = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_error(input logic [11:0] offset, input logic write_access);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
cfg_apb4.paddr = {20'd0, offset};
|
||||
cfg_apb4.psel = 1'b1;
|
||||
cfg_apb4.penable = 1'b0;
|
||||
cfg_apb4.pwrite = write_access;
|
||||
cfg_apb4.pwdata = 32'hFFFF_FFFF;
|
||||
cfg_apb4.pstrb = 4'hF;
|
||||
@(negedge clk_i);
|
||||
cfg_apb4.penable = 1'b1;
|
||||
do @(posedge clk_i); while (!cfg_apb4.pready);
|
||||
if (!cfg_apb4.pslverr) begin
|
||||
$fatal(1, "APB illegal access offset=%03x write=%0b did not return PSLVERR", offset,
|
||||
write_access);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
cfg_apb4.psel = 1'b0;
|
||||
cfg_apb4.penable = 1'b0;
|
||||
cfg_apb4.pwrite = 1'b0;
|
||||
cfg_apb4.pwdata = '0;
|
||||
cfg_apb4.pstrb = '0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
init_interfaces();
|
||||
repeat (3) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
perf_enable_i = 1'b1;
|
||||
|
||||
apb_read(`APB4_ONCHIP_RAM__IP_ID, 32'h5352_414D);
|
||||
apb_read(`APB4_ONCHIP_RAM__IP_VERSION, 32'h0001_0000);
|
||||
apb_read(`APB4_ONCHIP_RAM__MEMORY_BYTES, 32'(CapacityKiB * 1024));
|
||||
apb_read(`APB4_ONCHIP_RAM__BANK_COUNT, 32'(CapacityKiB / 4));
|
||||
apb_read(`APB4_ONCHIP_RAM__BANK_BYTES, 32'd4096);
|
||||
apb_error(`APB4_ONCHIP_RAM__IP_ID, 1'b1);
|
||||
apb_error(12'h018, 1'b0);
|
||||
apb_error(12'h001, 1'b0);
|
||||
|
||||
write_burst(SramEnd - 32'd63, 8'd15, `AXI4_BURST_SIZE_4BYTES, `AXI4_BURST_TYPE_INCR,
|
||||
32'hA500_0000, `AXI4_RESP_OKAY);
|
||||
read_burst(SramEnd - 32'd63, 8'd15, `AXI4_BURST_SIZE_4BYTES, `AXI4_BURST_TYPE_INCR,
|
||||
32'hA500_0000, `AXI4_RESP_OKAY, 1'b1, 1'b1);
|
||||
|
||||
for (int bank = 0; bank < (CapacityKiB / 4); bank++) begin
|
||||
write_single(SramBase + 32'(bank * 4096), `AXI4_BURST_SIZE_4BYTES, 32'hB000_0000 + bank, 4'hF,
|
||||
`AXI4_RESP_OKAY);
|
||||
read_single(SramBase + 32'(bank * 4096), `AXI4_BURST_SIZE_4BYTES, 32'hB000_0000 + bank,
|
||||
`AXI4_RESP_OKAY);
|
||||
end
|
||||
|
||||
write_single(SramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, 32'h1122_3344, 4'hF, `AXI4_RESP_OKAY);
|
||||
write_single(SramBase + 32'h101, `AXI4_BURST_SIZE_1BYTE, 32'h0000_AA00, 4'b0010,
|
||||
`AXI4_RESP_OKAY);
|
||||
write_single(SramBase + 32'h102, `AXI4_BURST_SIZE_2BYTES, 32'hBEEF_0000, 4'b1100,
|
||||
`AXI4_RESP_OKAY);
|
||||
read_single(SramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, 32'hBEEF_AA44, `AXI4_RESP_OKAY);
|
||||
|
||||
write_burst(SramBase + 32'h200, 8'd3, `AXI4_BURST_SIZE_4BYTES, `AXI4_BURST_TYPE_FIXED,
|
||||
32'hCAFE_0000, `AXI4_RESP_OKAY);
|
||||
read_single(SramBase + 32'h200, `AXI4_BURST_SIZE_4BYTES, 32'hCAFE_0003, `AXI4_RESP_OKAY);
|
||||
|
||||
write_burst(SramBase + 32'h248, 8'd3, `AXI4_BURST_SIZE_4BYTES, `AXI4_BURST_TYPE_WRAP,
|
||||
32'hFACE_0000, `AXI4_RESP_OKAY);
|
||||
read_burst(SramBase + 32'h248, 8'd3, `AXI4_BURST_SIZE_4BYTES, `AXI4_BURST_TYPE_WRAP,
|
||||
32'hFACE_0000, `AXI4_RESP_OKAY, 1'b1, 1'b0);
|
||||
|
||||
read_single(SramEnd + 1'b1, `AXI4_BURST_SIZE_4BYTES, 32'd0, `AXI4_RESP_DECODE_ERROR);
|
||||
read_single(SramBase + 32'd2, `AXI4_BURST_SIZE_4BYTES, 32'd0, `AXI4_RESP_SLAVE_ERROR);
|
||||
write_single(SramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, 32'hFFFF_FFFF, 4'b0000,
|
||||
`AXI4_RESP_OKAY);
|
||||
write_missing_last(SramBase + 32'h100, 32'hDEAD_BEEF);
|
||||
write_single(SramBase + 32'h100, `AXI4_BURST_SIZE_1BYTE, 32'h0000_5500, 4'b0010,
|
||||
`AXI4_RESP_SLAVE_ERROR);
|
||||
read_single(SramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, 32'hBEEF_AA44, `AXI4_RESP_OKAY);
|
||||
|
||||
@(negedge clk_i);
|
||||
mem_axi4.araddr = SramBase;
|
||||
mem_axi4.awaddr = SramBase + 32'd4;
|
||||
mem_axi4.arvalid = 1'b1;
|
||||
mem_axi4.awvalid = 1'b1;
|
||||
@(posedge clk_i);
|
||||
if (!mem_axi4.arready || mem_axi4.awready) begin
|
||||
$fatal(1, "simultaneous AR/AW did not select the read channel");
|
||||
end
|
||||
@(negedge clk_i);
|
||||
mem_axi4.arvalid = 1'b0;
|
||||
mem_axi4.awvalid = 1'b0;
|
||||
mem_axi4.rready = 1'b1;
|
||||
do @(posedge clk_i); while (!mem_axi4.rvalid);
|
||||
@(negedge clk_i);
|
||||
mem_axi4.rready = 1'b0;
|
||||
|
||||
apb_read(`APB4_ONCHIP_RAM__PERF_READ_REQUESTS, 32'(CapacityKiB / 4 + 8));
|
||||
if (u_dut.s_perf_write_requests_q == 32'd0 || u_dut.s_perf_read_beats_q == 32'd0 ||
|
||||
u_dut.s_perf_write_beats_q == 32'd0 || u_dut.s_perf_err_resps_q != 32'd4) begin
|
||||
$fatal(1, "performance counters did not record the directed traffic");
|
||||
end
|
||||
|
||||
$display("on-chip SRAM AXI4 test passed capacity_kib=%0d", CapacityKiB);
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,63 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module tc_sram_1024x32_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic cs_i = 1'b0;
|
||||
logic [ 9:0] addr_i = '0;
|
||||
logic [31:0] data_i = '0;
|
||||
logic [ 3:0] mask_i = '0;
|
||||
logic wren_i = 1'b0;
|
||||
logic [31:0] data_o;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
tc_sram_1024x32 u_dut (
|
||||
.clk_i (clk_i),
|
||||
.cs_i (cs_i),
|
||||
.addr_i(addr_i),
|
||||
.data_i(data_i),
|
||||
.mask_i(mask_i),
|
||||
.wren_i(wren_i),
|
||||
.data_o(data_o)
|
||||
);
|
||||
|
||||
task automatic write_word(input logic [9:0] address, input logic [31:0] data,
|
||||
input logic [3:0] mask);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
cs_i = 1'b1;
|
||||
addr_i = address;
|
||||
data_i = data;
|
||||
mask_i = mask;
|
||||
wren_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
cs_i = 1'b0;
|
||||
wren_i = 1'b0;
|
||||
mask_i = '0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic read_word(input logic [9:0] address, input logic [31:0] expected);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
cs_i = 1'b1;
|
||||
addr_i = address;
|
||||
wren_i = 1'b0;
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
if (data_o !== expected) begin
|
||||
$fatal(1, "1024x32 SRAM data=%08x expected=%08x", data_o, expected);
|
||||
end
|
||||
cs_i = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
write_word(10'h155, 32'h1122_3344, 4'hF);
|
||||
write_word(10'h155, 32'hAABB_CCDD, 4'b0101);
|
||||
read_word(10'h155, 32'h11BB_33DD);
|
||||
$display("1024x32 SRAM wrapper test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Reference in new issue
Block a user