@@ -1,5 +1,6 @@
|
||||
from typing import TYPE_CHECKING, Dict, Type
|
||||
import functools
|
||||
import sys
|
||||
|
||||
from peakrdl.plugins.exporter import ExporterSubcommandPlugin #pylint: disable=import-error
|
||||
from peakrdl.config import schema #pylint: disable=import-error
|
||||
@@ -94,6 +95,10 @@ class Exporter(ExporterSubcommandPlugin):
|
||||
default=False,
|
||||
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"
|
||||
)
|
||||
arg_group.add_argument(
|
||||
"--type-style",
|
||||
dest="type_style",
|
||||
@@ -125,6 +130,29 @@ class Exporter(ExporterSubcommandPlugin):
|
||||
def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> None:
|
||||
cpuifs = self.get_cpuifs()
|
||||
|
||||
retime_external_reg = False
|
||||
retime_external_regfile = False
|
||||
retime_external_mem = False
|
||||
retime_external_addrmap = False
|
||||
if options.rt_external:
|
||||
for key in options.rt_external.split(","):
|
||||
key = key.strip().lower()
|
||||
if key == "reg":
|
||||
retime_external_reg = True
|
||||
elif key == "regfile":
|
||||
retime_external_regfile = True
|
||||
elif key == "mem":
|
||||
retime_external_mem = True
|
||||
elif key == "addrmap":
|
||||
retime_external_addrmap = True
|
||||
elif key == "all":
|
||||
retime_external_reg = True
|
||||
retime_external_regfile = True
|
||||
retime_external_mem = True
|
||||
retime_external_addrmap = True
|
||||
else:
|
||||
print("error: invalid option for --rt-external: '%s'" % key, file=sys.stderr)
|
||||
|
||||
x = RegblockExporter()
|
||||
x.export(
|
||||
top_node,
|
||||
@@ -135,6 +163,10 @@ class Exporter(ExporterSubcommandPlugin):
|
||||
reuse_hwif_typedefs=(options.type_style == "lexical"),
|
||||
retime_read_fanin=options.rt_read_fanin,
|
||||
retime_read_response=options.rt_read_response,
|
||||
retime_external_reg=retime_external_reg,
|
||||
retime_external_regfile=retime_external_regfile,
|
||||
retime_external_mem=retime_external_mem,
|
||||
retime_external_addrmap=retime_external_addrmap,
|
||||
generate_hwif_report=options.hwif_report,
|
||||
address_width=options.addr_width,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import TYPE_CHECKING, Union, List
|
||||
from typing import TYPE_CHECKING, Union, List, Optional
|
||||
|
||||
from systemrdl.node import AddrmapNode, AddressableNode, RegNode, FieldNode
|
||||
from systemrdl.node import FieldNode, RegNode
|
||||
from systemrdl.walker import WalkerAction
|
||||
|
||||
from .utils import get_indexed_path
|
||||
from .struct_generator import RDLStructGenerator
|
||||
@@ -9,13 +10,15 @@ from .identifier_filter import kw_filter as kwf
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .exporter import RegblockExporter
|
||||
from systemrdl.node import AddrmapNode, AddressableNode
|
||||
from systemrdl.node import RegfileNode, MemNode
|
||||
|
||||
class AddressDecode:
|
||||
def __init__(self, exp:'RegblockExporter'):
|
||||
self.exp = exp
|
||||
|
||||
@property
|
||||
def top_node(self) -> AddrmapNode:
|
||||
def top_node(self) -> 'AddrmapNode':
|
||||
return self.exp.top_node
|
||||
|
||||
def get_strobe_struct(self) -> str:
|
||||
@@ -59,9 +62,57 @@ class AddressDecode:
|
||||
|
||||
return "decoded_reg_strb." + path
|
||||
|
||||
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)
|
||||
return "decoded_reg_strb." + path
|
||||
|
||||
|
||||
class DecodeStructGenerator(RDLStructGenerator):
|
||||
|
||||
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]:
|
||||
if node.external:
|
||||
self._enter_external_block(node)
|
||||
return WalkerAction.SkipDescendants
|
||||
super().enter_Addrmap(node)
|
||||
return WalkerAction.Continue
|
||||
|
||||
def exit_Addrmap(self, node: 'AddrmapNode') -> None:
|
||||
if node.external:
|
||||
return
|
||||
super().exit_Addrmap(node)
|
||||
|
||||
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:
|
||||
if node.external:
|
||||
return
|
||||
super().exit_Regfile(node)
|
||||
|
||||
def enter_Mem(self, node: 'MemNode') -> Optional[WalkerAction]:
|
||||
if node.external:
|
||||
self._enter_external_block(node)
|
||||
return WalkerAction.SkipDescendants
|
||||
super().enter_Mem(node)
|
||||
return WalkerAction.Continue
|
||||
|
||||
def exit_Mem(self, node: 'MemNode') -> None:
|
||||
if node.external:
|
||||
return
|
||||
super().exit_Mem(node)
|
||||
|
||||
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")
|
||||
@@ -89,23 +140,32 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
|
||||
self._array_stride_stack = [] # type: List[List[int]]
|
||||
|
||||
|
||||
def enter_AddressableComponent(self, node: 'AddressableNode') -> None:
|
||||
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
|
||||
super().enter_AddressableComponent(node)
|
||||
|
||||
if not node.is_array:
|
||||
return
|
||||
if node.is_array:
|
||||
# Collect strides for each array dimension
|
||||
current_stride = node.array_stride
|
||||
strides = []
|
||||
for dim in reversed(node.array_dimensions):
|
||||
strides.append(current_stride)
|
||||
current_stride *= dim
|
||||
strides.reverse()
|
||||
self._array_stride_stack.extend(strides)
|
||||
|
||||
# Collect strides for each array dimension
|
||||
current_stride = node.array_stride
|
||||
strides = []
|
||||
for dim in reversed(node.array_dimensions):
|
||||
strides.append(current_stride)
|
||||
current_stride *= dim
|
||||
strides.reverse()
|
||||
self._array_stride_stack.extend(strides)
|
||||
if node.external and not isinstance(node, RegNode):
|
||||
# Is an external block
|
||||
addr_str = self._get_address_str(node)
|
||||
strb = self.addr_decode.get_external_block_access_strobe(node)
|
||||
rhs = f"cpuif_req_masked & (cpuif_addr >= {addr_str}) & (cpuif_addr <= {addr_str} + 'h{(node.size - 1):x})"
|
||||
self.add_content(f"{strb} = {rhs};")
|
||||
self.add_content(f"is_external |= {rhs};")
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
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:
|
||||
a = f"'h{(node.raw_absolute_address - self.addr_decode.top_node.raw_absolute_address + subword_offset):x}"
|
||||
for i, stride in enumerate(self._array_stride_stack):
|
||||
a += f" + i{i}*'h{stride:x}"
|
||||
@@ -117,16 +177,21 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
|
||||
accesswidth = node.get_property('accesswidth')
|
||||
|
||||
if regwidth == accesswidth:
|
||||
s = f"{self.addr_decode.get_access_strobe(node)} = cpuif_req_masked & (cpuif_addr == {self._get_address_str(node)});"
|
||||
rhs = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node)})"
|
||||
s = f"{self.addr_decode.get_access_strobe(node)} = {rhs};"
|
||||
self.add_content(s)
|
||||
if node.external:
|
||||
self.add_content(f"is_external |= {rhs};")
|
||||
else:
|
||||
# Register is wide. Create a substrobe for each subword
|
||||
n_subwords = regwidth // accesswidth
|
||||
subword_stride = accesswidth // 8
|
||||
for i in range(n_subwords):
|
||||
s = f"{self.addr_decode.get_access_strobe(node)}[{i}] = 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:
|
||||
self.add_content(f"is_external |= {rhs};")
|
||||
|
||||
def exit_AddressableComponent(self, node: 'AddressableNode') -> None:
|
||||
super().exit_AddressableComponent(node)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from typing import TYPE_CHECKING, Union, Optional
|
||||
from systemrdl.node import AddrmapNode, FieldNode, SignalNode, RegNode
|
||||
from systemrdl.node import AddrmapNode, FieldNode, SignalNode, RegNode, AddressableNode
|
||||
from systemrdl.rdltypes import PropertyReference
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -205,6 +205,12 @@ class Dereferencer:
|
||||
"""
|
||||
return self.address_decode.get_access_strobe(obj, reduce_substrobes)
|
||||
|
||||
def get_external_block_access_strobe(self, obj: 'AddressableNode') -> str:
|
||||
"""
|
||||
Returns the Verilog string that represents the external block's access strobe
|
||||
"""
|
||||
return self.address_decode.get_external_block_access_strobe(obj)
|
||||
|
||||
def get_resetsignal(self, obj: Optional[SignalNode]) -> str:
|
||||
"""
|
||||
Returns a normalized active-high reset signal
|
||||
|
||||
@@ -18,6 +18,7 @@ from .cpuif.apb4 import APB4_Cpuif
|
||||
from .hwif import Hwif
|
||||
from .write_buffering import WriteBuffering
|
||||
from .read_buffering import ReadBuffering
|
||||
from .external_acks import ExternalWriteAckGenerator, ExternalReadAckGenerator
|
||||
|
||||
class RegblockExporter:
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
@@ -29,12 +30,12 @@ class RegblockExporter:
|
||||
self.top_node = None # type: AddrmapNode
|
||||
self.hwif = None # type: Hwif
|
||||
self.cpuif = None # type: CpuifBase
|
||||
self.address_decode = AddressDecode(self)
|
||||
self.field_logic = FieldLogic(self)
|
||||
self.address_decode = None # type: AddressDecode
|
||||
self.field_logic = None # type: FieldLogic
|
||||
self.readback = None # type: Readback
|
||||
self.write_buffering = WriteBuffering(self)
|
||||
self.read_buffering = ReadBuffering(self)
|
||||
self.dereferencer = Dereferencer(self)
|
||||
self.write_buffering = None # type: WriteBuffering
|
||||
self.read_buffering = None # type: ReadBuffering
|
||||
self.dereferencer = None # type: Dereferencer
|
||||
self.min_read_latency = 0
|
||||
self.min_write_latency = 0
|
||||
|
||||
@@ -97,6 +98,14 @@ class RegblockExporter:
|
||||
response path sequentially may not result in any meaningful timing improvement.
|
||||
|
||||
Enabling this option will increase read transfer latency by 1 clock cycle.
|
||||
retime_external_reg: bool
|
||||
Retime outputs to external ``reg`` components.
|
||||
retime_external_regfile: bool
|
||||
Retime outputs to external ``regfile`` components.
|
||||
retime_external_mem: bool
|
||||
Retime outputs to external ``mem`` components.
|
||||
retime_external_addrmap: bool
|
||||
Retime outputs to external ``addrmap`` components.
|
||||
generate_hwif_report: bool
|
||||
If set, generates a hwif report that can help designers understand
|
||||
the contents of the ``hwif_in`` and ``hwif_out`` structures.
|
||||
@@ -121,7 +130,11 @@ class RegblockExporter:
|
||||
|
||||
# Pipelining options
|
||||
retime_read_fanin = kwargs.pop("retime_read_fanin", False) # type: bool
|
||||
retime_read_response = kwargs.pop("retime_read_response", True) # type: bool
|
||||
retime_read_response = kwargs.pop("retime_read_response", False) # type: bool
|
||||
retime_external_reg = kwargs.pop("retime_external_reg", False) # type: bool
|
||||
retime_external_regfile = kwargs.pop("retime_external_regfile", False) # type: bool
|
||||
retime_external_mem = kwargs.pop("retime_external_mem", False) # type: bool
|
||||
retime_external_addrmap = kwargs.pop("retime_external_addrmap", False) # type: bool
|
||||
|
||||
# Check for stray kwargs
|
||||
if kwargs:
|
||||
@@ -164,11 +177,26 @@ class RegblockExporter:
|
||||
user_enums=scanner.user_enums,
|
||||
reuse_typedefs=reuse_hwif_typedefs,
|
||||
hwif_report_file=hwif_report_file,
|
||||
data_width=scanner.cpuif_data_width,
|
||||
)
|
||||
self.readback = Readback(
|
||||
self,
|
||||
retime_read_fanin
|
||||
retime_read_fanin,
|
||||
scanner.has_external_addressable
|
||||
)
|
||||
self.address_decode = AddressDecode(self)
|
||||
self.field_logic = FieldLogic(
|
||||
self,
|
||||
retime_external_reg=retime_external_reg,
|
||||
retime_external_regfile=retime_external_regfile,
|
||||
retime_external_mem=retime_external_mem,
|
||||
retime_external_addrmap=retime_external_addrmap,
|
||||
)
|
||||
self.write_buffering = WriteBuffering(self)
|
||||
self.read_buffering = ReadBuffering(self)
|
||||
self.dereferencer = Dereferencer(self)
|
||||
ext_write_acks = ExternalWriteAckGenerator(self)
|
||||
ext_read_acks = ExternalReadAckGenerator(self)
|
||||
|
||||
# Validate that there are no unsupported constructs
|
||||
validator = DesignValidator(self)
|
||||
@@ -181,6 +209,8 @@ class RegblockExporter:
|
||||
"has_writable_msb0_fields": scanner.has_writable_msb0_fields,
|
||||
"has_buffered_write_regs": scanner.has_buffered_write_regs,
|
||||
"has_buffered_read_regs": scanner.has_buffered_read_regs,
|
||||
"has_external_addressable": scanner.has_external_addressable,
|
||||
"has_external_block": scanner.has_external_block,
|
||||
"cpuif": self.cpuif,
|
||||
"hwif": self.hwif,
|
||||
"write_buffering": self.write_buffering,
|
||||
@@ -189,8 +219,11 @@ class RegblockExporter:
|
||||
"address_decode": self.address_decode,
|
||||
"field_logic": self.field_logic,
|
||||
"readback": self.readback,
|
||||
"ext_write_acks": ext_write_acks,
|
||||
"ext_read_acks": ext_read_acks,
|
||||
"get_always_ff_event": lambda resetsignal : get_always_ff_event(self.dereferencer, resetsignal),
|
||||
"retime_read_response": retime_read_response,
|
||||
"retime_read_fanin": retime_read_fanin,
|
||||
"min_read_latency": self.min_read_latency,
|
||||
"min_write_latency": self.min_write_latency,
|
||||
"kwf": kwf,
|
||||
|
||||
51
src/peakrdl_regblock/external_acks.py
Normal file
51
src/peakrdl_regblock/external_acks.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from systemrdl.walker import WalkerAction
|
||||
|
||||
from .forloop_generator import RDLForLoopGenerator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .exporter import RegblockExporter
|
||||
from systemrdl.node import AddressableNode
|
||||
|
||||
|
||||
class ExternalWriteAckGenerator(RDLForLoopGenerator):
|
||||
def __init__(self, exp: 'RegblockExporter') -> None:
|
||||
super().__init__()
|
||||
self.exp = exp
|
||||
|
||||
def get_implementation(self) -> str:
|
||||
content = self.get_content(self.exp.top_node)
|
||||
if content is None:
|
||||
return ""
|
||||
return content
|
||||
|
||||
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
|
||||
super().enter_AddressableComponent(node)
|
||||
|
||||
if node.external:
|
||||
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:
|
||||
super().__init__()
|
||||
self.exp = exp
|
||||
|
||||
def get_implementation(self) -> str:
|
||||
content = self.get_content(self.exp.top_node)
|
||||
if content is None:
|
||||
return ""
|
||||
return content
|
||||
|
||||
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
|
||||
super().enter_AddressableComponent(node)
|
||||
|
||||
if node.external:
|
||||
self.add_content(f"rd_ack |= {self.exp.hwif.get_external_rd_ack(node)};")
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
return WalkerAction.Continue
|
||||
@@ -20,8 +20,19 @@ if TYPE_CHECKING:
|
||||
from ..exporter import RegblockExporter
|
||||
|
||||
class FieldLogic:
|
||||
def __init__(self, exp:'RegblockExporter'):
|
||||
def __init__(
|
||||
self,
|
||||
exp:'RegblockExporter',
|
||||
retime_external_reg: bool,
|
||||
retime_external_regfile: bool,
|
||||
retime_external_mem: bool,
|
||||
retime_external_addrmap: bool,
|
||||
):
|
||||
self.exp = exp
|
||||
self.retime_external_reg = retime_external_reg
|
||||
self.retime_external_regfile = retime_external_regfile
|
||||
self.retime_external_mem = retime_external_mem
|
||||
self.retime_external_addrmap = retime_external_addrmap
|
||||
|
||||
self._hw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
|
||||
self._sw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
from typing import TYPE_CHECKING, List
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from systemrdl.walker import WalkerAction
|
||||
from systemrdl.node import RegNode, RegfileNode, MemNode, AddrmapNode
|
||||
|
||||
from ..struct_generator import RDLStructGenerator
|
||||
from ..forloop_generator import RDLForLoopGenerator
|
||||
from ..utils import get_always_ff_event
|
||||
from ..utils import get_always_ff_event, get_indexed_path
|
||||
from ..identifier_filter import kw_filter as kwf
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import FieldLogic
|
||||
from systemrdl.node import FieldNode, RegNode
|
||||
from systemrdl.node import FieldNode, AddressableNode
|
||||
from .bases import SVLogic
|
||||
|
||||
class CombinationalStructGenerator(RDLStructGenerator):
|
||||
@@ -18,6 +21,12 @@ class CombinationalStructGenerator(RDLStructGenerator):
|
||||
super().__init__()
|
||||
self.field_logic = field_logic
|
||||
|
||||
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
|
||||
super().enter_AddressableComponent(node)
|
||||
|
||||
if node.external:
|
||||
return WalkerAction.SkipDescendants
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Field(self, node: 'FieldNode') -> None:
|
||||
# If a field doesn't implement storage, it is not relevant here
|
||||
@@ -67,6 +76,13 @@ class FieldStorageStructGenerator(RDLStructGenerator):
|
||||
super().__init__()
|
||||
self.field_logic = field_logic
|
||||
|
||||
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
|
||||
super().enter_AddressableComponent(node)
|
||||
|
||||
if node.external:
|
||||
return WalkerAction.SkipDescendants
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Field(self, node: 'FieldNode') -> None:
|
||||
self.push_struct(kwf(node.inst_name))
|
||||
|
||||
@@ -88,14 +104,39 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
||||
self.field_storage_template = self.exp.jj_env.get_template(
|
||||
"field_logic/templates/field_storage.sv"
|
||||
)
|
||||
self.external_reg_template = self.exp.jj_env.get_template(
|
||||
"field_logic/templates/external_reg.sv"
|
||||
)
|
||||
self.external_block_template = self.exp.jj_env.get_template(
|
||||
"field_logic/templates/external_block.sv"
|
||||
)
|
||||
self.intr_fields = [] # type: List[FieldNode]
|
||||
self.halt_fields = [] # type: List[FieldNode]
|
||||
|
||||
|
||||
def enter_Reg(self, node: 'RegNode') -> None:
|
||||
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
|
||||
super().enter_AddressableComponent(node)
|
||||
|
||||
if node.external and not isinstance(node, RegNode):
|
||||
# Is an external block
|
||||
self.assign_external_block_outputs(node)
|
||||
|
||||
# Do not recurse
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Reg(self, node: 'RegNode') -> Optional[WalkerAction]:
|
||||
self.intr_fields = []
|
||||
self.halt_fields = []
|
||||
|
||||
if node.external:
|
||||
self.assign_external_reg_outputs(node)
|
||||
# Do not recurse to fields
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
return WalkerAction.Continue
|
||||
|
||||
|
||||
def enter_Field(self, node: 'FieldNode') -> None:
|
||||
if node.implements_storage:
|
||||
@@ -277,3 +318,49 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
||||
self.add_content(
|
||||
f"assign {output_identifier} = {value};"
|
||||
)
|
||||
|
||||
|
||||
def assign_external_reg_outputs(self, node: 'RegNode') -> None:
|
||||
prefix = "hwif_out." + get_indexed_path(self.exp.top_node, node)
|
||||
strb = self.exp.dereferencer.get_access_strobe(node)
|
||||
|
||||
width = min(self.exp.cpuif.data_width, node.get_property('regwidth'))
|
||||
if width != self.exp.cpuif.data_width:
|
||||
bslice = f"[{width - 1}:0]"
|
||||
else:
|
||||
bslice = ""
|
||||
|
||||
context = {
|
||||
"prefix": prefix,
|
||||
"strb": strb,
|
||||
"bslice": bslice,
|
||||
"retime": self.field_logic.retime_external_reg,
|
||||
"get_always_ff_event": lambda resetsignal : get_always_ff_event(self.exp.dereferencer, resetsignal),
|
||||
"get_resetsignal": self.exp.dereferencer.get_resetsignal,
|
||||
"resetsignal": self.exp.top_node.cpuif_reset,
|
||||
}
|
||||
self.add_content(self.external_reg_template.render(context))
|
||||
|
||||
def assign_external_block_outputs(self, node: 'AddressableNode') -> None:
|
||||
prefix = "hwif_out." + get_indexed_path(self.exp.top_node, node)
|
||||
strb = self.exp.dereferencer.get_external_block_access_strobe(node)
|
||||
addr_width = node.size.bit_length()
|
||||
|
||||
retime = False
|
||||
if isinstance(node, RegfileNode):
|
||||
retime = self.field_logic.retime_external_regfile
|
||||
elif isinstance(node, MemNode):
|
||||
retime = self.field_logic.retime_external_mem
|
||||
elif isinstance(node, AddrmapNode):
|
||||
retime = self.field_logic.retime_external_addrmap
|
||||
|
||||
context = {
|
||||
"prefix": prefix,
|
||||
"strb": strb,
|
||||
"addr_width": addr_width,
|
||||
"retime": retime,
|
||||
"get_always_ff_event": lambda resetsignal : get_always_ff_event(self.exp.dereferencer, resetsignal),
|
||||
"get_resetsignal": self.exp.dereferencer.get_resetsignal,
|
||||
"resetsignal": self.exp.top_node.cpuif_reset,
|
||||
}
|
||||
self.add_content(self.external_block_template.render(context))
|
||||
|
||||
31
src/peakrdl_regblock/field_logic/templates/external_block.sv
Normal file
31
src/peakrdl_regblock/field_logic/templates/external_block.sv
Normal file
@@ -0,0 +1,31 @@
|
||||
{% if retime -%}
|
||||
|
||||
|
||||
always_ff {{get_always_ff_event(resetsignal)}} begin
|
||||
if({{get_resetsignal(resetsignal)}}) begin
|
||||
{{prefix}}.req <= '0;
|
||||
{{prefix}}.addr <= '0;
|
||||
{{prefix}}.req_is_wr <= '0;
|
||||
{{prefix}}.wr_data <= '0;
|
||||
{{prefix}}.wr_biten <= '0;
|
||||
end else begin
|
||||
{{prefix}}.req <= {{strb}};
|
||||
{{prefix}}.addr <= decoded_addr[{{addr_width-1}}:0];
|
||||
{{prefix}}.req_is_wr <= decoded_req_is_wr;
|
||||
{{prefix}}.wr_data <= decoded_wr_data;
|
||||
{{prefix}}.wr_biten <= decoded_wr_biten;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
{%- else -%}
|
||||
|
||||
|
||||
assign {{prefix}}.req = {{strb}};
|
||||
assign {{prefix}}.addr = decoded_addr[{{addr_width-1}}:0];
|
||||
assign {{prefix}}.req_is_wr = decoded_req_is_wr;
|
||||
assign {{prefix}}.wr_data = decoded_wr_data;
|
||||
assign {{prefix}}.wr_biten = decoded_wr_biten;
|
||||
|
||||
|
||||
{%- endif %}
|
||||
28
src/peakrdl_regblock/field_logic/templates/external_reg.sv
Normal file
28
src/peakrdl_regblock/field_logic/templates/external_reg.sv
Normal file
@@ -0,0 +1,28 @@
|
||||
{% if retime -%}
|
||||
|
||||
|
||||
always_ff {{get_always_ff_event(resetsignal)}} begin
|
||||
if({{get_resetsignal(resetsignal)}}) begin
|
||||
{{prefix}}.req <= '0;
|
||||
{{prefix}}.req_is_wr <= '0;
|
||||
{{prefix}}.wr_data <= '0;
|
||||
{{prefix}}.wr_biten <= '0;
|
||||
end else begin
|
||||
{{prefix}}.req <= {{strb}};
|
||||
{{prefix}}.req_is_wr <= decoded_req_is_wr;
|
||||
{{prefix}}.wr_data <= decoded_wr_data{{bslice}};
|
||||
{{prefix}}.wr_biten <= decoded_wr_biten{{bslice}};
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
{%- else -%}
|
||||
|
||||
|
||||
assign {{prefix}}.req = {{strb}};
|
||||
assign {{prefix}}.req_is_wr = decoded_req_is_wr;
|
||||
assign {{prefix}}.wr_data = decoded_wr_data{{bslice}};
|
||||
assign {{prefix}}.wr_biten = decoded_wr_biten{{bslice}};
|
||||
|
||||
|
||||
{%- endif %}
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import TYPE_CHECKING, Union, Set, Dict, Optional, TextIO, Type, List
|
||||
|
||||
from systemrdl.node import AddrmapNode, SignalNode, FieldNode, RegNode
|
||||
from systemrdl.node import AddrmapNode, SignalNode, FieldNode, RegNode, AddressableNode
|
||||
from systemrdl.rdltypes import PropertyReference, UserEnum
|
||||
|
||||
from ..utils import get_indexed_path
|
||||
@@ -25,7 +25,8 @@ class Hwif:
|
||||
self, exp: 'RegblockExporter', package_name: str,
|
||||
user_enums: List[Type[UserEnum]],
|
||||
in_hier_signal_paths: Set[str], out_of_hier_signals: Dict[str, SignalNode],
|
||||
reuse_typedefs: bool, hwif_report_file: Optional[TextIO]
|
||||
reuse_typedefs: bool, hwif_report_file: Optional[TextIO],
|
||||
data_width: int
|
||||
):
|
||||
self.exp = exp
|
||||
self.package_name = package_name
|
||||
@@ -39,6 +40,8 @@ class Hwif:
|
||||
|
||||
self.hwif_report_file = hwif_report_file
|
||||
|
||||
self.data_width = data_width
|
||||
|
||||
if reuse_typedefs:
|
||||
self._gen_in_cls = InputStructGenerator_TypeScope
|
||||
self._gen_out_cls = OutputStructGenerator_TypeScope
|
||||
@@ -162,6 +165,26 @@ class Hwif:
|
||||
|
||||
raise RuntimeError(f"Unhandled reference to: {obj}")
|
||||
|
||||
def get_external_rd_data(self, node: AddressableNode) -> str:
|
||||
"""
|
||||
Returns the identifier string for an external component's rd_data signal
|
||||
"""
|
||||
path = get_indexed_path(self.top_node, node)
|
||||
return "hwif_in." + path + ".rd_data"
|
||||
|
||||
def get_external_rd_ack(self, node: AddressableNode) -> str:
|
||||
"""
|
||||
Returns the identifier string for an external component's rd_ack signal
|
||||
"""
|
||||
path = get_indexed_path(self.top_node, node)
|
||||
return "hwif_in." + path + ".rd_ack"
|
||||
|
||||
def get_external_wr_ack(self, node: AddressableNode) -> str:
|
||||
"""
|
||||
Returns the identifier string for an external component's wr_ack signal
|
||||
"""
|
||||
path = get_indexed_path(self.top_node, node)
|
||||
return "hwif_in." + path + ".wr_ack"
|
||||
|
||||
def get_implied_prop_input_identifier(self, field: FieldNode, prop: str) -> str:
|
||||
assert prop in {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from typing import TYPE_CHECKING, Optional, List, Type
|
||||
|
||||
from systemrdl.node import FieldNode
|
||||
from systemrdl.node import FieldNode, RegNode, AddrmapNode, MemNode
|
||||
from systemrdl.walker import WalkerAction
|
||||
|
||||
from ..struct_generator import RDLFlatStructGenerator
|
||||
from ..identifier_filter import kw_filter as kwf
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from systemrdl.node import Node, SignalNode, RegNode
|
||||
from systemrdl.node import Node, SignalNode, AddressableNode, RegfileNode
|
||||
from . import Hwif
|
||||
from systemrdl.rdltypes import UserEnum
|
||||
|
||||
@@ -65,6 +66,43 @@ class InputStructGenerator_Hier(HWIFStructGenerator):
|
||||
if path in self.hwif.in_hier_signal_paths:
|
||||
self.add_member(kwf(node.inst_name), node.width)
|
||||
|
||||
def _add_external_block_members(self, node: 'AddressableNode') -> None:
|
||||
self.add_member("rd_ack")
|
||||
self.add_member("rd_data", self.hwif.data_width)
|
||||
self.add_member("wr_ack")
|
||||
|
||||
def enter_Addrmap(self, node: 'AddrmapNode') -> None:
|
||||
super().enter_Addrmap(node)
|
||||
if node.external:
|
||||
self._add_external_block_members(node)
|
||||
return WalkerAction.SkipDescendants
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Regfile(self, node: 'RegfileNode') -> None:
|
||||
super().enter_Regfile(node)
|
||||
if node.external:
|
||||
self._add_external_block_members(node)
|
||||
return WalkerAction.SkipDescendants
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Mem(self, node: 'MemNode') -> Optional[WalkerAction]:
|
||||
super().enter_Mem(node)
|
||||
if node.external:
|
||||
self._add_external_block_members(node)
|
||||
return WalkerAction.SkipDescendants
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Reg(self, node: 'RegNode') -> Optional[WalkerAction]:
|
||||
super().enter_Reg(node)
|
||||
if node.external:
|
||||
width = min(self.hwif.data_width, node.get_property('regwidth'))
|
||||
self.add_member("rd_ack")
|
||||
self.add_member("rd_data", width)
|
||||
self.add_member("wr_ack")
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Field(self, node: 'FieldNode') -> None:
|
||||
type_name = self.get_typdef_name(node)
|
||||
self.push_struct(type_name, kwf(node.inst_name))
|
||||
@@ -120,6 +158,47 @@ class OutputStructGenerator_Hier(HWIFStructGenerator):
|
||||
)
|
||||
return f'{base}__out_t'
|
||||
|
||||
def _add_external_block_members(self, node: 'AddressableNode') -> None:
|
||||
self.add_member("req")
|
||||
self.add_member("addr", (node.size - 1).bit_length())
|
||||
self.add_member("req_is_wr")
|
||||
self.add_member("wr_data", self.hwif.data_width)
|
||||
self.add_member("wr_biten", self.hwif.data_width)
|
||||
|
||||
def enter_Addrmap(self, node: 'AddrmapNode') -> None:
|
||||
super().enter_Addrmap(node)
|
||||
if node.external:
|
||||
self._add_external_block_members(node)
|
||||
return WalkerAction.SkipDescendants
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Regfile(self, node: 'RegfileNode') -> None:
|
||||
super().enter_Regfile(node)
|
||||
if node.external:
|
||||
self._add_external_block_members(node)
|
||||
return WalkerAction.SkipDescendants
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Mem(self, node: 'MemNode') -> Optional[WalkerAction]:
|
||||
super().enter_Mem(node)
|
||||
if node.external:
|
||||
self._add_external_block_members(node)
|
||||
return WalkerAction.SkipDescendants
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Reg(self, node: 'RegNode') -> Optional[WalkerAction]:
|
||||
super().enter_Reg(node)
|
||||
if node.external:
|
||||
width = min(self.hwif.data_width, node.get_property('regwidth'))
|
||||
n_subwords = node.get_property("regwidth") // node.get_property("accesswidth")
|
||||
self.add_member("req", n_subwords)
|
||||
self.add_member("req_is_wr")
|
||||
self.add_member("wr_data", width)
|
||||
self.add_member("wr_biten", width)
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Field(self, node: 'FieldNode') -> None:
|
||||
type_name = self.get_typdef_name(node)
|
||||
self.push_struct(type_name, kwf(node.inst_name))
|
||||
@@ -162,6 +241,10 @@ class InputStructGenerator_TypeScope(InputStructGenerator_Hier):
|
||||
else:
|
||||
extra_suffix = ""
|
||||
|
||||
if node.external:
|
||||
# Node generates alternate external signals
|
||||
extra_suffix += "__external"
|
||||
|
||||
return f'{scope_path}__{node.type_name}{extra_suffix}__in_t'
|
||||
|
||||
class OutputStructGenerator_TypeScope(OutputStructGenerator_Hier):
|
||||
@@ -177,6 +260,10 @@ class OutputStructGenerator_TypeScope(OutputStructGenerator_Hier):
|
||||
else:
|
||||
extra_suffix = ""
|
||||
|
||||
if node.external:
|
||||
# Node generates alternate external signals
|
||||
extra_suffix += "__external"
|
||||
|
||||
return f'{scope_path}__{node.type_name}{extra_suffix}__out_t'
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
@@ -40,11 +40,30 @@ module {{module_name}} (
|
||||
{{cpuif.get_implementation()|indent}}
|
||||
|
||||
logic cpuif_req_masked;
|
||||
{%- if has_external_addressable %}
|
||||
logic external_req;
|
||||
logic external_pending;
|
||||
logic external_wr_ack;
|
||||
logic external_rd_ack;
|
||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||
external_pending <= '0;
|
||||
end else begin
|
||||
if(external_req & ~external_wr_ack & ~external_rd_ack) external_pending <= '1;
|
||||
else if(external_wr_ack | external_rd_ack) external_pending <= '0;
|
||||
end
|
||||
end
|
||||
{%- endif %}
|
||||
{% if min_read_latency == min_write_latency %}
|
||||
// Read & write latencies are balanced. Stalls not required
|
||||
{%- if has_external_addressable %}
|
||||
// except if external
|
||||
assign cpuif_req_stall_rd = external_pending;
|
||||
assign cpuif_req_stall_wr = external_pending;
|
||||
{%- else %}
|
||||
assign cpuif_req_stall_rd = '0;
|
||||
assign cpuif_req_stall_wr = '0;
|
||||
assign cpuif_req_masked = cpuif_req;
|
||||
{%- endif %}
|
||||
{%- elif min_read_latency > min_write_latency %}
|
||||
// Read latency > write latency. May need to delay next write that follows a read
|
||||
logic [{{min_read_latency - min_write_latency - 1}}:0] cpuif_req_stall_sr;
|
||||
@@ -57,9 +76,13 @@ module {{module_name}} (
|
||||
cpuif_req_stall_sr <= (cpuif_req_stall_sr >> 'd1);
|
||||
end
|
||||
end
|
||||
{%- if has_external_addressable %}
|
||||
assign cpuif_req_stall_rd = external_pending;
|
||||
assign cpuif_req_stall_wr = cpuif_req_stall_sr[0] | external_pending;
|
||||
{%- else %}
|
||||
assign cpuif_req_stall_rd = '0;
|
||||
assign cpuif_req_stall_wr = cpuif_req_stall_sr[0];
|
||||
assign cpuif_req_masked = cpuif_req & !(cpuif_req_is_wr & cpuif_req_stall_wr);
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
// Write latency > read latency. May need to delay next read that follows a write
|
||||
logic [{{min_write_latency - min_read_latency - 1}}:0] cpuif_req_stall_sr;
|
||||
@@ -72,26 +95,49 @@ module {{module_name}} (
|
||||
cpuif_req_stall_sr <= (cpuif_req_stall_sr >> 'd1);
|
||||
end
|
||||
end
|
||||
{%- if has_external_addressable %}
|
||||
assign cpuif_req_stall_rd = cpuif_req_stall_sr[0] | external_pending;
|
||||
assign cpuif_req_stall_wr = external_pending;
|
||||
{%- else %}
|
||||
assign cpuif_req_stall_rd = cpuif_req_stall_sr[0];
|
||||
assign cpuif_req_stall_wr = '0;
|
||||
assign cpuif_req_masked = cpuif_req & !(!cpuif_req_is_wr & cpuif_req_stall_rd);
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
assign cpuif_req_masked = cpuif_req
|
||||
& !(!cpuif_req_is_wr & cpuif_req_stall_rd)
|
||||
& !(cpuif_req_is_wr & cpuif_req_stall_wr);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Address Decode
|
||||
//--------------------------------------------------------------------------
|
||||
{{address_decode.get_strobe_struct()|indent}}
|
||||
decoded_reg_strb_t decoded_reg_strb;
|
||||
{%- if has_external_addressable %}
|
||||
logic decoded_strb_is_external;
|
||||
{% endif %}
|
||||
{%- if has_external_block %}
|
||||
logic [{{cpuif.addr_width-1}}:0] decoded_addr;
|
||||
{% endif %}
|
||||
logic decoded_req;
|
||||
logic decoded_req_is_wr;
|
||||
logic [{{cpuif.data_width-1}}:0] decoded_wr_data;
|
||||
logic [{{cpuif.data_width-1}}:0] decoded_wr_biten;
|
||||
|
||||
always_comb begin
|
||||
{%- if has_external_addressable %}
|
||||
automatic logic is_external = '0;
|
||||
{% endif %}
|
||||
{{address_decode.get_implementation()|indent(8)}}
|
||||
{%- if has_external_addressable %}
|
||||
decoded_strb_is_external = is_external;
|
||||
external_req = is_external;
|
||||
{% endif %}
|
||||
end
|
||||
|
||||
// Pass down signals to next stage
|
||||
{%- if has_external_block %}
|
||||
assign decoded_addr = cpuif_addr;
|
||||
{% endif %}
|
||||
assign decoded_req = cpuif_req_masked;
|
||||
assign decoded_req_is_wr = cpuif_req_is_wr;
|
||||
assign decoded_wr_data = cpuif_wr_data;
|
||||
@@ -104,10 +150,6 @@ module {{module_name}} (
|
||||
assign decoded_wr_biten_bswap = {<<{decoded_wr_biten}};
|
||||
{%- endif %}
|
||||
|
||||
// Writes are always granted with no error response
|
||||
assign cpuif_wr_ack = decoded_req & decoded_req_is_wr;
|
||||
assign cpuif_wr_err = '0;
|
||||
|
||||
{%- if has_buffered_write_regs %}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -135,9 +177,52 @@ module {{module_name}} (
|
||||
|
||||
{{read_buffering.get_implementation()|indent}}
|
||||
{%- endif %}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Write response
|
||||
//--------------------------------------------------------------------------
|
||||
{%- if has_external_addressable %}
|
||||
always_comb begin
|
||||
automatic logic wr_ack;
|
||||
wr_ack = '0;
|
||||
{{ext_write_acks.get_implementation()|indent(8)}}
|
||||
external_wr_ack = wr_ack;
|
||||
end
|
||||
assign cpuif_wr_ack = external_wr_ack | (decoded_req & decoded_req_is_wr & ~decoded_strb_is_external);
|
||||
{%- else %}
|
||||
assign cpuif_wr_ack = decoded_req & decoded_req_is_wr;
|
||||
{%- endif %}
|
||||
// Writes are always granted with no error response
|
||||
assign cpuif_wr_err = '0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Readback
|
||||
//--------------------------------------------------------------------------
|
||||
{%- if has_external_addressable %}
|
||||
logic readback_external_rd_ack_c;
|
||||
always_comb begin
|
||||
automatic logic rd_ack;
|
||||
rd_ack = '0;
|
||||
{{ext_read_acks.get_implementation()|indent(8)}}
|
||||
readback_external_rd_ack_c = rd_ack;
|
||||
end
|
||||
|
||||
logic readback_external_rd_ack;
|
||||
{%- if retime_read_fanin %}
|
||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||
readback_external_rd_ack <= '0;
|
||||
end else begin
|
||||
readback_external_rd_ack <= readback_external_rd_ack_c;
|
||||
end
|
||||
end
|
||||
|
||||
{%- else %}
|
||||
|
||||
assign readback_external_rd_ack = readback_external_rd_ack_c;
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
logic readback_err;
|
||||
logic readback_done;
|
||||
logic [{{cpuif.data_width-1}}:0] readback_data;
|
||||
@@ -148,14 +233,27 @@ module {{module_name}} (
|
||||
cpuif_rd_ack <= '0;
|
||||
cpuif_rd_data <= '0;
|
||||
cpuif_rd_err <= '0;
|
||||
{%- if has_external_addressable %}
|
||||
external_rd_ack <= '0;
|
||||
{%- endif %}
|
||||
end else begin
|
||||
{%- if has_external_addressable %}
|
||||
external_rd_ack <= readback_external_rd_ack;
|
||||
cpuif_rd_ack <= readback_done | readback_external_rd_ack;
|
||||
{%- else %}
|
||||
cpuif_rd_ack <= readback_done;
|
||||
{%- endif %}
|
||||
cpuif_rd_data <= readback_data;
|
||||
cpuif_rd_err <= readback_err;
|
||||
end
|
||||
end
|
||||
{% else %}
|
||||
{%- if has_external_addressable %}
|
||||
assign external_rd_ack = readback_external_rd_ack;
|
||||
assign cpuif_rd_ack = readback_done | readback_external_rd_ack;
|
||||
{%- else %}
|
||||
assign cpuif_rd_ack = readback_done;
|
||||
{%- endif %}
|
||||
assign cpuif_rd_data = readback_data;
|
||||
assign cpuif_rd_err = readback_err;
|
||||
{%- endif %}
|
||||
|
||||
@@ -9,9 +9,10 @@ if TYPE_CHECKING:
|
||||
from systemrdl.node import AddrmapNode
|
||||
|
||||
class Readback:
|
||||
def __init__(self, exp:'RegblockExporter', do_fanin_stage: bool):
|
||||
def __init__(self, exp:'RegblockExporter', do_fanin_stage: bool, has_external_addressable: bool):
|
||||
self.exp = exp
|
||||
self.do_fanin_stage = do_fanin_stage
|
||||
self.has_external_addressable = has_external_addressable
|
||||
|
||||
@property
|
||||
def top_node(self) -> 'AddrmapNode':
|
||||
@@ -33,6 +34,7 @@ class Readback:
|
||||
"get_always_ff_event": lambda resetsignal : get_always_ff_event(self.exp.dereferencer, resetsignal),
|
||||
"cpuif": self.exp.cpuif,
|
||||
"do_fanin_stage": self.do_fanin_stage,
|
||||
"has_external_addressable": self.has_external_addressable,
|
||||
}
|
||||
|
||||
if self.do_fanin_stage:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from systemrdl.node import RegNode
|
||||
from systemrdl.node import RegNode, AddressableNode
|
||||
from systemrdl.walker import WalkerAction
|
||||
|
||||
from ..forloop_generator import RDLForLoopGenerator, LoopBody
|
||||
|
||||
@@ -77,9 +78,26 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
|
||||
self.current_offset = start_offset + n_regs * dim
|
||||
|
||||
|
||||
def enter_Reg(self, node: RegNode) -> None:
|
||||
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.current_offset += 1
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_Reg(self, node: RegNode) -> WalkerAction:
|
||||
if node.external:
|
||||
self.process_external_reg(node)
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
if not node.has_sw_readable:
|
||||
return
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
accesswidth = node.get_property('accesswidth')
|
||||
regwidth = node.get_property('regwidth')
|
||||
@@ -100,6 +118,19 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
|
||||
else:
|
||||
self.process_reg(node)
|
||||
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
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')
|
||||
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;")
|
||||
else:
|
||||
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
|
||||
|
||||
@@ -35,7 +35,11 @@ always_ff @(posedge clk) begin
|
||||
readback_done_r <= '0;
|
||||
end else begin
|
||||
readback_array_r <= readback_array_c;
|
||||
{%- if has_external_addressable %}
|
||||
readback_done_r <= decoded_req & ~decoded_req_is_wr & ~decoded_strb_is_external;
|
||||
{%- else %}
|
||||
readback_done_r <= decoded_req & ~decoded_req_is_wr;
|
||||
{%- endif %}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,7 +58,11 @@ end
|
||||
// Reduce the array
|
||||
always_comb begin
|
||||
automatic logic [{{cpuif.data_width-1}}:0] readback_data_var;
|
||||
{%- if has_external_addressable %}
|
||||
readback_done = decoded_req & ~decoded_req_is_wr & ~decoded_strb_is_external;
|
||||
{%- else %}
|
||||
readback_done = decoded_req & ~decoded_req_is_wr;
|
||||
{%- endif %}
|
||||
readback_err = '0;
|
||||
readback_data_var = '0;
|
||||
for(int i=0; i<{{array_size}}; i++) readback_data_var |= readback_array[i];
|
||||
|
||||
@@ -2,10 +2,10 @@ from typing import TYPE_CHECKING, Set, Optional, Type, List
|
||||
from collections import OrderedDict
|
||||
|
||||
from systemrdl.walker import RDLListener, RDLWalker, WalkerAction
|
||||
from systemrdl.node import SignalNode
|
||||
from systemrdl.node import SignalNode, RegNode
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from systemrdl.node import Node, RegNode, FieldNode
|
||||
from systemrdl.node import Node, FieldNode, AddressableNode
|
||||
from .exporter import RegblockExporter
|
||||
from systemrdl.rdltypes import UserEnum
|
||||
|
||||
@@ -30,6 +30,9 @@ class DesignScanner(RDLListener):
|
||||
self.has_buffered_write_regs = False
|
||||
self.has_buffered_read_regs = False
|
||||
|
||||
self.has_external_block = False
|
||||
self.has_external_addressable = False
|
||||
|
||||
# Track any referenced enums
|
||||
self.user_enums = [] # type: List[Type[UserEnum]]
|
||||
|
||||
@@ -91,7 +94,13 @@ class DesignScanner(RDLListener):
|
||||
if value not in self.user_enums:
|
||||
self.user_enums.append(value)
|
||||
|
||||
return None
|
||||
return WalkerAction.Continue
|
||||
|
||||
def enter_AddressableComponent(self, node: 'AddressableNode') -> None:
|
||||
if node.external and node != self.exp.top_node:
|
||||
self.has_external_addressable = True
|
||||
if not isinstance(node, RegNode):
|
||||
self.has_external_block = True
|
||||
|
||||
def enter_Reg(self, node: 'RegNode') -> None:
|
||||
# The CPUIF's bus width is sized according to the largest accesswidth in the design
|
||||
|
||||
@@ -9,7 +9,7 @@ from .identifier_filter import kw_filter as kwf
|
||||
if TYPE_CHECKING:
|
||||
from typing import Union
|
||||
|
||||
from systemrdl.node import AddrmapNode, RegfileNode, RegNode, FieldNode, Node
|
||||
from systemrdl.node import AddrmapNode, RegfileNode, RegNode, FieldNode, Node, MemNode
|
||||
|
||||
|
||||
class _StructBase:
|
||||
@@ -144,6 +144,12 @@ class RDLStructGenerator(StructGenerator, RDLListener):
|
||||
def exit_Regfile(self, node: 'RegfileNode') -> None:
|
||||
self.pop_struct()
|
||||
|
||||
def enter_Mem(self, node: 'MemNode') -> None:
|
||||
self.push_struct(kwf(node.inst_name), node.array_dimensions)
|
||||
|
||||
def exit_Mem(self, node: 'MemNode') -> None:
|
||||
self.pop_struct()
|
||||
|
||||
def enter_Reg(self, node: 'RegNode') -> None:
|
||||
self.push_struct(kwf(node.inst_name), node.array_dimensions)
|
||||
|
||||
@@ -228,6 +234,13 @@ class RDLFlatStructGenerator(FlatStructGenerator, RDLListener):
|
||||
def exit_Regfile(self, node: 'RegfileNode') -> None:
|
||||
self.pop_struct()
|
||||
|
||||
def enter_Mem(self, node: 'MemNode') -> None:
|
||||
type_name = self.get_typdef_name(node)
|
||||
self.push_struct(type_name, kwf(node.inst_name), node.array_dimensions)
|
||||
|
||||
def exit_Mem(self, node: 'MemNode') -> None:
|
||||
self.pop_struct()
|
||||
|
||||
def enter_Reg(self, node: 'RegNode') -> None:
|
||||
type_name = self.get_typdef_name(node)
|
||||
self.push_struct(type_name, kwf(node.inst_name), node.array_dimensions)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from typing import TYPE_CHECKING, Optional, List
|
||||
|
||||
from systemrdl.walker import RDLListener, RDLWalker, WalkerAction
|
||||
from systemrdl.rdltypes import PropertyReference
|
||||
from systemrdl.node import Node
|
||||
from systemrdl.node import Node, RegNode, FieldNode, SignalNode, AddressableNode
|
||||
from systemrdl.node import RegfileNode, AddrmapNode
|
||||
|
||||
from .utils import roundup_pow2, is_pow2
|
||||
|
||||
from .utils import ref_is_internal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from systemrdl.node import RegNode, FieldNode, SignalNode, AddressableNode
|
||||
from .exporter import RegblockExporter
|
||||
|
||||
class DesignValidator(RDLListener):
|
||||
@@ -19,6 +21,9 @@ class DesignValidator(RDLListener):
|
||||
self.exp = exp
|
||||
self.msg = exp.top_node.env.msg
|
||||
|
||||
self._contains_external_block_stack = [] # type: List[bool]
|
||||
self.contains_external_block = False
|
||||
|
||||
def do_validate(self) -> None:
|
||||
RDLWalker().walk(self.exp.top_node, self)
|
||||
if self.msg.had_error:
|
||||
@@ -28,10 +33,6 @@ class DesignValidator(RDLListener):
|
||||
|
||||
def enter_Component(self, node: 'Node') -> Optional[WalkerAction]:
|
||||
if node.external and (node != self.exp.top_node):
|
||||
self.msg.error(
|
||||
"Exporter does not support external components",
|
||||
node.inst.inst_src_ref
|
||||
)
|
||||
# Do not inspect external components. None of my business
|
||||
return WalkerAction.SkipDescendants
|
||||
|
||||
@@ -48,8 +49,6 @@ class DesignValidator(RDLListener):
|
||||
"Property is assigned a reference that points to a component not internal to the regblock being exported.",
|
||||
src_ref
|
||||
)
|
||||
|
||||
|
||||
return None
|
||||
|
||||
def enter_Signal(self, node: 'SignalNode') -> None:
|
||||
@@ -80,6 +79,27 @@ class DesignValidator(RDLListener):
|
||||
node.inst.inst_src_ref
|
||||
)
|
||||
|
||||
if not isinstance(node, RegNode):
|
||||
# Entering a block-like node
|
||||
if node == self.exp.top_node:
|
||||
# Ignore top addrmap's external property when entering
|
||||
self._contains_external_block_stack.append(False)
|
||||
else:
|
||||
self._contains_external_block_stack.append(node.external)
|
||||
|
||||
def enter_Regfile(self, node: RegfileNode) -> None:
|
||||
self._check_sharedextbus(node)
|
||||
|
||||
def enter_Addrmap(self, node: AddrmapNode) -> None:
|
||||
self._check_sharedextbus(node)
|
||||
|
||||
def _check_sharedextbus(self, node: Node) -> None:
|
||||
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)
|
||||
)
|
||||
|
||||
def enter_Reg(self, node: 'RegNode') -> None:
|
||||
# accesswidth of wide registers must be consistent within the register block
|
||||
accesswidth = node.get_property('accesswidth')
|
||||
@@ -104,6 +124,16 @@ class DesignValidator(RDLListener):
|
||||
and (node.lsb // parent_accesswidth) != (node.msb // parent_accesswidth)
|
||||
):
|
||||
# field spans multiple sub-words
|
||||
if node.external:
|
||||
# External fields that span multiple subwords is not supported
|
||||
self.msg.error(
|
||||
"External fields that span multiple software-accessible "
|
||||
"subwords are not supported.",
|
||||
node.inst.inst_src_ref
|
||||
)
|
||||
# Skip remaining validation rules for external fields
|
||||
return
|
||||
|
||||
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
|
||||
@@ -126,3 +156,38 @@ class DesignValidator(RDLListener):
|
||||
"For more details, see: https://peakrdl-regblock.readthedocs.io/en/latest/udps/read_buffering.html",
|
||||
node.inst.inst_src_ref
|
||||
)
|
||||
|
||||
def exit_AddressableComponent(self, node: AddressableNode) -> None:
|
||||
if not isinstance(node, RegNode):
|
||||
# Exiting block-like node
|
||||
contains_external_block = self._contains_external_block_stack.pop()
|
||||
|
||||
if self._contains_external_block_stack:
|
||||
# Still in the design. Update stack
|
||||
self._contains_external_block_stack[-1] |= contains_external_block
|
||||
else:
|
||||
# Exiting top addrmap. Resolve final answer
|
||||
self.contains_external_block = contains_external_block
|
||||
|
||||
if contains_external_block:
|
||||
# Check that addressing follows strict alignment rules to allow
|
||||
# for simplified address bit-pruning
|
||||
if node.external:
|
||||
err_suffix = "is external"
|
||||
else:
|
||||
err_suffix = "contains an external addrmap/regfile/mem"
|
||||
|
||||
req_align = roundup_pow2(node.size)
|
||||
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
|
||||
)
|
||||
if node.is_array:
|
||||
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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user