Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/configuring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,13 @@ All VHDL regblock-specific options are defined under the ``[regblock-vhdl]`` TOM

If set to true, generate CPUIF error response if an illegal access is
performed to a read-only or write-only register.

.. data:: force_hwif_in

If set to true, force generation of the ``hwif_in`` port and corresponding
type even when no hardware input members are inferred.

.. data:: force_hwif_out

If set to true, force generation of the ``hwif_out`` port and corresponding
type even when no hardware output members are inferred.
5 changes: 5 additions & 0 deletions docs/hwif.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ All field inputs and outputs as well as signals are consolidated into these
record ports. The presence of each depends on the specific contents of the design
being exported.

If desired, empty ports can be forced using ``force_hwif_in`` and
``force_hwif_out`` exporter options. When a side is forced and would otherwise
be empty, a reserved placeholder member ``\0_dummy_entry\`` is generated to
keep the VHDL record type legal.


Using records for the hardware interface has the following benefits:

Expand Down
16 changes: 16 additions & 0 deletions src/peakrdl_regblock_vhdl/__peakrdl__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class Exporter(ExporterSubcommandPlugin):
"err_if_bad_addr": schema.Boolean(),
"err_if_bad_rw": schema.Boolean(),
"copy_utils_pkg": schema.Boolean(),
"force_hwif_in": schema.Boolean(),
"force_hwif_out": schema.Boolean(),
}

@functools.lru_cache()
Expand Down Expand Up @@ -168,6 +170,18 @@ def add_exporter_arguments(self, arg_group: 'argparse._ActionsContainer') -> Non
default=False,
help="Copy the reg_utils.vhd package into the output directory."
)
arg_group.add_argument(
"--force-hwif-in",
action="store_true",
default=False,
help="Force generation of hwif_in port and type, even if inferred empty."
)
arg_group.add_argument(
"--force-hwif-out",
action="store_true",
default=False,
help="Force generation of hwif_out port and type, even if inferred empty."
)

