@@ -36,6 +36,7 @@ class BaseTestCase(unittest.TestCase):
|
||||
retime_read_fanin = False
|
||||
retime_read_response = False
|
||||
reuse_hwif_typedefs = True
|
||||
retime_external = False
|
||||
|
||||
#: this gets auto-loaded via the _load_request autouse fixture
|
||||
request = None # type: pytest.FixtureRequest
|
||||
@@ -105,7 +106,11 @@ class BaseTestCase(unittest.TestCase):
|
||||
cpuif_cls=cls.cpuif.cpuif_cls,
|
||||
retime_read_fanin=cls.retime_read_fanin,
|
||||
retime_read_response=cls.retime_read_response,
|
||||
reuse_hwif_typedefs=cls.reuse_hwif_typedefs
|
||||
reuse_hwif_typedefs=cls.reuse_hwif_typedefs,
|
||||
retime_external_reg=cls.retime_external,
|
||||
retime_external_regfile=cls.retime_external,
|
||||
retime_external_mem=cls.retime_external,
|
||||
retime_external_addrmap=cls.retime_external,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
73
tests/lib/external_block.sv
Normal file
73
tests/lib/external_block.sv
Normal file
@@ -0,0 +1,73 @@
|
||||
module external_block #(
|
||||
parameter WIDTH = 32,
|
||||
parameter ADDR_WIDTH = 8
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire req,
|
||||
input wire req_is_wr,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
input wire [WIDTH-1:0] wr_data,
|
||||
input wire [WIDTH-1:0] wr_biten,
|
||||
output logic rd_ack,
|
||||
output logic [WIDTH-1:0] rd_data,
|
||||
output logic wr_ack
|
||||
);
|
||||
timeunit 1ns;
|
||||
timeprecision 1ps;
|
||||
|
||||
localparam ADDR_SHIFT = $clog2(WIDTH/8);
|
||||
localparam N_ENTRIES = 2**(ADDR_WIDTH - ADDR_SHIFT);
|
||||
logic [WIDTH-1:0] mem[N_ENTRIES];
|
||||
|
||||
|
||||
task do_write(int idx, logic [WIDTH-1:0] data, logic [WIDTH-1:0] biten);
|
||||
automatic int delay;
|
||||
// Random delay
|
||||
delay = $urandom_range(3,0);
|
||||
repeat(delay) @(posedge clk)
|
||||
$info("Write delay: %d", delay);
|
||||
|
||||
for(int b=0; b<WIDTH; b++) begin
|
||||
if(biten[b]) mem[idx][b] <= data[b];
|
||||
end
|
||||
wr_ack <= '1;
|
||||
endtask
|
||||
|
||||
task do_read(int idx);
|
||||
automatic int delay;
|
||||
// Random delay
|
||||
delay = $urandom_range(3,0);
|
||||
repeat(delay) @(posedge clk)
|
||||
$info("Read delay: %d", delay);
|
||||
|
||||
rd_data <= mem[idx];
|
||||
rd_ack <= '1;
|
||||
endtask;
|
||||
|
||||
|
||||
initial begin
|
||||
rd_ack <= '0;
|
||||
rd_data <= '0;
|
||||
wr_ack <= '0;
|
||||
for(int i=0; i<N_ENTRIES; i++) mem[i] <= '0;
|
||||
|
||||
forever begin
|
||||
// Wait for next clock edge
|
||||
@(posedge clk);
|
||||
rd_ack <= '0;
|
||||
rd_data <= '0;
|
||||
wr_ack <= '0;
|
||||
|
||||
// wait slightly longer to "peek" at the current cycle's state
|
||||
#1ns;
|
||||
|
||||
if(!rst && req) begin
|
||||
if(req_is_wr) do_write(addr >> ADDR_SHIFT, wr_data, wr_biten);
|
||||
else do_read(addr >> ADDR_SHIFT);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
78
tests/lib/external_reg.sv
Normal file
78
tests/lib/external_reg.sv
Normal file
@@ -0,0 +1,78 @@
|
||||
module external_reg #(
|
||||
parameter WIDTH = 32,
|
||||
parameter SUBWORDS = 1
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire [SUBWORDS-1:0] req,
|
||||
input wire req_is_wr,
|
||||
input wire [WIDTH-1:0] wr_data,
|
||||
input wire [WIDTH-1:0] wr_biten,
|
||||
output logic rd_ack,
|
||||
output logic [WIDTH-1:0] rd_data,
|
||||
output logic wr_ack
|
||||
);
|
||||
timeunit 1ns;
|
||||
timeprecision 1ps;
|
||||
logic [SUBWORDS-1:0][WIDTH-1:0] value;
|
||||
|
||||
|
||||
task do_write(logic [SUBWORDS-1:0] strb, logic [WIDTH-1:0] data, logic [WIDTH-1:0] biten);
|
||||
automatic int delay;
|
||||
// Random delay
|
||||
delay = $urandom_range(3,0);
|
||||
repeat(delay) @(posedge clk)
|
||||
$info("Write delay: %d", delay);
|
||||
|
||||
for(int i=0; i<SUBWORDS; i++) begin
|
||||
if(strb[i]) begin
|
||||
for(int b=0; b<WIDTH; b++) begin
|
||||
if(biten[b]) value[i][b] <= data[b];
|
||||
end
|
||||
end
|
||||
end
|
||||
wr_ack <= '1;
|
||||
endtask
|
||||
|
||||
task do_read(logic [SUBWORDS-1:0] strb);
|
||||
automatic int delay;
|
||||
// Random delay
|
||||
delay = $urandom_range(3,0);
|
||||
repeat(delay) @(posedge clk)
|
||||
$info("Read delay: %d", delay);
|
||||
|
||||
|
||||
for(int i=0; i<SUBWORDS; i++) begin
|
||||
if(strb[i]) begin
|
||||
rd_data <= value[i];
|
||||
end
|
||||
end
|
||||
rd_ack <= '1;
|
||||
endtask;
|
||||
|
||||
|
||||
initial begin
|
||||
rd_ack <= '0;
|
||||
rd_data <= '0;
|
||||
wr_ack <= '0;
|
||||
value <= '0;
|
||||
|
||||
forever begin
|
||||
// Wait for next clock edge
|
||||
@(posedge clk);
|
||||
rd_ack <= '0;
|
||||
rd_data <= '0;
|
||||
wr_ack <= '0;
|
||||
|
||||
// wait slightly longer to "peek" at the current cycle's state
|
||||
#1ns;
|
||||
|
||||
if(!rst && req) begin
|
||||
if(req_is_wr) do_write(req, wr_data, wr_biten);
|
||||
else do_read(req);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -22,6 +22,25 @@ class SimTestCase(BaseTestCase):
|
||||
|
||||
tb_template_file = "tb_template.sv"
|
||||
|
||||
# Paths are relative to the testcase dir
|
||||
extra_tb_files = [] # type: List[str]
|
||||
|
||||
# Whether to initialize the hwif_in struct at test startup
|
||||
init_hwif_in = True
|
||||
|
||||
# Control whether to include in clocking block
|
||||
clocking_hwif_in = True
|
||||
clocking_hwif_out = True
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_extra_tb_files(cls) -> List[str]:
|
||||
paths = []
|
||||
for path in cls.extra_tb_files:
|
||||
path = os.path.join(cls.get_testcase_dir(), path)
|
||||
paths.append(path)
|
||||
return paths
|
||||
|
||||
@classmethod
|
||||
def _generate_tb(cls):
|
||||
"""
|
||||
|
||||
@@ -13,6 +13,7 @@ class Simulator:
|
||||
def tb_files(self) -> List[str]:
|
||||
files = []
|
||||
files.extend(self.testcase_cls.cpuif.get_sim_files())
|
||||
files.extend(self.testcase_cls.get_extra_tb_files())
|
||||
files.append("regblock_pkg.sv")
|
||||
files.append("regblock.sv")
|
||||
files.append("tb.sv")
|
||||
|
||||
@@ -30,11 +30,11 @@ module tb;
|
||||
default clocking cb @(posedge clk);
|
||||
default input #1step output #1;
|
||||
output rst;
|
||||
{%- if exporter.hwif.has_input_struct %}
|
||||
{%- if exporter.hwif.has_input_struct and cls.clocking_hwif_in %}
|
||||
output hwif_in;
|
||||
{%- endif %}
|
||||
|
||||
{%- if exporter.hwif.has_output_struct %}
|
||||
{%- if exporter.hwif.has_output_struct and cls.clocking_hwif_out %}
|
||||
input hwif_out;
|
||||
{%- endif %}
|
||||
|
||||
@@ -68,12 +68,15 @@ module tb;
|
||||
{%- endif %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
{%- block dut_support %}
|
||||
{%- endblock %}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Test Sequence
|
||||
//--------------------------------------------------------------------------
|
||||
initial begin
|
||||
cb.rst <= '1;
|
||||
{%- if exporter.hwif.has_input_struct %}
|
||||
{%- if exporter.hwif.has_input_struct and cls.init_hwif_in %}
|
||||
cb.hwif_in <= '{default: '0};
|
||||
{%- endif %}
|
||||
|
||||
|
||||
@@ -492,7 +492,7 @@ valid-metaclass-classmethod-first-arg=cls
|
||||
ignored-parents=
|
||||
|
||||
# Maximum number of arguments for function / method.
|
||||
max-args=8
|
||||
max-args=16
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
0
tests/test_external/__init__.py
Normal file
0
tests/test_external/__init__.py
Normal file
29
tests/test_external/regblock.rdl
Normal file
29
tests/test_external/regblock.rdl
Normal file
@@ -0,0 +1,29 @@
|
||||
addrmap top {
|
||||
reg my_reg {
|
||||
field {sw=rw; hw=r;} whatever[32] = 0;
|
||||
};
|
||||
reg my_wide_reg {
|
||||
regwidth = 64;
|
||||
accesswidth = 32;
|
||||
field {sw=rw; hw=r;} whatever = 0;
|
||||
};
|
||||
|
||||
external my_reg ext_reg @ 0x00;
|
||||
my_reg int_reg @ 0x04;
|
||||
external my_wide_reg wide_ext_reg @ 0x10;
|
||||
external my_reg ext_reg_array[32] @ 0x100 += 4;
|
||||
|
||||
|
||||
external regfile {
|
||||
my_reg placeholder @ 8*4-4;
|
||||
} rf @ 0x1000;
|
||||
|
||||
addrmap {
|
||||
my_reg placeholder @ 8*4-4;
|
||||
} am @ 0x2000;
|
||||
|
||||
external mem {
|
||||
memwidth = 32;
|
||||
mementries = 8;
|
||||
} mm @ 0x3000;
|
||||
};
|
||||
207
tests/test_external/tb_template.sv
Normal file
207
tests/test_external/tb_template.sv
Normal file
@@ -0,0 +1,207 @@
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
|
||||
|
||||
{%- block dut_support %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
external_reg ext_reg_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
.req(hwif_out.ext_reg.req),
|
||||
.req_is_wr(hwif_out.ext_reg.req_is_wr),
|
||||
.wr_data(hwif_out.ext_reg.wr_data),
|
||||
.wr_biten(hwif_out.ext_reg.wr_biten),
|
||||
.rd_ack(hwif_in.ext_reg.rd_ack),
|
||||
.rd_data(hwif_in.ext_reg.rd_data),
|
||||
.wr_ack(hwif_in.ext_reg.wr_ack)
|
||||
);
|
||||
|
||||
external_reg #(
|
||||
.SUBWORDS(2)
|
||||
) wide_ext_reg_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
.req(hwif_out.wide_ext_reg.req),
|
||||
.req_is_wr(hwif_out.wide_ext_reg.req_is_wr),
|
||||
.wr_data(hwif_out.wide_ext_reg.wr_data),
|
||||
.wr_biten(hwif_out.wide_ext_reg.wr_biten),
|
||||
.rd_ack(hwif_in.wide_ext_reg.rd_ack),
|
||||
.rd_data(hwif_in.wide_ext_reg.rd_data),
|
||||
.wr_ack(hwif_in.wide_ext_reg.wr_ack)
|
||||
);
|
||||
|
||||
for(genvar i=0; i<32; i++) begin : array
|
||||
external_reg ext_reg_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
.req(hwif_out.ext_reg_array[i].req),
|
||||
.req_is_wr(hwif_out.ext_reg_array[i].req_is_wr),
|
||||
.wr_data(hwif_out.ext_reg_array[i].wr_data),
|
||||
.wr_biten(hwif_out.ext_reg_array[i].wr_biten),
|
||||
.rd_ack(hwif_in.ext_reg_array[i].rd_ack),
|
||||
.rd_data(hwif_in.ext_reg_array[i].rd_data),
|
||||
.wr_ack(hwif_in.ext_reg_array[i].wr_ack)
|
||||
);
|
||||
end
|
||||
|
||||
external_block #(
|
||||
.ADDR_WIDTH(5)
|
||||
) rf_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
.req(hwif_out.rf.req),
|
||||
.req_is_wr(hwif_out.rf.req_is_wr),
|
||||
.addr(hwif_out.rf.addr),
|
||||
.wr_data(hwif_out.rf.wr_data),
|
||||
.wr_biten(hwif_out.rf.wr_biten),
|
||||
.rd_ack(hwif_in.rf.rd_ack),
|
||||
.rd_data(hwif_in.rf.rd_data),
|
||||
.wr_ack(hwif_in.rf.wr_ack)
|
||||
);
|
||||
|
||||
external_block #(
|
||||
.ADDR_WIDTH(5)
|
||||
) am_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
.req(hwif_out.am.req),
|
||||
.req_is_wr(hwif_out.am.req_is_wr),
|
||||
.addr(hwif_out.am.addr),
|
||||
.wr_data(hwif_out.am.wr_data),
|
||||
.wr_biten(hwif_out.am.wr_biten),
|
||||
.rd_ack(hwif_in.am.rd_ack),
|
||||
.rd_data(hwif_in.am.rd_data),
|
||||
.wr_ack(hwif_in.am.wr_ack)
|
||||
);
|
||||
|
||||
external_block #(
|
||||
.ADDR_WIDTH(5)
|
||||
) mm_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
.req(hwif_out.mm.req),
|
||||
.req_is_wr(hwif_out.mm.req_is_wr),
|
||||
.addr(hwif_out.mm.addr),
|
||||
.wr_data(hwif_out.mm.wr_data),
|
||||
.wr_biten(hwif_out.mm.wr_biten),
|
||||
.rd_ack(hwif_in.mm.rd_ack),
|
||||
.rd_data(hwif_in.mm.rd_data),
|
||||
.wr_ack(hwif_in.mm.wr_ack)
|
||||
);
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
|
||||
{% block seq %}
|
||||
logic [31:0] x;
|
||||
{% sv_line_anchor %}
|
||||
##1;
|
||||
cb.rst <= '0;
|
||||
##1;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Simple read/write tests
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
repeat(20) begin
|
||||
x = $urandom();
|
||||
cpuif.write('h00, x);
|
||||
cpuif.assert_read('h00, x);
|
||||
assert(ext_reg_inst.value == x);
|
||||
end
|
||||
|
||||
for(int i=0; i<2; i++) begin
|
||||
repeat(20) begin
|
||||
x = $urandom();
|
||||
cpuif.write('h10 + i*4, x);
|
||||
cpuif.assert_read('h10 + i*4, x);
|
||||
assert(wide_ext_reg_inst.value[i] == x);
|
||||
end
|
||||
end
|
||||
|
||||
for(int i=0; i<32; i++) begin
|
||||
repeat(20) begin
|
||||
x = $urandom();
|
||||
cpuif.write('h100 + i*4, x);
|
||||
cpuif.assert_read('h100 + i*4, x);
|
||||
end
|
||||
end
|
||||
|
||||
for(int i=0; i<8; i++) begin
|
||||
repeat(20) begin
|
||||
x = $urandom();
|
||||
cpuif.write('h1000 + i*4, x);
|
||||
cpuif.assert_read('h1000 + i*4, x);
|
||||
assert(rf_inst.mem[i] == x);
|
||||
end
|
||||
end
|
||||
|
||||
for(int i=0; i<8; i++) begin
|
||||
repeat(20) begin
|
||||
x = $urandom();
|
||||
cpuif.write('h2000 + i*4, x);
|
||||
cpuif.assert_read('h2000 + i*4, x);
|
||||
assert(am_inst.mem[i] == x);
|
||||
end
|
||||
end
|
||||
|
||||
for(int i=0; i<8; i++) begin
|
||||
repeat(20) begin
|
||||
x = $urandom();
|
||||
cpuif.write('h3000 + i*4, x);
|
||||
cpuif.assert_read('h3000 + i*4, x);
|
||||
assert(mm_inst.mem[i] == x);
|
||||
end
|
||||
end
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Pipelined access
|
||||
//--------------------------------------------------------------------------
|
||||
// init array with unique known value
|
||||
cpuif.write('h4, 'h1234);
|
||||
for(int i=0; i<32; i++) begin
|
||||
cpuif.write('h100 + i*4, 'h100 + i);
|
||||
end
|
||||
for(int i=0; i<8; i++) begin
|
||||
cpuif.write('h1000 + i*4, 'h1000 + i);
|
||||
cpuif.write('h2000 + i*4, 'h2000 + i);
|
||||
cpuif.write('h3000 + i*4, 'h3000 + i);
|
||||
end
|
||||
|
||||
// random pipelined read/writes
|
||||
repeat(256) begin
|
||||
fork
|
||||
begin
|
||||
automatic int i, j;
|
||||
i = $urandom_range(31, 0);
|
||||
j = $urandom_range(7, 0);
|
||||
case($urandom_range(9,0))
|
||||
// external reg
|
||||
0: cpuif.write('h100 + i*4, 'h100 + i);
|
||||
1: cpuif.assert_read('h100 + i*4, 'h100 + i);
|
||||
// internal reg
|
||||
2: cpuif.write('h4, 'h1234);
|
||||
3: cpuif.assert_read('h4, 'h1234);
|
||||
// external regfile
|
||||
4: cpuif.write('h1000 + j*4, 'h1000 + j);
|
||||
5: cpuif.assert_read('h1000 + j*4, 'h1000 + j);
|
||||
// external addrmap
|
||||
6: cpuif.write('h2000 + j*4, 'h2000 + j);
|
||||
7: cpuif.assert_read('h2000 + j*4, 'h2000 + j);
|
||||
// external mem
|
||||
8: cpuif.write('h3000 + j*4, 'h3000 + j);
|
||||
9: cpuif.assert_read('h3000 + j*4, 'h3000 + j);
|
||||
endcase
|
||||
end
|
||||
join_none
|
||||
end
|
||||
wait fork;
|
||||
|
||||
{% endblock %}
|
||||
31
tests/test_external/testcase.py
Normal file
31
tests/test_external/testcase.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from parameterized import parameterized_class
|
||||
|
||||
from ..lib.sim_testcase import SimTestCase
|
||||
from ..lib.test_params import get_permutations
|
||||
from ..lib.cpuifs.apb4 import APB4
|
||||
from ..lib.cpuifs.axi4lite import AXI4Lite
|
||||
from ..lib.cpuifs.passthrough import Passthrough
|
||||
|
||||
TEST_PARAMS = get_permutations({
|
||||
"cpuif": [
|
||||
APB4(),
|
||||
AXI4Lite(),
|
||||
Passthrough(),
|
||||
],
|
||||
"retime_read_fanin": [True, False],
|
||||
"retime_read_response": [True, False],
|
||||
"retime_external": [True, False],
|
||||
})
|
||||
|
||||
@parameterized_class(TEST_PARAMS)
|
||||
class Test(SimTestCase):
|
||||
extra_tb_files = [
|
||||
"../lib/external_reg.sv",
|
||||
"../lib/external_block.sv",
|
||||
]
|
||||
init_hwif_in = False
|
||||
clocking_hwif_in = False
|
||||
timeout_clk_cycles = 30000
|
||||
|
||||
def test_dut(self):
|
||||
self.run_test()
|
||||
Reference in New Issue
Block a user