Add signed/fixedpoint properties (#140)
* declared intwidth, fracwidth, and is_signed UDPs * fix boolean type name in UDP definition * generate hwif fields with fixedpoint indices * make "counter" and "encode" properties mutualy exclusive with signed/fixedpoint * add signed/unsigned to hwif * improved fixedpoint error messages, added validation tests * added fixedpoint tests * fixedpoint/signed not allowed for signal components * added signed/fixedpoint UDP docs * handle single-bit fixedpoint numbers * fix too many positional arguments lint * changed spelling of fixedpoint to fixed-point * use "logic" in place of "unsigned logic" * split signed and fixedpoint docs, added examples * allow enums with is_signed=false * split signed and fixedpoint implementations * assorted nits picked * updated is_signed validation unit test
This commit is contained in:
@@ -35,11 +35,11 @@ class HWIFStructGenerator(RDLFlatStructGenerator):
|
||||
super().pop_struct()
|
||||
self.hwif_report_stack.pop()
|
||||
|
||||
def add_member(self, name: str, width: int = 1) -> None: # type: ignore # pylint: disable=arguments-differ
|
||||
super().add_member(name, width)
|
||||
def add_member(self, name: str, width: int = 1, *, lsb: int = 0, signed: bool = False) -> None: # type: ignore # pylint: disable=arguments-differ
|
||||
super().add_member(name, width, lsb=lsb, signed=signed)
|
||||
|
||||
if width > 1:
|
||||
suffix = f"[{width-1}:0]"
|
||||
if width > 1 or lsb != 0:
|
||||
suffix = f"[{lsb+width-1}:{lsb}]"
|
||||
else:
|
||||
suffix = ""
|
||||
|
||||
@@ -145,7 +145,14 @@ class InputStructGenerator_Hier(HWIFStructGenerator):
|
||||
# Provide input to field's next value if it is writable by hw, and it
|
||||
# was not overridden by the 'next' property
|
||||
if node.is_hw_writable and node.get_property('next') is None:
|
||||
self.add_member("next", node.width)
|
||||
# Get the field's LSB index (can be nonzero for fixed-point values)
|
||||
fracwidth = node.get_property("fracwidth")
|
||||
lsb = 0 if fracwidth is None else -fracwidth
|
||||
|
||||
# get the signedness of the field
|
||||
signed = node.get_property("is_signed")
|
||||
|
||||
self.add_member("next", node.width, lsb=lsb, signed=signed)
|
||||
|
||||
# Generate implied inputs
|
||||
for prop_name in ["we", "wel", "swwe", "swwel", "hwclr", "hwset"]:
|
||||
@@ -271,7 +278,14 @@ class OutputStructGenerator_Hier(HWIFStructGenerator):
|
||||
|
||||
# Expose field's value if it is readable by hw
|
||||
if node.is_hw_readable:
|
||||
self.add_member("value", node.width)
|
||||
# Get the node's LSB index (can be nonzero for fixed-point values)
|
||||
fracwidth = node.get_property("fracwidth")
|
||||
lsb = 0 if fracwidth is None else -fracwidth
|
||||
|
||||
# get the signedness of the field
|
||||
signed = node.get_property("is_signed")
|
||||
|
||||
self.add_member("value", node.width, lsb=lsb, signed=signed)
|
||||
|
||||
# Generate output bit signals enabled via property
|
||||
for prop_name in ["anded", "ored", "xored", "swmod", "swacc", "overflow", "underflow", "rd_swacc", "wr_swacc"]:
|
||||
|
||||
@@ -88,16 +88,30 @@ class StructGenerator:
|
||||
self._struct_stack.append(s)
|
||||
|
||||
|
||||
def add_member(self, name: str, width: int = 1, array_dimensions: Optional[List[int]] = None) -> None:
|
||||
def add_member(
|
||||
self,
|
||||
name: str,
|
||||
width: int = 1,
|
||||
array_dimensions: Optional[List[int]] = None,
|
||||
*,
|
||||
lsb: int = 0,
|
||||
signed: bool = False,
|
||||
) -> None:
|
||||
if array_dimensions:
|
||||
suffix = "[" + "][".join((str(n) for n in array_dimensions)) + "]"
|
||||
else:
|
||||
suffix = ""
|
||||
|
||||
if width == 1:
|
||||
m = f"logic {name}{suffix};"
|
||||
if signed:
|
||||
sign = "signed "
|
||||
else:
|
||||
m = f"logic [{width-1}:0] {name}{suffix};"
|
||||
# the default 'logic' type is unsigned per SV LRM 6.11.3
|
||||
sign = ""
|
||||
|
||||
if width == 1 and lsb == 0:
|
||||
m = f"logic {sign}{name}{suffix};"
|
||||
else:
|
||||
m = f"logic {sign}[{lsb+width-1}:{lsb}] {name}{suffix};"
|
||||
self.current_struct.children.append(m)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from .rw_buffering import BufferWrites, WBufferTrigger
|
||||
from .rw_buffering import BufferReads, RBufferTrigger
|
||||
from .extended_swacc import ReadSwacc, WriteSwacc
|
||||
from .fixedpoint import IntWidth, FracWidth
|
||||
from .signed import IsSigned
|
||||
|
||||
ALL_UDPS = [
|
||||
BufferWrites,
|
||||
@@ -9,4 +11,7 @@ ALL_UDPS = [
|
||||
RBufferTrigger,
|
||||
ReadSwacc,
|
||||
WriteSwacc,
|
||||
IntWidth,
|
||||
FracWidth,
|
||||
IsSigned,
|
||||
]
|
||||
|
||||
73
src/peakrdl_regblock/udps/fixedpoint.py
Normal file
73
src/peakrdl_regblock/udps/fixedpoint.py
Normal file
@@ -0,0 +1,73 @@
|
||||
from typing import Any
|
||||
|
||||
from systemrdl.component import Field
|
||||
from systemrdl.node import Node, FieldNode
|
||||
from systemrdl.udp import UDPDefinition
|
||||
|
||||
|
||||
class _FixedpointWidth(UDPDefinition):
|
||||
valid_components = {Field}
|
||||
valid_type = int
|
||||
|
||||
def validate(self, node: "Node", value: Any) -> None:
|
||||
assert isinstance(node, FieldNode)
|
||||
|
||||
intwidth = node.get_property("intwidth")
|
||||
fracwidth = node.get_property("fracwidth")
|
||||
assert intwidth is not None
|
||||
assert fracwidth is not None
|
||||
prop_ref = node.inst.property_src_ref.get(self.name)
|
||||
|
||||
# incompatible with "counter" fields
|
||||
if node.get_property("counter"):
|
||||
self.msg.error(
|
||||
"Fixed-point representations are not supported for counter fields.",
|
||||
prop_ref
|
||||
)
|
||||
|
||||
# incompatible with "encode" fields
|
||||
if node.get_property("encode") is not None:
|
||||
self.msg.error(
|
||||
"Fixed-point representations are not supported for fields encoded as an enum.",
|
||||
prop_ref
|
||||
)
|
||||
|
||||
# ensure node width = fracwidth + intwidth
|
||||
if intwidth + fracwidth != node.width:
|
||||
self.msg.error(
|
||||
f"Number of integer bits ({intwidth}) plus number of fractional bits ({fracwidth})"
|
||||
f" must be equal to the width of the component ({node.width}).",
|
||||
prop_ref
|
||||
)
|
||||
|
||||
|
||||
class IntWidth(_FixedpointWidth):
|
||||
name = "intwidth"
|
||||
|
||||
def get_unassigned_default(self, node: "Node") -> Any:
|
||||
"""
|
||||
If 'fracwidth' is defined, 'intwidth' is inferred from the node width.
|
||||
"""
|
||||
assert isinstance(node, FieldNode)
|
||||
fracwidth = node.get_property("fracwidth", default=None)
|
||||
if fracwidth is not None:
|
||||
return node.width - fracwidth
|
||||
else:
|
||||
# not a fixed-point number
|
||||
return None
|
||||
|
||||
|
||||
class FracWidth(_FixedpointWidth):
|
||||
name = "fracwidth"
|
||||
|
||||
def get_unassigned_default(self, node: "Node") -> Any:
|
||||
"""
|
||||
If 'intwidth' is defined, 'fracwidth' is inferred from the node width.
|
||||
"""
|
||||
assert isinstance(node, FieldNode)
|
||||
intwidth = node.get_property("intwidth", default=None)
|
||||
if intwidth is not None:
|
||||
return node.width - intwidth
|
||||
else:
|
||||
# not a fixed-point number
|
||||
return None
|
||||
33
src/peakrdl_regblock/udps/signed.py
Normal file
33
src/peakrdl_regblock/udps/signed.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from typing import Any
|
||||
|
||||
from systemrdl.component import Field
|
||||
from systemrdl.node import Node
|
||||
from systemrdl.udp import UDPDefinition
|
||||
|
||||
|
||||
class IsSigned(UDPDefinition):
|
||||
name = "is_signed"
|
||||
valid_components = {Field}
|
||||
valid_type = bool
|
||||
default_assignment = True
|
||||
|
||||
def validate(self, node: "Node", value: Any) -> None:
|
||||
# "counter" fields can not be signed
|
||||
if value and node.get_property("counter"):
|
||||
self.msg.error(
|
||||
"The property is_signed=true is not supported for counter fields.",
|
||||
node.inst.property_src_ref["is_signed"]
|
||||
)
|
||||
|
||||
# incompatible with "encode" fields
|
||||
if value and node.get_property("encode") is not None:
|
||||
self.msg.error(
|
||||
"The property is_signed=true is not supported for fields encoded as an enum.",
|
||||
node.inst.property_src_ref["is_signed"]
|
||||
)
|
||||
|
||||
def get_unassigned_default(self, node: "Node") -> Any:
|
||||
"""
|
||||
Unsigned by default if not specified.
|
||||
"""
|
||||
return False
|
||||
Reference in New Issue
Block a user