decoder compiles

This commit is contained in:
Arnav Sacheti
2025-10-19 00:06:45 -07:00
parent 0c66453ba0
commit bb1ac6bde9
21 changed files with 335 additions and 416 deletions

View File

@@ -65,13 +65,13 @@ class Exporter(ExporterSubcommandPlugin):
return cpuifs return cpuifs
def add_exporter_arguments(self, arg_group: "argparse._ActionsContainer") -> None: def add_exporter_arguments(self, arg_group: "argparse.ArgumentParser") -> None: # type: ignore
cpuifs = self.get_cpuifs() cpuifs = self.get_cpuifs()
arg_group.add_argument( arg_group.add_argument(
"--cpuif", "--cpuif",
choices=cpuifs.keys(), choices=cpuifs.keys(),
default="apb3", default="apb4",
help="Select the CPU interface protocol to use [apb3]", help="Select the CPU interface protocol to use [apb3]",
) )

View File

@@ -1,15 +1,12 @@
from .body import Body, SupportsStr from .body import Body, SupportsStr
from .for_loop_body import ForLoopBody
from .while_loop_body import WhileLoopBody
from .if_body import IfBody
from .combinational_body import CombinationalBody from .combinational_body import CombinationalBody
from .for_loop_body import ForLoopBody
from .if_body import IfBody
__all__ = [ __all__ = [
"Body", "Body",
"SupportsStr",
"ForLoopBody",
"WhileLoopBody",
"IfBody",
"CombinationalBody", "CombinationalBody",
"ForLoopBody",
"IfBody",
"SupportsStr",
] ]

View File

@@ -15,3 +15,6 @@ class Body:
def __add__(self, other: SupportsStr) -> Self: def __add__(self, other: SupportsStr) -> Self:
self.lines.append(other) self.lines.append(other)
return self return self
def __bool__(self) -> bool:
return bool(self.lines)

View File

@@ -1,8 +1,10 @@
from textwrap import indent from textwrap import indent
from .body import Body from .body import Body
class CombinationalBody(Body): class CombinationalBody(Body):
def __str__(self) -> str: def __str__(self) -> str:
return f"""always_comb begin return f"""always_comb begin
{indent(super().__str__(), "\t")} {indent(super().__str__(), " ")}
end""" end"""

View File

@@ -1,9 +1,10 @@
from textwrap import indent from textwrap import indent
from .body import Body from .body import Body
class ForLoopBody(Body): class ForLoopBody(Body):
def __init__(self, type: str, iterator: str, dim: int): def __init__(self, type: str, iterator: str, dim: int) -> None:
super().__init__() super().__init__()
self._type = type self._type = type
self._iterator = iterator self._iterator = iterator
@@ -11,5 +12,5 @@ class ForLoopBody(Body):
def __str__(self) -> str: def __str__(self) -> str:
return f"""for ({self._type} {self._iterator} = 0; {self._iterator} < {self._dim}; {self._iterator}++) begin return f"""for ({self._type} {self._iterator} = 0; {self._iterator} < {self._dim}; {self._iterator}++) begin
{indent(super().__str__(), "\t")} {indent(super().__str__(), " ")}
end""" end"""

View File

@@ -1,16 +1,18 @@
from __future__ import annotations from __future__ import annotations
from textwrap import indent
from types import EllipsisType from types import EllipsisType
from typing import Self from typing import Self
from .body import Body, SupportsStr from .body import Body, SupportsStr
class IfBody(Body): class IfBody(Body):
def __init__(self, *, indent: int = 4) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
# (None means 'else') # (None means 'else')
self._branches: list[tuple[SupportsStr | None, Body]] = [] self._branches: list[tuple[SupportsStr | None, Body]] = []
self._has_else = False self._has_else = False
self._indent = " " * indent
# --- Item access: if/else-if via condition; else via Ellipsis/None --- # --- Item access: if/else-if via condition; else via Ellipsis/None ---
def __getitem__(self, condition: SupportsStr | EllipsisType | None) -> Body: def __getitem__(self, condition: SupportsStr | EllipsisType | None) -> Body:
@@ -63,7 +65,7 @@ class IfBody(Body):
) -> bool: ) -> bool:
return False return False
def cm(self, condition: SupportsStr | None) -> "IfBody._BranchCtx": def cm(self, condition: SupportsStr | None) -> IfBody._BranchCtx:
"""Use with: with ifb.cm('cond') as b: ... ; use None for else.""" """Use with: with ifb.cm('cond') as b: ... ; use None for else."""
return IfBody._BranchCtx(self, condition) return IfBody._BranchCtx(self, condition)
@@ -79,6 +81,12 @@ class IfBody(Body):
out.append("else begin") out.append("else begin")
body_str = str(body) body_str = str(body)
if body_str: if body_str:
out.extend(self._indent + ln for ln in body_str.splitlines()) out.extend(indent(ln, " ") for ln in body_str.splitlines())
out.append("end") out.append("end")
return "\n".join(out) return "\n".join(out)
def __len__(self) -> int:
return len(self._branches)
def __bool__(self) -> bool:
return bool(self._branches)

View File

@@ -0,0 +1,15 @@
from textwrap import indent
from .body import Body
class StructBody(Body):
def __init__(self, name: str, packed: bool = True) -> None:
super().__init__()
self._name = name
self._packed = packed
def __str__(self) -> str:
return f"""typedef struct {"packed " if self._packed else ""} {{
{indent(super().__str__(), " ")}
}} {self._name};"""

View File

@@ -1,13 +0,0 @@
from textwrap import indent
from .body import Body
class WhileLoopBody(Body):
def __init__(self, condition: str):
super().__init__()
self._condition = condition
def __str__(self) -> str:
return f"""while ({self._condition}) begin
{indent(super().__str__(), "\t")}
end"""

View File

@@ -42,37 +42,4 @@ class APB3Cpuif(BaseCpuif):
if idx is not None: if idx is not None:
return f"{base}[{idx}].{signal}" return f"{base}[{idx}].{signal}"
raise ValueError("Must provide an index for arrayed interface signals") 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

