From dc5d5c2fd8bf42842a7dba13dab53192acde2da8 Mon Sep 17 00:00:00 2001 From: Dana Sorensen Date: Fri, 30 May 2025 10:54:23 -0600 Subject: [PATCH 1/4] added _iw, _fw, and _signed field macros --- src/peakrdl_cheader/__peakrdl__.py | 3 + src/peakrdl_cheader/header_generator.py | 11 ++++ src/peakrdl_cheader/udps/__init__.py | 8 +++ src/peakrdl_cheader/udps/fixedpoint.py | 75 +++++++++++++++++++++++++ src/peakrdl_cheader/udps/signed.py | 38 +++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 src/peakrdl_cheader/udps/__init__.py create mode 100644 src/peakrdl_cheader/udps/fixedpoint.py create mode 100644 src/peakrdl_cheader/udps/signed.py diff --git a/src/peakrdl_cheader/__peakrdl__.py b/src/peakrdl_cheader/__peakrdl__.py index 18b442f..a561125 100644 --- a/src/peakrdl_cheader/__peakrdl__.py +++ b/src/peakrdl_cheader/__peakrdl__.py @@ -5,6 +5,7 @@ from .exporter import CHeaderExporter from .c_standards import CStandard +from .udps import ALL_UDPS if TYPE_CHECKING: import argparse @@ -13,6 +14,8 @@ class Exporter(ExporterSubcommandPlugin): short_desc = "Generate a C header definition of an address space" + udp_definitions = ALL_UDPS + cfg_schema = { "std": schema.Choice(list(CStandard.__members__.keys())), "type_style": schema.Choice(['lexical', 'hier']), diff --git a/src/peakrdl_cheader/header_generator.py b/src/peakrdl_cheader/header_generator.py index ba41a4f..b0f08ac 100644 --- a/src/peakrdl_cheader/header_generator.py +++ b/src/peakrdl_cheader/header_generator.py @@ -144,6 +144,17 @@ def enter_Reg(self, node: RegNode) -> Optional[WalkerAction]: self.write(f"#define {field_prefix}_bp {field.low:d}\n") self.write(f"#define {field_prefix}_bw {field.width:d}\n") + intwidth = field.get_property('intwidth') + if intwidth is not None: + fracwidth = field.get_property('fracwidth') + assert fracwidth is not None, "Fixedpoint UDP class must be registered with exporter" + self.write(f"#define {field_prefix}_iw {intwidth:d}\n") + self.write(f"#define {field_prefix}_fw {fracwidth:d}\n") + + is_signed = field.get_property('is_signed') + if is_signed is not None: + self.write(f"#define {field_prefix}_signed {int(is_signed):d}\n") + reset = field.get_property('reset') if isinstance(reset, int): self.write(f"#define {field_prefix}_reset {reset:#x}\n") diff --git a/src/peakrdl_cheader/udps/__init__.py b/src/peakrdl_cheader/udps/__init__.py new file mode 100644 index 0000000..86fd44c --- /dev/null +++ b/src/peakrdl_cheader/udps/__init__.py @@ -0,0 +1,8 @@ +from .fixedpoint import IntWidth, FracWidth +from .signed import IsSigned + +ALL_UDPS = [ + IntWidth, + FracWidth, + IsSigned, +] diff --git a/src/peakrdl_cheader/udps/fixedpoint.py b/src/peakrdl_cheader/udps/fixedpoint.py new file mode 100644 index 0000000..38783c1 --- /dev/null +++ b/src/peakrdl_cheader/udps/fixedpoint.py @@ -0,0 +1,75 @@ +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) + print(f"Getting unasigned default for intwidth. {fracwidth=}") + 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) + print(f"Getting unasigned default for fracwidth. {intwidth=}") + if intwidth is not None: + return node.width - intwidth + else: + # not a fixed-point number + return None diff --git a/src/peakrdl_cheader/udps/signed.py b/src/peakrdl_cheader/udps/signed.py new file mode 100644 index 0000000..cc95482 --- /dev/null +++ b/src/peakrdl_cheader/udps/signed.py @@ -0,0 +1,38 @@ +from typing import Any + +from systemrdl.component import Field +from systemrdl.node import Node, FieldNode +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: + """ + No unassigned default. + """ + assert isinstance(node, FieldNode) + if node.get_property("fracwidth") is not None: + # default unsigned for fixed-point fields + return False + # otherwise no default + return None From d7e70668940fd78de008e0ec7e9ccdccb9ea87a6 Mon Sep 17 00:00:00 2001 From: Dana Sorensen Date: Fri, 30 May 2025 10:55:07 -0600 Subject: [PATCH 2/4] added fixed-point/signed unit tests --- tests/base.py | 4 ++++ tests/test_all.py | 3 ++- tests/testcases/fixedpoint.rdl | 25 +++++++++++++++++++++++++ tests/testcases/signed.rdl | 17 +++++++++++++++++ tests/testcases/udps.rdl | 15 +++++++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/testcases/fixedpoint.rdl create mode 100644 tests/testcases/signed.rdl create mode 100644 tests/testcases/udps.rdl diff --git a/tests/base.py b/tests/base.py index d651783..6561c1f 100644 --- a/tests/base.py +++ b/tests/base.py @@ -6,6 +6,7 @@ from systemrdl import RDLCompiler from peakrdl_cheader.exporter import CHeaderExporter from peakrdl_cheader.c_standards import CStandard +from peakrdl_cheader.udps import ALL_UDPS def get_permutations(spec): @@ -56,6 +57,9 @@ def do_export(self): rdl_path = os.path.join(os.path.dirname(__file__), self.rdl_file) rdlc = RDLCompiler() + for udp in ALL_UDPS: + rdlc.register_udp(udp) + rdlc.compile_file('testcases/udps.rdl') rdlc.compile_file(rdl_path) top_node = rdlc.elaborate() diff --git a/tests/test_all.py b/tests/test_all.py index 56d79b6..f2de5a4 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -6,9 +6,10 @@ exceptions = [ "testcases/wide_regs.rdl", + "testcases/udps.rdl", ] files = glob.glob("testcases/*.rdl") -files = [file for file in files if not file in exceptions] +files = [file for file in files if file not in exceptions] @parameterized_class(base.get_permutations({ "rdl_file": files, diff --git a/tests/testcases/fixedpoint.rdl b/tests/testcases/fixedpoint.rdl new file mode 100644 index 0000000..f987008 --- /dev/null +++ b/tests/testcases/fixedpoint.rdl @@ -0,0 +1,25 @@ +addrmap test_fixedpoint { + + reg { + default sw = rw; + default hw = r; + default is_signed; + default intwidth = 10; + field {} SQ10_22[31:0]; + } reg_a; + + reg { + default sw = r; + default hw = w; + default is_signed = false; + default fracwidth = 10; + field {} Q22_10[31:0]; + } reg_b; + + reg { + default sw = r; + default hw = w; + default fracwidth = 40; + field {} Qn8_40[31:0]; + } reg_c; +}; \ No newline at end of file diff --git a/tests/testcases/signed.rdl b/tests/testcases/signed.rdl new file mode 100644 index 0000000..e794be0 --- /dev/null +++ b/tests/testcases/signed.rdl @@ -0,0 +1,17 @@ +addrmap test_signed { + + reg { + default sw = rw; + default hw = r; + default is_signed; + field {} signed_field[31:0]; + } reg_a; + + reg { + default sw = r; + default hw = w; + default is_signed = false; + field {} unsigned_field[31:0]; + } reg_b; + +}; \ No newline at end of file diff --git a/tests/testcases/udps.rdl b/tests/testcases/udps.rdl new file mode 100644 index 0000000..4d5a505 --- /dev/null +++ b/tests/testcases/udps.rdl @@ -0,0 +1,15 @@ +property is_signed { + type = boolean; + component = field; + default = true; +}; + +property intwidth { + type = longint unsigned; + component = field; +}; + +property fracwidth { + type = longint unsigned; + component = field; +}; From 5cfbec5e6f588821dfb8f7e8f3aa74a82265b8a0 Mon Sep 17 00:00:00 2001 From: Dana Sorensen Date: Fri, 30 May 2025 10:55:27 -0600 Subject: [PATCH 3/4] added fixed-point/signed docs --- docs/output.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/output.rst b/docs/output.rst index 587d629..aadf2f7 100644 --- a/docs/output.rst +++ b/docs/output.rst @@ -44,12 +44,40 @@ other fields of the same name. Width of the field in bits. +.. data:: FIELD_NAME_iw + + Fixed-point integer bit width. + + Only emitted if the |intwidth|_ or |fracwidth|_ SystemRDL properties + are defined for the field. + +.. data:: FIELD_NAME_fw + + Fixed-point fractional bit width. + + Only emitted if the |intwidth|_ or |fracwidth|_ SystemRDL properties + are defined for the field. + +.. data:: FIELD_NAME_signed + + Field signedness. + + Only emitted if the |is_signed|_, |intwidth|_, or |fracwidth|_ SystemRDL + properties are defined for the field. + .. data:: FIELD_NAME_reset Field reset value. Only emitted if a field definition provides a constant reset value. +.. |intwidth| replace:: ``intwidth`` +.. _intwidth: https://peakrdl-regblock.readthedocs.io/en/latest/udps/fixedpoint.html +.. |fracwidth| replace:: ``fracwidth`` +.. _fracwidth: https://peakrdl-regblock.readthedocs.io/en/latest/udps/fixedpoint.html +.. |is_signed| replace:: ``is_signed`` +.. _is_signed: https://peakrdl-regblock.readthedocs.io/en/latest/udps/signed.html + Register bit-field structs -------------------------- From cc26e47ecfbcd34c7f4ae0e9eaad9664dafaac9a Mon Sep 17 00:00:00 2001 From: Dana Sorensen Date: Fri, 30 May 2025 11:38:30 -0600 Subject: [PATCH 4/4] removed debug printouts --- src/peakrdl_cheader/udps/fixedpoint.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/peakrdl_cheader/udps/fixedpoint.py b/src/peakrdl_cheader/udps/fixedpoint.py index 38783c1..cf47534 100644 --- a/src/peakrdl_cheader/udps/fixedpoint.py +++ b/src/peakrdl_cheader/udps/fixedpoint.py @@ -50,7 +50,6 @@ def get_unassigned_default(self, node: "Node") -> Any: """ assert isinstance(node, FieldNode) fracwidth = node.get_property("fracwidth", default=None) - print(f"Getting unasigned default for intwidth. {fracwidth=}") if fracwidth is not None: return node.width - fracwidth else: @@ -67,7 +66,6 @@ def get_unassigned_default(self, node: "Node") -> Any: """ assert isinstance(node, FieldNode) intwidth = node.get_property("intwidth", default=None) - print(f"Getting unasigned default for fracwidth. {intwidth=}") if intwidth is not None: return node.width - intwidth else: