Implement new SVInt object to defer literal expansion and allow bit-fiddling operations. Fix invalid bit-slicing of literals if field reset value is a constant. #71

This commit is contained in:
Alex Mykyta
2023-10-24 22:50:41 -07:00
parent b5b1ba790e
commit 62518b318b
14 changed files with 147 additions and 62 deletions

View File

@@ -3,10 +3,11 @@ from typing import TYPE_CHECKING, Union, List, Optional
from systemrdl.node import FieldNode, RegNode
from systemrdl.walker import WalkerAction
from .utils import get_indexed_path, get_sv_int
from .utils import get_indexed_path
from .struct_generator import RDLStructGenerator
from .forloop_generator import RDLForLoopGenerator
from .identifier_filter import kw_filter as kwf
from .sv_int import SVInt
if TYPE_CHECKING:
from .exporter import RegblockExporter
@@ -149,7 +150,7 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
# Is an external block
addr_str = self._get_address_str(node)
strb = self.addr_decode.get_external_block_access_strobe(node)
rhs = f"cpuif_req_masked & (cpuif_addr >= {addr_str}) & (cpuif_addr <= {addr_str} + {get_sv_int(node.size - 1, self.addr_decode.exp.ds.addr_width)})"
rhs = f"cpuif_req_masked & (cpuif_addr >= {addr_str}) & (cpuif_addr <= {addr_str} + {SVInt(node.size - 1, self.addr_decode.exp.ds.addr_width)})"
self.add_content(f"{strb} = {rhs};")
self.add_content(f"is_external |= {rhs};")
return WalkerAction.SkipDescendants
@@ -158,12 +159,12 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
def _get_address_str(self, node: 'AddressableNode', subword_offset: int=0) -> str:
a = get_sv_int(
a = str(SVInt(
node.raw_absolute_address - self.addr_decode.top_node.raw_absolute_address + subword_offset,
self.addr_decode.exp.ds.addr_width
)
))
for i, stride in enumerate(self._array_stride_stack):
a += f" + i{i}*{get_sv_int(stride, self.addr_decode.exp.ds.addr_width)}"
a += f" + i{i}*{SVInt(stride, self.addr_decode.exp.ds.addr_width)}"
return a

View File

