Skip to content

Commit d2b4911

Browse files
authored
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
1 parent 62f66fb commit d2b4911

20 files changed

+585
-10
lines changed

docs/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,5 @@ Links
9090
udps/read_buffering
9191
udps/write_buffering
9292
udps/extended_swacc
93+
udps/signed
94+
udps/fixedpoint

docs/udps/fixedpoint.rst

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
.. _fixedpoint:
2+
3+
Fixed-Point Fields
4+
==================
5+
6+
`Fixed-point <https://en.wikipedia.org/wiki/Fixed-point_arithmetic>`_ numbers
7+
can be used to efficiently represent real numbers using integers. Fixed-point
8+
numbers consist of some combination of integer bits and fractional bits. The
9+
number of integer/fractional bits is usually implicitly tracked (not stored)
10+
for each number, unlike for floating-point numbers.
11+
12+
For this SystemVerilog exporter, these properties only affect the signal type in
13+
the the ``hwif`` structs. There is no special handling in the internals of
14+
the regblock.
15+
16+
Properties
17+
----------
18+
Fields can be declared as fixed-point numbers using the following two properties:
19+
20+
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
21+
:lines: 46-54
22+
23+
The :ref:`is_signed<signed>` property can be used in conjunction with these
24+
properties to declare signed fixed-point fields.
25+
26+
These UDP definitions, along with others supported by PeakRDL-regblock, can be
27+
enabled by compiling the following file along with your design:
28+
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
29+
30+
.. describe:: intwidth
31+
32+
* The ``intwidth`` property defines the number of integer bits in the
33+
fixed-point representation (including the sign bit, if present).
34+
35+
.. describe:: fracwidth
36+
37+
* The ``fracwidth`` property defines the number of fractional bits in the
38+
fixed-point representation.
39+
40+
Representable Numbers
41+
^^^^^^^^^^^^^^^^^^^^^
42+
43+
The range of representable real numbers is summarized in the table below.
44+
45+
.. list-table:: Representable Numbers
46+
:header-rows: 1
47+
48+
* - Signedness
49+
- Minimum Value
50+
- Maximum Value
51+
- Step Size
52+
53+
* - Unsigned
54+
- :math:`0`
55+
- :math:`2^{\mathrm{intwidth}} - 2^{-\mathrm{fracwidth}}`
56+
- :math:`2^{-\mathrm{fracwidth}}`
57+
58+
* - Signed
59+
- :math:`-2^{\mathrm{intwidth}-1}`
60+
- :math:`2^{\mathrm{intwidth}-1} - 2^{-\mathrm{fracwidth}}`
61+
- :math:`2^{-\mathrm{fracwidth}}`
62+
63+
SystemVerilog Types
64+
^^^^^^^^^^^^^^^^^^^
65+
66+
When either ``intwidth`` or ``fracwidth`` are defined for a field, that field's
67+
type in the generated SystemVerilog ``hwif`` struct is
68+
``logic (signed) [intwidth-1:-fracwidth]``. The bit at index :math:`i` contributes
69+
a weight of :math:`2^i` to the real number represented.
70+
71+
Other Rules
72+
^^^^^^^^^^^
73+
* Only one of ``intwidth`` or ``fracwidth`` need be defined. The other is
74+
inferred from the field bit width.
75+
* The bit width of the field shall be equal to ``intwidth`` + ``fracwidth``.
76+
* If both ``intwidth`` and ``fracwidth`` are defined for a field, it is an
77+
error if their sum does not equal the bit width of the field.
78+
* Either ``fracwidth`` or ``intwidth`` can be a negative integer. Because
79+
SystemRDL does not have a signed integer type, the only way to achieve
80+
this is to define one of the widths as larger than the bit width of the
81+
component so that the other width is inferred as a negative number.
82+
* The properties defined above are mutually exclusive with the ``counter``
83+
property.
84+
* The properties defined above are mutually exclusive with the ``encode``
85+
property.
86+
87+
Examples
88+
--------
89+
90+
A 12-bit signed fixed-point field with 4 integer bits and 8 fractional bits
91+
can be declared with
92+
93+
.. code-block:: systemrdl
94+
:emphasize-lines: 3, 4
95+
96+
field {
97+
sw=rw; hw=r;
98+
intwidth = 4;
99+
is_signed;
100+
} fixedpoint_num[11:0] = 0;
101+
102+
This field can represent values from -8.0 to 7.99609375
103+
in steps of 0.00390625.