@@ -16,14 +16,14 @@ assign cpuif_rd_en = !{{cpuif.signal("PWRITE")}};
{%- for child in cpuif.addressable_children -%} {%- for child in cpuif.addressable_children -%}
{%- if child is array -%} {%- if child is array -%}
for (genvar g_idx = 0; g_idx < N_{{child.inst_name|upper}}S; g_idx++) begin : g_passthrough_{{child.inst_name|lower}} for (genvar gi = 0; gi < N_{{child.inst_name|upper}}S; gi++) begin : g_passthrough_{{child.inst_name|lower}}
assign {{cpuif.signal("PCLK", child, "g_idx")}} = {{cpuif.signal("PCLK")}}; assign {{cpuif.signal("PCLK", child, "gi")}} = {{cpuif.signal("PCLK")}};
assign {{cpuif.signal("PRESETn", child, "g_idx")}} = {{cpuif.signal("PRESETn")}}; assign {{cpuif.signal("PRESETn", child, "gi")}} = {{cpuif.signal("PRESETn")}};
assign {{cpuif.signal("PSELx", child, "g_idx")}} = (cpuif_wr_req[{{loop.index}}] || cpuif_rd_req[{{loop.index}}]) ? 1'b1 : 1'b0; assign {{cpuif.signal("PSELx", child, "gi")}} = (cpuif_wr_req[{{loop.index}}] || cpuif_rd_req[{{loop.index}}]) ? 1'b1 : 1'b0;
assign {{cpuif.signal("PENABLE", child, "g_idx")}} = {{cpuif.signal("PENABLE")}}; assign {{cpuif.signal("PENABLE", child, "gi")}} = {{cpuif.signal("PENABLE")}};
assign {{cpuif.signal("PWRITE", child, "g_idx")}} = (cpuif_wr_req[{{loop.index}}]) ? 1'b1 : 1'b0; assign {{cpuif.signal("PWRITE", child, "gi")}} = (cpuif_wr_req[{{loop.index}}]) ? 1'b1 : 1'b0;
assign {{cpuif.signal("PADDR", child, "g_idx")}} = cpuif_wr_addr{{child|address_slice}}; assign {{cpuif.signal("PADDR", child, "gi")}} = {{child|address_slice(cpuif_addr="cpuif_wr_addr")}};
assign {{cpuif.signal("PWDATA", child, "g_idx")}} = cpuif_wr_data; assign {{cpuif.signal("PWDATA", child, "gi")}} = cpuif_wr_data;
assign cpuif_rd_ack[loop.index] = {{cpuif.signal("PREADY", child)}}; assign cpuif_rd_ack[loop.index] = {{cpuif.signal("PREADY", child)}};
assign cpuif_rd_data[loop.index] = {{cpuif.signal("PRDATA", child)}}; assign cpuif_rd_data[loop.index] = {{cpuif.signal("PRDATA", child)}};
assign cpuif_rd_err[loop.index] = {{cpuif.signal("PSLVERR", child)}}; assign cpuif_rd_err[loop.index] = {{cpuif.signal("PSLVERR", child)}};
@@ -31,10 +31,10 @@ end
{%- else -%} {%- else -%}
assign {{cpuif.signal("PCLK", child)}} = {{cpuif.signal("PCLK")}}; assign {{cpuif.signal("PCLK", child)}} = {{cpuif.signal("PCLK")}};
assign {{cpuif.signal("PRESETn", child)}} = {{cpuif.signal("PRESETn")}}; assign {{cpuif.signal("PRESETn", child)}} = {{cpuif.signal("PRESETn")}};
assign {{cpuif.signal("PSELx", child)}} = (cpuif_wr_sel[{{loop.index0}}] || cpuif_rd_sel[{{loop.index0}}]) ? 1'b1 : 1'b0; assign {{cpuif.signal("PSELx", child)}} = (cpuif_wr_sel[{{loop.index0}}] || cpuif_rd_sel[{{loop.index0}}]) ? 1'b1 : 1'b0;
assign {{cpuif.signal("PENABLE", child)}} = {{cpuif.signal("PENABLE")}}; assign {{cpuif.signal("PENABLE", child)}} = {{cpuif.signal("PENABLE")}};
assign {{cpuif.signal("PWRITE", child)}} = (cpuif_wr_req[{{loop.index}}]) ? 1'b1 : 1'b0; assign {{cpuif.signal("PWRITE", child)}} = (cpuif_wr_req[{{loop.index}}]) ? 1'b1 : 1'b0;
assign {{cpuif.signal("PADDR", child)}} = cpuif_wr_addr{{child|address_slice}}; assign {{cpuif.signal("PADDR", child)}} = {{child|address_slice(cpuif_addr="cpuif_wr_addr")}};
assign {{cpuif.signal("PWDATA", child)}} = cpuif_wr_data; assign {{cpuif.signal("PWDATA", child)}} = cpuif_wr_data;
assign cpuif_rd_ack[loop.index] = {{cpuif.signal("PREADY", child)}}; assign cpuif_rd_ack[loop.index] = {{cpuif.signal("PREADY", child)}};
assign cpuif_rd_data[loop.index] = {{cpuif.signal("PRDATA", child)}}; assign cpuif_rd_data[loop.index] = {{cpuif.signal("PRDATA", child)}};

View File

@@ -1,4 +1,5 @@
from systemrdl.node import AddressableNode from systemrdl.node import AddressableNode
from ..base_cpuif import BaseCpuif from ..base_cpuif import BaseCpuif
@@ -44,36 +45,3 @@ class APB4Cpuif(BaseCpuif):
return f"{base}[{idx}].{signal}" return f"{base}[{idx}].{signal}"
raise ValueError("Must provide an index for arrayed interface signals") 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

@@ -1,4 +1,5 @@
from systemrdl.node import AddressableNode from systemrdl.node import AddressableNode
from ..base_cpuif import BaseCpuif from ..base_cpuif import BaseCpuif

View File

