Add ability to control default reset style. #34

This commit is contained in:
Alex Mykyta
2023-05-13 17:15:31 -07:00
parent 5e76956618
commit b350da3e7c
10 changed files with 151 additions and 39 deletions

View File

@@ -20,3 +20,17 @@ All regblock-specific options are defined under the ``[regblock]`` TOML heading.
[regblock] [regblock]
cpuifs.my-cpuif-name = "my_cpuif_module:MyCPUInterfaceClass" cpuifs.my-cpuif-name = "my_cpuif_module:MyCPUInterfaceClass"
.. data:: default_reset
Choose the default style of reset signal if not explicitly
specified by the SystemRDL design. If unspecified, the default reset
is active-high and synchronous.
Choice of:
* ``rst`` (default)
* ``rst_n``
* ``arst``
* ``arst_n``

View File

@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Dict, Type from typing import TYPE_CHECKING, Dict, Type, List, Any
import functools import functools
import sys import sys
@@ -15,13 +15,32 @@ if TYPE_CHECKING:
from systemrdl.node import AddrmapNode from systemrdl.node import AddrmapNode
class Choice(schema.String):
"""
Schema that matches against a specific set of allowed strings
Base PeakRDL does not have this schema yet. Polyfill here for now until it
is added and widely available.
"""
def __init__(self, choices: List[str]) -> None:
super().__init__()
self.choices = choices
def extract(self, data: Any, path: str, err_ctx: str) -> Any:
s = super().extract(data, path, err_ctx)
if s not in self.choices:
raise schema.SchemaException(f"{err_ctx}: Value '{s}' is not a valid choice. Must be one of: {','.join(self.choices)}")
return s
class Exporter(ExporterSubcommandPlugin): class Exporter(ExporterSubcommandPlugin):
short_desc = "Generate a SystemVerilog control/status register (CSR) block" short_desc = "Generate a SystemVerilog control/status register (CSR) block"
udp_definitions = ALL_UDPS udp_definitions = ALL_UDPS
cfg_schema = { cfg_schema = {
"cpuifs": {"*": schema.PythonObjectImport()} "cpuifs": {"*": schema.PythonObjectImport()},
"default_reset": Choice(["rst", "rst_n", "arst", "arst_n"]),
} }
@functools.lru_cache() @functools.lru_cache()
@@ -83,6 +102,34 @@ class Exporter(ExporterSubcommandPlugin):
help="Override the SystemVerilog package name" help="Override the SystemVerilog package name"
) )
arg_group.add_argument(
"--type-style",
dest="type_style",
choices=['lexical', 'hier'],
default="lexical",
help="""Choose how HWIF struct type names are generated.
The 'lexical' style will use RDL lexical scope & type names where
possible and attempt to re-use equivalent type definitions.
The 'hier' style uses component's hierarchy as the struct type name. [lexical]
"""
)
arg_group.add_argument(
"--hwif-report",
action="store_true",
default=False,
help="Generate a HWIF report file"
)
arg_group.add_argument(
"--addr-width",
type=int,
default=None,
help="""Override the CPU interface's address width. By default,
address width is sized to the contents of the regblock.
"""
)
arg_group.add_argument( arg_group.add_argument(
"--rt-read-fanin", "--rt-read-fanin",
action="store_true", action="store_true",
@@ -99,31 +146,14 @@ class Exporter(ExporterSubcommandPlugin):
"--rt-external", "--rt-external",
help="Retime outputs to external components. Specify a comma-separated list of: reg,regfile,mem,addrmap,all" help="Retime outputs to external components. Specify a comma-separated list of: reg,regfile,mem,addrmap,all"
) )
arg_group.add_argument(
"--type-style",
dest="type_style",
choices=['lexical', 'hier'],
default="lexical",
help="""Choose how HWIF struct type names are generated.
The 'lexical' style will use RDL lexical scope & type names where
possible and attempt to re-use equivalent type definitions.
The 'hier' style uses component's hierarchy as the struct type name. [lexical]
"""
)
arg_group.add_argument(
"--hwif-report",
action="store_true",
default=False,
help="Generate a HWIF report file"
)
arg_group.add_argument( arg_group.add_argument(
"--addr-width", "--default-reset",
type=int, choices=["rst", "rst_n", "arst", "arst_n"],
default=None, default=None,
help="""Override the CPU interface's address width. By default, help="""Choose the default style of reset signal if not explicitly
address width is sized to the contents of the regblock. specified by the SystemRDL design. If unspecified, the default reset
""" is active-high and synchronous [rst]"""
) )
@@ -153,6 +183,24 @@ class Exporter(ExporterSubcommandPlugin):
else: else:
print("error: invalid option for --rt-external: '%s'" % key, file=sys.stderr) print("error: invalid option for --rt-external: '%s'" % key, file=sys.stderr)
# Get default reset. Favor command-line over cfg. Fall back to 'rst'
default_rst = options.default_reset or self.cfg['default_reset'] or "rst"
if default_rst == "rst":
default_reset_activelow = False
default_reset_async = False
elif default_rst == "rst_n":
default_reset_activelow = True
default_reset_async = False
elif default_rst == "arst":
default_reset_activelow = False
default_reset_async = True
elif default_rst == "arst_n":
default_reset_activelow = True
default_reset_async = True
else:
raise RuntimeError
x = RegblockExporter() x = RegblockExporter()
x.export( x.export(
top_node, top_node,
@@ -169,4 +217,6 @@ class Exporter(ExporterSubcommandPlugin):
retime_external_addrmap=retime_external_addrmap, retime_external_addrmap=retime_external_addrmap,
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_async=default_reset_async,
) )