def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> None:
cpuifs = self.get_cpuifs()
Expand Down Expand Up @@ -234,4 +248,6 @@ def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> N
err_if_bad_rw=options.err_if_bad_rw or self.cfg['err_if_bad_rw'],
copy_utils_pkg=options.copy_utils_pkg,
default_reset_async=default_reset_async,
force_hwif_in=options.force_hwif_in or self.cfg['force_hwif_in'],
force_hwif_out=options.force_hwif_out or self.cfg['force_hwif_out'],
)
8 changes: 8 additions & 0 deletions src/peakrdl_regblock_vhdl/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ def export(self, node: Union[RootNode, AddrmapNode], output_dir:str, **kwargs: A
AXI4LITE.*RESP = 2'b10.
copy_utils_pkg: bool
If overridden to True, copy the reg_utils.vhd package into the output directory.
force_hwif_in: bool
If overridden to True, force generation of ``hwif_in`` even when no
inferred hardware input signals exist.
force_hwif_out: bool
If overridden to True, force generation of ``hwif_out`` even when no
inferred hardware output signals exist.
"""
# If it is the root node, skip to top addrmap
if isinstance(node, RootNode):
Expand Down Expand Up @@ -295,6 +301,8 @@ def __init__(self, top_node: AddrmapNode, kwargs: Any) -> None:

# General exporter options
self.copy_utils_pkg = kwargs.pop("copy_utils_pkg", False) # type: bool
self.force_hwif_in = kwargs.pop("force_hwif_in", False) # type: bool
self.force_hwif_out = kwargs.pop("force_hwif_out", False) # type: bool

#------------------------
# Info about the design
Expand Down
37 changes: 34 additions & 3 deletions src/peakrdl_regblock_vhdl/hwif/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def __init__(

self.has_input_struct = False
self.has_output_struct = False
self.input_needs_dummy_member = False
self.output_needs_dummy_member = False

self.hwif_report_file = hwif_report_file

Expand All @@ -40,6 +42,8 @@ def __init__(
self._gen_in_cls = InputStructGenerator_TypeScope
self._gen_out_cls = OutputStructGenerator_TypeScope

DUMMY_MEMBER_NAME = "\\0_dummy_entry\\"

@property
def ds(self) -> 'DesignState':
return self.exp.ds
Expand Down Expand Up @@ -79,9 +83,16 @@ def get_package_contents(self) -> str:
)
if structs_in is not None:
self.has_input_struct = True
self.input_needs_dummy_member = False
lines.append(structs_in)
else:
self.has_input_struct = False
if self.ds.force_hwif_in:
self.has_input_struct = True
self.input_needs_dummy_member = True
lines.append(self._forced_empty_struct(f"{self.top_node.inst_name}_in_t"))
else:
self.has_input_struct = False
self.input_needs_dummy_member = False

gen_out = self._gen_out_cls(self)
structs_out = gen_out.get_struct(
Expand All @@ -90,9 +101,16 @@ def get_package_contents(self) -> str:
)
if structs_out is not None:
self.has_output_struct = True
self.output_needs_dummy_member = False
lines.append(structs_out)
else:
self.has_output_struct = False
if self.ds.force_hwif_out:
self.has_output_struct = True
self.output_needs_dummy_member = True
lines.append(self._forced_empty_struct(f"{self.top_node.inst_name}_out_t"))
else:
self.has_output_struct = False
self.output_needs_dummy_member = False

gen_enum = EnumGenerator()
enums = gen_enum.get_enums(self.ds.user_enums)
Expand All @@ -111,13 +129,26 @@ def port_declaration(self) -> str:
lines = []
if self.has_input_struct:
type_name = f"{self.top_node.inst_name}_in_t"
lines.append(f"hwif_in : in {type_name}")
if self.input_needs_dummy_member:
lines.append(
"hwif_in : in "
f"{type_name} := ({self.DUMMY_MEMBER_NAME} => '0')"
)
else:
lines.append(f"hwif_in : in {type_name}")
if self.has_output_struct:
type_name = f"{self.top_node.inst_name}_out_t"
lines.append(f"hwif_out : out {type_name}")

return ";\n".join(lines)

def _forced_empty_struct(self, type_name: str) -> str:
return (
f"type {type_name} is record\n"
f" {self.DUMMY_MEMBER_NAME} : std_logic;\n"
"end record;"
)

#---------------------------------------------------------------------------
# hwif utility functions
#---------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions src/peakrdl_regblock_vhdl/module_tmpl.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ architecture rtl of {{ds.module_name}} is
{{ readback.signal_declaration | indent }}

begin
{%- if hwif.output_needs_dummy_member %}
hwif_out.\0_dummy_entry\ <= '0';
{%- endif %}

----------------------------------------------------------------------------
-- CPU Bus interface
Expand Down
13 changes: 12 additions & 1 deletion tests/lib/base_testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class BaseTestCase(unittest.TestCase):
default_reset_async = False
err_if_bad_addr = False
err_if_bad_rw = False
force_hwif_in = False
force_hwif_out = False

#: this gets auto-loaded via the _load_request autouse fixture
request = None # type: pytest.FixtureRequest
Expand Down Expand Up @@ -110,7 +112,16 @@ def export_regblock(self):

for lang, exporter, cpuif_cls, kwargs in (
("", self.sv_exporter, self.cpuif.sv_cpuif_cls, {}),
("vhdl_", self.vhdl_exporter, self.cpuif.vhdl_cpuif_cls, {"copy_utils_pkg": True}),
(
"vhdl_",
self.vhdl_exporter,
self.cpuif.vhdl_cpuif_cls,
{
"copy_utils_pkg": True,
"force_hwif_in": self.force_hwif_in,
"force_hwif_out": self.force_hwif_out,
}
),
):
exporter.export(
root,
Expand Down
Empty file.
8 changes: 8 additions & 0 deletions tests/test_force_empty_hwif/regblock.rdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
addrmap regblock {
default sw = rw;
default hw = na;

reg {
field {} f[31:0] = 0;
} my_reg;
};
72 changes: 72 additions & 0 deletions tests/test_force_empty_hwif/testcase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import os

from ..lib.base_testcase import BaseTestCase


class _BaseForceHwifCase(BaseTestCase):
def _read_generated(self):
run_dir = self.get_run_dir()
with open(os.path.join(run_dir, "vhdl_regblock_pkg.vhd"), encoding="utf-8") as f:
pkg = f.read()
with open(os.path.join(run_dir, "vhdl_regblock.vhd"), encoding="utf-8") as f:
mod = f.read()
return pkg, mod


class TestNoForce(_BaseForceHwifCase):
def test_no_hwif_ports(self):
pkg, mod = self._read_generated()

assert "type regblock_in_t is record" not in pkg
assert "type regblock_out_t is record" not in pkg
assert "\\0_dummy_entry\\" not in pkg

assert "hwif_in : in regblock_in_t" not in mod
assert "hwif_out : out regblock_out_t" not in mod
assert "hwif_out.\\0_dummy_entry\\ <= '0';" not in mod


class TestForceInOnly(_BaseForceHwifCase):
force_hwif_in = True

def test_force_hwif_in(self):
pkg, mod = self._read_generated()

assert "type regblock_in_t is record" in pkg
assert "\\0_dummy_entry\\ : std_logic;" in pkg
assert "type regblock_out_t is record" not in pkg

assert "hwif_in : in regblock_in_t := (\\0_dummy_entry\\ => '0')" in mod
assert "hwif_out : out regblock_out_t" not in mod
assert "hwif_out.\\0_dummy_entry\\ <= '0';" not in mod


class TestForceOutOnly(_BaseForceHwifCase):
force_hwif_out = True

def test_force_hwif_out(self):
pkg, mod = self._read_generated()

assert "type regblock_out_t is record" in pkg
assert "\\0_dummy_entry\\ : std_logic;" in pkg
assert "type regblock_in_t is record" not in pkg

assert "hwif_out : out regblock_out_t" in mod
assert "hwif_in : in regblock_in_t" not in mod
assert "hwif_out.\\0_dummy_entry\\ <= '0';" in mod


class TestForceBoth(_BaseForceHwifCase):
force_hwif_in = True
force_hwif_out = True

def test_force_both(self):
pkg, mod = self._read_generated()

assert "type regblock_in_t is record" in pkg
assert "type regblock_out_t is record" in pkg
assert pkg.count("\\0_dummy_entry\\ : std_logic;") == 2

assert "hwif_in : in regblock_in_t := (\\0_dummy_entry\\ => '0')" in mod
assert "hwif_out : out regblock_out_t" in mod
assert "hwif_out.\\0_dummy_entry\\ <= '0';" in mod