@@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Union, Optional
from systemrdl.node import AddrmapNode, FieldNode, SignalNode, RegNode, AddressableNode
from systemrdl.rdltypes import PropertyReference
from .utils import get_sv_int
from .sv_int import SVInt
if TYPE_CHECKING:
from .exporter import RegblockExporter, DesignState
@@ -38,7 +38,7 @@ class Dereferencer:
def top_node(self) -> AddrmapNode:
return self.exp.ds.top_node
def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference], width: Optional[int] = None) -> str:
def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference], width: Optional[int] = None) -> Union[SVInt, str]:
"""
Returns the Verilog string that represents the readable value associated
with the object.
@@ -52,20 +52,20 @@ class Dereferencer:
"""
if isinstance(obj, int):
# Is a simple scalar value
return get_sv_int(obj, width)
return SVInt(obj, width)
if isinstance(obj, FieldNode):
if obj.implements_storage:
return self.field_logic.get_storage_identifier(obj)
if self.hwif.has_value_input(obj):
return self.hwif.get_input_identifier(obj)
return self.hwif.get_input_identifier(obj, width)
# Field does not have a storage element, nor does it have a HW input
# must be a constant value as defined by its reset value
reset_value = obj.get_property('reset')
if reset_value is not None:
return self.get_value(reset_value)
return self.get_value(reset_value, obj.width)
else:
# No reset value defined!
obj.env.msg.warning(
@@ -76,11 +76,11 @@ class Dereferencer:
if isinstance(obj, SignalNode):
# Signals are always inputs from the hwif
return self.hwif.get_input_identifier(obj)
return self.hwif.get_input_identifier(obj, width)
if isinstance(obj, PropertyReference):
if isinstance(obj.node, FieldNode):
return self.get_field_propref_value(obj.node, obj.name)
return self.get_field_propref_value(obj.node, obj.name, width)
elif isinstance(obj.node, RegNode):
return self.get_reg_propref_value(obj.node, obj.name)
else:
@@ -89,7 +89,12 @@ class Dereferencer:
raise RuntimeError(f"Unhandled reference to: {obj}")
def get_field_propref_value(self, field: FieldNode, prop_name: str) -> str:
def get_field_propref_value(
self,
field: FieldNode,
prop_name: str,
width: Optional[int] = None,
) -> Union[SVInt, str]:
# Value reduction properties.
# Wrap with the appropriate Verilog reduction operator
if prop_name == "anded":
@@ -115,7 +120,7 @@ class Dereferencer:
'reset',
'resetsignal',
}:
return self.get_value(field.get_property(prop_name))
return self.get_value(field.get_property(prop_name), width)
# Field Next
if prop_name == "next":
@@ -124,7 +129,7 @@ class Dereferencer:
# unset by the user, points to the implied internal signal
return self.field_logic.get_field_combo_identifier(field, "next")
else:
return self.get_value(prop_value)
return self.get_value(prop_value, width)
# References to another component value, or an implied input
if prop_name in {'hwclr', 'hwset'}:
@@ -163,7 +168,7 @@ class Dereferencer:
else:
return f"!({self.get_value(prop_value)})"
else:
return self.get_value(prop_value)
return self.get_value(prop_value, width)
if prop_name == "swacc":
return self.field_logic.get_swacc_identifier(field)
@@ -232,7 +237,7 @@ class Dereferencer:
if isinstance(obj, SignalNode):
s = self.get_value(obj)
if obj.get_property('activehigh'):
return s
return str(s)
else:
return f"~{s}"

View File

@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Union
from systemrdl.rdltypes import PrecedenceType, InterruptType
@@ -11,6 +11,7 @@ from . import hw_set_clr
from . import hw_interrupts
from ..utils import get_indexed_path
from ..sv_int import SVInt
from .generators import CombinationalStructGenerator, FieldStorageStructGenerator, FieldLogicGenerator
@@ -99,12 +100,12 @@ class FieldLogic:
"""
prop_value = field.get_property('incr')
if prop_value:
return self.exp.dereferencer.get_value(prop_value)
return str(self.exp.dereferencer.get_value(prop_value))
# unset by the user, points to the implied input signal
return self.exp.hwif.get_implied_prop_input_identifier(field, "incr")
def get_counter_incrvalue(self, field: 'FieldNode') -> str:
def get_counter_incrvalue(self, field: 'FieldNode') -> Union[SVInt, str]:
"""
Return the string that represents the field's increment value
"""
@@ -115,7 +116,7 @@ class FieldLogic:
return self.exp.hwif.get_implied_prop_input_identifier(field, "incrvalue")
return "1'b1"
def get_counter_incrsaturate_value(self, field: 'FieldNode') -> str:
def get_counter_incrsaturate_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('incrsaturate')
if prop_value is True:
return self.exp.dereferencer.get_value(2**field.width - 1, field.width)
@@ -127,7 +128,7 @@ class FieldLogic:
"""
return field.get_property('incrsaturate') is not False
def get_counter_incrthreshold_value(self, field: 'FieldNode') -> str:
def get_counter_incrthreshold_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('incrthreshold')
if isinstance(prop_value, bool):
# No explicit value set. use max
@@ -140,12 +141,12 @@ class FieldLogic:
"""
prop_value = field.get_property('decr')
if prop_value:
return self.exp.dereferencer.get_value(prop_value)
return str(self.exp.dereferencer.get_value(prop_value))
# unset by the user, points to the implied input signal
return self.exp.hwif.get_implied_prop_input_identifier(field, "decr")
def get_counter_decrvalue(self, field: 'FieldNode') -> str:
def get_counter_decrvalue(self, field: 'FieldNode') -> Union[SVInt, str]:
"""
Return the string that represents the field's decrement value
"""
@@ -156,7 +157,7 @@ class FieldLogic:
return self.exp.hwif.get_implied_prop_input_identifier(field, "decrvalue")
return "1'b1"
def get_counter_decrsaturate_value(self, field: 'FieldNode') -> str:
def get_counter_decrsaturate_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('decrsaturate')
if prop_value is True:
return f"{field.width}'d0"
@@ -168,7 +169,7 @@ class FieldLogic:
"""
return field.get_property('decrsaturate') is not False
def get_counter_decrthreshold_value(self, field: 'FieldNode') -> str:
def get_counter_decrthreshold_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('decrthreshold')
if isinstance(prop_value, bool):
# No explicit value set. use min