View File

@@ -3,7 +3,7 @@ from systemrdl.node import AddrmapNode, FieldNode, SignalNode, RegNode, Addressa
from systemrdl.rdltypes import PropertyReference from systemrdl.rdltypes import PropertyReference
if TYPE_CHECKING: if TYPE_CHECKING:
from .exporter import RegblockExporter from .exporter import RegblockExporter, DesignState
from .hwif import Hwif from .hwif import Hwif
from .field_logic import FieldLogic from .field_logic import FieldLogic
from .addr_decode import AddressDecode from .addr_decode import AddressDecode
@@ -28,6 +28,10 @@ class Dereferencer:
def field_logic(self) -> 'FieldLogic': def field_logic(self) -> 'FieldLogic':
return self.exp.field_logic return self.exp.field_logic
@property
def ds(self) -> 'DesignState':
return self.exp.ds
@property @property
def top_node(self) -> AddrmapNode: def top_node(self) -> AddrmapNode:
return self.exp.ds.top_node return self.exp.ds.top_node
@@ -211,6 +215,16 @@ class Dereferencer:
""" """
return self.address_decode.get_external_block_access_strobe(obj) return self.address_decode.get_external_block_access_strobe(obj)
@property
def default_resetsignal_name(self) -> str:
s = "rst"
if self.ds.default_reset_async:
s = f"a{s}"
if self.ds.default_reset_activelow:
s = f"{s}_n"
return s
def get_resetsignal(self, obj: Optional[SignalNode] = None) -> str: def get_resetsignal(self, obj: Optional[SignalNode] = None) -> str:
""" """
Returns a normalized active-high reset signal Returns a normalized active-high reset signal
@@ -222,13 +236,23 @@ class Dereferencer:
else: else:
return f"~{s}" return f"~{s}"
# default reset signal # No explicit reset signal specified. Fall back to default reset signal
return "rst" s = self.default_resetsignal_name
if self.ds.default_reset_activelow:
s = f"~{s}"
return s
def get_always_ff_event(self, resetsignal: Optional[SignalNode] = None) -> str: def get_always_ff_event(self, resetsignal: Optional[SignalNode] = None) -> str:
if resetsignal is None: if resetsignal is None:
# No explicit reset signal specified. Fall back to default reset signal
if self.ds.default_reset_async:
if self.ds.default_reset_activelow:
return f"@(posedge clk or negedge {self.default_resetsignal_name})"
else:
return f"@(posedge clk or posedge {self.default_resetsignal_name})"
else:
return "@(posedge clk)" return "@(posedge clk)"
if resetsignal.get_property('async') and resetsignal.get_property('activehigh'): elif resetsignal.get_property('async') and resetsignal.get_property('activehigh'):
return f"@(posedge clk or posedge {self.get_value(resetsignal)})" return f"@(posedge clk or posedge {self.get_value(resetsignal)})"
elif resetsignal.get_property('async') and not resetsignal.get_property('activehigh'): elif resetsignal.get_property('async') and not resetsignal.get_property('activehigh'):
return f"@(posedge clk or negedge {self.get_value(resetsignal)})" return f"@(posedge clk or negedge {self.get_value(resetsignal)})"

