regblock -> busdecoder

This commit is contained in:
Arnav Sacheti
2025-10-10 22:30:59 -07:00
parent 9bf5cd1e68
commit b4f9eaff71
78 changed files with 904 additions and 705 deletions

View File

@@ -8,9 +8,9 @@ omit =
[paths]
source =
../src/peakrdl_regblock/
*/site-packages/*/peakrdl_regblock
*/site-packages/peakrdl_regblock
../src/peakrdl_busdecoder/
*/site-packages/*/peakrdl_busdecoder
*/site-packages/peakrdl_busdecoder
[report]
exclude_lines =

View File

@@ -104,8 +104,8 @@ Each testcase group has its own folder and contains the following:
`test_*/__init__.py`
: Empty file required for test discovery.
`test_*/regblock.rdl`
: Testcase RDL file. Testcase infrastructure will automatically compile this and generate the regblock output SystemVerilog.
`test_*/busdecoder.rdl`
: Testcase RDL file. Testcase infrastructure will automatically compile this and generate the busdecoder output SystemVerilog.
`test_*/tb_template.sv`
: Jinja template that defines the testcase-specific sequence.
@@ -116,4 +116,4 @@ Each testcase group has its own folder and contains the following:
## Parameterization
Testcase classes can be parameterized using the [parameterized](https://github.com/wolever/parameterized) extension. This allows the same testcase to be run against multiple permutations of regblock export modes such as CPU interfaces, retiming flop stages, or even RDL parameterizations.
Testcase classes can be parameterized using the [parameterized](https://github.com/wolever/parameterized) extension. This allows the same testcase to be run against multiple permutations of busdecoder export modes such as CPU interfaces, retiming flop stages, or even RDL parameterizations.

View File

@@ -9,7 +9,7 @@ def pytest_addoption(parser):
stub: run the testcase using a no-op simulator stub
skip: skip all the simulation tests
auto: choose the best simulator based on what is installed
"""
""",
)
parser.addoption(
@@ -20,19 +20,18 @@ def pytest_addoption(parser):
Launch sim tool in GUI mode
Only use this option when running a single test
"""
""",
)
parser.addoption(
"--rerun",
default=False,
action="store_true",
help=""",
Re-run simulation in-place without re-exporting regblock
Re-run simulation in-place without re-exporting busdecoder
Useful if hand-editing a testcase interactively.
"""
""",
)
parser.addoption(
@@ -44,5 +43,5 @@ def pytest_addoption(parser):
skip: skip all the simulation tests
auto: choose the best tool based on what is installed
"""
""",
)

View File

@@ -9,8 +9,8 @@ import pathlib
import pytest
from systemrdl import RDLCompiler
from peakrdl_regblock import RegblockExporter
from peakrdl_regblock.udps import ALL_UDPS
from peakrdl_busdecoder import BusDecoderExporter
from peakrdl_busdecoder.udps import ALL_UDPS
from .cpuifs.base import CpuifTestMode
from .cpuifs.apb4 import APB4
@@ -20,17 +20,17 @@ class BaseTestCase(unittest.TestCase):
#: Path to the testcase's RDL file.
#: Relative to the testcase's dir. If unset, the first RDL file found in the
#: testcase dir will be used
rdl_file = None # type: Optional[str]
rdl_file = None # type: Optional[str]
#: RDL type name to elaborate. If unset, compiler will automatically choose
#: the top.
rdl_elab_target = None # type: Optional[str]
rdl_elab_target = None # type: Optional[str]
#: Parameters to pass into RDL elaboration
rdl_elab_params = {}
#: Define what CPUIF to use for this testcase
cpuif = APB4() # type: CpuifTestMode
cpuif = APB4() # type: CpuifTestMode
# Other exporter args:
retime_read_fanin = False
@@ -41,9 +41,9 @@ class BaseTestCase(unittest.TestCase):
default_reset_async = False
#: this gets auto-loaded via the _load_request autouse fixture
request = None # type: pytest.FixtureRequest
request = None # type: pytest.FixtureRequest
exporter = RegblockExporter()
exporter = BusDecoderExporter()
@pytest.fixture(autouse=True)
def _load_request(self, request):
@@ -72,16 +72,15 @@ class BaseTestCase(unittest.TestCase):
"""
path = os.path.join(self.get_run_dir(), "params.txt")
with open(path, 'w') as f:
with open(path, "w") as f:
for k, v in self.__class__.__dict__.items():
if k.startswith("_") or callable(v):
continue
f.write(f"{k}: {repr(v)}\n")
def export_regblock(self):
def export_busdecoder(self):
"""
Call the peakrdl_regblock exporter to generate the DUT
Call the peakrdl_busdecoder exporter to generate the DUT
"""
this_dir = self.get_testcase_dir()
@@ -97,17 +96,17 @@ class BaseTestCase(unittest.TestCase):
for udp in ALL_UDPS:
rdlc.register_udp(udp)
# ... including the definition
udp_file = os.path.join(this_dir, "../../hdl-src/regblock_udps.rdl")
udp_file = os.path.join(this_dir, "../../hdl-src/busdecoder_udps.rdl")
rdlc.compile_file(udp_file)
rdlc.compile_file(rdl_file)
root = rdlc.elaborate(self.rdl_elab_target, "regblock", self.rdl_elab_params)
root = rdlc.elaborate(self.rdl_elab_target, "busdecoder", self.rdl_elab_params)
self.exporter.export(
root,
self.get_run_dir(),
module_name="regblock",
package_name="regblock_pkg",
module_name="busdecoder",
package_name="busdecoder_pkg",
cpuif_cls=self.cpuif.cpuif_cls,
retime_read_fanin=self.retime_read_fanin,
retime_read_response=self.retime_read_response,
@@ -137,4 +136,4 @@ class BaseTestCase(unittest.TestCase):
self._write_params()
# Convert testcase RDL file --> SV
self.export_regblock()
self.export_busdecoder()

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.apb3 import APB3_Cpuif, APB3_Cpuif_flattened
from peakrdl_busdecoder.cpuif.apb3 import APB3_Cpuif, APB3_Cpuif_flattened
class APB3(CpuifTestMode):
cpuif_cls = APB3_Cpuif
@@ -13,6 +14,7 @@ class APB3(CpuifTestMode):
]
tb_template = "tb_inst.sv"
class FlatAPB3(APB3):
cpuif_cls = APB3_Cpuif_flattened
rtl_files = []

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.apb4 import APB4_Cpuif, APB4_Cpuif_flattened
from peakrdl_busdecoder.cpuif.apb4 import APB4_Cpuif, APB4_Cpuif_flattened
class APB4(CpuifTestMode):
cpuif_cls = APB4_Cpuif
@@ -13,6 +14,7 @@ class APB4(CpuifTestMode):
]
tb_template = "tb_inst.sv"
class FlatAPB4(APB4):
cpuif_cls = APB4_Cpuif_flattened
rtl_files = []

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.avalon import Avalon_Cpuif, Avalon_Cpuif_flattened
from peakrdl_busdecoder.cpuif.avalon import Avalon_Cpuif, Avalon_Cpuif_flattened
class Avalon(CpuifTestMode):
cpuif_cls = Avalon_Cpuif
@@ -13,6 +14,7 @@ class Avalon(CpuifTestMode):
]
tb_template = "tb_inst.sv"
class FlatAvalon(Avalon):
cpuif_cls = Avalon_Cpuif_flattened
rtl_files = []

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.axi4lite import AXI4Lite_Cpuif, AXI4Lite_Cpuif_flattened
from peakrdl_busdecoder.cpuif.axi4lite import AXI4Lite_Cpuif, AXI4Lite_Cpuif_flattened
class AXI4Lite(CpuifTestMode):
cpuif_cls = AXI4Lite_Cpuif
@@ -13,6 +14,7 @@ class AXI4Lite(CpuifTestMode):
]
tb_template = "tb_inst.sv"
class FlatAXI4Lite(AXI4Lite):
cpuif_cls = AXI4Lite_Cpuif_flattened
rtl_files = []

