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

@@ -1,3 +1,3 @@
from .__about__ import __version__
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter

View File

@@ -6,7 +6,7 @@ from peakrdl.plugins.exporter import ExporterSubcommandPlugin
from peakrdl.config import schema
from peakrdl.plugins.entry_points import get_entry_points
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter
from .cpuif import CpuifBase, apb3, apb4, axi4lite, passthrough, avalon
from .udps import ALL_UDPS
@@ -14,6 +14,7 @@ if TYPE_CHECKING:
import argparse
from systemrdl.node import AddrmapNode
class Exporter(ExporterSubcommandPlugin):
short_desc = "Generate a SystemVerilog control/status register (CSR) block"
@@ -26,7 +27,6 @@ class Exporter(ExporterSubcommandPlugin):
@functools.lru_cache()
def get_cpuifs(self) -> Dict[str, Type[CpuifBase]]:
# All built-in CPUIFs
cpuifs = {
"passthrough": passthrough.PassthroughCpuif,
@@ -41,67 +41,74 @@ class Exporter(ExporterSubcommandPlugin):
}
# Load any cpuifs specified via entry points
for ep, dist in get_entry_points("peakrdl_regblock.cpuif"):
for ep, dist in get_entry_points("peakrdl_busdecoder.cpuif"):
name = ep.name
cpuif = ep.load()
if name in cpuifs:
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it already exists")
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it already exists"
)
if not issubclass(cpuif, CpuifBase):
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it not a CpuifBase class")
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it not a CpuifBase class"
)
cpuifs[name] = cpuif
# Load any CPUIFs via config import
for name, cpuif in self.cfg['cpuifs'].items():
for name, cpuif in self.cfg["cpuifs"].items():
if name in cpuifs:
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it already exists")
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it already exists"
)
if not issubclass(cpuif, CpuifBase):
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it not a CpuifBase class")
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it not a CpuifBase class"
)
cpuifs[name] = cpuif
return cpuifs
def add_exporter_arguments(self, arg_group: 'argparse._ActionsContainer') -> None:
def add_exporter_arguments(self, arg_group: "argparse._ActionsContainer") -> None:
cpuifs = self.get_cpuifs()
arg_group.add_argument(
"--cpuif",
choices=cpuifs.keys(),
default="apb3",
help="Select the CPU interface protocol to use [apb3]"
help="Select the CPU interface protocol to use [apb3]",
)
arg_group.add_argument(
"--module-name",
metavar="NAME",
default=None,
help="Override the SystemVerilog module name"
help="Override the SystemVerilog module name",
)
arg_group.add_argument(
"--package-name",
metavar="NAME",
default=None,
help="Override the SystemVerilog package name"
help="Override the SystemVerilog package name",
)
arg_group.add_argument(
"--type-style",
dest="type_style",
choices=['lexical', 'hier'],
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"
help="Generate a HWIF report file",
)
arg_group.add_argument(
@@ -109,25 +116,25 @@ class Exporter(ExporterSubcommandPlugin):
type=int,
default=None,
help="""Override the CPU interface's address width. By default,
address width is sized to the contents of the regblock.
"""
address width is sized to the contents of the busdecoder.
""",
)
arg_group.add_argument(
"--rt-read-fanin",
action="store_true",
default=False,
help="Enable additional read path retiming. Good for register blocks with large readback fan-in"
help="Enable additional read path retiming. Good for register blocks with large readback fan-in",
)
arg_group.add_argument(
"--rt-read-response",
action="store_true",
default=False,
help="Enable additional retiming stage between readback fan-in and cpu interface"
help="Enable additional retiming stage between readback fan-in and cpu interface",
)
arg_group.add_argument(
"--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(
@@ -136,11 +143,10 @@ class Exporter(ExporterSubcommandPlugin):
default=None,
help="""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 [rst]"""
is active-high and synchronous [rst]""",
)
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()
retime_external_reg = False
@@ -164,10 +170,13 @@ class Exporter(ExporterSubcommandPlugin):
retime_external_mem = True
retime_external_addrmap = True
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"
default_rst = options.default_reset or self.cfg["default_reset"] or "rst"
if default_rst == "rst":
default_reset_activelow = False
default_reset_async = False
@@ -183,8 +192,7 @@ class Exporter(ExporterSubcommandPlugin):
else:
raise RuntimeError
x = RegblockExporter()
x = BusDecoderExporter()
x.export(
top_node,
options.output,

View File

@@ -10,22 +10,23 @@ from .identifier_filter import kw_filter as kwf
from .sv_int import SVInt
if TYPE_CHECKING:
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter
from systemrdl.node import AddrmapNode, AddressableNode
from systemrdl.node import RegfileNode, MemNode
class AddressDecode:
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def get_strobe_struct(self) -> str:
struct_gen = DecodeStructGenerator()
s = struct_gen.get_struct(self.top_node, "decoded_reg_strb_t")
assert s is not None # guaranteed to have at least one reg
assert s is not None # guaranteed to have at least one reg
return s
def get_implementation(self) -> str:
@@ -34,7 +35,9 @@ class AddressDecode:
assert s is not None
return s
def get_access_strobe(self, node: Union[RegNode, FieldNode], reduce_substrobes: bool=True) -> str:
def get_access_strobe(
self, node: Union[RegNode, FieldNode], reduce_substrobes: bool = True
) -> str:
"""
Returns the Verilog string that represents the register/field's access strobe.
"""
@@ -42,8 +45,8 @@ class AddressDecode:
field = node
path = get_indexed_path(self.top_node, node.parent)
regwidth = node.parent.get_property('regwidth')
accesswidth = node.parent.get_property('accesswidth')
regwidth = node.parent.get_property("regwidth")
accesswidth = node.parent.get_property("accesswidth")
if regwidth > accesswidth:
# Is wide register.
# Determine the substrobe(s) relevant to this field
@@ -63,7 +66,7 @@ class AddressDecode:
return "decoded_reg_strb." + path
def get_external_block_access_strobe(self, node: 'AddressableNode') -> str:
def get_external_block_access_strobe(self, node: "AddressableNode") -> str:
assert node.external
assert not isinstance(node, RegNode)
path = get_indexed_path(self.top_node, node)
@@ -71,42 +74,41 @@ class AddressDecode:
class DecodeStructGenerator(RDLStructGenerator):
def _enter_external_block(self, node: 'AddressableNode') -> None:
def _enter_external_block(self, node: "AddressableNode") -> None:
self.add_member(
kwf(node.inst_name),
array_dimensions=node.array_dimensions,
)
def enter_Addrmap(self, node: 'AddrmapNode') -> Optional[WalkerAction]:
def enter_Addrmap(self, node: "AddrmapNode") -> Optional[WalkerAction]:
assert node.external
self._enter_external_block(node)
return WalkerAction.SkipDescendants
def exit_Addrmap(self, node: 'AddrmapNode') -> None:
def exit_Addrmap(self, node: "AddrmapNode") -> None:
assert node.external
def enter_Regfile(self, node: 'RegfileNode') -> Optional[WalkerAction]:
def enter_Regfile(self, node: "RegfileNode") -> Optional[WalkerAction]:
if node.external:
self._enter_external_block(node)
return WalkerAction.SkipDescendants
super().enter_Regfile(node)
return WalkerAction.Continue
def exit_Regfile(self, node: 'RegfileNode') -> None:
def exit_Regfile(self, node: "RegfileNode") -> None:
if node.external:
return
super().exit_Regfile(node)
def enter_Mem(self, node: 'MemNode') -> Optional[WalkerAction]:
def enter_Mem(self, node: "MemNode") -> Optional[WalkerAction]:
assert node.external
self._enter_external_block(node)
return WalkerAction.SkipDescendants
def exit_Mem(self, node: 'MemNode') -> None:
def exit_Mem(self, node: "MemNode") -> None:
assert node.external
def enter_Reg(self, node: 'RegNode') -> None:
def enter_Reg(self, node: "RegNode") -> None:
# if register is "wide", expand the strobe to be able to access the sub-words
n_subwords = node.get_property("regwidth") // node.get_property("accesswidth")
@@ -117,23 +119,24 @@ class DecodeStructGenerator(RDLStructGenerator):
)
# Stub out
def exit_Reg(self, node: 'RegNode') -> None:
def exit_Reg(self, node: "RegNode") -> None:
pass
def enter_Field(self, node: 'FieldNode') -> None:
def enter_Field(self, node: "FieldNode") -> None:
pass
class DecodeLogicGenerator(RDLForLoopGenerator):
def __init__(self, addr_decode: AddressDecode) -> None:
self.addr_decode = addr_decode
super().__init__()
# List of address strides for each dimension
self._array_stride_stack = [] # type: List[int]
self._array_stride_stack = [] # type: List[int]
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
def enter_AddressableComponent(
self, node: "AddressableNode"
) -> Optional[WalkerAction]:
super().enter_AddressableComponent(node)
if node.array_dimensions:
@@ -158,21 +161,23 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
return WalkerAction.Continue
def _get_address_str(self, node: 'AddressableNode', subword_offset: int=0) -> str:
def _get_address_str(self, node: "AddressableNode", subword_offset: int = 0) -> str:
expr_width = self.addr_decode.exp.ds.addr_width
a = str(SVInt(
node.raw_absolute_address - self.addr_decode.top_node.raw_absolute_address + subword_offset,
expr_width
))
a = str(
SVInt(
node.raw_absolute_address
- self.addr_decode.top_node.raw_absolute_address
+ subword_offset,
expr_width,
)
)
for i, stride in enumerate(self._array_stride_stack):
a += f" + ({expr_width})'(i{i}) * {SVInt(stride, expr_width)}"
return a
def enter_Reg(self, node: RegNode) -> None:
regwidth = node.get_property('regwidth')
accesswidth = node.get_property('accesswidth')
regwidth = node.get_property("regwidth")
accesswidth = node.get_property("accesswidth")
if regwidth == accesswidth:
rhs = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node)})"
@@ -194,7 +199,7 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
n_subwords = regwidth // accesswidth
subword_stride = accesswidth // 8
for i in range(n_subwords):
rhs = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node, subword_offset=(i*subword_stride))})"
rhs = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node, subword_offset=(i * subword_stride))})"
s = f"{self.addr_decode.get_access_strobe(node)}[{i}] = {rhs};"
self.add_content(s)
if node.external:
@@ -209,7 +214,7 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
else:
raise RuntimeError
def exit_AddressableComponent(self, node: 'AddressableNode') -> None:
def exit_AddressableComponent(self, node: "AddressableNode") -> None:
super().exit_AddressableComponent(node)
if not node.array_dimensions:

