apb4 if working?

This commit is contained in:
Arnav Sacheti
2025-10-20 22:09:25 -07:00
parent f956232a62
commit 395f584f52
9 changed files with 106 additions and 57 deletions

View File

@@ -1,3 +1,5 @@
from typing import overload
from systemrdl.node import AddressableNode from systemrdl.node import AddressableNode
from ...utils import get_indexed_path from ...utils import get_indexed_path
@@ -10,11 +12,11 @@ class APB4Cpuif(BaseCpuif):
def _port_declaration(self, child: AddressableNode) -> str: def _port_declaration(self, child: AddressableNode) -> str:
base = f"apb4_intf.master m_apb_{child.inst_name}" base = f"apb4_intf.master m_apb_{child.inst_name}"
if not child.is_array: if child.array_dimensions is None:
return base return base
if child.current_idx is not None: 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}_{'_'.join(map(str, child.current_idx))} {''.join(f'[{dim}]' for dim in child.array_dimensions)}"
return f"{base} [N_{child.inst_name.upper()}S]" return f"{base} {''.join(f'[{dim}]' for dim in child.array_dimensions)}"
@property @property
def port_declaration(self) -> str: def port_declaration(self) -> str:
@@ -24,38 +26,50 @@ class APB4Cpuif(BaseCpuif):
return ",\n".join(slave_ports + master_ports) return ",\n".join(slave_ports + master_ports)
def signal( @overload
self, def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ...
signal: str, @overload
node: AddressableNode | None = None, def signal(self, signal: str, node: AddressableNode, indexer: str) -> str: ...
) -> str: def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str:
if node is None: if node is None or indexer is None:
# Node is none, so this is a slave signal # Node is none, so this is a slave signal
return f"s_apb.{signal}" return f"s_apb.{signal}"
# Master 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: def fanout(self, node: AddressableNode) -> str:
fanout: dict[str, str] = {} fanout: dict[str, str] = {}
fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PSEL"] = ( fanout[self.signal("PSEL", node, "gi")] = (
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)}" 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[self.signal("PENABLE", node, "gi")] = self.signal("PENABLE")
fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PWRITE"] = ( fanout[self.signal("PWRITE", node, "gi")] = (
f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, 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[self.signal("PADDR", node, "gi")] = self.signal("PADDR")
fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PPROT"] = self.signal("PPROT") fanout[self.signal("PPROT", node, "gi")] = self.signal("PPROT")
fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PWDATA"] = "cpuif_wr_data" fanout[self.signal("PWDATA", node, "gi")] = "cpuif_wr_data"
fanout[f"m_apb_{get_indexed_path(node.parent, node, 'gi')}.PSTRB"] = "cpuif_wr_byte_en" fanout[self.signal("PSTRB", node, "gi")] = "cpuif_wr_byte_en"
return "\n".join(map(lambda kv: f"assign {kv[0]} = {kv[1]};", fanout.items())) 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: dict[str, str] = {}
fanin["cpuif_rd_data"] = self.signal("PRDATA", node) if node is None:
fanin["cpuif_rd_ack"] = self.signal("PREADY", node) fanin["cpuif_rd_ack"] = "'0"
fanin["cpuif_rd_err"] = self.signal("PSLVERR", node) 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())) return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))

View File

@@ -6,19 +6,24 @@
assert_bad_data_width: assert($bits({{cpuif.signal("PWDATA")}}) == {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH) 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); 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 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
{%- endif %} {%- endif %}
assign cpuif_req = {{cpuif.signal("PSELx")}}; assign cpuif_req = {{cpuif.signal("PSEL")}};
assign cpuif_wr_en = {{cpuif.signal("PWRITE")}}; assign cpuif_wr_en = {{cpuif.signal("PWRITE")}};
assign cpuif_rd_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_data = {{cpuif.signal("PWDATA")}};
assign cpuif_wr_byte_en = {{cpuif.signal("PSTRB")}}; assign cpuif_wr_byte_en = {{cpuif.signal("PSTRB")}};
assign {{cpuif.signal("PRDATA")}} = cpuif_rd_data; assign {{cpuif.signal("PRDATA")}} = cpuif_rd_data;
assign {{cpuif.signal("PREADY")}} = cpuif_rd_ack; 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 // Fanout CPU Bus interface signals

View File

