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:
@@ -22,6 +22,8 @@ class Exporter(ExporterSubcommandPlugin):
|
|||||||
cfg_schema = {
|
cfg_schema = {
|
||||||
"cpuifs": {"*": schema.PythonObjectImport()},
|
"cpuifs": {"*": schema.PythonObjectImport()},
|
||||||
"default_reset": schema.Choice(["rst", "rst_n", "arst", "arst_n"]),
|
"default_reset": schema.Choice(["rst", "rst_n", "arst", "arst_n"]),
|
||||||
|
"err_if_bad_addr": schema.Boolean(),
|
||||||
|
"err_if_bad_rw": schema.Boolean(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@functools.lru_cache()
|
@functools.lru_cache()
|
||||||
@@ -141,6 +143,20 @@ class Exporter(ExporterSubcommandPlugin):
|
|||||||
is active-high and synchronous [rst]"""
|
is active-high and synchronous [rst]"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
arg_group.add_argument(
|
||||||
|
"--err-if-bad-addr",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Generate CPUIF error response, when the address is decoded incorrectly"
|
||||||
|
)
|
||||||
|
|
||||||
|
arg_group.add_argument(
|
||||||
|
"--err-if-bad-rw",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="""Generate CPUIF error response, when an illegal access is
|
||||||
|
performed to a read-only or write-only register"""
|
||||||
|
)
|
||||||
|
|
||||||
def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> None:
|
def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> None:
|
||||||
cpuifs = self.get_cpuifs()
|
cpuifs = self.get_cpuifs()
|
||||||
@@ -203,5 +219,7 @@ class Exporter(ExporterSubcommandPlugin):
|
|||||||
generate_hwif_report=options.hwif_report,
|
generate_hwif_report=options.hwif_report,
|
||||||
address_width=options.addr_width,
|
address_width=options.addr_width,
|
||||||
default_reset_activelow=default_reset_activelow,
|
default_reset_activelow=default_reset_activelow,
|
||||||
|
err_if_bad_addr=options.err_if_bad_addr or self.cfg['err_if_bad_addr'],
|
||||||
|
err_if_bad_rw=options.err_if_bad_rw or self.cfg['err_if_bad_rw'],
|
||||||
default_reset_async=default_reset_async,
|
default_reset_async=default_reset_async,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import TYPE_CHECKING, Union, List, Optional
|
from typing import TYPE_CHECKING, Union, List, Optional
|
||||||
|
|
||||||
from systemrdl.node import FieldNode, RegNode
|
from systemrdl.node import FieldNode, RegNode, MemNode
|
||||||
from systemrdl.walker import WalkerAction
|
from systemrdl.walker import WalkerAction
|
||||||
|
|
||||||
from .utils import get_indexed_path
|
from .utils import get_indexed_path
|
||||||
@@ -12,7 +12,7 @@ from .sv_int import SVInt
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .exporter import RegblockExporter
|
from .exporter import RegblockExporter
|
||||||
from systemrdl.node import AddrmapNode, AddressableNode
|
from systemrdl.node import AddrmapNode, AddressableNode
|
||||||
from systemrdl.node import RegfileNode, MemNode
|
from systemrdl.node import RegfileNode
|
||||||
|
|
||||||
class AddressDecode:
|
class AddressDecode:
|
||||||
def __init__(self, exp:'RegblockExporter'):
|
def __init__(self, exp:'RegblockExporter'):
|
||||||
@@ -132,6 +132,33 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
|
|||||||
# List of address strides for each dimension
|
# List of address strides for each dimension
|
||||||
self._array_stride_stack = [] # type: List[int]
|
self._array_stride_stack = [] # type: List[int]
|
||||||
|
|
||||||
|
def _add_addressablenode_decoding_flags(self, node: 'AddressableNode') -> None:
|
||||||
|
addr_str = self._get_address_str(node)
|
||||||
|
addr_decoding_str = f"cpuif_req_masked & (cpuif_addr >= {addr_str}) & (cpuif_addr <= {addr_str} + {SVInt(node.size - 1, self.addr_decode.exp.ds.addr_width)})"
|
||||||
|
rhs = addr_decoding_str
|
||||||
|
rhs_valid_addr = addr_decoding_str
|
||||||
|
if isinstance(node, MemNode):
|
||||||
|
readable = node.is_sw_readable
|
||||||
|
writable = node.is_sw_writable
|
||||||
|
if readable and writable:
|
||||||
|
rhs_invalid_rw = "'0"
|
||||||
|
elif readable and not writable:
|
||||||
|
rhs = f"{addr_decoding_str} & !cpuif_req_is_wr"
|
||||||
|
rhs_invalid_rw = f"{addr_decoding_str} & cpuif_req_is_wr"
|
||||||
|
elif not readable and writable:
|
||||||
|
rhs = f"{addr_decoding_str} & cpuif_req_is_wr"
|
||||||
|
rhs_invalid_rw = f"{addr_decoding_str} & !cpuif_req_is_wr"
|
||||||
|
else:
|
||||||
|
raise RuntimeError
|
||||||
|
# Add decoding flags
|
||||||
|
self.add_content(f"{self.addr_decode.get_external_block_access_strobe(node)} = {rhs};")
|
||||||
|
self.add_content(f"is_external |= {rhs};")
|
||||||
|
if self.addr_decode.exp.ds.err_if_bad_addr:
|
||||||
|
self.add_content(f"is_valid_addr |= {rhs_valid_addr};")
|
||||||
|
if isinstance(node, MemNode):
|
||||||
|
if self.addr_decode.exp.ds.err_if_bad_rw:
|
||||||
|
self.add_content(f"is_invalid_rw |= {rhs_invalid_rw};")
|
||||||
|
|
||||||
|
|
||||||
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
|
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
|
||||||
super().enter_AddressableComponent(node)
|
super().enter_AddressableComponent(node)
|
||||||
@@ -149,11 +176,7 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
|
|||||||
|
|
||||||
if node.external and not isinstance(node, RegNode):
|
if node.external and not isinstance(node, RegNode):
|
||||||
# Is an external block
|
# Is an external block
|
||||||
addr_str = self._get_address_str(node)
|
self._add_addressablenode_decoding_flags(node)
|
||||||
strb = self.addr_decode.get_external_block_access_strobe(node)
|
|
||||||
rhs = f"cpuif_req_masked & (cpuif_addr >= {addr_str}) & (cpuif_addr <= {addr_str} + {SVInt(node.size - 1, self.addr_decode.exp.ds.addr_width)})"
|
|
||||||
self.add_content(f"{strb} = {rhs};")
|
|
||||||
self.add_content(f"is_external |= {rhs};")
|
|
||||||
return WalkerAction.SkipDescendants
|
return WalkerAction.SkipDescendants
|
||||||
|
|
||||||
return WalkerAction.Continue
|
return WalkerAction.Continue
|
||||||
@@ -170,44 +193,52 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
|
|||||||
return a
|
return a
|
||||||
|
|
||||||
|
|
||||||
|
def _add_reg_decoding_flags(self,
|
||||||
|
node: RegNode,
|
||||||
|
subword_index: Union[int, None] = None,
|
||||||
|
subword_stride: Union[int, None] = None) -> None:
|
||||||
|
if subword_index is None or subword_stride is None:
|
||||||
|
addr_decoding_str = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node)})"
|
||||||
|
else:
|
||||||
|
addr_decoding_str = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node, subword_offset=subword_index*subword_stride)})"
|
||||||
|
rhs_valid_addr = addr_decoding_str
|
||||||
|
readable = node.has_sw_readable
|
||||||
|
writable = node.has_sw_writable
|
||||||
|
if readable and writable:
|
||||||
|
rhs = addr_decoding_str
|
||||||
|
rhs_invalid_rw = "'0"
|
||||||
|
elif readable and not writable:
|
||||||
|
rhs = f"{addr_decoding_str} & !cpuif_req_is_wr"
|
||||||
|
rhs_invalid_rw = f"{addr_decoding_str} & cpuif_req_is_wr"
|
||||||
|
elif not readable and writable:
|
||||||
|
rhs = f"{addr_decoding_str} & cpuif_req_is_wr"
|
||||||
|
rhs_invalid_rw = f"{addr_decoding_str} & !cpuif_req_is_wr"
|
||||||
|
else:
|
||||||
|
raise RuntimeError
|
||||||
|
# Add decoding flags
|
||||||
|
if subword_index is None:
|
||||||
|
self.add_content(f"{self.addr_decode.get_access_strobe(node)} = {rhs};")
|
||||||
|
else:
|
||||||
|
self.add_content(f"{self.addr_decode.get_access_strobe(node)}[{subword_index}] = {rhs};")
|
||||||
|
if node.external:
|
||||||
|
self.add_content(f"is_external |= {rhs};")
|
||||||
|
if self.addr_decode.exp.ds.err_if_bad_addr:
|
||||||
|
self.add_content(f"is_valid_addr |= {rhs_valid_addr};")
|
||||||
|
if self.addr_decode.exp.ds.err_if_bad_rw:
|
||||||
|
self.add_content(f"is_invalid_rw |= {rhs_invalid_rw};")
|
||||||
|
|
||||||
def enter_Reg(self, node: RegNode) -> None:
|
def enter_Reg(self, node: RegNode) -> None:
|
||||||
regwidth = node.get_property('regwidth')
|
regwidth = node.get_property('regwidth')
|
||||||
accesswidth = node.get_property('accesswidth')
|
accesswidth = node.get_property('accesswidth')
|
||||||
|
|
||||||
if regwidth == accesswidth:
|
if regwidth == accesswidth:
|
||||||
rhs = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node)})"
|
self._add_reg_decoding_flags(node)
|
||||||
s = f"{self.addr_decode.get_access_strobe(node)} = {rhs};"
|
|
||||||
self.add_content(s)
|
|
||||||
if node.external:
|
|
||||||
readable = node.has_sw_readable
|
|
||||||
writable = node.has_sw_writable
|
|
||||||
if readable and writable:
|
|
||||||
self.add_content(f"is_external |= {rhs};")
|
|
||||||
elif readable and not writable:
|
|
||||||
self.add_content(f"is_external |= {rhs} & !cpuif_req_is_wr;")
|
|
||||||
elif not readable and writable:
|
|
||||||
self.add_content(f"is_external |= {rhs} & cpuif_req_is_wr;")
|
|
||||||
else:
|
|
||||||
raise RuntimeError
|
|
||||||
else:
|
else:
|
||||||
# Register is wide. Create a substrobe for each subword
|
# Register is wide. Create a substrobe for each subword
|
||||||
n_subwords = regwidth // accesswidth
|
n_subwords = regwidth // accesswidth
|
||||||
subword_stride = accesswidth // 8
|
subword_stride = accesswidth // 8
|
||||||
for i in range(n_subwords):
|
for i in range(n_subwords):
|
||||||
rhs = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node, subword_offset=i*subword_stride)})"
|
self._add_reg_decoding_flags(node, i, subword_stride)
|
||||||
s = f"{self.addr_decode.get_access_strobe(node)}[{i}] = {rhs};"
|
|
||||||
self.add_content(s)
|
|
||||||
if node.external:
|
|
||||||
readable = node.has_sw_readable
|
|
||||||
writable = node.has_sw_writable
|
|
||||||
if readable and writable:
|
|
||||||
self.add_content(f"is_external |= {rhs};")
|
|
||||||
elif readable and not writable:
|
|
||||||
self.add_content(f"is_external |= {rhs} & !cpuif_req_is_wr;")
|
|
||||||
elif not readable and writable:
|
|
||||||
self.add_content(f"is_external |= {rhs} & cpuif_req_is_wr;")
|
|
||||||
else:
|
|
||||||
raise RuntimeError
|
|
||||||
|
|
||||||
def exit_AddressableComponent(self, node: 'AddressableNode') -> None:
|
def exit_AddressableComponent(self, node: 'AddressableNode') -> None:
|
||||||
super().exit_AddressableComponent(node)
|
super().exit_AddressableComponent(node)
|
||||||
|
|||||||
@@ -120,6 +120,13 @@ class RegblockExporter:
|
|||||||
If overriden to True, default reset is active-low instead of active-high.
|
If overriden to True, default reset is active-low instead of active-high.
|
||||||
default_reset_async: bool
|
default_reset_async: bool
|
||||||
If overriden to True, default reset is asynchronous instead of synchronous.
|
If overriden to True, default reset is asynchronous instead of synchronous.
|
||||||
|
err_if_bad_addr: bool
|
||||||
|
If overriden to True: If the address is decoded incorrectly, the CPUIF response
|
||||||
|
signal shows an error. For example: APB.PSLVERR = 1'b1, AXI4LITE.*RESP = 2'b10.
|
||||||
|
err_if_bad_rw: bool
|
||||||
|
If overriden to True: If an illegal access is performed to a read-only or write-only
|
||||||
|
register, the CPUIF response signal shows an error. For example: APB.PSLVERR = 1'b1,
|
||||||
|
AXI4LITE.*RESP = 2'b10.
|
||||||
"""
|
"""
|
||||||
# If it is the root node, skip to top addrmap
|
# If it is the root node, skip to top addrmap
|
||||||
if isinstance(node, RootNode):
|
if isinstance(node, RootNode):
|
||||||
@@ -230,6 +237,10 @@ class DesignState:
|
|||||||
self.default_reset_activelow = kwargs.pop("default_reset_activelow", False) # type: bool
|
self.default_reset_activelow = kwargs.pop("default_reset_activelow", False) # type: bool
|
||||||
self.default_reset_async = kwargs.pop("default_reset_async", False) # type: bool
|
self.default_reset_async = kwargs.pop("default_reset_async", False) # type: bool
|
||||||
|
|
||||||
|
# Generating a cpuif error
|
||||||
|
self.err_if_bad_addr = kwargs.pop("err_if_bad_addr", False) # type: bool
|
||||||
|
self.err_if_bad_rw = kwargs.pop("err_if_bad_rw", False) # type: bool
|
||||||
|
|
||||||
#------------------------
|
#------------------------
|
||||||
# Info about the design
|
# Info about the design
|
||||||
#------------------------
|
#------------------------
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ module {{ds.module_name}}
|
|||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
{{address_decode.get_strobe_struct()|indent}}
|
{{address_decode.get_strobe_struct()|indent}}
|
||||||
decoded_reg_strb_t decoded_reg_strb;
|
decoded_reg_strb_t decoded_reg_strb;
|
||||||
|
logic decoded_err;
|
||||||
{%- if ds.has_external_addressable %}
|
{%- if ds.has_external_addressable %}
|
||||||
logic decoded_strb_is_external;
|
logic decoded_strb_is_external;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -138,11 +139,20 @@ module {{ds.module_name}}
|
|||||||
logic [{{cpuif.data_width-1}}:0] decoded_wr_biten;
|
logic [{{cpuif.data_width-1}}:0] decoded_wr_biten;
|
||||||
|
|
||||||
always_comb begin
|
always_comb begin
|
||||||
|
automatic logic is_valid_addr;
|
||||||
|
automatic logic is_invalid_rw;
|
||||||
{%- if ds.has_external_addressable %}
|
{%- if ds.has_external_addressable %}
|
||||||
automatic logic is_external;
|
automatic logic is_external;
|
||||||
is_external = '0;
|
is_external = '0;
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
{%- if ds.err_if_bad_addr %}
|
||||||
|
is_valid_addr = '0;
|
||||||
|
{%- else %}
|
||||||
|
is_valid_addr = '1; // No error checking on valid address access
|
||||||
|
{%- endif %}
|
||||||
|
is_invalid_rw = '0;
|
||||||
{{address_decode.get_implementation()|indent(8)}}
|
{{address_decode.get_implementation()|indent(8)}}
|
||||||
|
decoded_err = (~is_valid_addr | is_invalid_rw) & decoded_req;
|
||||||
{%- if ds.has_external_addressable %}
|
{%- if ds.has_external_addressable %}
|
||||||
decoded_strb_is_external = is_external;
|
decoded_strb_is_external = is_external;
|
||||||
external_req = is_external;
|
external_req = is_external;
|
||||||
@@ -225,7 +235,11 @@ module {{ds.module_name}}
|
|||||||
assign cpuif_wr_ack = decoded_req & decoded_req_is_wr;
|
assign cpuif_wr_ack = decoded_req & decoded_req_is_wr;
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
// Writes are always granted with no error response
|
// Writes are always granted with no error response
|
||||||
|
{%- if ds.err_if_bad_addr or ds.err_if_bad_rw %}
|
||||||
|
assign cpuif_wr_err = decoded_err;
|
||||||
|
{%- else %}
|
||||||
assign cpuif_wr_err = '0;
|
assign cpuif_wr_err = '0;
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Readback
|
// Readback
|
||||||
|
|||||||
@@ -29,12 +29,15 @@ end
|
|||||||
|
|
||||||
logic [{{cpuif.data_width-1}}:0] readback_array_r[{{fanin_array_size}}];
|
logic [{{cpuif.data_width-1}}:0] readback_array_r[{{fanin_array_size}}];
|
||||||
logic readback_done_r;
|
logic readback_done_r;
|
||||||
|
logic readback_err_r;
|
||||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||||
if({{get_resetsignal(cpuif.reset)}}) begin
|
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||||
for(int i=0; i<{{fanin_array_size}}; i++) readback_array_r[i] <= '0;
|
for(int i=0; i<{{fanin_array_size}}; i++) readback_array_r[i] <= '0;
|
||||||
readback_done_r <= '0;
|
readback_done_r <= '0;
|
||||||
|
readback_err_r <= '0;
|
||||||
end else begin
|
end else begin
|
||||||
readback_array_r <= readback_array_c;
|
readback_array_r <= readback_array_c;
|
||||||
|
readback_err_r <= decoded_err;
|
||||||
{%- if ds.has_external_addressable %}
|
{%- if ds.has_external_addressable %}
|
||||||
readback_done_r <= decoded_req & ~decoded_req_is_wr & ~decoded_strb_is_external;
|
readback_done_r <= decoded_req & ~decoded_req_is_wr & ~decoded_strb_is_external;
|
||||||
{%- else %}
|
{%- else %}
|
||||||
@@ -47,7 +50,11 @@ end
|
|||||||
always_comb begin
|
always_comb begin
|
||||||
automatic logic [{{cpuif.data_width-1}}:0] readback_data_var;
|
automatic logic [{{cpuif.data_width-1}}:0] readback_data_var;
|
||||||
readback_done = readback_done_r;
|
readback_done = readback_done_r;
|
||||||
|
{%- if ds.err_if_bad_addr or ds.err_if_bad_rw %}
|
||||||
|
readback_err = readback_err_r;
|
||||||
|
{%- else %}
|
||||||
readback_err = '0;
|
readback_err = '0;
|
||||||
|
{%- endif %}
|
||||||
readback_data_var = '0;
|
readback_data_var = '0;
|
||||||
for(int i=0; i<{{fanin_array_size}}; i++) readback_data_var |= readback_array_r[i];
|
for(int i=0; i<{{fanin_array_size}}; i++) readback_data_var |= readback_array_r[i];
|
||||||
readback_data = readback_data_var;
|
readback_data = readback_data_var;
|
||||||
@@ -63,7 +70,11 @@ always_comb begin
|
|||||||
{%- else %}
|
{%- else %}
|
||||||
readback_done = decoded_req & ~decoded_req_is_wr;
|
readback_done = decoded_req & ~decoded_req_is_wr;
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
{%- if ds.err_if_bad_addr or ds.err_if_bad_rw %}
|
||||||
|
readback_err = decoded_err;
|
||||||
|
{%- else %}
|
||||||
readback_err = '0;
|
readback_err = '0;
|
||||||
|
{%- endif %}
|
||||||
readback_data_var = '0;
|
readback_data_var = '0;
|
||||||
for(int i=0; i<{{array_size}}; i++) readback_data_var |= readback_array[i];
|
for(int i=0; i<{{array_size}}; i++) readback_data_var |= readback_array[i];
|
||||||
readback_data = readback_data_var;
|
readback_data = readback_data_var;
|
||||||
@@ -75,5 +86,9 @@ end
|
|||||||
{%- else %}
|
{%- else %}
|
||||||
assign readback_done = decoded_req & ~decoded_req_is_wr;
|
assign readback_done = decoded_req & ~decoded_req_is_wr;
|
||||||
assign readback_data = '0;
|
assign readback_data = '0;
|
||||||
|
{%- if ds.err_if_bad_addr or ds.err_if_bad_rw %}
|
||||||
|
assign readback_err = decoded_err;
|
||||||
|
{%- else %}
|
||||||
assign readback_err = '0;
|
assign readback_err = '0;
|
||||||
|
{%- endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ class BaseTestCase(unittest.TestCase):
|
|||||||
retime_external = False
|
retime_external = False
|
||||||
default_reset_activelow = False
|
default_reset_activelow = False
|
||||||
default_reset_async = False
|
default_reset_async = False
|
||||||
|
err_if_bad_addr = False
|
||||||
|
err_if_bad_rw = False
|
||||||
|
|
||||||
#: this gets auto-loaded via the _load_request autouse fixture
|
#: this gets auto-loaded via the _load_request autouse fixture
|
||||||
request = None # type: pytest.FixtureRequest
|
request = None # type: pytest.FixtureRequest
|
||||||
@@ -118,6 +120,8 @@ class BaseTestCase(unittest.TestCase):
|
|||||||
retime_external_addrmap=self.retime_external,
|
retime_external_addrmap=self.retime_external,
|
||||||
default_reset_activelow=self.default_reset_activelow,
|
default_reset_activelow=self.default_reset_activelow,
|
||||||
default_reset_async=self.default_reset_async,
|
default_reset_async=self.default_reset_async,
|
||||||
|
err_if_bad_addr=self.err_if_bad_addr,
|
||||||
|
err_if_bad_rw=self.err_if_bad_rw,
|
||||||
)
|
)
|
||||||
|
|
||||||
def delete_run_dir(self) -> None:
|
def delete_run_dir(self) -> None:
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ interface apb3_intf_driver #(
|
|||||||
|
|
||||||
semaphore txn_mutex = new(1);
|
semaphore txn_mutex = new(1);
|
||||||
|
|
||||||
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data);
|
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic expects_err = 1'b0);
|
||||||
txn_mutex.get();
|
txn_mutex.get();
|
||||||
##0;
|
##0;
|
||||||
|
|
||||||
@@ -68,11 +68,13 @@ interface apb3_intf_driver #(
|
|||||||
|
|
||||||
// Wait for response
|
// Wait for response
|
||||||
while(cb.PREADY !== 1'b1) @(cb);
|
while(cb.PREADY !== 1'b1) @(cb);
|
||||||
|
assert(!$isunknown(cb.PSLVERR)) else $error("Write to 0x%0x returned X's on PSLVERR", addr);
|
||||||
|
assert(cb.PSLVERR == expects_err) else $error("Error write response to 0x%x returned 0x%x. Expected 0x%x", addr, cb.PSLVERR, expects_err);
|
||||||
reset();
|
reset();
|
||||||
txn_mutex.put();
|
txn_mutex.put();
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
|
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data, input logic expects_err = 1'b0);
|
||||||
txn_mutex.get();
|
txn_mutex.get();
|
||||||
##0;
|
##0;
|
||||||
|
|
||||||
@@ -92,14 +94,15 @@ interface apb3_intf_driver #(
|
|||||||
while(cb.PREADY !== 1'b1) @(cb);
|
while(cb.PREADY !== 1'b1) @(cb);
|
||||||
assert(!$isunknown(cb.PRDATA)) else $error("Read from 0x%0x returned X's on PRDATA", addr);
|
assert(!$isunknown(cb.PRDATA)) else $error("Read from 0x%0x returned X's on PRDATA", addr);
|
||||||
assert(!$isunknown(cb.PSLVERR)) else $error("Read from 0x%0x returned X's on PSLVERR", addr);
|
assert(!$isunknown(cb.PSLVERR)) else $error("Read from 0x%0x returned X's on PSLVERR", addr);
|
||||||
|
assert(cb.PSLVERR == expects_err) else $error("Error read response from 0x%x returned 0x%x. Expected 0x%x", addr, cb.PSLVERR, expects_err);
|
||||||
data = cb.PRDATA;
|
data = cb.PRDATA;
|
||||||
reset();
|
reset();
|
||||||
txn_mutex.put();
|
txn_mutex.put();
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
|
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, input logic [DATA_WIDTH-1:0] mask = {DATA_WIDTH{1'b1}}, input logic expects_err = 1'b0);
|
||||||
logic [DATA_WIDTH-1:0] data;
|
logic [DATA_WIDTH-1:0] data;
|
||||||
read(addr, data);
|
read(addr, data, expects_err);
|
||||||
data &= mask;
|
data &= mask;
|
||||||
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
||||||
endtask
|
endtask
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ interface apb4_intf_driver #(
|
|||||||
|
|
||||||
semaphore txn_mutex = new(1);
|
semaphore txn_mutex = new(1);
|
||||||
|
|
||||||
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = '1);
|
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = {DATA_WIDTH{1'b1}}, logic expects_err = 0);
|
||||||
txn_mutex.get();
|
txn_mutex.get();
|
||||||
##0;
|
##0;
|
||||||
|
|
||||||
@@ -78,11 +78,13 @@ interface apb4_intf_driver #(
|
|||||||
|
|
||||||
// Wait for response
|
// Wait for response
|
||||||
while(cb.PREADY !== 1'b1) @(cb);
|
while(cb.PREADY !== 1'b1) @(cb);
|
||||||
|
assert(!$isunknown(cb.PSLVERR)) else $error("Write to 0x%0x returned X's on PSLVERR", addr);
|
||||||
|
assert(cb.PSLVERR == expects_err) else $error("Error write response to 0x%x returned 0x%x. Expected 0x%x", addr, cb.PSLVERR, expects_err);
|
||||||
reset();
|
reset();
|
||||||
txn_mutex.put();
|
txn_mutex.put();
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
|
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data, input logic expects_err = 0);
|
||||||
txn_mutex.get();
|
txn_mutex.get();
|
||||||
##0;
|
##0;
|
||||||
|
|
||||||
@@ -104,14 +106,15 @@ interface apb4_intf_driver #(
|
|||||||
while(cb.PREADY !== 1'b1) @(cb);
|
while(cb.PREADY !== 1'b1) @(cb);
|
||||||
assert(!$isunknown(cb.PRDATA)) else $error("Read from 0x%0x returned X's on PRDATA", addr);
|
assert(!$isunknown(cb.PRDATA)) else $error("Read from 0x%0x returned X's on PRDATA", addr);
|
||||||
assert(!$isunknown(cb.PSLVERR)) else $error("Read from 0x%0x returned X's on PSLVERR", addr);
|
assert(!$isunknown(cb.PSLVERR)) else $error("Read from 0x%0x returned X's on PSLVERR", addr);
|
||||||
|
assert(cb.PSLVERR == expects_err) else $error("Error read response from 0x%x returned 0x%x. Expected 0x%x", addr, cb.PSLVERR, expects_err);
|
||||||
data = cb.PRDATA;
|
data = cb.PRDATA;
|
||||||
reset();
|
reset();
|
||||||
txn_mutex.put();
|
txn_mutex.put();
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
|
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = {DATA_WIDTH{1'b1}}, input logic expects_err = 0);
|
||||||
logic [DATA_WIDTH-1:0] data;
|
logic [DATA_WIDTH-1:0] data;
|
||||||
read(addr, data);
|
read(addr, data, expects_err);
|
||||||
data &= mask;
|
data &= mask;
|
||||||
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
||||||
endtask
|
endtask
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ interface avalon_mm_intf_driver #(
|
|||||||
semaphore req_mutex = new(1);
|
semaphore req_mutex = new(1);
|
||||||
semaphore resp_mutex = new(1);
|
semaphore resp_mutex = new(1);
|
||||||
|
|
||||||
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = '1);
|
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = {DATA_WIDTH{1'b1}}, logic expects_err = 1'b0);
|
||||||
fork
|
fork
|
||||||
begin
|
begin
|
||||||
req_mutex.get();
|
req_mutex.get();
|
||||||
@@ -82,13 +82,14 @@ interface avalon_mm_intf_driver #(
|
|||||||
@cb;
|
@cb;
|
||||||
// Wait for response
|
// Wait for response
|
||||||
while(cb.av_writeresponsevalid !== 1'b1) @(cb);
|
while(cb.av_writeresponsevalid !== 1'b1) @(cb);
|
||||||
assert(!$isunknown(cb.av_response)) else $error("Read from 0x%0x returned X's on av_response", addr);
|
assert(!$isunknown(cb.av_response)) else $error("Write to 0x%0x returned X's on av_response", addr);
|
||||||
|
assert((cb.av_response == 2'b10) == expects_err) else $error("Error write response to 0x%x returned 0x%x. Expected 0x%x", addr, (cb.av_response == 2'b10), expects_err);
|
||||||
resp_mutex.put();
|
resp_mutex.put();
|
||||||
end
|
end
|
||||||
join
|
join
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
|
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data, input logic expects_err = 1'b0);
|
||||||
fork
|
fork
|
||||||
begin
|
begin
|
||||||
req_mutex.get();
|
req_mutex.get();
|
||||||
@@ -111,15 +112,16 @@ interface avalon_mm_intf_driver #(
|
|||||||
while(cb.av_readdatavalid !== 1'b1) @(cb);
|
while(cb.av_readdatavalid !== 1'b1) @(cb);
|
||||||
assert(!$isunknown(cb.av_readdata)) else $error("Read from 0x%0x returned X's on av_response", av_readdata);
|
assert(!$isunknown(cb.av_readdata)) else $error("Read from 0x%0x returned X's on av_response", av_readdata);
|
||||||
assert(!$isunknown(cb.av_response)) else $error("Read from 0x%0x returned X's on av_response", addr);
|
assert(!$isunknown(cb.av_response)) else $error("Read from 0x%0x returned X's on av_response", addr);
|
||||||
|
assert((cb.av_response == 2'b10) == expects_err) else $error("Error read response from 0x%x returned 0x%x. Expected 0x%x", addr, (cb.av_response == 2'b10), expects_err);
|
||||||
data = cb.av_readdata;
|
data = cb.av_readdata;
|
||||||
resp_mutex.put();
|
resp_mutex.put();
|
||||||
end
|
end
|
||||||
join
|
join
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
|
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = {DATA_WIDTH{1'b1}}, input logic expects_err = 1'b0);
|
||||||
logic [DATA_WIDTH-1:0] data;
|
logic [DATA_WIDTH-1:0] data;
|
||||||
read(addr, data);
|
read(addr, data, expects_err);
|
||||||
data &= mask;
|
data &= mask;
|
||||||
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
||||||
endtask
|
endtask
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ interface axi4lite_intf_driver #(
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = '1);
|
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, input logic [DATA_WIDTH/8-1:0] strb = {DATA_WIDTH{1'b1}}, logic expects_err = 1'b0);
|
||||||
write_request_t req;
|
write_request_t req;
|
||||||
write_response_t resp;
|
write_response_t resp;
|
||||||
|
|
||||||
@@ -175,7 +175,8 @@ interface axi4lite_intf_driver #(
|
|||||||
|
|
||||||
// Wait for response
|
// Wait for response
|
||||||
req.response_mbx.get(resp);
|
req.response_mbx.get(resp);
|
||||||
assert(!$isunknown(resp.bresp)) else $error("Read from 0x%0x returned X's on BRESP", addr);
|
assert(!$isunknown(resp.bresp)) else $error("Write to 0x%0x returned X's on BRESP", addr);
|
||||||
|
assert((resp.bresp==2'b10) == expects_err) else $error("Error write response to 0x%x returned 0x%x. Expected 0x%x", addr, (resp.bresp==2'b10), expects_err);
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -212,7 +213,7 @@ interface axi4lite_intf_driver #(
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
|
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data, input logic expects_err = 1'b0);
|
||||||
read_request_t req;
|
read_request_t req;
|
||||||
read_response_t resp;
|
read_response_t resp;
|
||||||
|
|
||||||
@@ -236,12 +237,13 @@ interface axi4lite_intf_driver #(
|
|||||||
|
|
||||||
assert(!$isunknown(resp.rdata)) else $error("Read from 0x%0x returned X's on RDATA", addr);
|
assert(!$isunknown(resp.rdata)) else $error("Read from 0x%0x returned X's on RDATA", addr);
|
||||||
assert(!$isunknown(resp.rresp)) else $error("Read from 0x%0x returned X's on RRESP", addr);
|
assert(!$isunknown(resp.rresp)) else $error("Read from 0x%0x returned X's on RRESP", addr);
|
||||||
|
assert((resp.rresp == 2'b10) == expects_err) else $error("Error read response from 0x%x returned 0x%x. Expected 0x%x", addr, (resp.rresp == 2'b10), expects_err);
|
||||||
data = resp.rdata;
|
data = resp.rdata;
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
|
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = {DATA_WIDTH{1'b1}}, input logic expects_err = 1'b0);
|
||||||
logic [DATA_WIDTH-1:0] data;
|
logic [DATA_WIDTH-1:0] data;
|
||||||
read(addr, data);
|
read(addr, data, expects_err);
|
||||||
data &= mask;
|
data &= mask;
|
||||||
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
||||||
endtask
|
endtask
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ interface obi_intf_driver #(
|
|||||||
end
|
end
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
|
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data, input logic expects_err = 1'b0);
|
||||||
request_t req;
|
request_t req;
|
||||||
response_t resp;
|
response_t resp;
|
||||||
logic [ID_WIDTH-1:0] id;
|
logic [ID_WIDTH-1:0] id;
|
||||||
@@ -141,18 +141,19 @@ interface obi_intf_driver #(
|
|||||||
assert(!$isunknown(resp.rdata)) else $error("Read from 0x%0x returned X's on rdata", addr);
|
assert(!$isunknown(resp.rdata)) else $error("Read from 0x%0x returned X's on rdata", addr);
|
||||||
assert(!$isunknown(resp.err)) else $error("Read from 0x%0x returned X's on err", addr);
|
assert(!$isunknown(resp.err)) else $error("Read from 0x%0x returned X's on err", addr);
|
||||||
assert(!$isunknown(resp.rid)) else $error("Read from 0x%0x returned X's on rid", addr);
|
assert(!$isunknown(resp.rid)) else $error("Read from 0x%0x returned X's on rid", addr);
|
||||||
|
assert(resp.err == expects_err) else $error("Error read response from 0x%x returned 0x%x. Expected 0x%x", addr, resp.err, expects_err);
|
||||||
data = resp.rdata;
|
data = resp.rdata;
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
|
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = {DATA_WIDTH{1'b1}}, input logic expects_err = 1'b0);
|
||||||
logic [DATA_WIDTH-1:0] data;
|
logic [DATA_WIDTH-1:0] data;
|
||||||
read(addr, data);
|
read(addr, data, expects_err);
|
||||||
data &= mask;
|
data &= mask;
|
||||||
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = '1);
|
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = {DATA_WIDTH{1'b1}}, input logic expects_err = 1'b0);
|
||||||
request_t req;
|
request_t req;
|
||||||
response_t resp;
|
response_t resp;
|
||||||
logic [ID_WIDTH-1:0] id;
|
logic [ID_WIDTH-1:0] id;
|
||||||
@@ -183,6 +184,7 @@ interface obi_intf_driver #(
|
|||||||
|
|
||||||
assert(!$isunknown(resp.err)) else $error("Read from 0x%0x returned X's on err", addr);
|
assert(!$isunknown(resp.err)) else $error("Read from 0x%0x returned X's on err", addr);
|
||||||
assert(!$isunknown(resp.rid)) else $error("Read from 0x%0x returned X's on rid", addr);
|
assert(!$isunknown(resp.rid)) else $error("Read from 0x%0x returned X's on rid", addr);
|
||||||
|
assert(resp.err == expects_err) else $error("Error write response to 0x%x returned 0x%x. Expected 0x%x", addr, resp.err, expects_err);
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ interface passthrough_driver #(
|
|||||||
semaphore txn_req_mutex = new(1);
|
semaphore txn_req_mutex = new(1);
|
||||||
semaphore txn_resp_mutex = new(1);
|
semaphore txn_resp_mutex = new(1);
|
||||||
|
|
||||||
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH-1:0] biten = '1);
|
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH-1:0] biten = {DATA_WIDTH{1'b1}}, logic expects_err = 1'b0);
|
||||||
fork
|
fork
|
||||||
begin
|
begin
|
||||||
// Initiate transfer
|
// Initiate transfer
|
||||||
@@ -71,12 +71,13 @@ interface passthrough_driver #(
|
|||||||
txn_resp_mutex.get();
|
txn_resp_mutex.get();
|
||||||
@cb;
|
@cb;
|
||||||
while(cb.m_cpuif_wr_ack !== 1'b1) @(cb);
|
while(cb.m_cpuif_wr_ack !== 1'b1) @(cb);
|
||||||
|
assert(cb.m_cpuif_wr_err == expects_err) else $error("Error write response to 0x%x returned 0x%x. Expected 0x%x", addr, cb.m_cpuif_wr_err, expects_err);
|
||||||
txn_resp_mutex.put();
|
txn_resp_mutex.put();
|
||||||
end
|
end
|
||||||
join
|
join
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
|
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data, input logic expects_err = 1'b0);
|
||||||
fork
|
fork
|
||||||
begin
|
begin
|
||||||
// Initiate transfer
|
// Initiate transfer
|
||||||
@@ -97,15 +98,16 @@ interface passthrough_driver #(
|
|||||||
@cb;
|
@cb;
|
||||||
while(cb.m_cpuif_rd_ack !== 1'b1) @(cb);
|
while(cb.m_cpuif_rd_ack !== 1'b1) @(cb);
|
||||||
assert(!$isunknown(cb.m_cpuif_rd_data)) else $error("Read from 0x%0x returned X's on m_cpuif_rd_data", addr);
|
assert(!$isunknown(cb.m_cpuif_rd_data)) else $error("Read from 0x%0x returned X's on m_cpuif_rd_data", addr);
|
||||||
|
assert(cb.m_cpuif_rd_err == expects_err) else $error("Error read response from 0x%x returned 0x%x. Expected 0x%x", addr, cb.m_cpuif_rd_err, expects_err);
|
||||||
data = cb.m_cpuif_rd_data;
|
data = cb.m_cpuif_rd_data;
|
||||||
txn_resp_mutex.put();
|
txn_resp_mutex.put();
|
||||||
end
|
end
|
||||||
join
|
join
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
|
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = {DATA_WIDTH{1'b1}}, input logic expects_err = 1'b0);
|
||||||
logic [DATA_WIDTH-1:0] data;
|
logic [DATA_WIDTH-1:0] data;
|
||||||
read(addr, data);
|
read(addr, data, expects_err);
|
||||||
data &= mask;
|
data &= mask;
|
||||||
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
||||||
endtask
|
endtask
|
||||||
|
|||||||
0
tests/test_cpuif_err_rsp/__init__.py
Normal file
0
tests/test_cpuif_err_rsp/__init__.py
Normal file
59
tests/test_cpuif_err_rsp/regblock.rdl
Normal file
59
tests/test_cpuif_err_rsp/regblock.rdl
Normal 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;
|
||||||
|
|
||||||
|
};
|
||||||
221
tests/test_cpuif_err_rsp/tb_template.sv
Normal file
221
tests/test_cpuif_err_rsp/tb_template.sv
Normal 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 %}
|
||||||
29
tests/test_cpuif_err_rsp/testcase.py
Normal file
29
tests/test_cpuif_err_rsp/testcase.py
Normal 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()
|
||||||
Reference in New Issue
Block a user