View File

@@ -113,6 +113,10 @@ class RegblockExporter:
address_width: int address_width: int
Override the CPU interface's address width. By default, address width Override the CPU interface's address width. By default, address width
is sized to the contents of the regblock. is sized to the contents of the regblock.
default_reset_activelow: bool
If overriden to True, default reset is active-low instead of active-high.
default_reset_async: bool
If overriden to True, default reset is asynchronous instead of synchronous.
""" """
# 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):
@@ -140,10 +144,7 @@ class RegblockExporter:
# Construct exporter components # Construct exporter components
self.cpuif = cpuif_cls(self) self.cpuif = cpuif_cls(self)
self.hwif = Hwif( self.hwif = Hwif(self, hwif_report_file=hwif_report_file)
self,
hwif_report_file=hwif_report_file,
)
self.readback = Readback(self) self.readback = Readback(self)
self.address_decode = AddressDecode(self) self.address_decode = AddressDecode(self)
self.field_logic = FieldLogic(self) self.field_logic = FieldLogic(self)
@@ -163,6 +164,7 @@ class RegblockExporter:
"write_buffering": self.write_buffering, "write_buffering": self.write_buffering,
"read_buffering": self.read_buffering, "read_buffering": self.read_buffering,
"get_resetsignal": self.dereferencer.get_resetsignal, "get_resetsignal": self.dereferencer.get_resetsignal,
"default_resetsignal_name": self.dereferencer.default_resetsignal_name,
"address_decode": self.address_decode, "address_decode": self.address_decode,
"field_logic": self.field_logic, "field_logic": self.field_logic,
"readback": self.readback, "readback": self.readback,
@@ -207,7 +209,6 @@ class DesignState:
self.package_name = kwargs.pop("package_name", None) or (self.module_name + "_pkg") # type: str self.package_name = kwargs.pop("package_name", None) or (self.module_name + "_pkg") # type: str
user_addr_width = kwargs.pop("address_width", None) # type: Optional[int] user_addr_width = kwargs.pop("address_width", None) # type: Optional[int]
# Pipelining options # Pipelining options
self.retime_read_fanin = kwargs.pop("retime_read_fanin", False) # type: bool self.retime_read_fanin = kwargs.pop("retime_read_fanin", False) # type: bool
self.retime_read_response = kwargs.pop("retime_read_response", False) # type: bool self.retime_read_response = kwargs.pop("retime_read_response", False) # type: bool
@@ -216,6 +217,10 @@ class DesignState:
self.retime_external_mem = kwargs.pop("retime_external_mem", False) # type: bool self.retime_external_mem = kwargs.pop("retime_external_mem", False) # type: bool
self.retime_external_addrmap = kwargs.pop("retime_external_addrmap", False) # type: bool self.retime_external_addrmap = kwargs.pop("retime_external_addrmap", False) # type: bool
# Default reset type
self.default_reset_activelow = kwargs.pop("default_reset_activelow", False) # type: bool
self.default_reset_async = kwargs.pop("default_reset_async", False) # type: bool
#------------------------ #------------------------
# Info about the design # Info about the design
#------------------------ #------------------------