docs/udps/intro.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,26 @@ To enable these UDPs, compile this RDL file prior to the rest of your design:
6060
- Enables an output strobe that is asserted on sw writes.
6161

6262
See: :ref:`extended_swacc`.
63+
64+
* - is_signed
65+
- field
66+
- boolean
67+
- Defines the signedness of a field.
68+
69+
See: :ref:`signed`.
70+
71+
* - intwidth
72+
- field
73+
- unsigned integer
74+
- Defines the number of integer bits in the fixed-point representation
75+
of a field.
76+
77+
See: :ref:`fixedpoint`.
78+
79+
* - fracwidth
80+
- field
81+
- unsigned integer
82+
- Defines the number of fractional bits in the fixed-point representation
83+
of a field.
84+
85+
See: :ref:`fixedpoint`.

docs/udps/signed.rst

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
.. _signed:
2+
3+
Signed Fields
4+
=============
5+
6+
SystemRDL does not natively provide a way to mark fields as signed or unsigned.
7+
The ``is_signed`` user-defined property fills this need.
8+
9+
For this SystemVerilog exporter, marking a field as signed only affects the
10+
signal type in the ``hwif`` structs. There is no special handling in the internals
11+
of the regblock.
12+
13+
Properties
14+
----------
15+
A field can be marked as signed using the following user-defined property:
16+
17+
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
18+
:lines: 40-44
19+
20+
This UDP definition, along with others supported by PeakRDL-regblock, can be
21+
enabled by compiling the following file along with your design:
22+
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
23+
24+
.. describe:: is_signed
25+
26+
* Assigned value is a boolean.
27+
* If true, the hardware interface field will have the type
28+
``logic signed [width-1:0]``.
29+
* If false or not defined for a field, the hardware interface field will
30+
have the type ``logic [width-1:0]``, which is unsigned by definition.
31+
32+
Other Rules
33+
^^^^^^^^^^^
34+
35+
* ``is_signed=true`` is mutually exclusive with the ``counter`` property.
36+
* ``is_signed=true`` is mutually exclusive with the ``encode`` property.
37+
38+
Examples
39+
--------
40+
Below are some examples of fields with different signedness.
41+
42+
Signed Fields
43+
^^^^^^^^^^^^^
44+
.. code-block:: systemrdl
45+
:emphasize-lines: 3, 8
46+
47+
field {
48+
sw=rw; hw=r;
49+
is_signed;
50+
} signed_num[63:0] = 0;
51+
52+
field {
53+
sw=r; hw=w;
54+
is_signed = true;
55+
} another_signed_num[19:0] = 20'hFFFFF; // -1
56+
57+
SystemRDL's own integer type is always unsigned. In order to specify a negative
58+
reset value, the two's complement value must be used as shown in the second
59+
example above.
60+
61+
Unsigned Fields
62+
^^^^^^^^^^^^^^^
63+
.. code-block:: systemrdl
64+
:emphasize-lines: 3, 8
65+
66+
field {
67+
sw=rw; hw=r;
68+
// fields are unsigned by default
69+
} unsigned_num[63:0] = 0;
70+
71+
field {
72+
sw=r; hw=w;
73+
is_signed = false;
74+
} another_unsigned_num[19:0] = 0;

hdl-src/regblock_udps.rdl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,19 @@ property wr_swacc {
3636
component = field;
3737
type = boolean;
3838
};
39+
40+
property is_signed {
41+
type = boolean;
42+
component = field;
43+
default = true;
44+
};
45+
46+
property intwidth {
47+
type = longint unsigned;
48+
component = field;
49+
};
50+
51+
property fracwidth {
52+
type = longint unsigned;
53+
component = field;
54+
};

src/peakrdl_regblock/hwif/generators.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ def pop_struct(self) -> None:
3535
super().pop_struct()
3636
self.hwif_report_stack.pop()
3737

