This commit is contained in:
Arnav Sacheti
2025-10-13 18:39:19 -07:00
parent b4f9eaff71
commit 35015d7051
79 changed files with 2401 additions and 5601 deletions

View File

@@ -0,0 +1,4 @@
from .apb4_cpuif import APB4Cpuif
from .apb4_cpuif_flat import APB4CpuifFlat
__all__ = ["APB4Cpuif", "APB4CpuifFlat"]

View File

@@ -0,0 +1,81 @@
from systemrdl.node import AddressableNode
from ..base_cpuif import BaseCpuif
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}"
if not child.is_array:
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]"
@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)
def signal(
self,
signal: str,
node: AddressableNode | None = None,
idx: str | int | None = None,
) -> str:
"""Returns the signal name for the given signal and node."""
if node is None:
# Node is none, so this is a slave signal
return f"s_apb.{signal}"
# Master signal
base = f"m_apb_{node.inst_name}"
if not node.is_array:
return f"{base}.{signal}"
if node.current_idx is not None:
# This is a specific instance of an array
return f"{base}_{'_'.join(map(str, node.current_idx))}.{signal}"
if idx is not None:
return f"{base}[{idx}].{signal}"
raise ValueError("Must provide an index for arrayed interface signals")
def get_address_predicate(self, node: AddressableNode) -> str:
"""
Returns a SystemVerilog expression that evaluates to true when the
address on the bus matches the address range of the given node.
"""
addr_mask = (1 << self.addr_width) - 1
addr = node.absolute_address & addr_mask
size = node.size
if size == 0:
raise ValueError("Node size must be greater than 0")
if (addr % size) != 0:
raise ValueError("Node address must be aligned to its size")
# Calculate the address range of the node
addr_start = addr
addr_end = addr + size - 1
if addr_end > addr_mask:
raise ValueError("Node address range exceeds address width")
if addr_start == addr_end:
return f"({self.signal('PADDR')} == 'h{addr_start:X})"
return f"({self.signal('PADDR')} >= 'h{addr_start:X} && {self.signal('PADDR')} <= 'h{addr_end:X})"
def get_address_decode_condition(self, node: AddressableNode) -> str:
"""
Returns a SystemVerilog expression that evaluates to true when the
address on the bus matches the address range of the given node.
"""
addr_pred = self.get_address_predicate(node)
return addr_pred

View File

@@ -0,0 +1,66 @@
from systemrdl.node import AddressableNode
from ..base_cpuif import BaseCpuif
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"input logic {self.signal('PSELx', child)}",
f"input logic {self.signal('PENABLE', child)}",
f"input logic {self.signal('PWRITE', child)}",
f"input logic [{self.addr_width - 1}:0] {self.signal('PADDR', child)}",
f"input logic [2:0] {self.signal('PPROT', child)}",
f"input logic [{self.data_width - 1}:0] {self.signal('PWDATA', child)}",
f"input logic [{self.data_width // 8 - 1}:0] {self.signal('PSTRB', child)}",
f"output logic [{self.data_width - 1}:0] {self.signal('PRDATA', child)}",
f"output logic {self.signal('PREADY', child)}",
f"output logic {self.signal('PSLVERR', child)}",
]
@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)
def signal(
self,
signal: str,
node: AddressableNode | None = None,
idx: str | int | None = None,
) -> str:
if node is None:
# Node is none, so this is a slave signal
return f"s_apb_{signal}"
# Master signal
base = f"m_apb_{node.inst_name}"
if not node.is_array:
return f"{base}_{signal}"
if node.current_idx is not None:
# This is a specific instance of an array
return f"{base}_{signal}_{'_'.join(map(str, node.current_idx))}"
if idx is not None:
return f"{base}_{signal}[{idx}]"
return f"{base}_{signal}[N_{node.inst_name.upper()}S]"

View File

@@ -0,0 +1,119 @@
{%- if cpuif.is_interface -%}
`ifndef SYNTHESIS
initial begin
assert_bad_addr_width: assert($bits({{cpuif.signal("PADDR")}}) >= {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH)
else $error("Interface address width of %0d is too small. Shall be at least %0d bits", $bits({{cpuif.signal("PADDR")}}), {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_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);
end
`endif
{% endif -%}
//======================================================
// APB Fanout
//======================================================
{%- for child in cpuif.addressable_children -%}
{%- if child is array -%}
for (genvar g_{{child.inst_name|lower}}_idx = 0; g_{{child.inst_name|lower}}_idx < N_{{child.inst_name|upper}}S; g_{{child.inst_name|lower}}_idx++) begin : g_passthrough_{{child.inst_name|lower}}
assign {{self.signal("PCLK", child, f"g_{child.inst_name.lower()}_idx")}} = {{self.signal("PCLK")}};
assign {{self.signal("PRESETn", child, f"g_{child.inst_name.lower()}_idx")}} = {{self.signal("PRESETn")}};
assign {{self.signal("PENABLE", child, f"g_{child.inst_name.lower()}_idx")}} = {{self.signal("PENABLE")}};
assign {{self.signal("PWRITE", child, f"g_{child.inst_name.lower()}_idx")}} = {{self.signal("PWRITE")}};
assign {{self.signal("PADDR", child, f"g_{child.inst_name.lower()}_idx")}} = {{self.signal("PADDR")}} [{{child.addr_width - 1}}:0]; // FIXME: Check slicing
assign {{self.signal("PPROT", child, f"g_{child.inst_name.lower()}_idx")}} = {{self.signal("PPROT")}};
assign {{self.signal("PWDATA", child, f"g_{child.inst_name.lower()}_idx")}} = {{self.signal("PWDATA")}};
assign {{self.signal("PSTRB", child, f"g_{child.inst_name.lower()}_idx")}} = {{self.signal("PSTRB")}};
end
{%- else -%}
assign {{self.signal("PCLK", child)}} = {{self.signal("PCLK")}};
assign {{self.signal("PRESETn", child)}} = {{self.signal("PRESETn")}};
assign {{self.signal("PENABLE", child)}} = {{self.signal("PENABLE")}};
assign {{self.signal("PWRITE", child)}} = {{self.signal("PWRITE")}};
assign {{self.signal("PADDR", child)}} = {{self.signal("PADDR")}} [{{child.addr_width - 1}}:0]; // FIXME: Check slicing
assign {{self.signal("PPROT", child)}} = {{self.signal("PPROT")}};
assign {{self.signal("PWDATA", child)}} = {{self.signal("PWDATA")}};
assign {{self.signal("PSTRB", child)}} = {{self.signal("PSTRB")}};
{%- endif -%}
{%- endfor -%}
//======================================================
// Address Decode Logic
//======================================================
always_comb begin
// Default all PSELx signals to 0
{%- for child in cpuif.addressable_children -%}
{%- if child is array -%}
for (int {{child.inst_name|lower}}_idx = 0; {{child.inst_name|lower}}_idx < N_{{child.inst_name|upper}}S; {{child.inst_name|lower}}_idx++) begin
{{self.signal("PSELx", child, f"{child.inst_name.lower()}_idx")}} = 1'b0;
end
{%- else -%}
{{self.signal("PSELx", child)}} = 1'b0;
{%- endif -%}
{%- endfor -%}
if ({{self.signal("PSELx")}}) begin
{%- for child in cpuif.addressable_children -%}
{%- if loop.first -%}
if ({{cpuif.get_address_decode_condition(child)}}) begin
{%- else -%}
end else if ({{cpuif.get_address_decode_condition(child)}}) begin
{%- endif -%}
// Address matched for {{child.inst_name}}
{%- if child is array -%}
for (genvar {{child.inst_name|lower}}_idx = 0; {{child.inst_name|lower}}_idx < N_{{child.inst_name|upper}}S; {{child.inst_name|lower}}_idx++) begin
{{self.signal("PSELx", child, f"{child.inst_name.lower()}_idx")}} = 1'b1;
end
{%- else -%}
{{self.signal("PSELx", child)}} = 1'b1;
{%- endif -%}
{%- if loop.last -%}
end else begin
// No address matched
{%- endif -%}
{%- endfor -%}
end
end else begin
// PSELx is low, nothing to do
end
end
//======================================================
// Read Data Mux
//======================================================
always_comb begin
// Default read data to 0
{{self.signal("PRDATA")}} = '0;
{{self.signal("PREADY")}} = 1'b1;
{{self.signal("PSLVERR")}} = 1'b0;
if ({{self.signal("PSELx")}} && !{{self.signal("PWRITE")}} && {{self.signal("PENABLE")}}) begin
{%- for child in cpuif.addressable_children -%}
{%- if loop.first -%}
if ({{cpuif.get_address_decode_condition(child)}}) begin
{%- else -%}
end else if ({{cpuif.get_address_decode_condition(child)}}) begin
{%- endif -%}
// Address matched for {{child.inst_name}}
{%- if child is array -%}
for (genvar {{child.inst_name|lower}}_idx = 0; {{child.inst_name|lower}}_idx < N_{{child.inst_name|upper}}S; {{child.inst_name|lower}}_idx++) begin
{{self.signal("PRDATA")}} = {{self.signal("PRDATA", child, f"{child.inst_name.lower()}_idx")}};
{{self.signal("PREADY")}} = {{self.signal("PREADY", child, f"{child.inst_name.lower()}_idx")}};
{{self.signal("PSLVERR")}} = {{self.signal("PSLVERR", child, f"{child.inst_name.lower()}_idx")}};
end
{%- else -%}
{{self.signal("PRDATA")}} = {{self.signal("PRDATA", child)}};
{{self.signal("PREADY")}} = {{self.signal("PREADY", child)}};
{{self.signal("PSLVERR")}} = {{self.signal("PSLVERR", child)}};
{%- endif -%}
{%- if loop.last -%}
end else begin
// No address matched
{{self.signal("PRDATA")}} = {'hdeadbeef}[{{ds.data_width - 1}}:0]; // Indicate error on no match
{{self.signal("PSLVERR")}} = 1'b1; // Indicate error on no match
end
{%- endif -%}
{%- endfor -%}
end else begin
// Not a read transfer, nothing to do
end
end