@@ -1,4 +1,4 @@
{%- if cpuif.is_interface -%} {%- if cpuif.is_interface %}
`ifndef SYNTHESIS `ifndef SYNTHESIS
initial begin initial begin
assert_bad_addr_width: assert($bits({{cpuif.signal("PADDR")}}) >= {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH) assert_bad_addr_width: assert($bits({{cpuif.signal("PADDR")}}) >= {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH)
@@ -7,55 +7,76 @@
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
`endif `endif
{% endif -%} {%- endif %}
assign cpuif_req = {{cpuif.signal("PSELx")}}; assign cpuif_req = {{cpuif.signal("PSELx")}};
assign cpuif_wr_en = {{cpuif.signal("PWRITE")}}; assign cpuif_wr_en = {{cpuif.signal("PWRITE")}};
assign cpuif_wr_data = {{cpuif.signal("PWDATA")}}; assign cpuif_wr_data = {{cpuif.signal("PWDATA")}};
assign cpuif_rd_en = !{{cpuif.signal("PWRITE")}}; assign cpuif_rd_en = !{{cpuif.signal("PWRITE")}};
{%- for child in cpuif.addressable_children -%} //--------------------------------------------------------------------------
{%- if child is array -%} // Fanout CPU Bus interface signals
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("PSELx", child, f"g_{child.inst_name.lower()}_idx")}} = (cpuif_wr_sel[{{loop.index}}] || cpuif_rd_sel[{{loop.index}}]) ? 1'b1 : 1'b0;
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")}} = (cpuif_wr_sel[{{loop.index}}]) ? 1'b1 : 1'b0;
assign {{self.signal("PADDR", child, f"g_{child.inst_name.lower()}_idx")}} = {{cpuif.get_address_slice(cpuif_wr_addr, child)}};
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")}} = cpuif_wr_data;
assign {{self.signal("PSTRB", child, f"g_{child.inst_name.lower()}_idx")}} = {{self.signal("PSTRB")}};
assign cpuif_rd_ack[loop.index] = {{cpuif.signal("PREADY", child)}};
assign cpuif_rd_data[loop.index] = {{cpuif.signal("PRDATA", child)}};
assign cpuif_rd_err[loop.index] = {{cpuif.signal("PSLVERR", child)}};
end
{%- else -%}
assign {{self.signal("PCLK", child)}} = {{self.signal("PCLK")}};
assign {{self.signal("PRESETn", child)}} = {{self.signal("PRESETn")}};
assign {{self.signal("PSELx", child)}} = (cpuif_wr_sel[{{loop.index0}}] || cpuif_rd_sel[{{loop.index0}}]) ? 1'b1 : 1'b0;
assign {{self.signal("PENABLE", child)}} = {{self.signal("PENABLE")}};
assign {{self.signal("PWRITE", child)}} = cpuif_wr_sel[{{loop.index}}] ? 1'b1 : 1'b0;
assign {{self.signal("PADDR", child)}} = {{cpuif.get_address_slice(cpuif_wr_addr, child)}};
assign {{self.signal("PPROT", child)}} = {{self.signal("PPROT")}};
assign {{self.signal("PWDATA", child)}} = cpuif_wr_data;
assign {{self.signal("PSTRB", child)}} = {{self.signal("PSTRB")}};
assign cpuif_rd_ack[loop.index] = {{cpuif.signal("PREADY", child)}};
assign cpuif_rd_data[loop.index] = {{cpuif.signal("PRDATA", child)}};
assign cpuif_rd_err[loop.index] = {{cpuif.signal("PSLVERR", child)}};
{%- endif -%}
{%- endfor -%}
// {%- for child in cpuif.addressable_children -%}
// {%- if child is array %}
// for (genvar gi = 0; gi < N_{{child.inst_name|upper}}S; gi++) begin : g_passthrough_{{child.inst_name|lower}}
// assign {{cpuif.signal("PCLK", child, "gi")}} = {{cpuif.signal("PCLK")}};
// assign {{cpuif.signal("PRESETn", child, "gi")}} = {{cpuif.signal("PRESETn")}};
// assign {{cpuif.signal("PSELx", child, "gi")}} = (cpuif_wr_sel[{{loop.index}}] || cpuif_rd_sel[{{loop.index}}]) ? 1'b1 : 1'b0;
// assign {{cpuif.signal("PENABLE", child, "gi")}} = {{cpuif.signal("PENABLE")}};
// assign {{cpuif.signal("PWRITE", child, "gi")}} = (cpuif_wr_sel[{{loop.index}}]) ? 1'b1 : 1'b0;
// assign {{cpuif.signal("PADDR", child, "gi")}} = {{child|address_slice(cpuif_addr="cpuif_wr_addr")}};
// assign {{cpuif.signal("PPROT", child, "gi")}} = {{cpuif.signal("PPROT")}};
// assign {{cpuif.signal("PWDATA", child, "gi")}} = cpuif_wr_data;
// assign {{cpuif.signal("PSTRB", child, "gi")}} = {{cpuif.signal("PSTRB")}};
// assign cpuif_rd_ack[{{loop.index}}] = {{cpuif.signal("PREADY", child, "gi")}};
// assign cpuif_rd_data[{{loop.index}}] = {{cpuif.signal("PRDATA", child, "gi")}};
// assign cpuif_rd_err[{{loop.index}}] = {{cpuif.signal("PSLVERR", child, "gi")}};
// end
// {%- else %}
// assign {{cpuif.signal("PCLK", child)}} = {{cpuif.signal("PCLK")}};
// assign {{cpuif.signal("PRESETn", child)}} = {{cpuif.signal("PRESETn")}};
// assign {{cpuif.signal("PSELx", child)}} = (cpuif_wr_sel[{{loop.index}}] || cpuif_rd_sel[{{loop.index}}]) ? 1'b1 : 1'b0;
// assign {{cpuif.signal("PENABLE", child)}} = {{cpuif.signal("PENABLE")}};
// assign {{cpuif.signal("PWRITE", child)}} = cpuif_wr_sel[{{loop.index}}] ? 1'b1 : 1'b0;
// assign {{cpuif.signal("PADDR", child)}} = {{child|address_slice(cpuif_addr="cpuif_wr_addr")}};
// assign {{cpuif.signal("PPROT", child)}} = {{cpuif.signal("PPROT")}};
// assign {{cpuif.signal("PWDATA", child)}} = cpuif_wr_data;
// assign {{cpuif.signal("PSTRB", child)}} = {{cpuif.signal("PSTRB")}};
// assign cpuif_rd_ack[{{loop.index}}] = {{cpuif.signal("PREADY", child)}};
// assign cpuif_rd_data[{{loop.index}}] = {{cpuif.signal("PRDATA", child)}};
// assign cpuif_rd_err[{{loop.index}}] = {{cpuif.signal("PSLVERR", child)}};
// {%- endif -%}
// {%- endfor %}
//--------------------------------------------------------------------------
// Fanin CPU Bus interface signals
//--------------------------------------------------------------------------
always_comb begin always_comb begin
{{cpuif.signal("PREADY")}} = 1'b0; {{cpuif.signal("PREADY")}} = 1'b0;
{{cpuif.signal("PRDATA")}} = '0; {{cpuif.signal("PRDATA")}} = '0;
{{cpuif.signal("PSLVERR")}} = 1'b0; {{cpuif.signal("PSLVERR")}} = 1'b0;
for(int i = 0; i < {{cpuif.addressable_children | length}}; i++) begin
if (cpuif_rd_sel[i]) begin {%- for child in cpuif.addressable_children -%}
{{cpuif.signal("PREADY")}} = cpuif_rd_ack[i]; {%- if loop.first %}
{{cpuif.signal("PRDATA")}} = cpuif_rd_data[i]; if (cpuif_rd_sel.{{child|get_path}}) begin
{{cpuif.signal("PSLVERR")}} = cpuif_rd_err[i]; {%- else %}
end end else if (cpuif_rd_sel.{{child|get_path}}) begin
{%- endif %}
{{cpuif.signal("PREADY")}} = cpuif_rd_ack[{{loop.index}}];
{{cpuif.signal("PRDATA")}} = cpuif_rd_data[{{loop.index}}];
{{cpuif.signal("PSLVERR")}} = cpuif_rd_err[{{loop.index}}];
{%- endfor %}
end end
// for(int i = 0; i < {{cpuif.addressable_children | length}}; i++) begin
// if (cpuif_rd_sel[i]) begin
// {{cpuif.signal("PREADY")}} = cpuif_rd_ack[i];
// {{cpuif.signal("PRDATA")}} = cpuif_rd_data[i];
// {{cpuif.signal("PSLVERR")}} = cpuif_rd_err[i];
// end
// end
end end

