Files
PeakRDL-regblock/peakrdl/regblock/addr_decode.py
2021-07-16 12:43:58 -07:00

129 lines
4.7 KiB
Python

import re
from typing import TYPE_CHECKING, List, Union
from systemrdl.node import Node, AddressableNode, RegNode, FieldNode
if TYPE_CHECKING:
from .exporter import RegblockExporter
class AddressDecode:
def __init__(self, exporter:'RegblockExporter', top_node:AddressableNode):
self.exporter = exporter
self.top_node = top_node
self._indent_level = 0
# List of address strides for each dimension
self._array_stride_stack = []
def get_strobe_struct(self) -> str:
lines = []
self._do_struct(lines, self.top_node, is_top=True)
return "\n".join(lines)
def get_implementation(self) -> str:
lines = []
self._do_address_decode_node(lines, self.top_node)
return "\n".join(lines)
def get_access_strobe(self, node: Union[RegNode, FieldNode]) -> str:
"""
Returns the Verilog string that represents the register/field's access strobe.
"""
if isinstance(node, FieldNode):
node = node.parent
path = node.get_rel_path(self.top_node, empty_array_suffix="[!]")
# replace unknown indexes with incrementing iterators i0, i1, ...
class repl:
def __init__(self):
self.i = 0
def __call__(self, match):
s = f'i{self.i}'
self.i += 1
return s
path = re.sub(r'!', repl(), path)
return "decoded_reg_strb." + path
#---------------------------------------------------------------------------
# Struct generation functions
#---------------------------------------------------------------------------
@property
def _indent(self) -> str:
return " " * self._indent_level
def _get_node_array_suffix(self, node:AddressableNode) -> str:
if node.is_array:
return "".join([f'[{dim}]' for dim in node.array_dimensions])
return ""
def _do_struct(self, lines:List[str], node:AddressableNode, is_top:bool = False) -> None:
if is_top:
lines.append(f"{self._indent}typedef struct {{")
else:
lines.append(f"{self._indent}struct {{")
self._indent_level += 1
for child in node.children():
if isinstance(child, RegNode):
lines.append(f"{self._indent}logic {child.inst_name}{self._get_node_array_suffix(child)};")
elif isinstance(child, AddressableNode):
self._do_struct(lines, child)
self._indent_level -= 1
if is_top:
lines.append(f"{self._indent}}} decoded_reg_strb_t;")
else:
lines.append(f"{self._indent}}} {node.inst_name}{self._get_node_array_suffix(node)};")
#---------------------------------------------------------------------------
# Access strobe generation functions
#---------------------------------------------------------------------------
def _push_array_dims(self, lines:List[str], node:AddressableNode):
if not node.is_array:
return
# 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()
for dim, stride in zip(node.array_dimensions, strides):
iterator = "i%d" % len(self._array_stride_stack)
self._array_stride_stack.append(stride)
lines.append(f"{self._indent}for(int {iterator}=0; {iterator}<{dim}; {iterator}++) begin")
self._indent_level += 1
def _pop_array_dims(self, lines:List[str], node:AddressableNode):
if not node.is_array:
return
for _ in node.array_dimensions:
self._array_stride_stack.pop()
self._indent_level -= 1
lines.append(f"{self._indent}end")
def _get_address_str(self, node:AddressableNode) -> str:
a = "'h%x" % (node.raw_absolute_address - self.top_node.raw_absolute_address)
for i, stride in enumerate(self._array_stride_stack):
a += f" + i{i}*'h{stride:x}"
return a
def _do_address_decode_node(self, lines:List[str], node:AddressableNode) -> None:
for child in node.children():
if isinstance(child, RegNode):
self._push_array_dims(lines, child)
lines.append(f"{self._indent}{self.get_access_strobe(child)} = cpuif_req & (cpuif_addr == {self._get_address_str(child)});")
self._pop_array_dims(lines, child)
elif isinstance(child, AddressableNode):
self._push_array_dims(lines, child)
self._do_address_decode_node(lines, child)
self._pop_array_dims(lines, child)