diff --git a/src/peakrdl_busdecoder/cpuif/apb4/apb4_cpuif.py b/src/peakrdl_busdecoder/cpuif/apb4/apb4_cpuif.py index 85b2941..49c59ef 100644 --- a/src/peakrdl_busdecoder/cpuif/apb4/apb4_cpuif.py +++ b/src/peakrdl_busdecoder/cpuif/apb4/apb4_cpuif.py @@ -1,3 +1,5 @@ +from typing import overload + from systemrdl.node import AddressableNode from ...utils import get_indexed_path @@ -10,11 +12,11 @@ class APB4Cpuif(BaseCpuif): def _port_declaration(self, child: AddressableNode) -> str: base = f"apb4_intf.master m_apb_{child.inst_name}" - if not child.is_array: + if child.array_dimensions is None: return base if child.current_idx is not None: - return f"{base}_{'_'.join(map(str, child.current_idx))} [N_{child.inst_name.upper()}S]" - return f"{base} [N_{child.inst_name.upper()}S]" + return f"{base}_{'_'.join(map(str, child.current_idx))} {''.join(f'[{dim}]' for dim in child.array_dimensions)}" + return f"{base} {''.join(f'[{dim}]' for dim in child.array_dimensions)}" @property def port_declaration(self) -> str: @@ -24,38 +26,50 @@ class APB4Cpuif(BaseCpuif): return ",\n".join(slave_ports + master_ports) - def signal( - self, - signal: str, - node: AddressableNode | None = None, - ) -> str: - if node is None: + @overload + def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ... + @overload + def signal(self, signal: str, node: AddressableNode, indexer: str) -> str: ... + def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str: + if node is None or indexer is None: # Node is none, so this is a slave signal return f"s_apb.{signal}" # Master signal - return f"m_apb_{node.inst_name}.{signal}" + return f"m_apb_{get_indexed_path(node.parent, node, indexer, skip_kw_filter=True)}.{signal}" def fanout(self, node: AddressableNode) -> str: fanout: dict[str, str] = {} - fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PSEL"] = ( - f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node)}|cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node)}" + fanout[self.signal("PSEL", node, "gi")] = ( + f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}|cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}" ) - fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PSEL"] = self.signal("PSEL") - fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PWRITE"] = ( + fanout[self.signal("PENABLE", node, "gi")] = self.signal("PENABLE") + fanout[self.signal("PWRITE", node, "gi")] = ( f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}" ) - fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PADDR"] = self.signal("PADDR") - fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PPROT"] = self.signal("PPROT") - fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PWDATA"] = "cpuif_wr_data" - fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PSTRB"] = "cpuif_wr_byte_en" + fanout[self.signal("PADDR", node, "gi")] = self.signal("PADDR") + fanout[self.signal("PPROT", node, "gi")] = self.signal("PPROT") + fanout[self.signal("PWDATA", node, "gi")] = "cpuif_wr_data" + fanout[self.signal("PSTRB", node, "gi")] = "cpuif_wr_byte_en" return "\n".join(map(lambda kv: f"assign {kv[0]} = {kv[1]};", fanout.items())) - def fanin(self, node: AddressableNode) -> str: + def fanin(self, node: AddressableNode | None = None) -> str: fanin: dict[str, str] = {} - fanin["cpuif_rd_data"] = self.signal("PRDATA", node) - fanin["cpuif_rd_ack"] = self.signal("PREADY", node) - fanin["cpuif_rd_err"] = self.signal("PSLVERR", node) + if node is None: + fanin["cpuif_rd_ack"] = "'0" + fanin["cpuif_rd_err"] = "'0" + else: + fanin["cpuif_rd_ack"] = self.signal("PREADY", node, "i") + fanin["cpuif_rd_err"] = self.signal("PSLVERR", node, "i") + + return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items())) + + def readback(self, node: AddressableNode | None = None) -> str: + fanin: dict[str, str] = {} + if node is None: + fanin["cpuif_rd_data"] = "'0" + else: + fanin["cpuif_rd_data"] = self.signal("PRDATA", node, "i") return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items())) diff --git a/src/peakrdl_busdecoder/cpuif/apb4/apb4_tmpl.sv b/src/peakrdl_busdecoder/cpuif/apb4/apb4_tmpl.sv index a115c46..77bec0c 100644 --- a/src/peakrdl_busdecoder/cpuif/apb4/apb4_tmpl.sv +++ b/src/peakrdl_busdecoder/cpuif/apb4/apb4_tmpl.sv @@ -6,19 +6,24 @@ assert_bad_data_width: assert($bits({{cpuif.signal("PWDATA")}}) == {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH) else $error("Interface data width of %0d is incorrect. Shall be %0d bits", $bits({{cpuif.signal("PWDATA")}}), {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH); end + assert_wr_sel: assert (@(posedge {{cpuif.signal("PCLK")}}) {{cpuif.signal("PSEL")}} && {{cpuif.signal("PWRITE")}} |-> ##1 ({{cpuif.signal("PREADY")}} || {{cpuif.signal("PSLVERR")}})) + else $error("APB4 Slave port SEL implies that cpuif_wr_sel must be one-hot encoded"); `endif {%- endif %} -assign cpuif_req = {{cpuif.signal("PSELx")}}; +assign cpuif_req = {{cpuif.signal("PSEL")}}; assign cpuif_wr_en = {{cpuif.signal("PWRITE")}}; assign cpuif_rd_en = !{{cpuif.signal("PWRITE")}}; +assign cpuif_wr_addr = {{cpuif.signal("PADDR")}}; +assign cpuif_rd_addr = {{cpuif.signal("PADDR")}}; + assign cpuif_wr_data = {{cpuif.signal("PWDATA")}}; assign cpuif_wr_byte_en = {{cpuif.signal("PSTRB")}}; assign {{cpuif.signal("PRDATA")}} = cpuif_rd_data; assign {{cpuif.signal("PREADY")}} = cpuif_rd_ack; -assign {{cpuif.signal("PSLVERR")}} = cpuif_rd_err; +assign {{cpuif.signal("PSLVERR")}} = cpuif_rd_err | cpuif_rd_sel.cpuif_err | cpuif_wr_sel.cpuif_err; //-------------------------------------------------------------------------- // Fanout CPU Bus interface signals diff --git a/src/peakrdl_busdecoder/cpuif/base_cpuif.py b/src/peakrdl_busdecoder/cpuif/base_cpuif.py index 852d9bd..65dec48 100644 --- a/src/peakrdl_busdecoder/cpuif/base_cpuif.py +++ b/src/peakrdl_busdecoder/cpuif/base_cpuif.py @@ -1,12 +1,10 @@ import inspect import os -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING import jinja2 as jj from systemrdl.node import AddressableNode -from systemrdl.walker import RDLSteerableWalker -from ..listener import BusDecoderListener from ..utils import clog2, get_indexed_path, is_pow2, roundup_pow2 from .fanin_gen import FaninGenerator from .fanout_gen import FanoutGenerator @@ -55,7 +53,7 @@ class BaseCpuif: the module's definition """ array_parameters = [ - f"parameter N_{child.inst_name.upper()}S = {child.n_elements}" + f"localparam N_{child.inst_name.upper()}S = {child.n_elements}" for child in self.addressable_children if self.check_is_array(child) ] @@ -88,7 +86,7 @@ class BaseCpuif: jj_env.filters["roundup_pow2"] = roundup_pow2 # type: ignore jj_env.filters["address_slice"] = self.get_address_slice # type: ignore jj_env.filters["get_path"] = lambda x: get_indexed_path(self.exp.ds.top_node, x, "i") # type: ignore - jj_env.filters["walk"] = self.walk # type: ignore + jj_env.filters["walk"] = self.exp.walk # type: ignore context = { # type: ignore "cpuif": self, @@ -106,14 +104,11 @@ class BaseCpuif: return f"({cpuif_addr} - 'h{addr:x})[{clog2(size) - 1}:0]" - def walk(self, listener_cls: type[BusDecoderListener], **kwargs: Any) -> str: # noqa: ANN401 - walker = RDLSteerableWalker() - listener = listener_cls(self.exp.ds, **kwargs) - walker.walk(self.exp.ds.top_node, listener, skip_top=True) - return str(listener) - def fanout(self, node: AddressableNode) -> str: raise NotImplementedError - def fanin(self, node: AddressableNode) -> str: + def fanin(self, node: AddressableNode | None = None) -> str: + raise NotImplementedError + + def readback(self, node: AddressableNode | None = None) -> str: raise NotImplementedError diff --git a/src/peakrdl_busdecoder/cpuif/fanin_gen.py b/src/peakrdl_busdecoder/cpuif/fanin_gen.py index 219f820..07b4fba 100644 --- a/src/peakrdl_busdecoder/cpuif/fanin_gen.py +++ b/src/peakrdl_busdecoder/cpuif/fanin_gen.py @@ -4,9 +4,10 @@ from typing import TYPE_CHECKING from systemrdl.node import AddressableNode from systemrdl.walker import WalkerAction -from ..body import Body, CombinationalBody, ForLoopBody +from ..body import Body, CombinationalBody, ForLoopBody, IfBody from ..design_state import DesignState from ..listener import BusDecoderListener +from ..utils import get_indexed_path if TYPE_CHECKING: from .base_cpuif import BaseCpuif @@ -18,7 +19,10 @@ class FaninGenerator(BusDecoderListener): self._cpuif = cpuif self._stack: deque[Body] = deque() - self._stack.append(CombinationalBody()) + cb = CombinationalBody() + cb += cpuif.fanin() + cb += cpuif.readback() + self._stack.append(cb) def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None: action = super().enter_AddressableComponent(node) @@ -32,7 +36,19 @@ class FaninGenerator(BusDecoderListener): ) self._stack.append(fb) - self._stack[-1] += self._cpuif.fanin(node) + if action == WalkerAction.Continue: + ifb = IfBody() + with ifb.cm( + f"cpuif_rd_sel.{get_indexed_path(self._cpuif.exp.ds.top_node, node)} || cpuif_wr_sel.{get_indexed_path(self._cpuif.exp.ds.top_node, node)}" + ) as b: + b += self._cpuif.fanin(node) + self._stack[-1] += ifb + + ifb = IfBody() + with ifb.cm(f"cpuif_rd_sel.{get_indexed_path(self._cpuif.exp.ds.top_node, node)}") as b: + b += self._cpuif.readback(node) + self._stack[-1] += ifb + return action def exit_AddressableComponent(self, node: AddressableNode) -> None: diff --git a/src/peakrdl_busdecoder/cpuif/fanout_gen.py b/src/peakrdl_busdecoder/cpuif/fanout_gen.py index 6b79805..55831df 100644 --- a/src/peakrdl_busdecoder/cpuif/fanout_gen.py +++ b/src/peakrdl_busdecoder/cpuif/fanout_gen.py @@ -32,7 +32,9 @@ class FanoutGenerator(BusDecoderListener): ) self._stack.append(fb) - self._stack[-1] += self._cpuif.fanout(node) + if action == WalkerAction.Continue: + self._stack[-1] += self._cpuif.fanout(node) + return action def exit_AddressableComponent(self, node: AddressableNode) -> None: diff --git a/src/peakrdl_busdecoder/decode_logic_gen.py b/src/peakrdl_busdecoder/decode_logic_gen.py index 8c96cfb..d053426 100644 --- a/src/peakrdl_busdecoder/decode_logic_gen.py +++ b/src/peakrdl_busdecoder/decode_logic_gen.py @@ -39,17 +39,20 @@ class DecodeLogicGenerator(BusDecoderListener): # Initial Stack Conditions self._decode_stack.append(IfBody()) - def cpuif_addr_predicate(self, node: AddressableNode) -> list[str]: + def cpuif_addr_predicate(self, node: AddressableNode, total_size: bool = True) -> list[str]: # Generate address bounds addr_width = self._ds.addr_width l_bound = SVInt( node.raw_absolute_address, addr_width, ) - u_bound = l_bound + SVInt(node.total_size, addr_width) + if total_size: + u_bound = l_bound + SVInt(node.total_size, addr_width) + else: + u_bound = l_bound + SVInt(node.size, addr_width) array_stack = list(self._array_stride_stack) - if node.array_dimensions: + if total_size and node.array_dimensions: array_stack = array_stack[: -len(node.array_dimensions)] # Handle arrayed components @@ -79,7 +82,6 @@ class DecodeLogicGenerator(BusDecoderListener): conditions: list[str] = [] conditions.extend(self.cpuif_addr_predicate(node)) conditions.extend(self.cpuif_prot_predicate(node)) - condition = " && ".join(f"({c})" for c in conditions) # Generate condition string and manage stack @@ -111,12 +113,14 @@ class DecodeLogicGenerator(BusDecoderListener): return ifb = self._decode_stack.pop() - if ifb: - self._decode_stack[-1] += ifb - else: - self._decode_stack[-1] += ( - f"{self._flavor.cpuif_select}.{get_indexed_path(self._ds.top_node, node)} = 1'b1;" - ) + if not ifb and isinstance(ifb, IfBody): + conditions: list[str] = [] + conditions.extend(self.cpuif_addr_predicate(node, total_size=False)) + condition = " && ".join(f"({c})" for c in conditions) + + with ifb.cm(condition) as b: + b += f"{self._flavor.cpuif_select}.{get_indexed_path(self._ds.top_node, node)} = 1'b1;" + self._decode_stack[-1] += ifb for _ in node.array_dimensions: b = self._decode_stack.pop() @@ -135,6 +139,6 @@ class DecodeLogicGenerator(BusDecoderListener): body = self._decode_stack[-1] if isinstance(body, IfBody): with body.cm(...) as b: - b += f"{self._flavor.cpuif_select}.bad_addr = 1'b1;" + b += f"{self._flavor.cpuif_select}.cpuif_err = 1'b1;" return str(body) diff --git a/src/peakrdl_busdecoder/listener.py b/src/peakrdl_busdecoder/listener.py index d459d58..b0623b1 100644 --- a/src/peakrdl_busdecoder/listener.py +++ b/src/peakrdl_busdecoder/listener.py @@ -1,6 +1,6 @@ from collections import deque -from systemrdl.node import AddressableNode, RegNode +from systemrdl.node import AddressableNode from systemrdl.walker import RDLListener, WalkerAction from .design_state import DesignState @@ -10,13 +10,16 @@ class BusDecoderListener(RDLListener): def __init__(self, ds: DesignState) -> None: self._array_stride_stack: deque[int] = deque() # Tracks nested array strides self._ds = ds + self._depth = 0 def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None: if node.array_dimensions: assert node.array_stride is not None, "Array stride should be defined for arrayed components" self._array_stride_stack.extend(node.array_dimensions) - if isinstance(node, RegNode): + self._depth += 1 + + if self._depth > 1: return WalkerAction.SkipDescendants return WalkerAction.Continue @@ -25,5 +28,7 @@ class BusDecoderListener(RDLListener): for _ in node.array_dimensions: self._array_stride_stack.pop() + self._depth -= 1 + def __str__(self) -> str: return "" diff --git a/src/peakrdl_busdecoder/struct_gen.py b/src/peakrdl_busdecoder/struct_gen.py index d7718c0..c225824 100644 --- a/src/peakrdl_busdecoder/struct_gen.py +++ b/src/peakrdl_busdecoder/struct_gen.py @@ -22,6 +22,10 @@ class StructGenerator(BusDecoderListener): def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None: action = super().enter_AddressableComponent(node) + self._skip = False + if action == WalkerAction.SkipDescendants: + self._skip = True + if node.children(): # Push new body onto stack body = StructBody(f"cpuif_sel_{node.inst_name}_t", True, True) @@ -34,7 +38,7 @@ class StructGenerator(BusDecoderListener): if node.children(): body = self._stack.pop() - if body and isinstance(body, StructBody): + if body and isinstance(body, StructBody) and not self._skip: self._stack.appendleft(body) type = body.name @@ -42,11 +46,12 @@ class StructGenerator(BusDecoderListener): if node.array_dimensions: for dim in node.array_dimensions: - name += f"[{dim}]" + name = f"[{dim - 1}:0]{name}" self._stack[-1] += f"{type} {name};" super().exit_AddressableComponent(node) def __str__(self) -> str: + self._stack[-1] += "logic cpuif_err;" return "\n".join(map(str, self._stack)) diff --git a/src/peakrdl_busdecoder/utils.py b/src/peakrdl_busdecoder/utils.py index 0f838c5..eebe174 100644 --- a/src/peakrdl_busdecoder/utils.py +++ b/src/peakrdl_busdecoder/utils.py @@ -7,7 +7,9 @@ from systemrdl.rdltypes.references import PropertyReference from .identifier_filter import kw_filter as kwf -def get_indexed_path(top_node: Node, target_node: Node, indexer: str = "i") -> str: +def get_indexed_path( + top_node: Node, target_node: Node, indexer: str = "i", skip_kw_filter: bool = False +) -> str: """ Get the relative path from top_node to target_node, replacing any unknown array indexes with incrementing iterators (i0, i1, ...). @@ -30,7 +32,8 @@ def get_indexed_path(top_node: Node, target_node: Node, indexer: str = "i") -> s def kw_filter_repl(m: Match[str]) -> str: return kwf(m.group(0)) - path = re.sub(r"\w+", kw_filter_repl, path) + if not skip_kw_filter: + path = re.sub(r"\w+", kw_filter_repl, path) return path