View File

@@ -3,7 +3,7 @@
module {{ds.module_name}} ( module {{ds.module_name}} (
input wire clk, input wire clk,
input wire rst, input wire {{default_resetsignal_name}},
{%- for signal in ds.out_of_hier_signals.values() %} {%- for signal in ds.out_of_hier_signals.values() %}
{%- if signal.width == 1 %} {%- if signal.width == 1 %}

View File

@@ -34,6 +34,7 @@ class Readback:
"array_assignments" : array_assignments, "array_assignments" : array_assignments,
"array_size" : array_size, "array_size" : array_size,
'get_always_ff_event': self.exp.dereferencer.get_always_ff_event, 'get_always_ff_event': self.exp.dereferencer.get_always_ff_event,
'get_resetsignal': self.exp.dereferencer.get_resetsignal,
"cpuif": self.exp.cpuif, "cpuif": self.exp.cpuif,
"do_fanin_stage": self.do_fanin_stage, "do_fanin_stage": self.do_fanin_stage,
"ds": self.ds, "ds": self.ds,

View File

@@ -29,8 +29,8 @@ 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;
always_ff @(posedge clk) begin always_ff {{get_always_ff_event(cpuif.reset)}} begin
if(rst) 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;
end else begin end else begin

View File

@@ -37,6 +37,8 @@ class BaseTestCase(unittest.TestCase):
retime_read_response = False retime_read_response = False
reuse_hwif_typedefs = True reuse_hwif_typedefs = True
retime_external = False retime_external = False
default_reset_activelow = False
default_reset_async = 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
@@ -111,6 +113,8 @@ class BaseTestCase(unittest.TestCase):
retime_external_regfile=cls.retime_external, retime_external_regfile=cls.retime_external,
retime_external_mem=cls.retime_external, retime_external_mem=cls.retime_external,
retime_external_addrmap=cls.retime_external, retime_external_addrmap=cls.retime_external,
default_reset_activelow=cls.default_reset_activelow,
default_reset_async=cls.default_reset_async,
) )
@classmethod @classmethod

View File

@@ -10,6 +10,11 @@ module tb;
clk = ~clk; clk = ~clk;
end end
logic rst_n, arst, arst_n;
assign rst_n = ~rst;
assign arst = rst;
assign arst_n = ~rst;
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// DUT Signal declarations // DUT Signal declarations
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------

View File

@@ -4,15 +4,24 @@ from parameterized import parameterized_class
from ..lib.sim_testcase import SimTestCase from ..lib.sim_testcase import SimTestCase
from ..lib.synth_testcase import SynthTestCase from ..lib.synth_testcase import SynthTestCase
from ..lib.test_params import TEST_PARAMS from ..lib.test_params import TEST_PARAMS, get_permutations
from ..lib.simulators.xilinx import Xilinx from ..lib.simulators.xilinx import Xilinx
import pytest import pytest
@parameterized_class(TEST_PARAMS) @parameterized_class(TEST_PARAMS)
class Test(SimTestCase): class TestParameterizations(SimTestCase):
def test_dut(self): def test_dut(self):
self.run_test() self.run_test()
@parameterized_class(get_permutations({
"default_reset_activelow": [True, False],
"default_reset_async": [True, False],
}))
class TestDefaultResets(SimTestCase):
def test_dut(self):
self.run_test()
@parameterized_class(TEST_PARAMS) @parameterized_class(TEST_PARAMS)
class TestSynth(SynthTestCase): class TestSynth(SynthTestCase):
def test_dut(self): def test_dut(self):