@@ -1,12 +1,10 @@
import inspect import inspect
import os import os
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING
import jinja2 as jj import jinja2 as jj
from systemrdl.node import AddressableNode 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 ..utils import clog2, get_indexed_path, is_pow2, roundup_pow2
from .fanin_gen import FaninGenerator from .fanin_gen import FaninGenerator
from .fanout_gen import FanoutGenerator from .fanout_gen import FanoutGenerator
@@ -55,7 +53,7 @@ class BaseCpuif:
the module's definition the module's definition
""" """
array_parameters = [ 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 for child in self.addressable_children
if self.check_is_array(child) if self.check_is_array(child)
] ]
@@ -88,7 +86,7 @@ class BaseCpuif:
jj_env.filters["roundup_pow2"] = roundup_pow2 # type: ignore jj_env.filters["roundup_pow2"] = roundup_pow2 # type: ignore
jj_env.filters["address_slice"] = self.get_address_slice # 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["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 context = { # type: ignore
"cpuif": self, "cpuif": self,
@@ -106,14 +104,11 @@ class BaseCpuif:
return f"({cpuif_addr} - 'h{addr:x})[{clog2(size) - 1}:0]" 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: def fanout(self, node: AddressableNode) -> str:
raise NotImplementedError 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 raise NotImplementedError

View File

@@ -4,9 +4,10 @@ from typing import TYPE_CHECKING
from systemrdl.node import AddressableNode from systemrdl.node import AddressableNode
from systemrdl.walker import WalkerAction from systemrdl.walker import WalkerAction
from ..body import Body, CombinationalBody, ForLoopBody from ..body import Body, CombinationalBody, ForLoopBody, IfBody
from ..design_state import DesignState from ..design_state import DesignState
from ..listener import BusDecoderListener from ..listener import BusDecoderListener
from ..utils import get_indexed_path
if TYPE_CHECKING: if TYPE_CHECKING:
from .base_cpuif import BaseCpuif from .base_cpuif import BaseCpuif
@@ -18,7 +19,10 @@ class FaninGenerator(BusDecoderListener):
self._cpuif = cpuif self._cpuif = cpuif
self._stack: deque[Body] = deque() 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: def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None:
action = super().enter_AddressableComponent(node) action = super().enter_AddressableComponent(node)
@@ -32,7 +36,19 @@ class FaninGenerator(BusDecoderListener):
) )
self._stack.append(fb) 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 return action
def exit_AddressableComponent(self, node: AddressableNode) -> None: def exit_AddressableComponent(self, node: AddressableNode) -> None:

View File

@@ -32,7 +32,9 @@ class FanoutGenerator(BusDecoderListener):
) )
self._stack.append(fb) self._stack.append(fb)
if action == WalkerAction.Continue:
self._stack[-1] += self._cpuif.fanout(node) self._stack[-1] += self._cpuif.fanout(node)
return action return action
def exit_AddressableComponent(self, node: AddressableNode) -> None: def exit_AddressableComponent(self, node: AddressableNode) -> None:

View File

@@ -39,17 +39,20 @@ class DecodeLogicGenerator(BusDecoderListener):
# Initial Stack Conditions # Initial Stack Conditions
self._decode_stack.append(IfBody()) 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 # Generate address bounds
addr_width = self._ds.addr_width addr_width = self._ds.addr_width
l_bound = SVInt( l_bound = SVInt(
node.raw_absolute_address, node.raw_absolute_address,
addr_width, addr_width,
) )
if total_size:
u_bound = l_bound + SVInt(node.total_size, addr_width) 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) 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)] array_stack = array_stack[: -len(node.array_dimensions)]
# Handle arrayed components # Handle arrayed components
@@ -79,7 +82,6 @@ class DecodeLogicGenerator(BusDecoderListener):
conditions: list[str] = [] conditions: list[str] = []
conditions.extend(self.cpuif_addr_predicate(node)) conditions.extend(self.cpuif_addr_predicate(node))
conditions.extend(self.cpuif_prot_predicate(node)) conditions.extend(self.cpuif_prot_predicate(node))
condition = " && ".join(f"({c})" for c in conditions) condition = " && ".join(f"({c})" for c in conditions)
# Generate condition string and manage stack # Generate condition string and manage stack
@@ -111,12 +113,14 @@ class DecodeLogicGenerator(BusDecoderListener):
return return
ifb = self._decode_stack.pop() ifb = self._decode_stack.pop()
if ifb: 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 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;"
)
for _ in node.array_dimensions: for _ in node.array_dimensions:
b = self._decode_stack.pop() b = self._decode_stack.pop()
@@ -135,6 +139,6 @@ class DecodeLogicGenerator(BusDecoderListener):
body = self._decode_stack[-1] body = self._decode_stack[-1]
if isinstance(body, IfBody): if isinstance(body, IfBody):
with body.cm(...) as b: 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) return str(body)