View File

@@ -4,16 +4,17 @@ import inspect
import jinja2 as jj
from peakrdl_regblock.cpuif.base import CpuifBase
from peakrdl_busdecoder.cpuif.base import CpuifBase
from ..sv_line_anchor import SVLineAnchor
if TYPE_CHECKING:
from peakrdl_regblock import RegblockExporter
from peakrdl_busdecoder import BusDecoderExporter
from ..sim_testcase import SimTestCase
class CpuifTestMode:
cpuif_cls = None # type: CpuifBase
cpuif_cls = None # type: CpuifBase
# Files required by the DUT
# Paths are relative to the class that assigns this
@@ -21,13 +22,12 @@ class CpuifTestMode:
# Files required by the sim testbench
# Paths are relative to the class that assigns this
tb_files = [] # type: List[str]
tb_files = [] # type: List[str]
# Path is relative to the class that assigns this
tb_template = ""
def _get_class_dir_of_variable(self, varname:str) -> str:
def _get_class_dir_of_variable(self, varname: str) -> str:
"""
Traverse up the MRO and find the first class that explicitly assigns
the variable of name varname. Returns the directory that contains the
@@ -39,34 +39,29 @@ class CpuifTestMode:
return class_dir
raise RuntimeError
def _get_file_paths(self, varname:str) -> List[str]:
def _get_file_paths(self, varname: str) -> List[str]:
class_dir = self._get_class_dir_of_variable(varname)
files = getattr(self, varname)
cwd = os.getcwd()
new_files = []
for file in files:
relpath = os.path.relpath(
os.path.join(class_dir, file),
cwd
)
relpath = os.path.relpath(os.path.join(class_dir, file), cwd)
new_files.append(relpath)
return new_files
def get_sim_files(self) -> List[str]:
files = self._get_file_paths("rtl_files") + self._get_file_paths("tb_files")
unique_files = []
[unique_files.append(f) for f in files if f not in unique_files]
return unique_files
def get_synth_files(self) -> List[str]:
return self._get_file_paths("rtl_files")
def get_tb_inst(self, testcase: 'SimTestCase', exporter: 'RegblockExporter') -> str:
def get_tb_inst(
self, testcase: "SimTestCase", exporter: "BusDecoderExporter"
) -> str:
class_dir = self._get_class_dir_of_variable("tb_template")
loader = jj.FileSystemLoader(class_dir)
jj_env = jj.Environment(

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.passthrough import PassthroughCpuif
from peakrdl_busdecoder.cpuif.passthrough import PassthroughCpuif
class Passthrough(CpuifTestMode):
cpuif_cls = PassthroughCpuif

View File

@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
from ..sim_testcase import SimTestCase
class Simulator:
name = ""
@@ -10,7 +11,7 @@ class Simulator:
def is_installed(cls) -> bool:
raise NotImplementedError
def __init__(self, testcase: 'SimTestCase' = None) -> None:
def __init__(self, testcase: "SimTestCase" = None) -> None:
self.testcase = testcase
@property
@@ -22,8 +23,8 @@ class Simulator:
files = []
files.extend(self.testcase.cpuif.get_sim_files())
files.extend(self.testcase.get_extra_tb_files())
files.append("regblock_pkg.sv")
files.append("regblock.sv")
files.append("busdecoder_pkg.sv")
files.append("busdecoder.sv")
files.append("tb.sv")
return files
@@ -31,5 +32,5 @@ class Simulator:
def compile(self) -> None:
raise NotImplementedError
def run(self, plusargs:List[str] = None) -> None:
def run(self, plusargs: List[str] = None) -> None:
raise NotImplementedError

View File

@@ -6,13 +6,13 @@ import pytest
from .base_testcase import BaseTestCase
from .synthesizers import get_synthesizer_cls
class SynthTestCase(BaseTestCase):
class SynthTestCase(BaseTestCase):
def _get_synth_files(self) -> List[str]:
files = []
files.extend(self.cpuif.get_synth_files())
files.append("regblock_pkg.sv")
files.append("regblock.sv")
files.append("busdecoder_pkg.sv")
files.append("busdecoder.sv")
return files

View File

@@ -25,7 +25,7 @@ set_msg_config -severity {CRITICAL WARNING} -new_severity "ERROR"
set_part [lindex [get_parts] 0]
read_verilog -sv $files
read_xdc $this_dir/constr.xdc
synth_design -top regblock -mode out_of_context
synth_design -top busdecoder -mode out_of_context
#write_checkpoint -force synth.dcp

View File

@@ -28,11 +28,11 @@ module tb;
// DUT Signal declarations
//--------------------------------------------------------------------------
{%- if exporter.hwif.has_input_struct %}
regblock_pkg::regblock__in_t hwif_in;
busdecoder_pkg::busdecoder__in_t hwif_in;
{%- endif %}
{%- if exporter.hwif.has_output_struct %}
regblock_pkg::regblock__out_t hwif_out;
busdecoder_pkg::busdecoder__out_t hwif_out;
{%- endif %}
{%- if exporter.ds.has_paritycheck %}
@@ -76,7 +76,7 @@ module tb;
// DUT
//--------------------------------------------------------------------------
{% sv_line_anchor %}
regblock dut (.*);
busdecoder dut (.*);
{%- if exporter.hwif.has_output_struct %}
{% sv_line_anchor %}

View File

@@ -16,13 +16,13 @@ pip install -r requirements.txt
pip install -e "../[cli]"
# Run lint
pylint --rcfile pylint.rc ../src/peakrdl_regblock
pylint --rcfile pylint.rc ../src/peakrdl_busdecoder
# Run static type checking
mypy ../src/peakrdl_regblock
mypy ../src/peakrdl_busdecoder
# Run unit tests
pytest --workers auto --cov=peakrdl_regblock --synth-tool skip
pytest --workers auto --cov=peakrdl_busdecoder --synth-tool skip
# Generate coverage report
coverage html -i -d htmlcov

View File

@@ -7,16 +7,16 @@
##1;
// check enum values
assert(regblock_pkg::top__my_enum__val_1 == 'd3);
assert(regblock_pkg::top__my_enum__val_2 == 'd4);
assert(busdecoder_pkg::top__my_enum__val_1 == 'd3);
assert(busdecoder_pkg::top__my_enum__val_2 == 'd4);
// check initial conditions
cpuif.assert_read('h0, regblock_pkg::top__my_enum__val_2);
cpuif.assert_read('h0, busdecoder_pkg::top__my_enum__val_2);
//---------------------------------
// set r0 = val_1
cpuif.write('h0, regblock_pkg::top__my_enum__val_1);
cpuif.write('h0, busdecoder_pkg::top__my_enum__val_1);
cpuif.assert_read('h0, regblock_pkg::top__my_enum__val_1);
cpuif.assert_read('h0, busdecoder_pkg::top__my_enum__val_1);
{% endblock %}

View File

@@ -296,8 +296,8 @@
// Check register struct bit-order
repeat(32) begin
regblock_pkg::top__my_reg_alt__external__fields__in_t fields_in;
regblock_pkg::top__my_reg_alt__external__fields__out_t fields_out;
busdecoder_pkg::top__my_reg_alt__external__fields__in_t fields_in;
busdecoder_pkg::top__my_reg_alt__external__fields__out_t fields_out;
fields_in = $urandom();
fields_out = $urandom();

View File

@@ -7,6 +7,6 @@
##1;
// check block size
assert(regblock_pkg::REGBLOCK_SIZE == {{exporter.ds.top_node.size}});
assert(busdecoder_pkg::REGBLOCK_SIZE == {{exporter.ds.top_node.size}});
{% endblock %}

View File

@@ -1,4 +1,4 @@
addrmap regblock {
addrmap busdecoder {
default sw=rw;
default hw=r;

View File

@@ -2,7 +2,7 @@
{% block seq %}
{% sv_line_anchor %}
assert(regblock_pkg::N_REGS == {{testcase.n_regs}});
assert(regblock_pkg::REGWIDTH == {{testcase.regwidth}});
assert(regblock_pkg::NAME == "{{testcase.name}}");
assert(busdecoder_pkg::N_REGS == {{testcase.n_regs}});
assert(busdecoder_pkg::REGWIDTH == {{testcase.regwidth}});
assert(busdecoder_pkg::NAME == "{{testcase.name}}");
{% endblock %}

View File

@@ -1,4 +1,4 @@
addrmap regblock {
addrmap busdecoder {
default sw=rw;
default hw=r;

View File

@@ -1,17 +1,19 @@
import os
from peakrdl_regblock.cpuif.apb3 import APB3_Cpuif
from peakrdl_busdecoder.cpuif.apb3 import APB3_Cpuif
from ..lib.cpuifs.apb3 import APB3
from ..lib.base_testcase import BaseTestCase
#-------------------------------------------------------------------------------
# -------------------------------------------------------------------------------
class ClassOverride_Cpuif(APB3_Cpuif):
@property
def port_declaration(self) -> str:
return "user_apb3_intf.slave s_apb"
class ClassOverride_cpuiftestmode(APB3):
cpuif_cls = ClassOverride_Cpuif
@@ -20,19 +22,19 @@ class Test_class_override(BaseTestCase):
cpuif = ClassOverride_cpuiftestmode()
def test_override_success(self):
output_file = os.path.join(self.get_run_dir(), "regblock.sv")
output_file = os.path.join(self.get_run_dir(), "busdecoder.sv")
with open(output_file, "r") as f:
self.assertIn(
"user_apb3_intf.slave s_apb",
f.read()
)
self.assertIn("user_apb3_intf.slave s_apb", f.read())
# -------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
class TemplateOverride_Cpuif(APB3_Cpuif):
# contains the text "USER TEMPLATE OVERRIDE"
template_path = "user_apb3_tmpl.sv"
class TemplateOverride_cpuiftestmode(APB3):
cpuif_cls = TemplateOverride_Cpuif
@@ -41,9 +43,6 @@ class Test_template_override(BaseTestCase):
cpuif = TemplateOverride_cpuiftestmode()
def test_override_success(self):
output_file = os.path.join(self.get_run_dir(), "regblock.sv")
output_file = os.path.join(self.get_run_dir(), "busdecoder.sv")
with open(output_file, "r") as f:
self.assertIn(
"USER TEMPLATE OVERRIDE",
f.read()
)
self.assertIn("USER TEMPLATE OVERRIDE", f.read())

View File

@@ -5,6 +5,7 @@ from systemrdl.messages import RDLCompileError
from ..lib.base_testcase import BaseTestCase
class TestValidationErrors(BaseTestCase):
def setUp(self) -> None:
# Stub usual pre-test setup
@@ -19,11 +20,10 @@ class TestValidationErrors(BaseTestCase):
f = io.StringIO()
with contextlib.redirect_stderr(f):
with self.assertRaises(RDLCompileError):
self.export_regblock()
self.export_busdecoder()
stderr = f.getvalue()
self.assertRegex(stderr, err_regex)
def test_unaligned_reg(self) -> None:
self.assert_validate_error(
"unaligned_reg.rdl",
@@ -39,7 +39,7 @@ class TestValidationErrors(BaseTestCase):
def test_bad_external_ref(self) -> None:
self.assert_validate_error(
"external_ref.rdl",
"Property is assigned a reference that points to a component not internal to the regblock being exported",
"Property is assigned a reference that points to a component not internal to the busdecoder being exported",
)
def test_sharedextbus_not_supported(self) -> None:
@@ -51,7 +51,7 @@ class TestValidationErrors(BaseTestCase):
def test_inconsistent_accesswidth(self) -> None:
self.assert_validate_error(
"inconsistent_accesswidth.rdl",
r"Multi-word registers that have an accesswidth \(16\) that are inconsistent with this regblock's CPU bus width \(32\) are not supported",
r"Multi-word registers that have an accesswidth \(16\) that are inconsistent with this busdecoder's CPU bus width \(32\) are not supported",
)
def test_unbuffered_wide_w_fields(self) -> None:
@@ -117,5 +117,5 @@ class TestValidationErrors(BaseTestCase):
def test_signed_enum(self) -> None:
self.assert_validate_error(
"signed_enum.rdl",
"The property is_signed=true is not supported for fields encoded as an enum."
"The property is_signed=true is not supported for fields encoded as an enum.",
)