View File

@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
import jinja2 as jj import jinja2 as jj
from systemrdl.node import AddressableNode from systemrdl.node import AddressableNode
from ..utils import clog2, is_pow2, roundup_pow2 from ..utils import clog2, get_indexed_path, is_pow2, roundup_pow2
if TYPE_CHECKING: if TYPE_CHECKING:
from ..exporter import BusDecoderExporter from ..exporter import BusDecoderExporter
@@ -83,6 +83,7 @@ class BaseCpuif:
jj_env.filters["is_pow2"] = is_pow2 # type: ignore jj_env.filters["is_pow2"] = is_pow2 # type: ignore
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
context = { context = {
"cpuif": self, "cpuif": self,
@@ -92,33 +93,13 @@ class BaseCpuif:
template = jj_env.get_template(self.template_path) template = jj_env.get_template(self.template_path)
return template.render(context) return template.render(context)
def get_address_slice(self, node: AddressableNode) -> str: def get_address_slice(self, node: AddressableNode, cpuif_addr: str = "cpuif_addr") -> str:
""" addr = node.raw_absolute_address - self.exp.ds.top_node.raw_absolute_address
Returns a SystemVerilog expression that extracts the address bits
relevant to the given node.
"""
addr_mask = (1 << self.addr_width) - 1
addr = node.absolute_address & addr_mask
size = node.size size = node.size
if size == 0:
raise ValueError(f"Node size '{size:#X}' must be greater than 0")
if (addr % size) != 0:
raise ValueError(f"Node address '{addr:#X}' must be aligned to its size '{size:#X}'")
# Calculate the address range of the node addr_msb = clog2(addr + size) - 1
addr_start = addr addr_lsb = clog2(addr)
addr_end = addr + size - 1
if addr_end > addr_mask:
raise ValueError("Node address range exceeds address width")
# Calculate the number of bits needed to represent the size if addr_msb == addr_lsb:
size_bits = size.bit_length() - 1 return f"{cpuif_addr}[{addr_lsb}]"
if size_bits < 0: return f"{cpuif_addr}[{addr_msb}:{addr_lsb}]"
size_bits = 0
if size_bits >= self.addr_width:
# Node covers entire address space, so return full address
return ""
# Extract the relevant bits from PADDR
return f"[{self.addr_width - 1}:{size_bits}]"

View File

@@ -1,72 +1,92 @@
from collections import deque from collections import deque
from enum import Enum
from systemrdl.node import AddressableNode from systemrdl.node import AddressableNode
from systemrdl.walker import RDLListener, RDLSimpleWalker, WalkerAction from systemrdl.walker import RDLListener, RDLSimpleWalker, WalkerAction
from .body import Body, ForLoopBody, IfBody from .body import Body, ForLoopBody, IfBody
from .design_state import DesignState
from .sv_int import SVInt from .sv_int import SVInt
from .utils import get_indexed_path
class DecodeLogicFlavor(Enum):
READ = "rd"
WRITE = "wr"
@property
def cpuif_address(self) -> str:
return f"cpuif_{self.value}_addr"
@property
def cpuif_select(self) -> str:
return f"cpuif_{self.value}_sel"
class AddressDecode: class AddressDecode:
def __init__(self, node: AddressableNode, addr_width: int) -> None: def __init__(self, flavor: DecodeLogicFlavor, ds: DesignState) -> None:
self._node = node self._flavor = flavor
self._addr_width = addr_width self._ds = ds
def walk(self) -> str: def walk(self) -> str:
walker = RDLSimpleWalker() walker = RDLSimpleWalker()
dlg = DecodeLogicGenerator(self) dlg = DecodeLogicGenerator(self._flavor, self._ds)
walker.walk(self._node, dlg, skip_top=True) walker.walk(self._ds.top_node, dlg, skip_top=True)
return str(dlg) return str(dlg)
@property
def node(self) -> AddressableNode:
return self._node
@property
def addr_width(self) -> int:
return self._addr_width
class DecodeLogicGenerator(RDLListener): class DecodeLogicGenerator(RDLListener):
cpuif_addr_signal = "addr"
cpuif_sel_prefix = "cpuif_"
def __init__( def __init__(
self, self,
address_decoder: AddressDecode, flavor: DecodeLogicFlavor,
max_depth: int = 1, ds: DesignState,
) -> None: ) -> None:
self._address_decoder = address_decoder self._ds = ds
self._depth = 0 self._flavor = flavor
self._max_depth = max_depth
self._stack: list[Body] = [IfBody()] self._decode_stack: deque[Body] = deque() # Tracks decoder body
self._conditions: deque[str] = deque() self._cond_stack: deque[str] = deque() # Tracks conditions nested for loops
self._select_signal = [f"{self.cpuif_sel_prefix}{address_decoder.node.inst_name}"] self._array_stride_stack: deque[int] = deque() # Tracks nested array strids
# Stack to keep track of array strides for nested arrayed components # Initial Stack Conditions
self._array_stride_stack: list[int] = [] self._decode_stack.append(IfBody())
def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None: def cpuif_addr_predicate(self, node: AddressableNode) -> list[str]:
# Generate address bounds # Generate address bounds
addr_width = self._address_decoder.addr_width addr_width = self._ds.addr_width
l_bound = SVInt( l_bound = SVInt(
node.raw_absolute_address - self._address_decoder.node.raw_absolute_address, node.raw_address_offset,
addr_width, addr_width,
) )
u_bound = l_bound + SVInt(node.total_size, addr_width) u_bound = l_bound + SVInt(node.total_size, addr_width)
# Handle arrayed components # Handle arrayed components
l_bound_str = str(l_bound) l_bound_comp = [str(l_bound)]
u_bound_str = str(u_bound) u_bound_comp = [str(u_bound)]
for i, stride in enumerate(self._array_stride_stack): for i, stride in enumerate(self._array_stride_stack):
l_bound_str += f" + (({addr_width})'(i{i}) * {SVInt(stride, addr_width)})" l_bound_comp.append(f"({addr_width}'(i{i})*{SVInt(stride, addr_width)})")
u_bound_str += f" + (({addr_width})'(i{i}) * {SVInt(stride, addr_width)})" u_bound_comp.append(f"({addr_width}'(i{i})*{SVInt(stride, addr_width)})")
# Generate condition string # Generate Conditions
condition = ( return [
f"({self.cpuif_addr_signal} >= ({l_bound_str})) && ({self.cpuif_addr_signal} < ({u_bound_str}))" f"{self._flavor.cpuif_address} >= ({'+'.join(l_bound_comp)})",
) f"{self._flavor.cpuif_address} < ({'+'.join(u_bound_comp)})",
]
def cpuif_prot_predicate(self, node: AddressableNode) -> list[str]:
if self._flavor == DecodeLogicFlavor.READ:
# Can we have PROT on read? (axi full?)
return []
# TODO: Implement
return []
def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None:
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)
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"
@@ -80,26 +100,25 @@ class DecodeLogicGenerator(RDLListener):
self._array_stride_stack.extend(strides) self._array_stride_stack.extend(strides)
# Generate condition string and manage stack # Generate condition string and manage stack
signal = node.inst_name if isinstance(self._decode_stack[-1], IfBody) and node.array_dimensions:
if isinstance(self._stack[-1], IfBody) and node.array_dimensions:
# arrayed component with new if-body # arrayed component with new if-body
self._conditions.append(condition) self._cond_stack.append(condition)
for dim in node.array_dimensions: for i, dim in enumerate(
node.array_dimensions,
start=len(self._array_stride_stack) - len(node.array_dimensions),
):
fb = ForLoopBody( fb = ForLoopBody(
"int", "int",
f"i{self._depth}", f"i{i}",
dim, dim,
) )
self._stack.append(fb) self._decode_stack.append(fb)
signal += f"[i{self._depth}]"
self._depth += 1
self._stack.append(IfBody()) self._decode_stack.append(IfBody())
elif isinstance(self._stack[-1], IfBody): elif isinstance(self._decode_stack[-1], IfBody):
# non-arrayed component with if-body # non-arrayed component with if-body
with self._stack[-1].cm(condition) as b: with self._decode_stack[-1].cm(condition) as b:
b += f"{'.'.join([*self._select_signal, signal])} = 1'b1;" b += f"{self._flavor.cpuif_select}.{get_indexed_path(self._ds.top_node, node)} = 1'b1;"
self._select_signal.append(signal)
# if node.external: # if node.external:
# return WalkerAction.SkipDescendants # return WalkerAction.SkipDescendants
@@ -107,26 +126,34 @@ class DecodeLogicGenerator(RDLListener):
return WalkerAction.Continue return WalkerAction.Continue
def exit_AddressableComponent(self, node: AddressableNode) -> None: def exit_AddressableComponent(self, node: AddressableNode) -> None:
self._select_signal.pop()
if not node.array_dimensions: if not node.array_dimensions:
return return
ifb = self._stack.pop() ifb = self._decode_stack.pop()
self._stack[-1] += ifb 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;"
)
for _ in node.array_dimensions: for _ in node.array_dimensions:
self._depth -= 1 b = self._decode_stack.pop()
if not b:
continue
b = self._stack.pop() if isinstance(self._decode_stack[-1], IfBody):
if b.lines: with self._decode_stack[-1].cm(self._cond_stack.pop()) as parent_b:
if isinstance(self._stack[-1], IfBody): parent_b += b
with self._stack[-1].cm(self._conditions.pop()) as parent_b: else:
parent_b += b self._decode_stack[-1] += b
else:
self._stack[-1] += b
self._array_stride_stack.pop() self._array_stride_stack.pop()
def __str__(self) -> str: def __str__(self) -> str:
return str(self._stack[-1]) body = self._decode_stack[-1]
if isinstance(body, IfBody):
with body.cm(...) as b:
b += f"{self._flavor.cpuif_select}.bad_addr = 1'b1;"
return str(body)

