Refactor cpuif classes to use Interface abstraction (#14)
* Initial plan * Refactor cpuif classes to use Interface abstraction Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Fix type annotation consistency in Interface.signal() Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Add runtime validation and documentation for indexer types Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Remove unused variable in SVInterface.signal() Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Fix master port directions in APB3 and APB4 flat interfaces Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Fix AXI4LiteCpuifFlat and apply code formatting Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * PSELx -> PSEL * cleanup marker warnings --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>
This commit is contained in:
@@ -1,48 +1,37 @@
|
||||
from typing import overload
|
||||
from typing import TYPE_CHECKING, overload
|
||||
|
||||
from systemrdl.node import AddressableNode
|
||||
|
||||
from ...utils import get_indexed_path
|
||||
from ..base_cpuif import BaseCpuif
|
||||
from .apb4_interface import APB4SVInterface
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ...exporter import BusDecoderExporter
|
||||
|
||||
|
||||
class APB4Cpuif(BaseCpuif):
|
||||
template_path = "apb4_tmpl.sv"
|
||||
is_interface = True
|
||||
|
||||
def _port_declaration(self, child: AddressableNode) -> str:
|
||||
base = f"apb4_intf.master m_apb_{child.inst_name}"
|
||||
def __init__(self, exp: "BusDecoderExporter") -> None:
|
||||
super().__init__(exp)
|
||||
self._interface = APB4SVInterface(self)
|
||||
|
||||
# When unrolled, current_idx is set - append it to the name
|
||||
if child.current_idx is not None:
|
||||
base = f"{base}_{'_'.join(map(str, child.current_idx))}"
|
||||
|
||||
# Only add array dimensions if this should be treated as an array
|
||||
if self.check_is_array(child):
|
||||
assert child.array_dimensions is not None
|
||||
return f"{base} {''.join(f'[{dim}]' for dim in child.array_dimensions)}"
|
||||
|
||||
return base
|
||||
@property
|
||||
def is_interface(self) -> bool:
|
||||
return self._interface.is_interface
|
||||
|
||||
@property
|
||||
def port_declaration(self) -> str:
|
||||
"""Returns the port declaration for the APB4 interface."""
|
||||
slave_ports: list[str] = ["apb4_intf.slave s_apb"]
|
||||
master_ports: list[str] = list(map(self._port_declaration, self.addressable_children))
|
||||
|
||||
return ",\n".join(slave_ports + master_ports)
|
||||
return self._interface.get_port_declaration("s_apb", "m_apb_")
|
||||
|
||||
@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_{get_indexed_path(node.parent, node, indexer, skip_kw_filter=True)}.{signal}"
|
||||
return self._interface.signal(signal, node, indexer)
|
||||
|
||||
def fanout(self, node: AddressableNode) -> str:
|
||||
fanout: dict[str, str] = {}
|
||||
|
||||
@@ -1,50 +1,29 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from systemrdl.node import AddressableNode
|
||||
|
||||
from ...utils import get_indexed_path
|
||||
from ..base_cpuif import BaseCpuif
|
||||
from .apb4_interface import APB4FlatInterface
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ...exporter import BusDecoderExporter
|
||||
|
||||
|
||||
class APB4CpuifFlat(BaseCpuif):
|
||||
template_path = "apb4_tmpl.sv"
|
||||
is_interface = False
|
||||
|
||||
def _port_declaration(self, child: AddressableNode) -> list[str]:
|
||||
return [
|
||||
f"input logic {self.signal('PCLK', child)}",
|
||||
f"input logic {self.signal('PRESETn', child)}",
|
||||
f"output logic {self.signal('PSELx', child)}",
|
||||
f"output logic {self.signal('PENABLE', child)}",
|
||||
f"output logic {self.signal('PWRITE', child)}",
|
||||
f"output logic [{self.addr_width - 1}:0] {self.signal('PADDR', child)}",
|
||||
f"output logic [2:0] {self.signal('PPROT', child)}",
|
||||
f"output logic [{self.data_width - 1}:0] {self.signal('PWDATA', child)}",
|
||||
f"output logic [{self.data_width // 8 - 1}:0] {self.signal('PSTRB', child)}",
|
||||
f"input logic [{self.data_width - 1}:0] {self.signal('PRDATA', child)}",
|
||||
f"input logic {self.signal('PREADY', child)}",
|
||||
f"input logic {self.signal('PSLVERR', child)}",
|
||||
]
|
||||
def __init__(self, exp: "BusDecoderExporter") -> None:
|
||||
super().__init__(exp)
|
||||
self._interface = APB4FlatInterface(self)
|
||||
|
||||
@property
|
||||
def is_interface(self) -> bool:
|
||||
return self._interface.is_interface
|
||||
|
||||
@property
|
||||
def port_declaration(self) -> str:
|
||||
slave_ports: list[str] = [
|
||||
f"input logic {self.signal('PCLK')}",
|
||||
f"input logic {self.signal('PRESETn')}",
|
||||
f"input logic {self.signal('PSELx')}",
|
||||
f"input logic {self.signal('PENABLE')}",
|
||||
f"input logic {self.signal('PWRITE')}",
|
||||
f"input logic [{self.addr_width - 1}:0] {self.signal('PADDR')}",
|
||||
f"input logic [2:0] {self.signal('PPROT')}",
|
||||
f"input logic [{self.data_width - 1}:0] {self.signal('PWDATA')}",
|
||||
f"input logic [{self.data_width // 8 - 1}:0] {self.signal('PSTRB')}",
|
||||
f"output logic [{self.data_width - 1}:0] {self.signal('PRDATA')}",
|
||||
f"output logic {self.signal('PREADY')}",
|
||||
f"output logic {self.signal('PSLVERR')}",
|
||||
]
|
||||
master_ports: list[str] = []
|
||||
for child in self.addressable_children:
|
||||
master_ports.extend(self._port_declaration(child))
|
||||
|
||||
return ",\n".join(slave_ports + master_ports)
|
||||
return self._interface.get_port_declaration("s_apb_", "m_apb_")
|
||||
|
||||
def signal(
|
||||
self,
|
||||
@@ -52,27 +31,11 @@ class APB4CpuifFlat(BaseCpuif):
|
||||
node: AddressableNode | None = None,
|
||||
idx: str | int | None = None,
|
||||
) -> str:
|
||||
mapped_signal = "PSELx" if signal == "PSEL" else signal
|
||||
if node is None:
|
||||
# Node is none, so this is a slave signal
|
||||
return f"s_apb_{mapped_signal}"
|
||||
|
||||
# Master signal
|
||||
base = f"m_apb_{node.inst_name}"
|
||||
if not self.check_is_array(node):
|
||||
# Not an array or an unrolled element
|
||||
if node.current_idx is not None:
|
||||
# This is a specific instance of an unrolled array
|
||||
return f"{base}_{mapped_signal}_{'_'.join(map(str, node.current_idx))}"
|
||||
return f"{base}_{mapped_signal}"
|
||||
# Is an array
|
||||
if idx is not None:
|
||||
return f"{base}_{mapped_signal}[{idx}]"
|
||||
return f"{base}_{mapped_signal}[N_{node.inst_name.upper()}S]"
|
||||
return self._interface.signal(signal, node, idx)
|
||||
|
||||
def fanout(self, node: AddressableNode) -> str:
|
||||
fanout: dict[str, str] = {}
|
||||
fanout[self.signal("PSELx", node)] = (
|
||||
fanout[self.signal("PSEL", node)] = (
|
||||
f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'i')}|cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'i')}"
|
||||
)
|
||||
fanout[self.signal("PENABLE", node)] = self.signal("PENABLE")
|
||||
|
||||
60
src/peakrdl_busdecoder/cpuif/apb4/apb4_interface.py
Normal file
60
src/peakrdl_busdecoder/cpuif/apb4/apb4_interface.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""APB4-specific interface implementations."""
|
||||
|
||||
from systemrdl.node import AddressableNode
|
||||
|
||||
from ..interface import FlatInterface, SVInterface
|
||||
|
||||
|
||||
class APB4SVInterface(SVInterface):
|
||||
"""APB4 SystemVerilog interface."""
|
||||
|
||||
def get_interface_type(self) -> str:
|
||||
return "apb4_intf"
|
||||
|
||||
def get_slave_name(self) -> str:
|
||||
return "s_apb"
|
||||
|
||||
def get_master_prefix(self) -> str:
|
||||
return "m_apb_"
|
||||
|
||||
|
||||
class APB4FlatInterface(FlatInterface):
|
||||
"""APB4 flat signal interface."""
|
||||
|
||||
def get_slave_prefix(self) -> str:
|
||||
return "s_apb_"
|
||||
|
||||
def get_master_prefix(self) -> str:
|
||||
return "m_apb_"
|
||||
|
||||
def _get_slave_port_declarations(self, slave_prefix: str) -> list[str]:
|
||||
return [
|
||||
f"input logic {slave_prefix}PCLK",
|
||||
f"input logic {slave_prefix}PRESETn",
|
||||
f"input logic {slave_prefix}PSEL",
|
||||
f"input logic {slave_prefix}PENABLE",
|
||||
f"input logic {slave_prefix}PWRITE",
|
||||
f"input logic [{self.cpuif.addr_width - 1}:0] {slave_prefix}PADDR",
|
||||
f"input logic [2:0] {slave_prefix}PPROT",
|
||||
f"input logic [{self.cpuif.data_width - 1}:0] {slave_prefix}PWDATA",
|
||||
f"input logic [{self.cpuif.data_width // 8 - 1}:0] {slave_prefix}PSTRB",
|
||||
f"output logic [{self.cpuif.data_width - 1}:0] {slave_prefix}PRDATA",
|
||||
f"output logic {slave_prefix}PREADY",
|
||||
f"output logic {slave_prefix}PSLVERR",
|
||||
]
|
||||
|
||||
def _get_master_port_declarations(self, child: AddressableNode, master_prefix: str) -> list[str]:
|
||||
return [
|
||||
f"output logic {self.signal('PCLK', child)}",
|
||||
f"output logic {self.signal('PRESETn', child)}",
|
||||
f"output logic {self.signal('PSEL', child)}",
|
||||
f"output logic {self.signal('PENABLE', child)}",
|
||||
f"output logic {self.signal('PWRITE', child)}",
|
||||
f"output logic [{self.cpuif.addr_width - 1}:0] {self.signal('PADDR', child)}",
|
||||
f"output logic [2:0] {self.signal('PPROT', child)}",
|
||||
f"output logic [{self.cpuif.data_width - 1}:0] {self.signal('PWDATA', child)}",
|
||||
f"output logic [{self.cpuif.data_width // 8 - 1}:0] {self.signal('PSTRB', child)}",
|
||||
f"input logic [{self.cpuif.data_width - 1}:0] {self.signal('PRDATA', child)}",
|
||||
f"input logic {self.signal('PREADY', child)}",
|
||||
f"input logic {self.signal('PSLVERR', child)}",
|
||||
]
|
||||
Reference in New Issue
Block a user