Error response for unmapped address or forbidden read/write access (#168)

* feat: add ability to enable error output on the cpuif, when decoding errors occur (generate_cpuif_err in API).

* fix: move signal to new place (after automatic vers)

* feat: add info about new api (generate_cpuif_err)

* fix: repair readback with latency

* Adding generate_cpuif_err argument to peakrdl-regblock to generate cpuif error response, when the address is decoded incorrectly

* add sw rd or/and wr attribure error response related and add error respone for external mem

* add sw rd or/and wr error response test

* add sw rd or/and wr error response for external register test and fix generation of rtl logic for external register

* add sw rd or/and wr error response for external mem test

* add sw rd or/and wr error response for apb3 imterfaces driver

* add error response test for APB4, AXI4Lite and Avalon interfaces

* rename --generate_cpuif_err to --generate-cpuif-err

* style: minor typo fixes and test clean-up

* refactor: move expected error check inside write/read functions

* feat: add error response check to OBI testbench interface

* feat: split generate-cpuif-err option into err-if-bad-addr and err-if-bad-rw options

* feat: add err_if_bad_addr/rw to cfg_schema

* feat: extend cpuif_err_rsp test to cover all combinations of bad_addr/bad_rw

* style: lint fixes

* fix: removed redundant if node.external condition to help coverage

* Fix dangling hwif_in signals in testcase

---------

Co-authored-by: Denis Trifonov <d.trifonov@yadro.com>
Co-authored-by: Dominik Tanous <tanous@kandou.com>
Co-authored-by: Sebastien Baillou <baillou@kandou.com>
Co-authored-by: Alex Mykyta <amykyta3@users.noreply.github.com>
This commit is contained in:
sbaillou
2025-10-26 02:22:15 +01:00
committed by GitHub
parent bb765e6ae3
commit d69af23be5
16 changed files with 477 additions and 61 deletions

View File

View File

@@ -0,0 +1,59 @@
addrmap top {
default regwidth = 32;
default sw=rw;
default hw=na;
reg {
field {
sw=rw; hw=na; // Storage element
} f[31:0] = 40;
} r_rw;
reg {
field {
sw=r; hw=na; // Wire/Bus - constant value
} f[31:0] = 80;
} r_r;
reg {
field {
sw=w; hw=r; // Storage element
} f[31:0] = 100;
} r_w;
external reg {
field {
sw=rw; hw=na; // Storage element
} f[31:0];
} er_rw;
external reg {
field {
sw=r; hw=na; // Wire/Bus - constant value
} f[31:0];
} er_r;
external reg {
field {
sw=w; hw=na; // Storage element
} f[31:0];
} er_w;
external mem {
memwidth = 32;
mementries = 2;
} mem_rw @ 0x20;
external mem {
memwidth = 32;
mementries = 2;
sw=r;
} mem_r @ 0x28;
external mem {
memwidth = 32;
mementries = 2;
sw=w;
} mem_w @ 0x30;
};

View File

@@ -0,0 +1,221 @@
{% extends "lib/tb_base.sv" %}
{%- block dut_support %}
{% sv_line_anchor %}
external_reg ext_reg_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.er_rw.req),
.req_is_wr(hwif_out.er_rw.req_is_wr),
.wr_data(hwif_out.er_rw.wr_data),
.wr_biten(hwif_out.er_rw.wr_biten),
.rd_ack(hwif_in.er_rw.rd_ack),
.rd_data(hwif_in.er_rw.rd_data),
.wr_ack(hwif_in.er_rw.wr_ack)
);
external_reg ro_reg_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.er_r.req),
.req_is_wr(hwif_out.er_r.req_is_wr),
.wr_data(32'b0),
.wr_biten(32'b0),
.rd_ack(hwif_in.er_r.rd_ack),
.rd_data(hwif_in.er_r.rd_data),
.wr_ack()
);
external_reg wo_reg_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.er_w.req),
.req_is_wr(hwif_out.er_w.req_is_wr),
.wr_data(hwif_out.er_w.wr_data),
.wr_biten(hwif_out.er_w.wr_biten),
.rd_ack(),
.rd_data(),
.wr_ack(hwif_in.er_w.wr_ack)
);
external_block #(
.ADDR_WIDTH(3)
) mem_rw_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.mem_rw.req),
.req_is_wr(hwif_out.mem_rw.req_is_wr),
.addr(hwif_out.mem_rw.addr),
.wr_data(hwif_out.mem_rw.wr_data),
.wr_biten(hwif_out.mem_rw.wr_biten),
.rd_ack(hwif_in.mem_rw.rd_ack),
.rd_data(hwif_in.mem_rw.rd_data),
.wr_ack(hwif_in.mem_rw.wr_ack)
);
external_block #(
.ADDR_WIDTH(3)
) mem_ro_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.mem_r.req),
.req_is_wr(hwif_out.mem_r.req_is_wr),
.addr(hwif_out.mem_r.addr),
.wr_data(32'b0),
.wr_biten(32'b0),
.rd_ack(hwif_in.mem_r.rd_ack),
.rd_data(hwif_in.mem_r.rd_data),
.wr_ack(hwif_in.mem_r.wr_ack)
);
external_block #(
.ADDR_WIDTH(3)
) mem_wo_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.mem_w.req),
.req_is_wr(hwif_out.mem_w.req_is_wr),
.addr(hwif_out.mem_w.addr),
.wr_data(hwif_out.mem_w.wr_data),
.wr_biten(hwif_out.mem_w.wr_biten),
.rd_ack(),
.rd_data(),
.wr_ack(hwif_in.mem_w.wr_ack)
);
assign hwif_in.mem_w.rd_ack = '0;
assign hwif_in.mem_w.rd_data = '0;
{%- endblock %}
{% block seq %}
logic wr_err;
logic expected_wr_err;
logic expected_rd_err;
logic bad_addr_expected_err;
logic bad_rw_expected_wr_err;
logic bad_rw_expected_rd_err;
logic [5:0] addr;
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
{%- if testcase.err_if_bad_addr %}
bad_addr_expected_err = 1'b1;
{%- else %}
bad_addr_expected_err = 1'b0;
{%- endif %}
{%- if testcase.err_if_bad_rw %}
bad_rw_expected_wr_err = 1'b1;
bad_rw_expected_rd_err = 1'b1;
{%- else %}
bad_rw_expected_wr_err = 1'b0;
bad_rw_expected_rd_err = 1'b0;
{%- endif %}
// r_rw - sw=rw; hw=na; // Storage element
addr = 'h0;
expected_rd_err = 'h0;
expected_wr_err = 'h0;
cpuif.assert_read(addr, 40, .expects_err(expected_rd_err));
cpuif.write('h0, 61, .expects_err(expected_wr_err));
cpuif.assert_read('h0, 61, .expects_err(expected_rd_err));
// r_r - sw=r; hw=na; // Wire/Bus - constant value
addr = 'h4;
expected_rd_err = 'h0;
expected_wr_err = bad_rw_expected_wr_err;
cpuif.assert_read(addr, 80, .expects_err(expected_rd_err));
cpuif.write(addr, 81, .expects_err(expected_wr_err));
cpuif.assert_read(addr, 80, .expects_err(expected_rd_err));
// r_w - sw=w; hw=r; // Storage element
addr = 'h8;
expected_rd_err = bad_rw_expected_rd_err;
expected_wr_err = 'h0;
cpuif.assert_read(addr, 0, .expects_err(expected_rd_err));
assert(cb.hwif_out.r_w.f.value == 100);
cpuif.write(addr, 101, .expects_err(expected_wr_err));
cpuif.assert_read(addr, 0, .expects_err(expected_rd_err));
assert(cb.hwif_out.r_w.f.value == 101);
// External registers
// er_rw - sw=rw; hw=na; // Storage element
addr = 'hC;
expected_rd_err = 'h0;
expected_wr_err = 'h0;
ext_reg_inst.value = 'h8C;
cpuif.assert_read(addr, 'h8C, .expects_err(expected_rd_err));
cpuif.write(addr, 'h8D, .expects_err(expected_wr_err));
cpuif.assert_read(addr, 'h8D, .expects_err(expected_rd_err));
// er_r - sw=r; hw=na; // Wire/Bus - constant value
addr = 'h10;
expected_rd_err = 'h0;
expected_wr_err = bad_rw_expected_wr_err;
ro_reg_inst.value = 'hB4;
cpuif.assert_read(addr, 'hB4, .expects_err(expected_rd_err));
cpuif.write(addr, 'hB5, .expects_err(expected_wr_err));
cpuif.assert_read(addr, 'hB4, .expects_err(expected_rd_err));
// er_w - sw=w; hw=r; // Storage element
addr = 'h14;
expected_rd_err = bad_rw_expected_rd_err;
expected_wr_err = 'h0;
wo_reg_inst.value = 'hC8;
cpuif.assert_read(addr, 0, .expects_err(expected_rd_err));
assert(wo_reg_inst.value == 'hC8);
cpuif.write(addr, 'hC9, .expects_err(expected_wr_err));
cpuif.assert_read(addr, 0, .expects_err(expected_rd_err));
assert(wo_reg_inst.value == 'hC9);
// Reading/writing from/to non existing register
addr = 'h18;
cpuif.assert_read(addr, 0, .expects_err(bad_addr_expected_err));
cpuif.write(addr, 'h8C, .expects_err(bad_addr_expected_err));
// External memories
// mem_rw - sw=rw;
addr = 'h20;
expected_rd_err = 'h0;
expected_wr_err = 'h0;
mem_rw_inst.mem[0] = 'h8C;
cpuif.assert_read(addr, 'h8C, .expects_err(expected_rd_err));
cpuif.write(addr, 'h8D, .expects_err(expected_wr_err));
cpuif.assert_read(addr, 'h8D, .expects_err(expected_rd_err));
// mem_r - sw=r;
addr = 'h28;
expected_rd_err = 'h0;
expected_wr_err = bad_rw_expected_wr_err;
mem_ro_inst.mem[0] = 'hB4;
cpuif.assert_read(addr, 'hB4, .expects_err(expected_rd_err));
cpuif.write(addr, 'hB5, .expects_err(expected_wr_err));
cpuif.assert_read(addr, 'hB4, .expects_err(expected_rd_err));
// mem_w - sw=w;
addr = 'h30;
expected_rd_err = bad_rw_expected_rd_err;
expected_wr_err = 'h0;
mem_wo_inst.mem[0] = 'hC8;
cpuif.assert_read(addr, 0, .expects_err(expected_rd_err));
assert(mem_wo_inst.mem[0] == 'hC8);
cpuif.write(addr, 'hC9, .expects_err(expected_wr_err));
cpuif.assert_read(addr, 0, .expects_err(expected_rd_err));
assert(mem_wo_inst.mem[0] == 'hC9);
{% endblock %}

View File

@@ -0,0 +1,29 @@
from parameterized import parameterized_class
from ..lib.sim_testcase import SimTestCase
from ..lib.test_params import get_permutations
from ..lib.cpuifs import ALL_CPUIF
@parameterized_class(
# To reduce the number of tests, cover all CPUIFs with both error injections enabled, and all
# combinations of bad_addr/bad_rw with the default CPUIF only.
get_permutations({
"cpuif": ALL_CPUIF,
"err_if_bad_addr": [True],
"err_if_bad_rw": [True],
}) +
get_permutations({
"err_if_bad_addr": [True, False],
"err_if_bad_rw": [True, False],
})
)
class Test(SimTestCase):
extra_tb_files = [
"../lib/external_reg.sv",
"../lib/external_block.sv",
]
init_hwif_in = False
clocking_hwif_in = False
def test_dut(self):
self.run_test()