View File

@@ -1,12 +1,10 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from systemrdl.node import RegNode from systemrdl.node import AddressableNode, AddrmapNode, Node, RegNode
from systemrdl.walker import RDLListener, RDLWalker, WalkerAction from systemrdl.walker import RDLListener, RDLWalker, WalkerAction
if TYPE_CHECKING: if TYPE_CHECKING:
from systemrdl.node import AddressableNode, AddrmapNode, Node from .design_state import DesignState
from .exporter import DesignState
class DesignScanner(RDLListener): class DesignScanner(RDLListener):
@@ -22,7 +20,7 @@ class DesignScanner(RDLListener):
self.msg = self.top_node.env.msg self.msg = self.top_node.env.msg
@property @property
def top_node(self) -> "AddrmapNode": def top_node(self) -> AddrmapNode:
return self.ds.top_node return self.ds.top_node
def do_scan(self) -> None: def do_scan(self) -> None:
@@ -30,7 +28,7 @@ class DesignScanner(RDLListener):
if self.msg.had_error: if self.msg.had_error:
self.msg.fatal("Unable to export due to previous errors") self.msg.fatal("Unable to export due to previous errors")
def enter_Component(self, node: "Node") -> WalkerAction | None: def enter_Component(self, node: Node) -> WalkerAction:
if node.external and (node != self.top_node): if node.external and (node != self.top_node):
# Do not inspect external components. None of my business # Do not inspect external components. None of my business
return WalkerAction.SkipDescendants return WalkerAction.SkipDescendants
@@ -41,7 +39,7 @@ class DesignScanner(RDLListener):
return WalkerAction.Continue return WalkerAction.Continue
def enter_AddressableComponent(self, node: "AddressableNode") -> None: def enter_AddressableComponent(self, node: AddressableNode) -> None:
if node.external and node != self.top_node: if node.external and node != self.top_node:
self.ds.has_external_addressable = True self.ds.has_external_addressable = True
if not isinstance(node, RegNode): if not isinstance(node, RegNode):

