Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,4 @@ Links
udps/read_buffering
udps/write_buffering
udps/extended_swacc
udps/fixedpoint
75 changes: 75 additions & 0 deletions docs/udps/fixedpoint.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
.. _fixedpoint:

Signed and Fixedpoint Fields
============================

SystemRDL does not natively provide a way to mark fields as signed or unsigned.
The ``is_signed`` user-defined property fills this need. Similarly, the
``fracwidth`` and ``intwidth`` user-defined properties can be used to declare
the fixedpoint representation of a field.

For this SystemVerilog exporter, these properties only affect the signal type in
the the ``hwif`` structs. There is no special handling in the internals of
the regblock.

Properties
----------
The behavior of signed and/or fixedpoint fields is defined using the following
three properties:

.. literalinclude:: ../../hdl-src/regblock_udps.rdl
:lines: 40-54

These UDP definitions, along with others supported by PeakRDL-regblock can be
enabled by compiling the following file along with your design:
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.

.. describe:: is_signed

* Assigned value is a boolean.
* If true, the hardware interface field will have the
``logic signed [width-1:0]`` type.
* If false, the hardware interface field will have the
``logic unsigned [width-1:0]`` type.
* If not defined for a field, the field will have the ``logic [width-1:0]``
type.

.. describe:: intwidth

* The ``intwidth`` property defines the number of integer bits in the
fixedpoint representation (including the sign bit, if present).
* If ``intwidth`` is defined for a field and ``is_signed`` is not,
``is_signed`` is inferred as false (unsigned).
* If ``is_signed`` is true, the fixedpoint representation has a range from
:math:`-2^{\mathrm{intwidth}-1}` to :math:`2^{\mathrm{intwidth}-1} -2^{-\mathrm{fracwidth}}`.
* If ``is_signed`` is false, the fixedpoint representation has a range from
:math:`0` to :math:`2^{\mathrm{intwidth}} - 2^{-\mathrm{fracwidth}}`.
* The type of the field in the ``hwif`` struct is
``logic (un)signed [intwidth-1:-fracwidth]``.

.. describe:: fracwidth

* The ``fracwidth`` property defines the number of fractional bits in the
fixedpoint representation.
* The weight of the least significant bit of the field is
:math:`2^{-\mathrm{fracwidth}}`.
* If ``fracwidth`` is defined for a field and ``is_signed`` is not,
``is_signed`` is inferred as false (unsigned).
* The type of the field in the ``hwif`` struct is
``logic (un)signed [intwidth-1:-fracwidth]``.

Other Rules
^^^^^^^^^^^
* Only one of ``fracwidth`` or ``intwidth`` need be defined. The other is
inferred from the field bit width.
* The bit width of the field shall be equal to ``fracwidth`` + ``intwidth``.
* If both ``intwidth`` and ``fracwidth`` are defined for a field, it is an
error if their sum does not equal the bit width of the field.
* Either ``fracwidth`` or ``intwidth`` can be a negative integer. Because
SystemRDL does not have a signed integer type, the only way to achieve
this is to define one of the widths as larger than the bit width of the
component so that the other width is inferred as a negative number.
* The properties defined above are mutually exclusive with the ``counter``
property (with the exception of ``is_signed=false``).
* The properties defined above are mutually exclusive with the ``encode``
property.
23 changes: 23 additions & 0 deletions docs/udps/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,26 @@ To enable these UDPs, compile this RDL file prior to the rest of your design:
- Enables an output strobe that is asserted on sw writes.

See: :ref:`extended_swacc`.

* - is_signed
- field
- boolean
- Defines the signedness of a field.

See: :ref:`fixedpoint`.

* - intwidth
- field
- unsigned integer
- Defines the number of integer bits in the fixedpoint representation
of a field.

See: :ref:`fixedpoint`.

* - fracwidth
- field
- unsigned integer
- Defines the number of fractional bits in the fixedpoint representation
of a field.

See: :ref:`fixedpoint`.
16 changes: 16 additions & 0 deletions hdl-src/regblock_udps.rdl
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,19 @@ property wr_swacc {
component = field;
type = boolean;
};

property is_signed {
type = boolean;
component = field;
default = true;
};

property intwidth {
type = longint unsigned;
component = field;
};

property fracwidth {
type = longint unsigned;
component = field;
};
26 changes: 20 additions & 6 deletions src/peakrdl_regblock/hwif/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ def pop_struct(self) -> None:
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: Optional[bool] = None) -> 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 = ""

Expand Down Expand Up @@ -145,7 +145,14 @@ def enter_Field(self, node: 'FieldNode') -> None:
# 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 fixedpoint 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"]:
Expand Down Expand Up @@ -271,7 +278,14 @@ def enter_Field(self, node: 'FieldNode') -> None:

# 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 fixedpoint 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"]:
Expand Down
23 changes: 19 additions & 4 deletions src/peakrdl_regblock/struct_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,31 @@ def push_struct(self, inst_name: str, array_dimensions: Optional[List[int]] = No
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: Optional[bool] = None,
) -> 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 is None:
sign = ""
elif signed:
sign = "signed "
else:
m = f"logic [{width-1}:0] {name}{suffix};"
sign = "unsigned "

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)


Expand Down
4 changes: 4 additions & 0 deletions src/peakrdl_regblock/udps/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .rw_buffering import BufferWrites, WBufferTrigger
from .rw_buffering import BufferReads, RBufferTrigger
from .extended_swacc import ReadSwacc, WriteSwacc
from .fixedpoint import IntWidth, FracWidth, IsSigned

ALL_UDPS = [
BufferWrites,
Expand All @@ -9,4 +10,7 @@
RBufferTrigger,
ReadSwacc,
WriteSwacc,
IntWidth,
FracWidth,
IsSigned,
]
100 changes: 100 additions & 0 deletions src/peakrdl_regblock/udps/fixedpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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(
"Fixedpoint representations are not supported for counter fields.",
prop_ref
)

# incompatible with "encode" fields
if node.get_property("encode") is not None:
self.msg.error(
"Fixedpoint 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
fracwidth = node.get_property("fracwidth", default=None)
if fracwidth is not None:
assert isinstance(node, FieldNode)
return node.width - fracwidth
else:
# not a fixedpoint 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
intwidth = node.get_property("intwidth", default=None)
if intwidth is not None:
assert isinstance(node, FieldNode)
return node.width - intwidth
else:
# not a fixedpoint number
return None


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 node.get_property("encode") is not None:
self.msg.error(
"The is_signed property is not supported for fields encoded as an enum.",
node.inst.property_src_ref["is_signed"]
)

def get_unassigned_default(self, node: "Node") -> Any:
intwidth = node.get_property("intwidth")
if intwidth is not None:
# it's a fixedpoint number, default to unsigned
return False
else:
# not a fixedpoint number
return None
Empty file.
39 changes: 39 additions & 0 deletions tests/test_fixedpoint/regblock.rdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
addrmap top {
default accesswidth = 64;
default regwidth = 64;
reg {
field {
sw = rw; hw = r;
intwidth = 8;
fracwidth = 8;
} f_Q8_8[16] = 0;
field {
sw = r; hw = w;
intwidth = 32;
} f_Q32_n12[20];
field {
sw = rw; hw = r;
fracwidth = 32;
is_signed;
} f_SQn8_32[24] = 0;
field {
sw = rw; hw = r;
fracwidth = 7;
is_signed;
} f_SQn6_7 = 0;
} r1 @ 0x0;

reg {
field {
sw = r; hw = w;
is_signed;
} f_signed[16];
field {
sw = rw; hw = r;
is_signed = false;
} f_unsigned[16] = 0;
field {
sw = r; hw = w;
} f_no_sign[16];
} r2 @ 0x8;
};
Loading