View File

@@ -17,7 +17,7 @@ class HWSet(NextStateConditional):
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "hwset")
else:
# signal or field
identifier = self.exp.dereferencer.get_value(prop)
identifier = str(self.exp.dereferencer.get_value(prop))
return identifier
def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -50,7 +50,7 @@ class HWClear(NextStateConditional):
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "hwclr")
else:
# signal or field
identifier = self.exp.dereferencer.get_value(prop)
identifier = str(self.exp.dereferencer.get_value(prop))
return identifier
def get_assignments(self, field: 'FieldNode') -> List[str]:

View File

@@ -24,7 +24,7 @@ class AlwaysWrite(NextStateConditional):
def get_assignments(self, field: 'FieldNode') -> List[str]:
hwmask = field.get_property('hwmask')
hwenable = field.get_property('hwenable')
I = self.exp.hwif.get_input_identifier(field)
I = str(self.exp.hwif.get_input_identifier(field))
R = self.exp.field_logic.get_storage_identifier(field)
if hwmask is not None:
M = self.exp.dereferencer.get_value(hwmask)
@@ -55,7 +55,7 @@ class WEWrite(AlwaysWrite):
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "we")
else:
# signal or field
identifier = self.exp.dereferencer.get_value(prop)
identifier = str(self.exp.dereferencer.get_value(prop))
return identifier
class WELWrite(AlwaysWrite):
@@ -73,5 +73,5 @@ class WELWrite(AlwaysWrite):
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "wel")
else:
# signal or field
identifier = self.exp.dereferencer.get_value(prop)
identifier = str(self.exp.dereferencer.get_value(prop))
return f"!{identifier}"

View File