View File

@@ -1,5 +1,6 @@
from ..base import CpuifBase
class AXI4Lite_Cpuif(CpuifBase):
template_path = "axi4lite_tmpl.sv"
is_interface = True
@@ -8,11 +9,11 @@ class AXI4Lite_Cpuif(CpuifBase):
def port_declaration(self) -> str:
return "axi4lite_intf.slave s_axil"
def signal(self, name:str) -> str:
def signal(self, name: str) -> str:
return "s_axil." + name.upper()
@property
def regblock_latency(self) -> int:
def busdecoder_latency(self) -> int:
return max(self.exp.ds.min_read_latency, self.exp.ds.min_write_latency)
@property
@@ -23,7 +24,7 @@ class AXI4Lite_Cpuif(CpuifBase):
Anything beyond that does not have any effect, aside from adding unnecessary
logic and additional buffer-bloat latency.
"""
return self.regblock_latency + 2
return self.busdecoder_latency + 2
@property
def resp_buffer_size(self) -> int:
@@ -42,29 +43,25 @@ class AXI4Lite_Cpuif_flattened(AXI4Lite_Cpuif):
lines = [
"output logic " + self.signal("awready"),
"input wire " + self.signal("awvalid"),
f"input wire [{self.addr_width-1}:0] " + self.signal("awaddr"),
f"input wire [{self.addr_width - 1}:0] " + self.signal("awaddr"),
"input wire [2:0] " + self.signal("awprot"),
"output logic " + self.signal("wready"),
"input wire " + self.signal("wvalid"),
f"input wire [{self.data_width-1}:0] " + self.signal("wdata"),
f"input wire [{self.data_width_bytes-1}:0]" + self.signal("wstrb"),
f"input wire [{self.data_width - 1}:0] " + self.signal("wdata"),
f"input wire [{self.data_width_bytes - 1}:0]" + self.signal("wstrb"),
"input wire " + self.signal("bready"),
"output logic " + self.signal("bvalid"),
"output logic [1:0] " + self.signal("bresp"),
"output logic " + self.signal("arready"),
"input wire " + self.signal("arvalid"),
f"input wire [{self.addr_width-1}:0] " + self.signal("araddr"),
f"input wire [{self.addr_width - 1}:0] " + self.signal("araddr"),
"input wire [2:0] " + self.signal("arprot"),
"input wire " + self.signal("rready"),
"output logic " + self.signal("rvalid"),
f"output logic [{self.data_width-1}:0] " + self.signal("rdata"),
f"output logic [{self.data_width - 1}:0] " + self.signal("rdata"),
"output logic [1:0] " + self.signal("rresp"),
]
return ",\n".join(lines)
def signal(self, name:str) -> str:
def signal(self, name: str) -> str:
return "s_axil_" + name

View File

@@ -7,14 +7,14 @@ import jinja2 as jj
from ..utils import clog2, is_pow2, roundup_pow2
if TYPE_CHECKING:
from ..exporter import RegblockExporter
from ..exporter import BusDecoderExporter
class CpuifBase:
# Path is relative to the location of the class that assigns this variable
template_path = ""
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
self.reset = exp.ds.top_node.cpuif_reset
@@ -53,7 +53,6 @@ class CpuifBase:
return class_dir
raise RuntimeError
def get_implementation(self) -> str:
class_dir = self._get_template_path_class_dir()
loader = jj.FileSystemLoader(class_dir)

View File

@@ -5,40 +5,46 @@ from systemrdl.rdltypes import PropertyReference
from .sv_int import SVInt
if TYPE_CHECKING:
from .exporter import RegblockExporter, DesignState
from .exporter import BusDecoderExporter, DesignState
from .hwif import Hwif
from .field_logic import FieldLogic
from .addr_decode import AddressDecode
class Dereferencer:
"""
This class provides an interface to convert conceptual SystemRDL references
into Verilog identifiers
"""
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
@property
def hwif(self) -> 'Hwif':
def hwif(self) -> "Hwif":
return self.exp.hwif
@property
def address_decode(self) -> 'AddressDecode':
def address_decode(self) -> "AddressDecode":
return self.exp.address_decode
@property
def field_logic(self) -> 'FieldLogic':
def field_logic(self) -> "FieldLogic":
return self.exp.field_logic
@property
def ds(self) -> 'DesignState':
def ds(self) -> "DesignState":
return self.exp.ds
@property
def top_node(self) -> AddrmapNode:
return self.exp.ds.top_node
def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference], width: Optional[int] = None) -> Union[SVInt, str]:
def get_value(
self,
obj: Union[int, FieldNode, SignalNode, PropertyReference],
width: Optional[int] = None,
) -> Union[SVInt, str]:
"""
Returns the Verilog string that represents the readable value associated
with the object.
@@ -63,14 +69,14 @@ class Dereferencer:
# Field does not have a storage element, nor does it have a HW input
# must be a constant value as defined by its reset value
reset_value = obj.get_property('reset')
reset_value = obj.get_property("reset")
if reset_value is not None:
return self.get_value(reset_value, obj.width)
else:
# No reset value defined!
obj.env.msg.warning(
f"Field '{obj.inst_name}' is a constant but does not have a known value (missing reset). Assigning it a value of X.",
obj.inst.inst_src_ref
obj.inst.inst_src_ref,
)
return "'X"
@@ -88,7 +94,6 @@ class Dereferencer:
raise RuntimeError(f"Unhandled reference to: {obj}")
def get_field_propref_value(
self,
field: FieldNode,
@@ -109,16 +114,16 @@ class Dereferencer:
# references that directly access a property value
if prop_name in {
'decrvalue',
'enable',
'haltenable',
'haltmask',
'hwenable',
'hwmask',
'incrvalue',
'mask',
'reset',
'resetsignal',
"decrvalue",
"enable",
"haltenable",
"haltmask",
"hwenable",
"hwmask",
"incrvalue",
"mask",
"reset",
"resetsignal",
}:
return self.get_value(field.get_property(prop_name), width)
@@ -132,7 +137,7 @@ class Dereferencer:
return self.get_value(prop_value, width)
# References to another component value, or an implied input
if prop_name in {'hwclr', 'hwset'}:
if prop_name in {"hwclr", "hwset"}:
prop_value = field.get_property(prop_name)
if prop_value is True:
# Points to inferred hwif input
@@ -175,7 +180,6 @@ class Dereferencer:
if prop_name == "swmod":
return self.field_logic.get_swmod_identifier(field)
# translate aliases
aliases = {
"saturate": "incrsaturate",
@@ -184,37 +188,37 @@ class Dereferencer:
prop_name = aliases.get(prop_name, prop_name)
# Counter properties
if prop_name == 'incr':
if prop_name == "incr":
return self.field_logic.get_counter_incr_strobe(field)
if prop_name == 'decr':
if prop_name == "decr":
return self.field_logic.get_counter_decr_strobe(field)
if prop_name in {
'decrsaturate',
'decrthreshold',
'incrsaturate',
'incrthreshold',
'overflow',
'underflow',
"decrsaturate",
"decrthreshold",
"incrsaturate",
"incrthreshold",
"overflow",
"underflow",
}:
return self.field_logic.get_field_combo_identifier(field, prop_name)
raise RuntimeError(f"Unhandled reference to: {field}->{prop_name}")
def get_reg_propref_value(self, reg: RegNode, prop_name: str) -> str:
if prop_name in {'halt', 'intr'}:
if prop_name in {"halt", "intr"}:
return self.hwif.get_implied_prop_output_identifier(reg, prop_name)
raise NotImplementedError
def get_access_strobe(self, obj: Union[RegNode, FieldNode], reduce_substrobes: bool=True) -> str:
def get_access_strobe(
self, obj: Union[RegNode, FieldNode], reduce_substrobes: bool = True
) -> str:
"""
Returns the Verilog string that represents the register's access strobe
"""
return self.address_decode.get_access_strobe(obj, reduce_substrobes)
def get_external_block_access_strobe(self, obj: 'AddressableNode') -> str:
def get_external_block_access_strobe(self, obj: "AddressableNode") -> str:
"""
Returns the Verilog string that represents the external block's access strobe
"""
@@ -229,14 +233,13 @@ class Dereferencer:
s = f"{s}_n"
return s
def get_resetsignal(self, obj: Optional[SignalNode] = None) -> str:
"""
Returns a normalized active-high reset signal
"""
if isinstance(obj, SignalNode):
s = self.get_value(obj)
if obj.get_property('activehigh'):
if obj.get_property("activehigh"):
return str(s)
else:
return f"~{s}"
@@ -257,8 +260,12 @@ class Dereferencer:
return f"@(posedge clk or posedge {self.default_resetsignal_name})"
else:
return "@(posedge clk)"
elif 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)})"
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 "@(posedge clk)"

View File

