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,121 @@
import re
from typing import Match, overload
from systemrdl.rdltypes.references import PropertyReference
from systemrdl.node import Node, AddrmapNode
from .identifier_filter import kw_filter as kwf
from .sv_int import SVInt
def get_indexed_path(top_node: Node, target_node: Node) -> str:
"""
TODO: Add words about indexing and why i'm doing this. Copy from logbook
"""
path = target_node.get_rel_path(top_node, empty_array_suffix="[!]")
# replace unknown indexes with incrementing iterators i0, i1, ...
class ReplaceUnknown:
def __init__(self) -> None:
self.i = 0
def __call__(self, match: Match[str]) -> str:
s = f"i{self.i}"
self.i += 1
return s
path = re.sub(r"!", ReplaceUnknown(), path)
# Sanitize any SV keywords
def kw_filter_repl(m: Match[str]) -> str:
return kwf(m.group(0))
path = re.sub(r"\w+", kw_filter_repl, path)
return path
def clog2(n: int) -> int:
return (n - 1).bit_length()
def is_pow2(x: int) -> bool:
return (x > 0) and ((x & (x - 1)) == 0)
def roundup_pow2(x: int) -> int:
return 1 << (x - 1).bit_length()
def ref_is_internal(top_node: AddrmapNode, ref: Node | PropertyReference) -> bool:
"""
Determine whether the reference is internal to the top node.
For the sake of this exporter, root signals are treated as internal.
"""
# current_node: Optional[Node]
if isinstance(ref, Node):
current_node = ref
elif isinstance(ref, PropertyReference):
current_node = ref.node
else:
raise RuntimeError
while current_node is not None:
if current_node == top_node:
# reached top node without finding any external components
# is internal!
return True
if current_node.external:
# not internal!
return False
current_node = current_node.parent
# A root signal was referenced, which dodged the top addrmap
# This is considered internal for this exporter
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)