@@ -1,10 +1,11 @@
from typing import TYPE_CHECKING, Union, Set, Dict, Optional, TextIO, Type, List
from typing import TYPE_CHECKING, Union, Optional, TextIO
from systemrdl.node import AddrmapNode, SignalNode, FieldNode, RegNode, AddressableNode
from systemrdl.rdltypes import PropertyReference, UserEnum
from systemrdl.rdltypes import PropertyReference
from ..utils import get_indexed_path
from ..identifier_filter import kw_filter as kwf
from ..sv_int import SVInt
from .generators import InputStructGenerator_Hier, OutputStructGenerator_Hier
from .generators import InputStructGenerator_TypeScope, OutputStructGenerator_TypeScope
@@ -127,7 +128,11 @@ class Hwif:
return obj.is_hw_readable
def get_input_identifier(self, obj: Union[FieldNode, SignalNode, PropertyReference]) -> str:
def get_input_identifier(
self,
obj: Union[FieldNode, SignalNode, PropertyReference],
width: Optional[int] = None,
) -> Union[SVInt, str]:
"""
Returns the identifier string that best represents the input object.
@@ -143,7 +148,7 @@ class Hwif:
next_value = obj.get_property('next')
if next_value is not None:
# 'next' property replaces the inferred input signal
return self.exp.dereferencer.get_value(next_value)
return self.exp.dereferencer.get_value(next_value, width)
# Otherwise, use inferred
path = get_indexed_path(self.top_node, obj)
return "hwif_in." + path + ".next"

View File

@@ -5,7 +5,7 @@ from systemrdl.walker import WalkerAction
from ..struct_generator import RDLFlatStructGenerator
from ..identifier_filter import kw_filter as kwf
from ..utils import get_sv_int
from ..sv_int import SVInt
if TYPE_CHECKING:
from systemrdl.node import Node, SignalNode, AddressableNode, RegfileNode
@@ -289,7 +289,7 @@ class EnumGenerator:
lines = []
for enum_member in user_enum:
lines.append(f" {prefix}__{enum_member.name} = {get_sv_int(enum_member.value)}")
lines.append(f" {prefix}__{enum_member.name} = {SVInt(enum_member.value)}")
return (
"typedef enum {\n"

View File

@@ -1,10 +1,11 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Union
from systemrdl.node import AddrmapNode, RegNode, FieldNode, SignalNode
from systemrdl.node import AddrmapNode, RegNode, SignalNode
from .storage_generator import RBufStorageStructGenerator
from .implementation_generator import RBufLogicGenerator
from ..utils import get_indexed_path
from ..sv_int import SVInt
if TYPE_CHECKING:
from ..exporter import RegblockExporter
@@ -47,12 +48,12 @@ class ReadBuffering:
elif isinstance(trigger, SignalNode):
s = self.exp.dereferencer.get_value(trigger)
if trigger.get_property('activehigh'):
return s
return str(s)
else:
return f"~{s}"
else:
# Trigger is a field or propref bit
return self.exp.dereferencer.get_value(trigger)
return str(self.exp.dereferencer.get_value(trigger))
def get_rbuf_data(self, node: RegNode) -> str:
return "rbuf_storage." + get_indexed_path(self.top_node, node) + ".data"

View File

@@ -5,6 +5,8 @@ from systemrdl.walker import WalkerAction
from ..forloop_generator import RDLForLoopGenerator, LoopBody
from ..utils import do_bitswap, do_slice
if TYPE_CHECKING:
from ..exporter import RegblockExporter
@@ -147,7 +149,7 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
value = self.exp.dereferencer.get_value(field)
if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation
value = f"{{<<{{{value}}}}}"
value = do_bitswap(value)
self.add_content(f"assign readback_array[{self.current_offset_str}][{field.high}:{field.low}] = {rd_strb} ? {value} : '0;")
@@ -223,9 +225,9 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_low = field.width - 1 - f_low
f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low
value = f"{{<<{{{self.exp.dereferencer.get_value(field)}[{f_high}:{f_low}]}}}}"
value = do_bitswap(do_slice(self.exp.dereferencer.get_value(field), f_high, f_low))
else:
value = self.exp.dereferencer.get_value(field) + f"[{f_high}:{f_low}]"
value = do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
self.add_content(f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;")
bidx = accesswidth
@@ -234,7 +236,7 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
value = self.exp.dereferencer.get_value(field)
if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation
value = f"{{<<{{{value}}}}}"
value = do_bitswap(value)
self.add_content(f"assign readback_array[{self.current_offset_str}][{field.high}:{field.low}] = {rd_strb} ? {value} : '0;")
bidx = field.high + 1
@@ -299,7 +301,7 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
value = self.exp.dereferencer.get_value(field)
if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation
value = f"{{<<{{{value}}}}}"
value = do_bitswap(value)
self.add_content(f"assign readback_array[{self.current_offset_str}][{high}:{low}] = {rd_strb} ? {value} : '0;")
@@ -328,9 +330,9 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low
value = f"{{<<{{{self.exp.dereferencer.get_value(field)}[{f_high}:{f_low}]}}}}"
value = do_bitswap(do_slice(self.exp.dereferencer.get_value(field), f_high, f_low))
else:
value = self.exp.dereferencer.get_value(field) + f"[{f_high}:{f_low}]"
value = do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
self.add_content(f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;")
@@ -358,9 +360,9 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low
value = f"{{<<{{{self.exp.dereferencer.get_value(field)}[{f_high}:{f_low}]}}}}"
value = do_bitswap(do_slice(self.exp.dereferencer.get_value(field), f_high, f_low))
else:
value = self.exp.dereferencer.get_value(field) + f"[{f_high}:{f_low}]"
value = do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
self.add_content(f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;")

View File

@@ -0,0 +1,17 @@
from typing import Optional
class SVInt:
def __init__(self, value: int, width: Optional[int] = None) -> None:
self.value = value
self.width = width
def __str__(self) -> str:
if self.width is not None:
# Explicit width
return f"{self.width}'h{self.value:x}"
elif self.value.bit_length() > 32:
# SV standard only enforces that unsized literals shall be at least 32-bits
# To support larger literals, they need to be sized explicitly
return f"{self.value.bit_length()}'h{self.value:x}"
else:
return f"'h{self.value:x}"

View File

@@ -1,10 +1,11 @@
import re
from typing import Match, Union, Optional
from typing import Match, Union
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:
"""
@@ -65,13 +66,36 @@ def ref_is_internal(top_node: AddrmapNode, ref: Union[Node, PropertyReference])
# This is considerd internal for this exporter
return True
def get_sv_int(n: int, width: Optional[int]=None) -> str:
if width is not None:
# Explicit width
return f"{width}'h{n:x}"
elif n.bit_length() > 32:
# SV standard only enforces that unsized literals shall be at least 32-bits
# To support larger literals, they need to be sized explicitly
return f"{n.bit_length()}'h{n:x}"
def do_slice(value: Union[SVInt, str], high: int, low: int) -> Union[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:
return f"'h{n:x}"
# 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)
def do_bitswap(value: Union[SVInt, str]) -> Union[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

@@ -5,6 +5,7 @@ from systemrdl.node import AddrmapNode, RegNode, FieldNode, SignalNode
from .storage_generator import WBufStorageStructGenerator
from .implementation_generator import WBufLogicGenerator
from ..utils import get_indexed_path
from ..sv_int import SVInt
if TYPE_CHECKING:
from ..exporter import RegblockExporter
@@ -42,7 +43,7 @@ class WriteBuffering:
prefix = self.get_wbuf_prefix(node)
return f"{prefix}.pending && {self.get_trigger(node)}"
def get_raw_trigger(self, node: 'RegNode') -> str:
def get_raw_trigger(self, node: 'RegNode') -> Union[SVInt, str]:
trigger = node.get_property('wbuffer_trigger')
if isinstance(trigger, RegNode):
@@ -67,7 +68,7 @@ class WriteBuffering:
# Trigger is a field or propref bit
return self.exp.dereferencer.get_value(trigger)
def get_trigger(self, node: Union[RegNode, FieldNode]) -> str:
def get_trigger(self, node: Union[RegNode, FieldNode]) -> Union[SVInt, str]:
if isinstance(node, FieldNode):
node = node.parent
trigger = node.get_property('wbuffer_trigger')

View File

@@ -90,4 +90,22 @@ addrmap top {
} counter_reg;
counter_reg.f1_cnt->incr = r_reg2.f1->swacc;
counter_reg.f2_cnt->incr = r_reg2.f2->swacc;
reg {
regwidth = 32;
accesswidth = 16;
default sw=r;
default hw=r;
field {} f1[31:0] = 0x1234_5678;
} r_reg3;
reg {
regwidth = 32;
accesswidth = 16;
default sw=r;
default hw=r;
field {} f1[0:31] = 0x1234_5678;
} r_reg4;
};

View File

@@ -108,4 +108,14 @@
// counter_reg
cpuif.assert_read('h30, 16'h0204);
// r_reg3
cpuif.assert_read('h34, 16'h5678);
cpuif.assert_read('h36, 16'h1234);
assert(cb.hwif_out.r_reg3.f1.value == 32'h12345678);
// r_reg4
cpuif.assert_read('h38, 16'h2C48);
cpuif.assert_read('h3A, 16'h1E6A);
assert(cb.hwif_out.r_reg4.f1.value == 32'h12345678);
{% endblock %}