@@ -26,7 +26,8 @@ if TYPE_CHECKING:
from systemrdl.node import SignalNode
from systemrdl.rdltypes import UserEnum
class RegblockExporter:
class BusDecoderExporter:
hwif: Hwif
cpuif: CpuifBase
address_decode: AddressDecode
@@ -35,28 +36,35 @@ class RegblockExporter:
write_buffering: WriteBuffering
read_buffering: ReadBuffering
dereferencer: Dereferencer
ds: 'DesignState'
ds: "DesignState"
def __init__(self, **kwargs: Any) -> None:
# Check for stray kwargs
if kwargs:
raise TypeError(f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'")
raise TypeError(
f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'"
)
loader = jj.ChoiceLoader([
jj.FileSystemLoader(os.path.dirname(__file__)),
jj.PrefixLoader({
'base': jj.FileSystemLoader(os.path.dirname(__file__)),
}, delimiter=":")
])
loader = jj.ChoiceLoader(
[
jj.FileSystemLoader(os.path.dirname(__file__)),
jj.PrefixLoader(
{
"base": jj.FileSystemLoader(os.path.dirname(__file__)),
},
delimiter=":",
),
]
)
self.jj_env = jj.Environment(
loader=loader,
undefined=jj.StrictUndefined,
)
def export(self, node: Union[RootNode, AddrmapNode], output_dir:str, **kwargs: Any) -> None:
def export(
self, node: Union[RootNode, AddrmapNode], output_dir: str, **kwargs: Any
) -> None:
"""
Parameters
----------
@@ -65,7 +73,7 @@ class RegblockExporter:
output_dir: str
Path to the output directory where generated SystemVerilog will be written.
Output includes two files: a module definition and package definition.
cpuif_cls: :class:`peakrdl_regblock.cpuif.CpuifBase`
cpuif_cls: :class:`peakrdl_busdecoder.cpuif.CpuifBase`
Specify the class type that implements the CPU interface of your choice.
Defaults to AMBA APB4.
module_name: str
@@ -115,7 +123,7 @@ class RegblockExporter:
the contents of the ``hwif_in`` and ``hwif_out`` structures.
address_width: int
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 busdecoder.
default_reset_activelow: bool
If overriden to True, default reset is active-low instead of active-high.
default_reset_async: bool
@@ -129,16 +137,18 @@ class RegblockExporter:
self.ds = DesignState(top_node, kwargs)
cpuif_cls = kwargs.pop("cpuif_cls", None) or APB4_Cpuif # type: Type[CpuifBase]
generate_hwif_report = kwargs.pop("generate_hwif_report", False) # type: bool
cpuif_cls = kwargs.pop("cpuif_cls", None) or APB4_Cpuif # type: Type[CpuifBase]
generate_hwif_report = kwargs.pop("generate_hwif_report", False) # type: bool
# Check for stray kwargs
if kwargs:
raise TypeError(f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'")
raise TypeError(
f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'"
)
if generate_hwif_report:
path = os.path.join(output_dir, f"{self.ds.module_name}_hwif.rpt")
hwif_report_file = open(path, "w", encoding='utf-8') # pylint: disable=consider-using-with
hwif_report_file = open(path, "w", encoding="utf-8") # pylint: disable=consider-using-with
else:
hwif_report_file = None
@@ -181,7 +191,7 @@ class RegblockExporter:
"get_always_ff_event": self.dereferencer.get_always_ff_event,
"ds": self.ds,
"kwf": kwf,
"SVInt" : SVInt,
"SVInt": SVInt,
}
# Write out design
@@ -210,34 +220,38 @@ class DesignState:
self.top_node = top_node
msg = top_node.env.msg
#------------------------
# ------------------------
# Extract compiler args
#------------------------
self.reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool
self.module_name = kwargs.pop("module_name", None) or kwf(self.top_node.inst_name) # 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]
# ------------------------
self.reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool
self.module_name = kwargs.pop("module_name", None) or kwf(
self.top_node.inst_name
) # 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]
# Pipelining options
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_external_reg = kwargs.pop("retime_external_reg", False) # type: bool
self.retime_external_regfile = kwargs.pop("retime_external_regfile", 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_read_fanin = kwargs.pop("retime_read_fanin", False) # type: bool
self.retime_read_response = kwargs.pop("retime_read_response", False) # type: bool
self.retime_external_reg = kwargs.pop("retime_external_reg", False) # type: bool
self.retime_external_regfile = kwargs.pop("retime_external_regfile", 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
# 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
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
#------------------------
# ------------------------
self.cpuif_data_width = 0
# Collections of signals that were actually referenced by the design
self.in_hier_signal_paths = set() # type: Set[str]
self.out_of_hier_signals = OrderedDict() # type: OrderedDict[str, SignalNode]
self.in_hier_signal_paths = set() # type: Set[str]
self.out_of_hier_signals = OrderedDict() # type: OrderedDict[str, SignalNode]
self.has_writable_msb0_fields = False
self.has_buffered_write_regs = False
@@ -249,7 +263,7 @@ class DesignState:
self.has_paritycheck = False
# Track any referenced enums
self.user_enums = [] # type: List[Type[UserEnum]]
self.user_enums = [] # type: List[Type[UserEnum]]
# Scan the design to fill in above variables
DesignScanner(self).do_scan()
@@ -260,17 +274,21 @@ class DesignState:
# Assume 32-bits
msg.warning(
"Addrmap being exported only contains external components. Unable to infer the CPUIF bus width. Assuming 32-bits.",
self.top_node.inst.def_src_ref
self.top_node.inst.def_src_ref,
)
self.cpuif_data_width = 32
#------------------------
# ------------------------
# Min address width encloses the total size AND at least 1 useful address bit
self.addr_width = max(clog2(self.top_node.size), clog2(self.cpuif_data_width//8) + 1)
self.addr_width = max(
clog2(self.top_node.size), clog2(self.cpuif_data_width // 8) + 1
)
if user_addr_width is not None:
if user_addr_width < self.addr_width:
msg.fatal(f"User-specified address width shall be greater than or equal to {self.addr_width}.")
msg.fatal(
f"User-specified address width shall be greater than or equal to {self.addr_width}."
)
self.addr_width = user_addr_width
@property

View File

@@ -6,12 +6,12 @@ from systemrdl.node import RegNode
from .forloop_generator import RDLForLoopGenerator
if TYPE_CHECKING:
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter
from systemrdl.node import AddressableNode
class ExternalWriteAckGenerator(RDLForLoopGenerator):
def __init__(self, exp: 'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__()
self.exp = exp
@@ -21,19 +21,21 @@ class ExternalWriteAckGenerator(RDLForLoopGenerator):
return ""
return content
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node)
if node.external:
if not isinstance(node, RegNode) or node.has_sw_writable:
self.add_content(f"wr_ack |= {self.exp.hwif.get_external_wr_ack(node)};")
self.add_content(
f"wr_ack |= {self.exp.hwif.get_external_wr_ack(node)};"
)
return WalkerAction.SkipDescendants
return WalkerAction.Continue
class ExternalReadAckGenerator(RDLForLoopGenerator):
def __init__(self, exp: 'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__()
self.exp = exp
@@ -43,12 +45,14 @@ class ExternalReadAckGenerator(RDLForLoopGenerator):
return ""
return content
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node)
if node.external:
if not isinstance(node, RegNode) or node.has_sw_readable:
self.add_content(f"rd_ack |= {self.exp.hwif.get_external_rd_ack(node)};")
self.add_content(
f"rd_ack |= {self.exp.hwif.get_external_rd_ack(node)};"
)
return WalkerAction.SkipDescendants
return WalkerAction.Continue

View File

@@ -14,28 +14,33 @@ from . import hw_interrupts_with_write
from ..utils import get_indexed_path
from ..sv_int import SVInt
from .generators import CombinationalStructGenerator, FieldStorageStructGenerator, FieldLogicGenerator
from .generators import (
CombinationalStructGenerator,
FieldStorageStructGenerator,
FieldLogicGenerator,
)
if TYPE_CHECKING:
from typing import Dict, List
from systemrdl.node import AddrmapNode, FieldNode
from ..exporter import RegblockExporter, DesignState
from ..exporter import BusDecoderExporter, DesignState
class FieldLogic:
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
self._hw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
self._sw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
self._hw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
self._sw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
self.init_conditionals()
@property
def ds(self) -> 'DesignState':
def ds(self) -> "DesignState":
return self.exp.ds
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def get_storage_struct(self) -> str:
@@ -65,10 +70,10 @@ class FieldLogic:
return ""
return s
#---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# Field utility functions
#---------------------------------------------------------------------------
def get_storage_identifier(self, field: 'FieldNode') -> str:
# ---------------------------------------------------------------------------
def get_storage_identifier(self, field: "FieldNode") -> str:
"""
Returns the Verilog string that represents the storage register element
for the referenced field
@@ -77,7 +82,7 @@ class FieldLogic:
path = get_indexed_path(self.top_node, field)
return f"field_storage.{path}.value"
def get_next_q_identifier(self, field: 'FieldNode') -> str:
def get_next_q_identifier(self, field: "FieldNode") -> str:
"""
Returns the Verilog string that represents the storage register element
for the delayed 'next' input value
@@ -86,7 +91,7 @@ class FieldLogic:
path = get_indexed_path(self.top_node, field)
return f"field_storage.{path}.next_q"
def get_field_combo_identifier(self, field: 'FieldNode', name: str) -> str:
def get_field_combo_identifier(self, field: "FieldNode", name: str) -> str:
"""
Returns a Verilog string that represents a field's internal combinational
signal.
@@ -95,94 +100,94 @@ class FieldLogic:
path = get_indexed_path(self.top_node, field)
return f"field_combo.{path}.{name}"
def get_counter_incr_strobe(self, field: 'FieldNode') -> str:
def get_counter_incr_strobe(self, field: "FieldNode") -> str:
"""
Return the Verilog string that represents the field's incr strobe signal.
"""
prop_value = field.get_property('incr')
prop_value = field.get_property("incr")
if prop_value:
return str(self.exp.dereferencer.get_value(prop_value))
# unset by the user, points to the implied input signal
return self.exp.hwif.get_implied_prop_input_identifier(field, "incr")
def get_counter_incrvalue(self, field: 'FieldNode') -> Union[SVInt, str]:
def get_counter_incrvalue(self, field: "FieldNode") -> Union[SVInt, str]:
"""
Return the string that represents the field's increment value
"""
incrvalue = field.get_property('incrvalue')
incrvalue = field.get_property("incrvalue")
if incrvalue is not None:
return self.exp.dereferencer.get_value(incrvalue, field.width)
if field.get_property('incrwidth'):
if field.get_property("incrwidth"):
return self.exp.hwif.get_implied_prop_input_identifier(field, "incrvalue")
return "1'b1"
def get_counter_incrsaturate_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('incrsaturate')
def get_counter_incrsaturate_value(self, field: "FieldNode") -> Union[SVInt, str]:
prop_value = field.get_property("incrsaturate")
if prop_value is True:
return self.exp.dereferencer.get_value(2**field.width - 1, field.width)
return self.exp.dereferencer.get_value(prop_value, field.width)
def counter_incrsaturates(self, field: 'FieldNode') -> bool:
def counter_incrsaturates(self, field: "FieldNode") -> bool:
"""
Returns True if the counter saturates
"""
return field.get_property('incrsaturate') is not False
return field.get_property("incrsaturate") is not False
def get_counter_incrthreshold_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('incrthreshold')
def get_counter_incrthreshold_value(self, field: "FieldNode") -> Union[SVInt, str]:
prop_value = field.get_property("incrthreshold")
if isinstance(prop_value, bool):
# No explicit value set. use max
return self.exp.dereferencer.get_value(2**field.width - 1, field.width)
return self.exp.dereferencer.get_value(prop_value, field.width)
def get_counter_decr_strobe(self, field: 'FieldNode') -> str:
def get_counter_decr_strobe(self, field: "FieldNode") -> str:
"""
Return the Verilog string that represents the field's incr strobe signal.
"""
prop_value = field.get_property('decr')
prop_value = field.get_property("decr")
if prop_value:
return str(self.exp.dereferencer.get_value(prop_value))
# unset by the user, points to the implied input signal
return self.exp.hwif.get_implied_prop_input_identifier(field, "decr")
def get_counter_decrvalue(self, field: 'FieldNode') -> Union[SVInt, str]:
def get_counter_decrvalue(self, field: "FieldNode") -> Union[SVInt, str]:
"""
Return the string that represents the field's decrement value
"""
decrvalue = field.get_property('decrvalue')
decrvalue = field.get_property("decrvalue")
if decrvalue is not None:
return self.exp.dereferencer.get_value(decrvalue, field.width)
if field.get_property('decrwidth'):
if field.get_property("decrwidth"):
return self.exp.hwif.get_implied_prop_input_identifier(field, "decrvalue")
return "1'b1"
def get_counter_decrsaturate_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('decrsaturate')
def get_counter_decrsaturate_value(self, field: "FieldNode") -> Union[SVInt, str]:
prop_value = field.get_property("decrsaturate")
if prop_value is True:
return f"{field.width}'d0"
return self.exp.dereferencer.get_value(prop_value, field.width)
def counter_decrsaturates(self, field: 'FieldNode') -> bool:
def counter_decrsaturates(self, field: "FieldNode") -> bool:
"""
Returns True if the counter saturates
"""
return field.get_property('decrsaturate') is not False
return field.get_property("decrsaturate") is not False
def get_counter_decrthreshold_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('decrthreshold')
def get_counter_decrthreshold_value(self, field: "FieldNode") -> Union[SVInt, str]:
prop_value = field.get_property("decrthreshold")
if isinstance(prop_value, bool):
# No explicit value set. use min
return f"{field.width}'d0"
return self.exp.dereferencer.get_value(prop_value, field.width)
def get_swacc_identifier(self, field: 'FieldNode') -> str:
def get_swacc_identifier(self, field: "FieldNode") -> str:
"""
Asserted when field is software accessed (read or write)
"""
buffer_reads = field.parent.get_property('buffer_reads')
buffer_writes = field.parent.get_property('buffer_writes')
buffer_reads = field.parent.get_property("buffer_reads")
buffer_writes = field.parent.get_property("buffer_writes")
if buffer_reads and buffer_writes:
rstrb = self.exp.read_buffering.get_trigger(field.parent)
wstrb = self.exp.write_buffering.get_write_strobe(field)
@@ -199,11 +204,11 @@ class FieldLogic:
strb = self.exp.dereferencer.get_access_strobe(field)
return strb
def get_rd_swacc_identifier(self, field: 'FieldNode') -> str:
def get_rd_swacc_identifier(self, field: "FieldNode") -> str:
"""
Asserted when field is software accessed (read)
"""
buffer_reads = field.parent.get_property('buffer_reads')
buffer_reads = field.parent.get_property("buffer_reads")
if buffer_reads:
rstrb = self.exp.read_buffering.get_trigger(field.parent)
return rstrb
@@ -211,11 +216,11 @@ class FieldLogic:
strb = self.exp.dereferencer.get_access_strobe(field)
return f"{strb} && !decoded_req_is_wr"
def get_wr_swacc_identifier(self, field: 'FieldNode') -> str:
def get_wr_swacc_identifier(self, field: "FieldNode") -> str:
"""
Asserted when field is software accessed (write)
"""
buffer_writes = field.parent.get_property('buffer_writes')
buffer_writes = field.parent.get_property("buffer_writes")
if buffer_writes:
wstrb = self.exp.write_buffering.get_write_strobe(field)
return wstrb
@@ -223,18 +228,17 @@ class FieldLogic:
strb = self.exp.dereferencer.get_access_strobe(field)
return f"{strb} && decoded_req_is_wr"
def get_swmod_identifier(self, field: 'FieldNode') -> str:
def get_swmod_identifier(self, field: "FieldNode") -> str:
"""
Asserted when field is modified by software (written or read with a
set or clear side effect).
"""
w_modifiable = field.is_sw_writable
r_modifiable = field.get_property('onread') is not None
buffer_writes = field.parent.get_property('buffer_writes')
buffer_reads = field.parent.get_property('buffer_reads')
r_modifiable = field.get_property("onread") is not None
buffer_writes = field.parent.get_property("buffer_writes")
buffer_reads = field.parent.get_property("buffer_reads")
accesswidth = field.parent.get_property("accesswidth")
astrb = self.exp.dereferencer.get_access_strobe(field)
conditions = []
@@ -267,43 +271,42 @@ class FieldLogic:
else:
return " || ".join(conditions)
def get_parity_identifier(self, field: 'FieldNode') -> str:
def get_parity_identifier(self, field: "FieldNode") -> str:
"""
Returns the identifier for the stored 'golden' parity value of the field
"""
path = get_indexed_path(self.top_node, field)
return f"field_storage.{path}.parity"
def get_parity_error_identifier(self, field: 'FieldNode') -> str:
def get_parity_error_identifier(self, field: "FieldNode") -> str:
"""
Returns the identifier for whether the field currently has a parity error
"""
path = get_indexed_path(self.top_node, field)
return f"field_combo.{path}.parity_error"
def has_next_q(self, field: 'FieldNode') -> bool:
def has_next_q(self, field: "FieldNode") -> bool:
"""
Some fields require a delayed version of their 'next' input signal in
order to do edge-detection.
Returns True if this is the case.
"""
if field.get_property('intr type') in {
if field.get_property("intr type") in {
InterruptType.posedge,
InterruptType.negedge,
InterruptType.bothedge
InterruptType.bothedge,
}:
return True
return False
def get_wbus_bitslice(self, field: 'FieldNode', subword_idx: int = 0) -> str:
def get_wbus_bitslice(self, field: "FieldNode", subword_idx: int = 0) -> str:
"""
Get the bitslice range string of the internal cpuif's data/biten bus
that corresponds to this field
"""
if field.parent.get_property('buffer_writes'):
if field.parent.get_property("buffer_writes"):
# register is buffered.
# write buffer is the full width of the register. no need to deal with subwords
high = field.high
@@ -311,7 +314,7 @@ class FieldLogic:
if field.msb < field.lsb:
# slice is for an msb0 field.
# mirror it
regwidth = field.parent.get_property('regwidth')
regwidth = field.parent.get_property("regwidth")
low = regwidth - 1 - low
high = regwidth - 1 - high
low, high = high, low
@@ -321,7 +324,7 @@ class FieldLogic:
# values unchanged.
# For fields within a wide register (accesswidth < regwidth), low/high
# may be shifted down and clamped depending on which sub-word is being accessed
accesswidth = field.parent.get_property('accesswidth')
accesswidth = field.parent.get_property("accesswidth")
# Shift based on subword
high = field.high - (subword_idx * accesswidth)
@@ -341,11 +344,11 @@ class FieldLogic:
return f"[{high}:{low}]"
def get_wr_biten(self, field: 'FieldNode', subword_idx: int=0) -> str:
def get_wr_biten(self, field: "FieldNode", subword_idx: int = 0) -> str:
"""
Get the bit-enable slice that corresponds to this field
"""
if field.parent.get_property('buffer_writes'):
if field.parent.get_property("buffer_writes"):
# Is buffered. Use value from write buffer
# No need to check msb0 ordering. Bus is pre-swapped, and bitslice
# accounts for it
@@ -363,11 +366,11 @@ class FieldLogic:
value = "decoded_wr_biten" + bslice
return value
def get_wr_data(self, field: 'FieldNode', subword_idx: int=0) -> str:
def get_wr_data(self, field: "FieldNode", subword_idx: int = 0) -> str:
"""
Get the write data slice that corresponds to this field
"""
if field.parent.get_property('buffer_writes'):
if field.parent.get_property("buffer_writes"):
# Is buffered. Use value from write buffer
# No need to check msb0 ordering. Bus is pre-swapped, and bitslice
# accounts for it
@@ -385,10 +388,12 @@ class FieldLogic:
value = "decoded_wr_data" + bslice
return value
#---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# Field Logic Conditionals
#---------------------------------------------------------------------------
def add_hw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
# ---------------------------------------------------------------------------
def add_hw_conditional(
self, conditional: NextStateConditional, precedence: AssignmentPrecedence
) -> None:
"""
Register a NextStateConditional action for hardware-triggered field updates.
Categorizing conditionals correctly by hw/sw ensures that the RDL precedence
@@ -404,8 +409,9 @@ class FieldLogic:
self._hw_conditionals[precedence] = []
self._hw_conditionals[precedence].append(conditional)
def add_sw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
def add_sw_conditional(
self, conditional: NextStateConditional, precedence: AssignmentPrecedence
) -> None:
"""
Register a NextStateConditional action for software-triggered field updates.
Categorizing conditionals correctly by hw/sw ensures that the RDL precedence
@@ -421,7 +427,6 @@ class FieldLogic:
self._sw_conditionals[precedence] = []
self._sw_conditionals[precedence].append(conditional)
def init_conditionals(self) -> None:
"""
Initialize all possible conditionals here.
@@ -430,46 +435,117 @@ class FieldLogic:
same assignment precedence.
"""
self.add_sw_conditional(sw_onread.ClearOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
self.add_sw_conditional(sw_onread.SetOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
self.add_sw_conditional(
sw_onread.ClearOnRead(self.exp), AssignmentPrecedence.SW_ONREAD
)
self.add_sw_conditional(
sw_onread.SetOnRead(self.exp), AssignmentPrecedence.SW_ONREAD
)
self.add_sw_conditional(sw_onwrite.Write(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteZeroToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteZeroClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteZeroSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteOneToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteOneClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteOneSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(
sw_onwrite.Write(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteSet(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteClear(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteZeroToggle(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteZeroClear(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteZeroSet(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteOneToggle(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteOneClear(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteOneSet(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(sw_singlepulse.Singlepulse(self.exp), AssignmentPrecedence.SW_SINGLEPULSE)
self.add_sw_conditional(
sw_singlepulse.Singlepulse(self.exp), AssignmentPrecedence.SW_SINGLEPULSE
)
self.add_hw_conditional(hw_interrupts_with_write.PosedgeStickybitWE(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.PosedgeStickybitWEL(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.NegedgeStickybitWE(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.NegedgeStickybitWEL(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.BothedgeStickybitWE(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.BothedgeStickybitWEL(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.StickyWE(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.StickyWEL(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.StickybitWE(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.StickybitWEL(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts.PosedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts.NegedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts.BothedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts.Sticky(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts.Stickybit(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_write.WEWrite(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_write.WELWrite(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_write.AlwaysWrite(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(
hw_interrupts_with_write.PosedgeStickybitWE(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.PosedgeStickybitWEL(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.NegedgeStickybitWE(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.NegedgeStickybitWEL(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.BothedgeStickybitWE(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.BothedgeStickybitWEL(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.StickyWE(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts_with_write.StickyWEL(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts_with_write.StickybitWE(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.StickybitWEL(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts.PosedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts.NegedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts.BothedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts.Sticky(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts.Stickybit(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_write.WEWrite(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_write.WELWrite(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_write.AlwaysWrite(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(hw_set_clr.HWClear(self.exp), AssignmentPrecedence.HWCLR)
self.add_hw_conditional(
hw_set_clr.HWClear(self.exp), AssignmentPrecedence.HWCLR
)
self.add_hw_conditional(hw_set_clr.HWSet(self.exp), AssignmentPrecedence.HWSET)
def _get_X_conditionals(self, conditionals: 'Dict[int, List[NextStateConditional]]', field: 'FieldNode') -> 'List[NextStateConditional]':
def _get_X_conditionals(
self, conditionals: "Dict[int, List[NextStateConditional]]", field: "FieldNode"
) -> "List[NextStateConditional]":
result = []
precedences = sorted(conditionals.keys(), reverse=True)
for precedence in precedences:
@@ -479,15 +555,14 @@ class FieldLogic:
break
return result
def get_conditionals(self, field: 'FieldNode') -> 'List[NextStateConditional]':
def get_conditionals(self, field: "FieldNode") -> "List[NextStateConditional]":
"""
Get a list of NextStateConditional objects that apply to the given field.
The returned list is sorted in priority order - the conditional with highest
precedence is first in the list.
"""
sw_precedence = field.get_property('precedence') == PrecedenceType.sw
sw_precedence = field.get_property("precedence") == PrecedenceType.sw
result = []
if sw_precedence:

View File

@@ -6,7 +6,8 @@ from ..utils import get_indexed_path
if TYPE_CHECKING:
from systemrdl.node import FieldNode
from ..exporter import RegblockExporter
from ..exporter import BusDecoderExporter
class AssignmentPrecedence(enum.IntEnum):
"""
@@ -29,16 +30,15 @@ class AssignmentPrecedence(enum.IntEnum):
# Hardware access assignment groups
HW_WRITE = 3000
HWSET = 2000
HWCLR = 1000
HWCLR = 1000
COUNTER_INCR_DECR = 0
class SVLogic:
"""
Represents a SystemVerilog logic signal
"""
def __init__(self, name: str, width: int, default_assignment: str) -> None:
self.name = name
self.width = width
@@ -67,10 +67,10 @@ class NextStateConditional:
# Optional comment to emit next to the conditional
comment = ""
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
def is_match(self, field: 'FieldNode') -> bool:
def is_match(self, field: "FieldNode") -> bool:
"""
Returns True if this conditional is relevant to the field. If so,
it instructs the FieldBuilder that Verilog for this conditional shall
@@ -78,17 +78,16 @@ class NextStateConditional:
"""
raise NotImplementedError
def get_field_path(self, field:'FieldNode') -> str:
def get_field_path(self, field: "FieldNode") -> str:
return get_indexed_path(self.exp.ds.top_node, field)
def get_predicate(self, field: 'FieldNode') -> str:
def get_predicate(self, field: "FieldNode") -> str:
"""
Returns the rendered conditional text
"""
raise NotImplementedError
def get_assignments(self, field: 'FieldNode') -> List[str]:
def get_assignments(self, field: "FieldNode") -> List[str]:
"""
Returns a list of rendered assignment strings
This will basically always be two:
@@ -97,13 +96,14 @@ class NextStateConditional:
"""
raise NotImplementedError
def get_extra_combo_signals(self, field: 'FieldNode') -> List[SVLogic]:
def get_extra_combo_signals(self, field: "FieldNode") -> List[SVLogic]:
"""
Return any additional combinational signals that this conditional
will assign if present.
"""
return []
class NextStateUnconditional(NextStateConditional):
"""
Use this class if predicate can never evaluate to false.

View File

@@ -12,7 +12,8 @@ from .generators import InputStructGenerator_TypeScope, OutputStructGenerator_Ty
from .generators import EnumGenerator
if TYPE_CHECKING:
from ..exporter import RegblockExporter, DesignState
from ..exporter import BusDecoderExporter, DesignState
class Hwif:
"""
@@ -22,10 +23,7 @@ class Hwif:
- Signal inputs (except those that are promoted to the top)
"""
def __init__(
self, exp: 'RegblockExporter',
hwif_report_file: Optional[TextIO]
):
def __init__(self, exp: "BusDecoderExporter", hwif_report_file: Optional[TextIO]):
self.exp = exp
self.has_input_struct = False
@@ -41,31 +39,25 @@ class Hwif:
self._gen_out_cls = OutputStructGenerator_TypeScope
@property
def ds(self) -> 'DesignState':
def ds(self) -> "DesignState":
return self.exp.ds
@property
def top_node(self) -> AddrmapNode:
return self.exp.ds.top_node
def get_extra_package_params(self) -> str:
lines = [""]
for param in self.top_node.inst.parameters:
value = param.get_value()
if isinstance(value, int):
lines.append(
f"localparam {param.name} = {SVInt(value)};"
)
lines.append(f"localparam {param.name} = {SVInt(value)};")
elif isinstance(value, str):
lines.append(
f"localparam {param.name} = {value};"
)
lines.append(f"localparam {param.name} = {value};")
return "\n".join(lines)
def get_package_contents(self) -> str:
"""
If this hwif requires a package, generate the string
@@ -74,8 +66,7 @@ class Hwif:
gen_in = self._gen_in_cls(self)
structs_in = gen_in.get_struct(
self.top_node,
f"{self.top_node.inst_name}__in_t"
self.top_node, f"{self.top_node.inst_name}__in_t"
)
if structs_in is not None:
self.has_input_struct = True
@@ -85,8 +76,7 @@ class Hwif:
gen_out = self._gen_out_cls(self)
structs_out = gen_out.get_struct(
self.top_node,
f"{self.top_node.inst_name}__out_t"
self.top_node, f"{self.top_node.inst_name}__out_t"
)
if structs_out is not None:
self.has_output_struct = True
@@ -101,7 +91,6 @@ class Hwif:
return "\n\n".join(lines)
@property
def port_declaration(self) -> str:
"""
@@ -122,9 +111,9 @@ class Hwif:
return ",\n".join(lines)
#---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# hwif utility functions
#---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
def has_value_input(self, obj: Union[FieldNode, SignalNode]) -> bool:
"""
Returns True if the object infers an input wire in the hwif
@@ -137,14 +126,12 @@ class Hwif:
else:
raise RuntimeError
def has_value_output(self, obj: FieldNode) -> bool:
"""
Returns True if the object infers an output wire in the hwif
"""
return obj.is_hw_readable
def get_input_identifier(
self,
obj: Union[FieldNode, SignalNode, PropertyReference],
@@ -162,7 +149,7 @@ class Hwif:
raises an exception if obj is invalid
"""
if isinstance(obj, FieldNode):
next_value = obj.get_property('next')
next_value = obj.get_property("next")
if next_value is not None:
# 'next' property replaces the inferred input signal
return self.exp.dereferencer.get_value(next_value, width)
@@ -203,13 +190,20 @@ class Hwif:
def get_implied_prop_input_identifier(self, field: FieldNode, prop: str) -> str:
assert prop in {
'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel',
'incr', 'decr', 'incrvalue', 'decrvalue'
"hwclr",
"hwset",
"swwe",
"swwel",
"we",
"wel",
"incr",
"decr",
"incrvalue",
"decrvalue",
}
path = get_indexed_path(self.top_node, field)
return "hwif_in." + path + "." + prop
def get_output_identifier(self, obj: Union[FieldNode, PropertyReference]) -> str:
"""
Returns the identifier string that best represents the output object.
@@ -233,17 +227,27 @@ class Hwif:
raise RuntimeError(f"Unhandled reference to: {obj}")
def get_implied_prop_output_identifier(self, node: Union[FieldNode, RegNode], prop: str) -> str:
def get_implied_prop_output_identifier(
self, node: Union[FieldNode, RegNode], prop: str
) -> str:
if isinstance(node, FieldNode):
assert prop in {
"anded", "ored", "xored", "swmod", "swacc",
"incrthreshold", "decrthreshold", "overflow", "underflow",
"rd_swacc", "wr_swacc",
"anded",
"ored",
"xored",
"swmod",
"swacc",
"incrthreshold",
"decrthreshold",
"overflow",
"underflow",
"rd_swacc",
"wr_swacc",
}
elif isinstance(node, RegNode):
assert prop in {
"intr", "halt",
"intr",
"halt",
}
path = get_indexed_path(self.top_node, node)
return "hwif_out." + path + "." + prop

View File

@@ -1,5 +1,5 @@
// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator
// https://github.com/SystemRDL/PeakRDL-regblock
// Generated by PeakRDL-busdecoder - A free and open-source SystemVerilog generator
// https://github.com/SystemRDL/PeakRDL-busdecoder
module {{ds.module_name}}
{%- if cpuif.parameters %} #(

View File

@@ -1,5 +1,5 @@
// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator
// https://github.com/SystemRDL/PeakRDL-regblock
// Generated by PeakRDL-busdecoder - A free and open-source SystemVerilog generator
// https://github.com/SystemRDL/PeakRDL-busdecoder
package {{ds.package_name}};

View File

@@ -6,12 +6,12 @@ from systemrdl.walker import WalkerAction
from .forloop_generator import RDLForLoopGenerator
if TYPE_CHECKING:
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter
from systemrdl.node import FieldNode, AddressableNode
class ParityErrorReduceGenerator(RDLForLoopGenerator):
def __init__(self, exp: 'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__()
self.exp = exp
@@ -21,14 +21,14 @@ class ParityErrorReduceGenerator(RDLForLoopGenerator):
return ""
return content
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node)
if node.external:
return WalkerAction.SkipDescendants
return WalkerAction.Continue
def enter_Field(self, node: 'FieldNode') -> None:
if node.get_property('paritycheck') and node.implements_storage:
def enter_Field(self, node: "FieldNode") -> None:
if node.get_property("paritycheck") and node.implements_storage:
self.add_content(
f"err |= {self.exp.field_logic.get_parity_error_identifier(node)};"
)

View File

@@ -8,15 +8,15 @@ from ..utils import get_indexed_path
from ..sv_int import SVInt
if TYPE_CHECKING:
from ..exporter import RegblockExporter
from ..exporter import BusDecoderExporter
class ReadBuffering:
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def get_storage_struct(self) -> str:
@@ -32,14 +32,16 @@ class ReadBuffering:
return s
def get_trigger(self, node: RegNode) -> str:
trigger = node.get_property('rbuffer_trigger')
trigger = node.get_property("rbuffer_trigger")
if isinstance(trigger, RegNode):
# Trigger is a register.
# trigger when lowermost address of the register is written
regwidth = trigger.get_property('regwidth')
accesswidth = trigger.get_property('accesswidth')
strb_prefix = self.exp.dereferencer.get_access_strobe(trigger, reduce_substrobes=False)
regwidth = trigger.get_property("regwidth")
accesswidth = trigger.get_property("accesswidth")
strb_prefix = self.exp.dereferencer.get_access_strobe(
trigger, reduce_substrobes=False
)
if accesswidth < regwidth:
return f"{strb_prefix}[0] && !decoded_req_is_wr"
@@ -47,7 +49,7 @@ class ReadBuffering:
return f"{strb_prefix} && !decoded_req_is_wr"
elif isinstance(trigger, SignalNode):
s = self.exp.dereferencer.get_value(trigger)
if trigger.get_property('activehigh'):
if trigger.get_property("activehigh"):
return str(s)
else:
return f"~{s}"

View File

@@ -4,19 +4,20 @@ import math
from .generators import ReadbackAssignmentGenerator
if TYPE_CHECKING:
from ..exporter import RegblockExporter, DesignState
from ..exporter import BusDecoderExporter, DesignState
from systemrdl.node import AddrmapNode
class Readback:
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
@property
def ds(self) -> 'DesignState':
def ds(self) -> "DesignState":
return self.exp.ds
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def get_implementation(self) -> str:
@@ -30,10 +31,10 @@ class Readback:
self.ds.retime_read_fanin = False
context = {
"array_assignments" : array_assignments,
"array_size" : array_size,
'get_always_ff_event': self.exp.dereferencer.get_always_ff_event,
'get_resetsignal': self.exp.dereferencer.get_resetsignal,
"array_assignments": array_assignments,
"array_size": array_size,
"get_always_ff_event": self.exp.dereferencer.get_always_ff_event,
"get_resetsignal": self.exp.dereferencer.get_resetsignal,
"cpuif": self.exp.cpuif,
"ds": self.ds,
}
@@ -61,12 +62,10 @@ class Readback:
else:
fanin_loop_iter = fanin_array_size
context['fanin_stride'] = fanin_stride
context['fanin_array_size'] = fanin_array_size
context['fanin_residual_stride'] = fanin_residual_stride
context['fanin_loop_iter'] = fanin_loop_iter
context["fanin_stride"] = fanin_stride
context["fanin_array_size"] = fanin_array_size
context["fanin_residual_stride"] = fanin_residual_stride
context["fanin_loop_iter"] = fanin_loop_iter
template = self.exp.jj_env.get_template(
"readback/templates/readback.sv"
)
template = self.exp.jj_env.get_template("readback/templates/readback.sv")
return template.render(context)

View File

@@ -8,7 +8,8 @@ from ..forloop_generator import RDLForLoopGenerator, LoopBody
from ..utils import do_bitswap, do_slice
if TYPE_CHECKING:
from ..exporter import RegblockExporter
from ..exporter import BusDecoderExporter
class ReadbackLoopBody(LoopBody):
def __init__(self, dim: int, iterator: str, i_type: str) -> None:
@@ -22,11 +23,12 @@ class ReadbackLoopBody(LoopBody):
s = s.replace(token, str(self.n_regs))
return s
class ReadbackAssignmentGenerator(RDLForLoopGenerator):
i_type = "genvar"
loop_body_cls = ReadbackLoopBody
def __init__(self, exp:'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__()
self.exp = exp
@@ -34,8 +36,8 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# array. The array width is equal to the CPUIF bus width. Each entry in
# the array represents an aligned read access.
self.current_offset = 0
self.start_offset_stack = [] # type: List[int]
self.dim_stack = [] # type: List[int]
self.start_offset_stack = [] # type: List[int]
self.dim_stack = [] # type: List[int]
@property
def current_offset_str(self) -> str:
@@ -72,22 +74,23 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# Number of registers enclosed in this loop
n_regs = self.current_offset - start_offset
self.current_loop.n_regs = n_regs # type: ignore
self.current_loop.n_regs = n_regs # type: ignore
super().pop_loop()
# Advance current scope's offset to account for loop's contents
self.current_offset = start_offset + n_regs * dim
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node)
if node.external and not isinstance(node, RegNode):
# External block
strb = self.exp.hwif.get_external_rd_ack(node)
data = self.exp.hwif.get_external_rd_data(node)
self.add_content(f"assign readback_array[{self.current_offset_str}] = {strb} ? {data} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}] = {strb} ? {data} : '0;"
)
self.current_offset += 1
return WalkerAction.SkipDescendants
@@ -101,12 +104,12 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
self.process_external_reg(node)
return WalkerAction.SkipDescendants
accesswidth = node.get_property('accesswidth')
regwidth = node.get_property('regwidth')
rbuf = node.get_property('buffer_reads')
accesswidth = node.get_property("accesswidth")
regwidth = node.get_property("regwidth")
rbuf = node.get_property("buffer_reads")
if rbuf:
trigger = node.get_property('rbuffer_trigger')
is_own_trigger = (isinstance(trigger, RegNode) and trigger == node)
trigger = node.get_property("rbuffer_trigger")
is_own_trigger = isinstance(trigger, RegNode) and trigger == node
if is_own_trigger:
if accesswidth < regwidth:
self.process_buffered_reg_with_bypass(node, regwidth, accesswidth)
@@ -125,18 +128,26 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
def process_external_reg(self, node: RegNode) -> None:
strb = self.exp.hwif.get_external_rd_ack(node)
data = self.exp.hwif.get_external_rd_data(node)
regwidth = node.get_property('regwidth')
regwidth = node.get_property("regwidth")
if regwidth < self.exp.cpuif.data_width:
self.add_content(f"assign readback_array[{self.current_offset_str}][{self.exp.cpuif.data_width-1}:{regwidth}] = '0;")
self.add_content(f"assign readback_array[{self.current_offset_str}][{regwidth-1}:0] = {strb} ? {data} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{self.exp.cpuif.data_width - 1}:{regwidth}] = '0;"
)
self.add_content(
f"assign readback_array[{self.current_offset_str}][{regwidth - 1}:0] = {strb} ? {data} : '0;"
)
else:
self.add_content(f"assign readback_array[{self.current_offset_str}] = {strb} ? {data} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}] = {strb} ? {data} : '0;"
)
self.current_offset += 1
def process_reg(self, node: RegNode) -> None:
current_bit = 0
rd_strb = f"({self.exp.dereferencer.get_access_strobe(node)} && !decoded_req_is_wr)"
rd_strb = (
f"({self.exp.dereferencer.get_access_strobe(node)} && !decoded_req_is_wr)"
)
# Fields are sorted by ascending low bit
for field in node.fields():
if not field.is_sw_readable:
@@ -144,51 +155,67 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# insert reserved assignment before this field if needed
if field.low != current_bit:
self.add_content(f"assign readback_array[{self.current_offset_str}][{field.low-1}:{current_bit}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{field.low - 1}:{current_bit}] = '0;"
)
value = self.exp.dereferencer.get_value(field)
if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation
value = do_bitswap(value)
self.add_content(f"assign readback_array[{self.current_offset_str}][{field.high}:{field.low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{field.high}:{field.low}] = {rd_strb} ? {value} : '0;"
)
current_bit = field.high + 1
# Insert final reserved assignment if needed
bus_width = self.exp.cpuif.data_width
if current_bit < bus_width:
self.add_content(f"assign readback_array[{self.current_offset_str}][{bus_width-1}:{current_bit}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{bus_width - 1}:{current_bit}] = '0;"
)
self.current_offset += 1
def process_buffered_reg(self, node: RegNode, regwidth: int, accesswidth: int) -> None:
def process_buffered_reg(
self, node: RegNode, regwidth: int, accesswidth: int
) -> None:
rbuf = self.exp.read_buffering.get_rbuf_data(node)
if accesswidth < regwidth:
# Is wide reg
n_subwords = regwidth // accesswidth
astrb = self.exp.dereferencer.get_access_strobe(node, reduce_substrobes=False)
astrb = self.exp.dereferencer.get_access_strobe(
node, reduce_substrobes=False
)
for i in range(n_subwords):
rd_strb = f"({astrb}[{i}] && !decoded_req_is_wr)"
bslice = f"[{(i + 1) * accesswidth - 1}:{i*accesswidth}]"
self.add_content(f"assign readback_array[{self.current_offset_str}] = {rd_strb} ? {rbuf}{bslice} : '0;")
bslice = f"[{(i + 1) * accesswidth - 1}:{i * accesswidth}]"
self.add_content(
f"assign readback_array[{self.current_offset_str}] = {rd_strb} ? {rbuf}{bslice} : '0;"
)
self.current_offset += 1
else:
# Is regular reg
rd_strb = f"({self.exp.dereferencer.get_access_strobe(node)} && !decoded_req_is_wr)"
self.add_content(f"assign readback_array[{self.current_offset_str}][{regwidth-1}:0] = {rd_strb} ? {rbuf} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{regwidth - 1}:0] = {rd_strb} ? {rbuf} : '0;"
)
bus_width = self.exp.cpuif.data_width
if regwidth < bus_width:
self.add_content(f"assign readback_array[{self.current_offset_str}][{bus_width-1}:{regwidth}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{bus_width - 1}:{regwidth}] = '0;"
)
self.current_offset += 1
def process_buffered_reg_with_bypass(self, node: RegNode, regwidth: int, accesswidth: int) -> None:
def process_buffered_reg_with_bypass(
self, node: RegNode, regwidth: int, accesswidth: int
) -> None:
"""
Special case for a buffered register when the register is its own trigger.
First sub-word shall bypass the read buffer and assign directly.
@@ -210,7 +237,9 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
if bidx < field.low:
# insert padding before
self.add_content(f"assign readback_array[{self.current_offset_str}][{field.low - 1}:{bidx}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{field.low - 1}:{bidx}] = '0;"
)
if field.high >= accesswidth:
# field gets truncated
@@ -225,11 +254,17 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_low = field.width - 1 - f_low
f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low
value = do_bitswap(do_slice(self.exp.dereferencer.get_value(field), f_high, f_low))
value = do_bitswap(
do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
)
else:
value = do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
value = do_slice(
self.exp.dereferencer.get_value(field), f_high, f_low
)
self.add_content(f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;"
)
bidx = accesswidth
else:
# field fits in subword
@@ -237,12 +272,16 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation
value = do_bitswap(value)
self.add_content(f"assign readback_array[{self.current_offset_str}][{field.high}:{field.low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{field.high}:{field.low}] = {rd_strb} ? {value} : '0;"
)
bidx = field.high + 1
# pad up remainder of subword
if bidx < accesswidth:
self.add_content(f"assign readback_array[{self.current_offset_str}][{accesswidth-1}:{bidx}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{accesswidth - 1}:{bidx}] = '0;"
)
self.current_offset += 1
# Assign remainder of subwords from read buffer
@@ -250,29 +289,35 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
rbuf = self.exp.read_buffering.get_rbuf_data(node)
for i in range(1, n_subwords):
rd_strb = f"({astrb}[{i}] && !decoded_req_is_wr)"
bslice = f"[{(i + 1) * accesswidth - 1}:{i*accesswidth}]"
self.add_content(f"assign readback_array[{self.current_offset_str}] = {rd_strb} ? {rbuf}{bslice} : '0;")
bslice = f"[{(i + 1) * accesswidth - 1}:{i * accesswidth}]"
self.add_content(
f"assign readback_array[{self.current_offset_str}] = {rd_strb} ? {rbuf}{bslice} : '0;"
)
self.current_offset += 1
def process_wide_reg(self, node: RegNode, accesswidth: int) -> None:
bus_width = self.exp.cpuif.data_width
subword_idx = 0
current_bit = 0 # Bit-offset within the wide register
access_strb = self.exp.dereferencer.get_access_strobe(node, reduce_substrobes=False)
current_bit = 0 # Bit-offset within the wide register
access_strb = self.exp.dereferencer.get_access_strobe(
node, reduce_substrobes=False
)
# Fields are sorted by ascending low bit
for field in node.fields():
if not field.is_sw_readable:
continue
# insert zero assignment before this field if needed
if field.low >= accesswidth*(subword_idx+1):
if field.low >= accesswidth * (subword_idx + 1):
# field does not start in this subword
if current_bit > accesswidth * subword_idx:
# current subword had content. Assign remainder
low = current_bit % accesswidth
high = bus_width - 1
self.add_content(f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;"
)
self.current_offset += 1
# Advance to subword that contains the start of the field
@@ -283,17 +328,20 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# assign zero up to start of this field
low = current_bit % accesswidth
high = (field.low % accesswidth) - 1
self.add_content(f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;"
)
current_bit = field.low
# Assign field
# loop until the entire field's assignments have been generated
field_pos = field.low
while current_bit <= field.high:
# Assign the field
rd_strb = f"({access_strb}[{subword_idx}] && !decoded_req_is_wr)"
if (field_pos == field.low) and (field.high < accesswidth*(subword_idx+1)):
if (field_pos == field.low) and (
field.high < accesswidth * (subword_idx + 1)
):
# entire field fits into this subword
low = field.low - accesswidth * subword_idx
high = field.high - accesswidth * subword_idx
@@ -303,15 +351,17 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# Field gets bitswapped since it is in [low:high] orientation
value = do_bitswap(value)
self.add_content(f"assign readback_array[{self.current_offset_str}][{high}:{low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{high}:{low}] = {rd_strb} ? {value} : '0;"
)
current_bit = field.high + 1
if current_bit == accesswidth*(subword_idx+1):
if current_bit == accesswidth * (subword_idx + 1):
# Field ends at the subword boundary
subword_idx += 1
self.current_offset += 1
elif field.high >= accesswidth*(subword_idx+1):
elif field.high >= accesswidth * (subword_idx + 1):
# only a subset of the field can fit into this subword
# high end gets truncated
@@ -330,11 +380,19 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low
value = do_bitswap(do_slice(self.exp.dereferencer.get_value(field), f_high, f_low))
value = do_bitswap(
do_slice(
self.exp.dereferencer.get_value(field), f_high, f_low
)
)
else:
value = do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
value = do_slice(
self.exp.dereferencer.get_value(field), f_high, f_low
)
self.add_content(f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;"
)
# advance to the next subword
subword_idx += 1
@@ -360,14 +418,22 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low
value = do_bitswap(do_slice(self.exp.dereferencer.get_value(field), f_high, f_low))
value = do_bitswap(
do_slice(
self.exp.dereferencer.get_value(field), f_high, f_low
)
)
else:
value = do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
value = do_slice(
self.exp.dereferencer.get_value(field), f_high, f_low
)
self.add_content(f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;"
)
current_bit = field.high + 1
if current_bit == accesswidth*(subword_idx+1):
if current_bit == accesswidth * (subword_idx + 1):
# Field ends at the subword boundary
subword_idx += 1
self.current_offset += 1
@@ -377,5 +443,7 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# current subword had content. Assign remainder
low = current_bit % accesswidth
high = bus_width - 1
self.add_content(f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;"
)
self.current_offset += 1

View File

@@ -15,12 +15,13 @@ class DesignScanner(RDLListener):
Also collects any information that is required prior to the start of the export process.
"""
def __init__(self, ds:'DesignState') -> None:
def __init__(self, ds: "DesignState") -> None:
self.ds = ds
self.msg = self.top_node.env.msg
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.ds.top_node
def _get_out_of_hier_field_reset(self) -> None:
@@ -28,7 +29,7 @@ class DesignScanner(RDLListener):
current_node = self.top_node.parent
while current_node is not None:
for signal in current_node.signals():
if signal.get_property('field_reset'):
if signal.get_property("field_reset"):
path = signal.get_path()
self.ds.out_of_hier_signals[path] = signal
return
@@ -50,19 +51,19 @@ class DesignScanner(RDLListener):
# Ensure addrmap is not a bridge. This concept does not make sense for
# terminal components.
if self.top_node.get_property('bridge'):
if self.top_node.get_property("bridge"):
self.msg.error(
"Regblock generator does not support exporting bridge address maps",
self.top_node.inst.property_src_ref.get('bridge', self.top_node.inst.inst_src_ref)
"BusDecoder generator does not support exporting bridge address maps",
self.top_node.inst.property_src_ref.get(
"bridge", self.top_node.inst.inst_src_ref
),
)
RDLWalker().walk(self.top_node, self)
if self.msg.had_error:
self.msg.fatal(
"Unable to export due to previous errors"
)
self.msg.fatal("Unable to export due to previous errors")
def enter_Component(self, node: 'Node') -> Optional[WalkerAction]:
def enter_Component(self, node: "Node") -> Optional[WalkerAction]:
if node.external and (node != self.top_node):
# Do not inspect external components. None of my business
return WalkerAction.SkipDescendants
@@ -84,36 +85,42 @@ class DesignScanner(RDLListener):
return WalkerAction.Continue
def enter_AddressableComponent(self, node: 'AddressableNode') -> None:
def enter_AddressableComponent(self, node: "AddressableNode") -> None:
if node.external and node != self.top_node:
self.ds.has_external_addressable = True
if not isinstance(node, RegNode):
self.ds.has_external_block = True
def enter_Reg(self, node: 'RegNode') -> None:
def enter_Reg(self, node: "RegNode") -> None:
# The CPUIF's bus width is sized according to the largest accesswidth in the design
accesswidth = node.get_property('accesswidth')
accesswidth = node.get_property("accesswidth")
self.ds.cpuif_data_width = max(self.ds.cpuif_data_width, accesswidth)
self.ds.has_buffered_write_regs = self.ds.has_buffered_write_regs or bool(node.get_property('buffer_writes'))
self.ds.has_buffered_read_regs = self.ds.has_buffered_read_regs or bool(node.get_property('buffer_reads'))
self.ds.has_buffered_write_regs = self.ds.has_buffered_write_regs or bool(
node.get_property("buffer_writes")
)
self.ds.has_buffered_read_regs = self.ds.has_buffered_read_regs or bool(
node.get_property("buffer_reads")
)
def enter_Signal(self, node: 'SignalNode') -> None:
if node.get_property('field_reset'):
def enter_Signal(self, node: "SignalNode") -> None:
if node.get_property("field_reset"):
path = node.get_path()
self.ds.in_hier_signal_paths.add(path)
def enter_Field(self, node: 'FieldNode') -> None:
def enter_Field(self, node: "FieldNode") -> None:
if node.is_sw_writable and (node.msb < node.lsb):
self.ds.has_writable_msb0_fields = True
if node.get_property('paritycheck') and node.implements_storage:
if node.get_property("paritycheck") and node.implements_storage:
self.ds.has_paritycheck = True
if node.get_property('reset') is None:
if node.get_property("reset") is None:
self.msg.warning(
f"Field '{node.inst_name}' includes parity check logic, but "
"its reset value was not defined. Will result in an undefined "
"value on the module's 'parity_error' output.",
self.top_node.inst.property_src_ref.get('paritycheck', self.top_node.inst.inst_src_ref)
self.top_node.inst.property_src_ref.get(
"paritycheck", self.top_node.inst.inst_src_ref
),
)

View File

@@ -10,33 +10,33 @@ from .utils import roundup_pow2, is_pow2
from .utils import ref_is_internal
if TYPE_CHECKING:
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter
class DesignValidator(RDLListener):
"""
Performs additional rule-checks on the design that check for limitations
imposed by this exporter.
"""
def __init__(self, exp:'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
self.exp = exp
self.ds = exp.ds
self.msg = self.top_node.env.msg
self._contains_external_block_stack = [] # type: List[bool]
self._contains_external_block_stack = [] # type: List[bool]
self.contains_external_block = False
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def do_validate(self) -> None:
RDLWalker().walk(self.top_node, self)
if self.msg.had_error:
self.msg.fatal(
"Unable to export due to previous errors"
)
self.msg.fatal("Unable to export due to previous errors")
def enter_Component(self, node: 'Node') -> Optional[WalkerAction]:
def enter_Component(self, node: "Node") -> Optional[WalkerAction]:
if node.external and (node != self.top_node):
# Do not inspect external components. None of my business
return WalkerAction.SkipDescendants
@@ -49,39 +49,41 @@ class DesignValidator(RDLListener):
if isinstance(value, PropertyReference):
src_ref = value.src_ref
else:
src_ref = node.inst.property_src_ref.get(prop_name, node.inst.inst_src_ref)
src_ref = node.inst.property_src_ref.get(
prop_name, node.inst.inst_src_ref
)
self.msg.error(
"Property is assigned a reference that points to a component not internal to the regblock being exported.",
src_ref
"Property is assigned a reference that points to a component not internal to the busdecoder being exported.",
src_ref,
)
return None
def enter_Signal(self, node: 'SignalNode') -> None:
def enter_Signal(self, node: "SignalNode") -> None:
# If encountering a CPUIF reset that is nested within the register model,
# warn that it will be ignored.
# Only cpuif resets in the top-level node or above will be honored
if node.get_property('cpuif_reset') and (node.parent != self.top_node):
if node.get_property("cpuif_reset") and (node.parent != self.top_node):
self.msg.warning(
"Only cpuif_reset signals that are instantiated in the top-level "
"addrmap or above will be honored. Any cpuif_reset signals nested "
"within children of the addrmap being exported will be ignored.",
node.inst.inst_src_ref
node.inst.inst_src_ref,
)
def enter_AddressableComponent(self, node: 'AddressableNode') -> None:
def enter_AddressableComponent(self, node: "AddressableNode") -> None:
# All registers must be aligned to the internal data bus width
alignment = self.exp.cpuif.data_width_bytes
if (node.raw_address_offset % alignment) != 0:
self.msg.error(
"Unaligned registers are not supported. Address offset of "
f"instance '{node.inst_name}' must be a multiple of {alignment}",
node.inst.inst_src_ref
node.inst.inst_src_ref,
)
if node.is_array and (node.array_stride % alignment) != 0: # type: ignore # is_array implies stride is not none
if node.is_array and (node.array_stride % alignment) != 0: # type: ignore # is_array implies stride is not none
self.msg.error(
"Unaligned registers are not supported. Address stride of "
f"instance array '{node.inst_name}' must be a multiple of {alignment}",
node.inst.inst_src_ref
node.inst.inst_src_ref,
)
if not isinstance(node, RegNode):
@@ -99,49 +101,49 @@ class DesignValidator(RDLListener):
self._check_sharedextbus(node)
def _check_sharedextbus(self, node: Union[RegfileNode, AddrmapNode]) -> None:
if node.get_property('sharedextbus'):
if node.get_property("sharedextbus"):
self.msg.error(
"This exporter does not support enabling the 'sharedextbus' property yet.",
node.inst.property_src_ref.get('sharedextbus', node.inst.inst_src_ref)
node.inst.property_src_ref.get("sharedextbus", node.inst.inst_src_ref),
)
def enter_Reg(self, node: 'RegNode') -> None:
def enter_Reg(self, node: "RegNode") -> None:
# accesswidth of wide registers must be consistent within the register block
accesswidth = node.get_property('accesswidth')
regwidth = node.get_property('regwidth')
accesswidth = node.get_property("accesswidth")
regwidth = node.get_property("regwidth")
if accesswidth < regwidth:
# register is 'wide'
if accesswidth != self.exp.cpuif.data_width:
self.msg.error(
f"Multi-word registers that have an accesswidth ({accesswidth}) "
"that are inconsistent with this regblock's CPU bus width "
"that are inconsistent with this busdecoder's CPU bus width "
f"({self.exp.cpuif.data_width}) are not supported.",
node.inst.inst_src_ref
node.inst.inst_src_ref,
)
def enter_Field(self, node: 'FieldNode') -> None:
parent_accesswidth = node.parent.get_property('accesswidth')
parent_regwidth = node.parent.get_property('regwidth')
if (
(parent_accesswidth < parent_regwidth)
and (node.lsb // parent_accesswidth) != (node.msb // parent_accesswidth)
):
def enter_Field(self, node: "FieldNode") -> None:
parent_accesswidth = node.parent.get_property("accesswidth")
parent_regwidth = node.parent.get_property("regwidth")
if (parent_accesswidth < parent_regwidth) and (
node.lsb // parent_accesswidth
) != (node.msb // parent_accesswidth):
# field spans multiple sub-words
if node.is_sw_writable and not node.parent.get_property('buffer_writes'):
if node.is_sw_writable and not node.parent.get_property("buffer_writes"):
# ... and is writable without the protection of double-buffering
# Enforce 10.6.1-f
self.msg.error(
f"Software-writable field '{node.inst_name}' shall not span"
" multiple software-accessible subwords. Consider enabling"
" write double-buffering.\n"
"For more details, see: https://peakrdl-regblock.readthedocs.io/en/latest/udps/write_buffering.html",
node.inst.inst_src_ref
"For more details, see: https://peakrdl-busdecoder.readthedocs.io/en/latest/udps/write_buffering.html",
node.inst.inst_src_ref,
)
if node.get_property('onread') is not None and not node.parent.get_property('buffer_reads'):
if node.get_property("onread") is not None and not node.parent.get_property(
"buffer_reads"
):
# ... is modified by an onread action without the atomicity of read buffering
# Enforce 10.6.1-f
self.msg.error(
@@ -149,8 +151,8 @@ class DesignValidator(RDLListener):
" subwords and is modified on-read, making it impossible to"
" access its value correctly. Consider enabling read"
" double-buffering. \n"
"For more details, see: https://peakrdl-regblock.readthedocs.io/en/latest/udps/read_buffering.html",
node.inst.inst_src_ref
"For more details, see: https://peakrdl-busdecoder.readthedocs.io/en/latest/udps/read_buffering.html",
node.inst.inst_src_ref,
)
# Check for unsynthesizable reset
@@ -166,10 +168,9 @@ class DesignValidator(RDLListener):
if is_async_reset:
self.msg.error(
"A field that uses an asynchronous reset cannot use a dynamic reset value. This is not synthesizable.",
node.inst.inst_src_ref
node.inst.inst_src_ref,
)
def exit_AddressableComponent(self, node: AddressableNode) -> None:
if not isinstance(node, RegNode):
# Exiting block-like node
@@ -194,14 +195,14 @@ class DesignValidator(RDLListener):
if (node.raw_address_offset % req_align) != 0:
self.msg.error(
f"Address offset +0x{node.raw_address_offset:x} of instance '{node.inst_name}' is not a power of 2 multiple of its size 0x{node.size:x}. "
f"This is required by the regblock exporter if a component {err_suffix}.",
node.inst.inst_src_ref
f"This is required by the busdecoder exporter if a component {err_suffix}.",
node.inst.inst_src_ref,
)
if node.is_array:
assert node.array_stride is not None
if not is_pow2(node.array_stride):
self.msg.error(
f"Address stride of instance array '{node.inst_name}' is not a power of 2"
f"This is required by the regblock exporter if a component {err_suffix}.",
node.inst.inst_src_ref
f"This is required by the busdecoder exporter if a component {err_suffix}.",
node.inst.inst_src_ref,
)

View File

@@ -8,25 +8,23 @@ from ..utils import get_indexed_path
from ..sv_int import SVInt
if TYPE_CHECKING:
from ..exporter import RegblockExporter
from ..exporter import BusDecoderExporter
class WriteBuffering:
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def get_storage_struct(self) -> str:
struct_gen = WBufStorageStructGenerator(self)
s = struct_gen.get_struct(self.top_node, "wbuf_storage_t")
assert s is not None
return s + "\nwbuf_storage_t wbuf_storage;"
def get_implementation(self) -> str:
gen = WBufLogicGenerator(self)
s = gen.get_content(self.top_node)
@@ -43,24 +41,26 @@ class WriteBuffering:
prefix = self.get_wbuf_prefix(node)
return f"{prefix}.pending && {self.get_trigger(node)}"
def get_raw_trigger(self, node: 'RegNode') -> Union[SVInt, str]:
trigger = node.get_property('wbuffer_trigger')
def get_raw_trigger(self, node: "RegNode") -> Union[SVInt, str]:
trigger = node.get_property("wbuffer_trigger")
if isinstance(trigger, RegNode):
# Trigger is a register.
# trigger when uppermost address of the register is written
regwidth = trigger.get_property('regwidth')
accesswidth = trigger.get_property('accesswidth')
strb_prefix = self.exp.dereferencer.get_access_strobe(trigger, reduce_substrobes=False)
regwidth = trigger.get_property("regwidth")
accesswidth = trigger.get_property("accesswidth")
strb_prefix = self.exp.dereferencer.get_access_strobe(
trigger, reduce_substrobes=False
)
if accesswidth < regwidth:
n_subwords = regwidth // accesswidth
return f"{strb_prefix}[{n_subwords-1}] && decoded_req_is_wr"
return f"{strb_prefix}[{n_subwords - 1}] && decoded_req_is_wr"
else:
return f"{strb_prefix} && decoded_req_is_wr"
elif isinstance(trigger, SignalNode):
s = self.exp.dereferencer.get_value(trigger)
if trigger.get_property('activehigh'):
if trigger.get_property("activehigh"):
return s
else:
return f"~{s}"
@@ -71,7 +71,7 @@ class WriteBuffering:
def get_trigger(self, node: Union[RegNode, FieldNode]) -> Union[SVInt, str]:
if isinstance(node, FieldNode):
node = node.parent
trigger = node.get_property('wbuffer_trigger')
trigger = node.get_property("wbuffer_trigger")
if isinstance(trigger, RegNode) and trigger == node:
# register is its own trigger