View File

@@ -1,6 +1,6 @@
from collections import deque from collections import deque
from systemrdl.node import AddressableNode, RegNode from systemrdl.node import AddressableNode
from systemrdl.walker import RDLListener, WalkerAction from systemrdl.walker import RDLListener, WalkerAction
from .design_state import DesignState from .design_state import DesignState
@@ -10,13 +10,16 @@ class BusDecoderListener(RDLListener):
def __init__(self, ds: DesignState) -> None: def __init__(self, ds: DesignState) -> None:
self._array_stride_stack: deque[int] = deque() # Tracks nested array strides self._array_stride_stack: deque[int] = deque() # Tracks nested array strides
self._ds = ds self._ds = ds
self._depth = 0
def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None: def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None:
if node.array_dimensions: if node.array_dimensions:
assert node.array_stride is not None, "Array stride should be defined for arrayed components" assert node.array_stride is not None, "Array stride should be defined for arrayed components"
self._array_stride_stack.extend(node.array_dimensions) self._array_stride_stack.extend(node.array_dimensions)
if isinstance(node, RegNode): self._depth += 1
if self._depth > 1:
return WalkerAction.SkipDescendants return WalkerAction.SkipDescendants
return WalkerAction.Continue return WalkerAction.Continue
@@ -25,5 +28,7 @@ class BusDecoderListener(RDLListener):
for _ in node.array_dimensions: for _ in node.array_dimensions:
self._array_stride_stack.pop() self._array_stride_stack.pop()
self._depth -= 1
def __str__(self) -> str: def __str__(self) -> str:
return "" return ""

View File

@@ -22,6 +22,10 @@ class StructGenerator(BusDecoderListener):
def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None: def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None:
action = super().enter_AddressableComponent(node) action = super().enter_AddressableComponent(node)
self._skip = False
if action == WalkerAction.SkipDescendants:
self._skip = True
if node.children(): if node.children():
# Push new body onto stack # Push new body onto stack
body = StructBody(f"cpuif_sel_{node.inst_name}_t", True, True) body = StructBody(f"cpuif_sel_{node.inst_name}_t", True, True)
@@ -34,7 +38,7 @@ class StructGenerator(BusDecoderListener):
if node.children(): if node.children():
body = self._stack.pop() body = self._stack.pop()
if body and isinstance(body, StructBody): if body and isinstance(body, StructBody) and not self._skip:
self._stack.appendleft(body) self._stack.appendleft(body)
type = body.name type = body.name
@@ -42,11 +46,12 @@ class StructGenerator(BusDecoderListener):
if node.array_dimensions: if node.array_dimensions:
for dim in node.array_dimensions: for dim in node.array_dimensions:
name += f"[{dim}]" name = f"[{dim - 1}:0]{name}"
self._stack[-1] += f"{type} {name};" self._stack[-1] += f"{type} {name};"
super().exit_AddressableComponent(node) super().exit_AddressableComponent(node)
def __str__(self) -> str: def __str__(self) -> str:
self._stack[-1] += "logic cpuif_err;"
return "\n".join(map(str, self._stack)) return "\n".join(map(str, self._stack))

View File

@@ -7,7 +7,9 @@ from systemrdl.rdltypes.references import PropertyReference
from .identifier_filter import kw_filter as kwf 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 Get the relative path from top_node to target_node, replacing any unknown
array indexes with incrementing iterators (i0, i1, ...). array indexes with incrementing iterators (i0, i1, ...).
@@ -30,6 +32,7 @@ def get_indexed_path(top_node: Node, target_node: Node, indexer: str = "i") -> s
def kw_filter_repl(m: Match[str]) -> str: def kw_filter_repl(m: Match[str]) -> str:
return kwf(m.group(0)) return kwf(m.group(0))
if not skip_kw_filter:
path = re.sub(r"\w+", kw_filter_repl, path) path = re.sub(r"\w+", kw_filter_repl, path)
return path return path