View File

@@ -0,0 +1,72 @@
from typing import TypedDict
from systemrdl.node import AddrmapNode
from systemrdl.rdltypes.user_enum import UserEnum
from .design_scanner import DesignScanner
from .identifier_filter import kw_filter as kwf
from .utils import clog2
class DesignStateKwargs(TypedDict, total=False):
reuse_hwif_typedefs: bool
module_name: str
package_name: str
address_width: int
cpuif_unroll: bool
class DesignState:
"""
Dumping ground for all sorts of variables that are relevant to a particular
design.
"""
def __init__(self, top_node: AddrmapNode, kwargs: DesignStateKwargs) -> None:
self.top_node = top_node
msg = top_node.env.msg
# ------------------------
# Extract compiler args
# ------------------------
self.reuse_hwif_typedefs: bool = kwargs.pop("reuse_hwif_typedefs", True)
self.module_name: str = kwargs.pop("module_name", None) or kwf(self.top_node.inst_name)
self.package_name: str = kwargs.pop("package_name", None) or f"{self.module_name}_pkg"
user_addr_width: int | None = kwargs.pop("address_width", None)
self.cpuif_unroll: bool = kwargs.pop("cpuif_unroll", False)
# ------------------------
# Info about the design
# ------------------------
self.cpuif_data_width = 0
# Track any referenced enums
self.user_enums: list[type[UserEnum]] = []
self.has_external_addressable = False
self.has_external_block = False
# Scan the design to fill in above variables
DesignScanner(self).do_scan()
if self.cpuif_data_width == 0:
# Scanner did not find any registers in the design being exported,
# so the width is not known.
# Assume 32-bits
msg.warning(
"Addrmap being exported only contains external components. Unable to infer the CPUIF bus width. Assuming 32-bits.",
self.top_node.inst.def_src_ref,
)
self.cpuif_data_width = 32
# ------------------------
# Min address width encloses the total size AND at least 1 useful address bit
self.addr_width = max(clog2(self.top_node.size), clog2(self.cpuif_data_width // 8) + 1)
if user_addr_width is None:
return
if user_addr_width < self.addr_width:
msg.fatal(f"User-specified address width shall be greater than or equal to {self.addr_width}.")
self.addr_width = user_addr_width

View File

@@ -1,53 +1,63 @@
import os import os
from datetime import datetime from datetime import datetime
from importlib.metadata import version from importlib.metadata import version
from typing import TYPE_CHECKING, Any from pathlib import Path
from typing import TYPE_CHECKING, TypedDict
import jinja2 as jj import jinja2 as jj
from systemrdl.node import AddrmapNode, RootNode from systemrdl.node import AddrmapNode, RootNode
from systemrdl.rdltypes.user_enum import UserEnum from typing_extensions import Unpack
from .cpuif import BaseCpuif from .cpuif import BaseCpuif
from .cpuif.apb4 import APB4Cpuif from .cpuif.apb4 import APB4Cpuif
from .decoder import AddressDecode from .decoder import AddressDecode, DecodeLogicFlavor
from .design_state import DesignState
from .identifier_filter import kw_filter as kwf from .identifier_filter import kw_filter as kwf
from .scan_design import DesignScanner
from .sv_int import SVInt from .sv_int import SVInt
from .utils import clog2
from .validate_design import DesignValidator from .validate_design import DesignValidator
class ExporterKwargs(TypedDict, total=False):
cpuif_cls: type[BaseCpuif]
module_name: str
package_name: str
address_width: int
cpuif_unroll: bool
reuse_hwif_typedefs: bool
if TYPE_CHECKING: if TYPE_CHECKING:
pass pass
class BusDecoderExporter: class BusDecoderExporter:
cpuif: BaseCpuif cpuif: BaseCpuif
address_decode: AddressDecode address_decode: type[AddressDecode]
ds: "DesignState" ds: DesignState
def __init__(self, **kwargs: Any) -> None: def __init__(self, **kwargs: Unpack[ExporterKwargs]) -> None:
# Check for stray kwargs # Check for stray kwargs
if kwargs: if kwargs:
raise TypeError(f"got an unexpected keyword argument '{next(iter(kwargs.keys()))}'") raise TypeError(f"got an unexpected keyword argument '{next(iter(kwargs.keys()))}'")
loader = jj.ChoiceLoader( fs_loader = jj.FileSystemLoader(Path(__file__).parent)
c_loader = jj.ChoiceLoader(
[ [
jj.FileSystemLoader(os.path.dirname(__file__)), fs_loader,
jj.PrefixLoader( jj.PrefixLoader(
{ {"base": fs_loader},
"base": jj.FileSystemLoader(os.path.dirname(__file__)),
},
delimiter=":", delimiter=":",
), ),
] ]
) )
self.jj_env = jj.Environment( self.jj_env = jj.Environment(
loader=loader, loader=c_loader,
undefined=jj.StrictUndefined, undefined=jj.StrictUndefined,
) )
self.jj_env.filters["kwf"] = kwf
def export(self, node: RootNode | AddrmapNode, output_dir: str, **kwargs: Any) -> None: def export(self, node: RootNode | AddrmapNode, output_dir: str, **kwargs: Unpack[ExporterKwargs]) -> None:
""" """
Parameters Parameters
---------- ----------
@@ -68,7 +78,7 @@ class BusDecoderExporter:
address_width: int address_width: int
Override the CPU interface's address width. By default, address width Override the CPU interface's address width. By default, address width
is sized to the contents of the busdecoder. is sized to the contents of the busdecoder.
unroll: bool cpuif_unroll: bool
Unroll arrayed addressable nodes into separate instances in the CPU Unroll arrayed addressable nodes into separate instances in the CPU
interface. By default, arrayed nodes are kept as arrays. interface. By default, arrayed nodes are kept as arrays.
""" """
@@ -88,7 +98,7 @@ class BusDecoderExporter:
# Construct exporter components # Construct exporter components
self.cpuif = cpuif_cls(self) self.cpuif = cpuif_cls(self)
self.address_decode = AddressDecode(top_node, self.ds.addr_width) self.address_decode = AddressDecode
# Validate that there are no unsupported constructs # Validate that there are no unsupported constructs
DesignValidator(self).do_validate() DesignValidator(self).do_validate()
@@ -99,8 +109,8 @@ class BusDecoderExporter:
"version": version("peakrdl-busdecoder"), "version": version("peakrdl-busdecoder"),
"cpuif": self.cpuif, "cpuif": self.cpuif,
"address_decode": self.address_decode, "address_decode": self.address_decode,
"DecodeLogicFlavor": DecodeLogicFlavor,
"ds": self.ds, "ds": self.ds,
"kwf": kwf,
"SVInt": SVInt, "SVInt": SVInt,
} }
@@ -115,59 +125,3 @@ class BusDecoderExporter:
template = self.jj_env.get_template("module_tmpl.sv") template = self.jj_env.get_template("module_tmpl.sv")
stream = template.stream(context) stream = template.stream(context)
stream.dump(module_file_path) stream.dump(module_file_path)
class DesignState:
"""
Dumping ground for all sorts of variables that are relevant to a particular
design.
"""
def __init__(self, top_node: AddrmapNode, kwargs: Any) -> None:
self.top_node = top_node
msg = top_node.env.msg
# ------------------------
# Extract compiler args
# ------------------------
self.reuse_hwif_typedefs: bool = kwargs.pop("reuse_hwif_typedefs", True)
self.module_name: str = kwargs.pop("module_name", None) or kwf(self.top_node.inst_name)
self.package_name: str = kwargs.pop("package_name", None) or (self.module_name + "_pkg")
user_addr_width: int | None = kwargs.pop("address_width", None)
self.cpuif_unroll: bool = kwargs.pop("cpuif_unroll", False)
# ------------------------
# Info about the design
# ------------------------
self.cpuif_data_width = 0
# Track any referenced enums
self.user_enums: list[type[UserEnum]] = []
self.has_external_addressable = False
self.has_external_block = False
# Scan the design to fill in above variables
DesignScanner(self).do_scan()
if self.cpuif_data_width == 0:
# Scanner did not find any registers in the design being exported,
# so the width is not known.
# Assume 32-bits
msg.warning(
"Addrmap being exported only contains external components. Unable to infer the CPUIF bus width. Assuming 32-bits.",
self.top_node.inst.def_src_ref,
)
self.cpuif_data_width = 32
# ------------------------
# Min address width encloses the total size AND at least 1 useful address bit
self.addr_width = max(clog2(self.top_node.size), clog2(self.cpuif_data_width // 8) + 1)
if user_addr_width is not None:
if user_addr_width < self.addr_width:
msg.fatal(
f"User-specified address width shall be greater than or equal to {self.addr_width}."
)
self.addr_width = user_addr_width

View File

@@ -39,8 +39,11 @@ module {{ds.module_name}}
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Child instance signals // Child instance signals
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
logic [{{cpuif.addressable_children | length}}-1:0] cpuif_wr_sel; typedef struct packed {
logic [{{cpuif.addressable_children | length}}-1:0] cpuif_rd_sel;
} cpuif_sel_t;
cpuif_sel_t cpuif_wr_sel;
cpuif_sel_t cpuif_rd_sel;
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Slave <-> Internal CPUIF <-> Master // Slave <-> Internal CPUIF <-> Master
@@ -56,19 +59,7 @@ module {{ds.module_name}}
if (cpuif_req && cpuif_wr_en) begin if (cpuif_req && cpuif_wr_en) begin
// A write request is pending // A write request is pending
{%- for child in cpuif.addressable_children -%} {{address_decode(DecodeLogicFlavor.WRITE, ds).walk()|indent(12)}}
{%- if loop.first -%}
if {{child|address_decode}} begin
{%- else -%}
end else if {{child|address_decode}} begin
{%- endif -%}
// Address matched for {{child.inst_name}}
cpuif_wr_sel[{{loop.index}}] = 1'b1;
{%- endfor -%}
end else begin
// No address match, all select signals remain 0
cpuif_wr_err = 1'b1; // Indicate error on no match
end
end else begin end else begin
// No write request, all select signals remain 0 // No write request, all select signals remain 0
end end
@@ -83,19 +74,7 @@ module {{ds.module_name}}
if (cpuif_req && cpuif_rd_en) begin if (cpuif_req && cpuif_rd_en) begin
// A read request is pending // A read request is pending
{%- for child in cpuif.addressable_children -%} {{address_decode(DecodeLogicFlavor.READ, ds).walk()|indent(12)}}
{%- if loop.first -%}
if {{child|address_decode}} begin
{%- else -%}
end else if {{child|address_decode}} begin
{%- endif -%}
// Address matched for {{child.inst_name}}
cpuif_rd_sel[{{loop.index}}] = 1'b1;
{%- endfor -%}
end else begin
// No address match, all select signals remain 0
cpuif_rd_err = 1'b1; // Indicate error on no match
end
end else begin end else begin
// No read request, all select signals remain 0 // No read request, all select signals remain 0
end end

View File

@@ -1,17 +1,16 @@
import re import re
from re import Match from re import Match
from typing import overload
from systemrdl.node import AddrmapNode, Node from systemrdl.node import AddrmapNode, Node
from systemrdl.rdltypes.references import PropertyReference from systemrdl.rdltypes.references import PropertyReference
from .identifier_filter import kw_filter as kwf from .identifier_filter import kw_filter as kwf
from .sv_int import SVInt
def get_indexed_path(top_node: Node, target_node: Node) -> str: def get_indexed_path(top_node: Node, target_node: Node, indexer: str = "i") -> str:
""" """
TODO: Add words about indexing and why i'm doing this. Copy from logbook Get the relative path from top_node to target_node, replacing any unknown
array indexes with incrementing iterators (i0, i1, ...).
""" """
path = target_node.get_rel_path(top_node, empty_array_suffix="[!]") path = target_node.get_rel_path(top_node, empty_array_suffix="[!]")
@@ -21,7 +20,7 @@ def get_indexed_path(top_node: Node, target_node: Node) -> str:
self.i = 0 self.i = 0
def __call__(self, match: Match[str]) -> str: def __call__(self, match: Match[str]) -> str:
s = f"i{self.i}" s = f"{indexer}{self.i}"
self.i += 1 self.i += 1
return s return s
@@ -54,13 +53,10 @@ def ref_is_internal(top_node: AddrmapNode, ref: Node | PropertyReference) -> boo
For the sake of this exporter, root signals are treated as internal. For the sake of this exporter, root signals are treated as internal.
""" """
# current_node: Optional[Node] if isinstance(ref, PropertyReference):
if isinstance(ref, Node):
current_node = ref
elif isinstance(ref, PropertyReference):
current_node = ref.node current_node = ref.node
else: else:
raise RuntimeError current_node = ref
while current_node is not None: while current_node is not None:
if current_node == top_node: if current_node == top_node:
@@ -77,46 +73,3 @@ def ref_is_internal(top_node: AddrmapNode, ref: Node | PropertyReference) -> boo
# A root signal was referenced, which dodged the top addrmap # A root signal was referenced, which dodged the top addrmap
# This is considered internal for this exporter # This is considered internal for this exporter
return True return True
@overload
def do_slice(value: SVInt, high: int, low: int) -> SVInt: ...
@overload
def do_slice(value: str, high: int, low: int) -> str: ...
def do_slice(value: SVInt | str, high: int, low: int) -> SVInt | str:
if isinstance(value, str):
# If string, assume this is an identifier. Append bit-slice
if high == low:
return f"{value}[{low}]"
else:
return f"{value}[{high}:{low}]"
else:
# it is an SVInt literal. Slice it down
mask = (1 << (high + 1)) - 1
v = (value.value & mask) >> low
if value.width is not None:
w = high - low + 1
else:
w = None
return SVInt(v, w)
@overload
def do_bitswap(value: SVInt) -> SVInt: ...
@overload
def do_bitswap(value: str) -> str: ...
def do_bitswap(value: SVInt | str) -> SVInt | str:
if isinstance(value, str):
# If string, assume this is an identifier. Wrap in a streaming operator
return "{<<{" + value + "}}"
else:
# it is an SVInt literal. bitswap it
assert value.width is not None # width must be known!
v = value.value
vswap = 0
for _ in range(value.width):
vswap = (vswap << 1) + (v & 1)
v >>= 1
return SVInt(vswap, value.width)

View File

@@ -1,7 +1,7 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from systemrdl.node import AddressableNode, AddrmapNode, FieldNode, Node, RegfileNode, RegNode, SignalNode from systemrdl.node import AddressableNode, AddrmapNode, FieldNode, Node, RegfileNode, RegNode, SignalNode
from systemrdl.rdltypes import PropertyReference from systemrdl.rdltypes.references import PropertyReference
from systemrdl.walker import RDLListener, RDLWalker, WalkerAction from systemrdl.walker import RDLListener, RDLWalker, WalkerAction
from .utils import is_pow2, ref_is_internal, roundup_pow2 from .utils import is_pow2, ref_is_internal, roundup_pow2
@@ -148,21 +148,6 @@ class DesignValidator(RDLListener):
node.inst.inst_src_ref, node.inst.inst_src_ref,
) )
# Check for unsynthesizable reset
reset = node.get_property("reset")
if not (reset is None or isinstance(reset, int)):
# Has reset that is not a constant value
resetsignal = node.get_property("resetsignal")
if resetsignal:
is_async_reset = resetsignal.get_property("async")
else:
is_async_reset = self.ds.default_reset_async
if is_async_reset:
self.msg.error(
"A field that uses an asynchronous reset cannot use a dynamic reset value. This is not synthesizable.",
node.inst.inst_src_ref,
)
def exit_AddressableComponent(self, node: AddressableNode) -> None: def exit_AddressableComponent(self, node: AddressableNode) -> None:
if not isinstance(node, RegNode): if not isinstance(node, RegNode):