38-
def add_member(self, name: str, width: int = 1) -> None: # type: ignore # pylint: disable=arguments-differ
39-
super().add_member(name, width)
38+
def add_member(self, name: str, width: int = 1, *, lsb: int = 0, signed: bool = False) -> None: # type: ignore # pylint: disable=arguments-differ
39+
super().add_member(name, width, lsb=lsb, signed=signed)
4040

41-
if width > 1:
42-
suffix = f"[{width-1}:0]"
41+
if width > 1 or lsb != 0:
42+
suffix = f"[{lsb+width-1}:{lsb}]"
4343
else:
4444
suffix = ""
4545

@@ -145,7 +145,14 @@ def enter_Field(self, node: 'FieldNode') -> None:
145145
# Provide input to field's next value if it is writable by hw, and it
146146
# was not overridden by the 'next' property
147147
if node.is_hw_writable and node.get_property('next') is None:
148-
self.add_member("next", node.width)
148+
# Get the field's LSB index (can be nonzero for fixed-point values)
149+
fracwidth = node.get_property("fracwidth")
150+
lsb = 0 if fracwidth is None else -fracwidth
151+
152+
# get the signedness of the field
153+
signed = node.get_property("is_signed")
154+
155+
self.add_member("next", node.width, lsb=lsb, signed=signed)
149156

150157
# Generate implied inputs
151158
for prop_name in ["we", "wel", "swwe", "swwel", "hwclr", "hwset"]:
@@ -271,7 +278,14 @@ def enter_Field(self, node: 'FieldNode') -> None:
271278

272279
# Expose field's value if it is readable by hw
273280
if node.is_hw_readable:
274-
self.add_member("value", node.width)
281+
# Get the node's LSB index (can be nonzero for fixed-point values)
282+
fracwidth = node.get_property("fracwidth")
283+
lsb = 0 if fracwidth is None else -fracwidth
284+
285+
# get the signedness of the field
286+
signed = node.get_property("is_signed")
287+
288+
self.add_member("value", node.width, lsb=lsb, signed=signed)
275289

276290
# Generate output bit signals enabled via property
277291
for prop_name in ["anded", "ored", "xored", "swmod", "swacc", "overflow", "underflow", "rd_swacc", "wr_swacc"]:

src/peakrdl_regblock/struct_generator.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,30 @@ def push_struct(self, inst_name: str, array_dimensions: Optional[List[int]] = No
8888
self._struct_stack.append(s)
8989

9090

91-
def add_member(self, name: str, width: int = 1, array_dimensions: Optional[List[int]] = None) -> None:
91+
def add_member(
92+
self,
93+
name: str,
94+
width: int = 1,
95+
array_dimensions: Optional[List[int]] = None,
96+
*,
97+
lsb: int = 0,
98+
signed: bool = False,
99+
) -> None:
92100
if array_dimensions:
93101
suffix = "[" + "][".join((str(n) for n in array_dimensions)) + "]"
94102
else:
95103
suffix = ""
96104

97-
if width == 1:
98-
m = f"logic {name}{suffix};"
105+
if signed:
106+
sign = "signed "
99107
else:
100-
m = f"logic [{width-1}:0] {name}{suffix};"
108+
# the default 'logic' type is unsigned per SV LRM 6.11.3
109+
sign = ""
110+
111+
if width == 1 and lsb == 0:
112+
m = f"logic {sign}{name}{suffix};"
113+
else:
114+
m = f"logic {sign}[{lsb+width-1}:{lsb}] {name}{suffix};"
101115
self.current_struct.children.append(m)
102116

103117

src/peakrdl_regblock/udps/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from .rw_buffering import BufferWrites, WBufferTrigger
22
from .rw_buffering import BufferReads, RBufferTrigger
33
from .extended_swacc import ReadSwacc, WriteSwacc
4+
from .fixedpoint import IntWidth, FracWidth
5+
from .signed import IsSigned
46

57
ALL_UDPS = [
68
BufferWrites,
@@ -9,4 +11,7 @@
911
RBufferTrigger,
1012
ReadSwacc,
1113
WriteSwacc,
14+
IntWidth,
15+
FracWidth,
16+
IsSigned,
1217
]

0 commit comments

Comments
 (0)