220 lines
8.2 KiB
Python
220 lines
8.2 KiB
Python
from typing import TYPE_CHECKING, Union, List, Optional
|
|
|
|
from systemrdl.node import FieldNode, RegNode
|
|
from systemrdl.walker import WalkerAction
|
|
|
|
from .utils import get_indexed_path
|
|
from .struct_generator import RDLStructGenerator
|
|
from .forloop_generator import RDLForLoopGenerator
|
|
from .identifier_filter import kw_filter as kwf
|
|
from .sv_int import SVInt
|
|
|
|
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':
|
|
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
|
|
return s
|
|
|
|
def get_implementation(self) -> str:
|
|
gen = DecodeLogicGenerator(self)
|
|
s = gen.get_content(self.top_node)
|
|
assert s is not None
|
|
return s
|
|
|
|
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.
|
|
"""
|
|
if isinstance(node, FieldNode):
|
|
field = node
|
|
path = get_indexed_path(self.top_node, node.parent)
|
|
|
|
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
|
|
sidx_hi = field.msb // accesswidth
|
|
sidx_lo = field.lsb // accesswidth
|
|
if sidx_hi == sidx_lo:
|
|
suffix = f"[{sidx_lo}]"
|
|
else:
|
|
suffix = f"[{sidx_hi}:{sidx_lo}]"
|
|
path += suffix
|
|
|
|
if sidx_hi != sidx_lo and reduce_substrobes:
|
|
return "|decoded_reg_strb." + path
|
|
|
|
else:
|
|
path = get_indexed_path(self.top_node, node)
|
|
|
|
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]:
|
|
assert node.external
|
|
self._enter_external_block(node)
|
|
return WalkerAction.SkipDescendants
|
|
|
|
def exit_Addrmap(self, node: 'AddrmapNode') -> None:
|
|
assert node.external
|
|
|
|
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]:
|
|
assert node.external
|
|
self._enter_external_block(node)
|
|
return WalkerAction.SkipDescendants
|
|
|
|
def exit_Mem(self, node: 'MemNode') -> None:
|
|
assert node.external
|
|
|
|
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")
|
|
|
|
self.add_member(
|
|
kwf(node.inst_name),
|
|
width=n_subwords,
|
|
array_dimensions=node.array_dimensions,
|
|
)
|
|
|
|
# Stub out
|
|
def exit_Reg(self, node: 'RegNode') -> None:
|
|
pass
|
|
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]
|
|
|
|
|
|
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
|
|
super().enter_AddressableComponent(node)
|
|
|
|
if node.array_dimensions:
|
|
assert node.array_stride is not None
|
|
# 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} + {SVInt(node.size - 1, self.addr_decode.exp.ds.addr_width)})"
|
|
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:
|
|
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
|
|
))
|
|
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')
|
|
|
|
if regwidth == accesswidth:
|
|
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:
|
|
readable = node.has_sw_readable
|
|
writable = node.has_sw_writable
|
|
if readable and writable:
|
|
self.add_content(f"is_external |= {rhs};")
|
|
elif readable and not writable:
|
|
self.add_content(f"is_external |= {rhs} & !cpuif_req_is_wr;")
|
|
elif not readable and writable:
|
|
self.add_content(f"is_external |= {rhs} & cpuif_req_is_wr;")
|
|
else:
|
|
raise RuntimeError
|
|
else:
|
|
# Register is wide. Create a substrobe for each subword
|
|
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))})"
|
|
s = f"{self.addr_decode.get_access_strobe(node)}[{i}] = {rhs};"
|
|
self.add_content(s)
|
|
if node.external:
|
|
readable = node.has_sw_readable
|
|
writable = node.has_sw_writable
|
|
if readable and writable:
|
|
self.add_content(f"is_external |= {rhs};")
|
|
elif readable and not writable:
|
|
self.add_content(f"is_external |= {rhs} & !cpuif_req_is_wr;")
|
|
elif not readable and writable:
|
|
self.add_content(f"is_external |= {rhs} & cpuif_req_is_wr;")
|
|
else:
|
|
raise RuntimeError
|
|
|
|
def exit_AddressableComponent(self, node: 'AddressableNode') -> None:
|
|
super().exit_AddressableComponent(node)
|
|
|
|
if not node.array_dimensions:
|
|
return
|
|
|
|
for _ in node.array_dimensions:
|
|
self._array_stride_stack.pop()
|