From 489b4d1310d05d44d2363249772f57758328a76a Mon Sep 17 00:00:00 2001 From: Alex Mykyta Date: Wed, 9 Nov 2022 21:22:59 -0800 Subject: [PATCH] Add 'rd_swacc' and 'wr_swacc' UDPs. #21 --- docs/index.rst | 1 + docs/udps/extended_swacc.rst | 47 +++++++++++++ docs/udps/intro.rst | 14 ++++ hdl-src/regblock_udps.rdl | 10 +++ src/peakrdl_regblock/__about__.py | 2 +- src/peakrdl_regblock/dereferencer.py | 4 ++ src/peakrdl_regblock/field_logic/__init__.py | 24 +++++++ .../field_logic/generators.py | 16 ++++- src/peakrdl_regblock/hwif/__init__.py | 1 + src/peakrdl_regblock/hwif/generators.py | 2 +- src/peakrdl_regblock/udps/__init__.py | 12 ++++ src/peakrdl_regblock/udps/extended_swacc.py | 23 +++++++ .../{udps.py => udps/rw_buffering.py} | 9 --- tests/test_extended_swacc_swmod/__init__.py | 0 tests/test_extended_swacc_swmod/regblock.rdl | 17 +++++ .../test_extended_swacc_swmod/tb_template.sv | 68 +++++++++++++++++++ tests/test_extended_swacc_swmod/testcase.py | 5 ++ 17 files changed, 243 insertions(+), 12 deletions(-) create mode 100644 docs/udps/extended_swacc.rst create mode 100644 src/peakrdl_regblock/udps/__init__.py create mode 100644 src/peakrdl_regblock/udps/extended_swacc.py rename src/peakrdl_regblock/{udps.py => udps/rw_buffering.py} (97%) create mode 100644 tests/test_extended_swacc_swmod/__init__.py create mode 100644 tests/test_extended_swacc_swmod/regblock.rdl create mode 100644 tests/test_extended_swacc_swmod/tb_template.sv create mode 100644 tests/test_extended_swacc_swmod/testcase.py diff --git a/docs/index.rst b/docs/index.rst index 9f90a74..036b7e9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -123,3 +123,4 @@ Links udps/intro udps/read_buffering udps/write_buffering + udps/extended_swacc diff --git a/docs/udps/extended_swacc.rst b/docs/udps/extended_swacc.rst new file mode 100644 index 0000000..0c03197 --- /dev/null +++ b/docs/udps/extended_swacc.rst @@ -0,0 +1,47 @@ +.. _extended_swacc: + +Read/Write-specific swacc +========================= + +SystemRDL defines the ``swacc`` property, but it does not distinguish between +read and write operations - it is asserted on *all* software accesses. +Similarly, the spec defines ``swmod`` which gets asserted on software writes, +but can also get asserted if the field has on-read side-effects. + +What if you just wanted a plan and simple strobe that is asserted when software +reads or writes a field? The ``rd_swacc`` and ``wr_swacc`` UDPs provide this +functionality. + + +Properties +---------- +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>`. + +``rd_swacc`` + If true, infers an output signal ``hwif_out..rd_swacc`` that is asserted + when accessed by a software read operation. The output signal is asserted + on the same clock cycle that the field is being sampled during the software + read operation. + + .. wavedrom:: + + {"signal": [ + {"name": "clk", "wave": "p...."}, + {"name": "hwif_in..next", "wave": "x.=x.", "data": ["D"]}, + {"name": "hwif_out..rd_swacc", "wave": "0.10."} + ]} + + +``wr_swacc`` + If true, infers an output signal ``hwif_out..wr_swacc`` that is asserted + as the field is being modified by a software write operation. + + .. wavedrom:: + + {"signal": [ + {"name": "clk", "wave": "p....."}, + {"name": "hwif_out..value", "wave": "=..=..", "data": ["old", "new"]}, + {"name": "hwif_out..wr_swacc", "wave": "0.10.."} + ]} diff --git a/docs/udps/intro.rst b/docs/udps/intro.rst index f44cb06..9c31ac5 100644 --- a/docs/udps/intro.rst +++ b/docs/udps/intro.rst @@ -46,3 +46,17 @@ To enable these UDPs, compile this RDL file prior to the rest of your design: - Defines the buffered write commit trigger. See: :ref:`write_buffering`. + + * - rd_swacc + - field + - boolean + - Enables an output strobe that is asserted on sw reads. + + See: :ref:`extended_swacc`. + + * - wr_swacc + - field + - boolean + - Enables an output strobe that is asserted on sw writes. + + See: :ref:`extended_swacc`. diff --git a/hdl-src/regblock_udps.rdl b/hdl-src/regblock_udps.rdl index 4d7cc0d..d9e36ba 100644 --- a/hdl-src/regblock_udps.rdl +++ b/hdl-src/regblock_udps.rdl @@ -26,3 +26,13 @@ property wbuffer_trigger { component = reg; type = ref; }; + +property rd_swacc { + component = field; + type = boolean; +}; + +property wr_swacc { + component = field; + type = boolean; +}; diff --git a/src/peakrdl_regblock/__about__.py b/src/peakrdl_regblock/__about__.py index 777f190..3e2f46a 100644 --- a/src/peakrdl_regblock/__about__.py +++ b/src/peakrdl_regblock/__about__.py @@ -1 +1 @@ -__version__ = "0.8.0" +__version__ = "0.9.0" diff --git a/src/peakrdl_regblock/dereferencer.py b/src/peakrdl_regblock/dereferencer.py index 5a464bc..d2538c3 100644 --- a/src/peakrdl_regblock/dereferencer.py +++ b/src/peakrdl_regblock/dereferencer.py @@ -161,6 +161,10 @@ def get_field_propref_value(self, field: FieldNode, prop_name: str) -> str: return self.field_logic.get_swacc_identifier(field) if prop_name == "swmod": return self.field_logic.get_swmod_identifier(field) + if prop_name == "rd_swacc": + return self.field_logic.get_rd_swacc_identifier(field) + if prop_name == "wr_swacc": + return self.field_logic.get_wr_swacc_identifier(field) # translate aliases diff --git a/src/peakrdl_regblock/field_logic/__init__.py b/src/peakrdl_regblock/field_logic/__init__.py index d1fb5ae..e6351ce 100644 --- a/src/peakrdl_regblock/field_logic/__init__.py +++ b/src/peakrdl_regblock/field_logic/__init__.py @@ -193,6 +193,30 @@ def get_swacc_identifier(self, field: 'FieldNode') -> str: strb = self.exp.dereferencer.get_access_strobe(field) return strb + def get_rd_swacc_identifier(self, field: 'FieldNode') -> str: + """ + Asserted when field is software accessed (read) + """ + buffer_reads = field.parent.get_property('buffer_reads') + if buffer_reads: + rstrb = self.exp.read_buffering.get_trigger(field.parent) + return rstrb + else: + strb = self.exp.dereferencer.get_access_strobe(field) + return f"{strb} && !decoded_req_is_wr" + + def get_wr_swacc_identifier(self, field: 'FieldNode') -> str: + """ + Asserted when field is software accessed (write) + """ + buffer_writes = field.parent.get_property('buffer_writes') + if buffer_writes: + wstrb = self.exp.write_buffering.get_write_strobe(field) + return wstrb + else: + strb = self.exp.dereferencer.get_access_strobe(field) + return f"{strb} && decoded_req_is_wr" + def get_swmod_identifier(self, field: 'FieldNode') -> str: """ Asserted when field is modified by software (written or read with a diff --git a/src/peakrdl_regblock/field_logic/generators.py b/src/peakrdl_regblock/field_logic/generators.py index a163c10..4243964 100644 --- a/src/peakrdl_regblock/field_logic/generators.py +++ b/src/peakrdl_regblock/field_logic/generators.py @@ -224,20 +224,33 @@ def assign_field_outputs(self, node: 'FieldNode') -> None: f"assign {output_identifier} = {value};" ) + # Software access strobes if node.get_property('swmod'): output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "swmod") value = self.field_logic.get_swmod_identifier(node) self.add_content( f"assign {output_identifier} = {value};" ) - if node.get_property('swacc'): output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "swacc") value = self.field_logic.get_swacc_identifier(node) self.add_content( f"assign {output_identifier} = {value};" ) + if node.get_property('rd_swacc'): + output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "rd_swacc") + value = self.field_logic.get_rd_swacc_identifier(node) + self.add_content( + f"assign {output_identifier} = {value};" + ) + if node.get_property('wr_swacc'): + output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "wr_swacc") + value = self.field_logic.get_wr_swacc_identifier(node) + self.add_content( + f"assign {output_identifier} = {value};" + ) + # Counter thresholds if node.get_property('incrthreshold') is not False: # (explicitly not False. Not 0) output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "incrthreshold") value = self.field_logic.get_field_combo_identifier(node, 'incrthreshold') @@ -251,6 +264,7 @@ def assign_field_outputs(self, node: 'FieldNode') -> None: f"assign {output_identifier} = {value};" ) + # Counter events if node.get_property('overflow'): output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "overflow") value = self.field_logic.get_field_combo_identifier(node, 'overflow') diff --git a/src/peakrdl_regblock/hwif/__init__.py b/src/peakrdl_regblock/hwif/__init__.py index 40fe146..e56e305 100644 --- a/src/peakrdl_regblock/hwif/__init__.py +++ b/src/peakrdl_regblock/hwif/__init__.py @@ -190,6 +190,7 @@ def get_implied_prop_output_identifier(self, node: Union[FieldNode, RegNode], pr assert prop in { "anded", "ored", "xored", "swmod", "swacc", "incrthreshold", "decrthreshold", "overflow", "underflow", + "rd_swacc", "wr_swacc", } elif isinstance(node, RegNode): assert prop in { diff --git a/src/peakrdl_regblock/hwif/generators.py b/src/peakrdl_regblock/hwif/generators.py index 42ed7b3..2ec86f5 100644 --- a/src/peakrdl_regblock/hwif/generators.py +++ b/src/peakrdl_regblock/hwif/generators.py @@ -128,7 +128,7 @@ def enter_Field(self, node: 'FieldNode') -> None: self.add_member("value", node.width) # Generate output bit signals enabled via property - for prop_name in ["anded", "ored", "xored", "swmod", "swacc", "overflow", "underflow"]: + for prop_name in ["anded", "ored", "xored", "swmod", "swacc", "overflow", "underflow", "rd_swacc", "wr_swacc"]: if node.get_property(prop_name): self.add_member(prop_name) diff --git a/src/peakrdl_regblock/udps/__init__.py b/src/peakrdl_regblock/udps/__init__.py new file mode 100644 index 0000000..5c40f24 --- /dev/null +++ b/src/peakrdl_regblock/udps/__init__.py @@ -0,0 +1,12 @@ +from .rw_buffering import BufferWrites, WBufferTrigger +from .rw_buffering import BufferReads, RBufferTrigger +from .extended_swacc import ReadSwacc, WriteSwacc + +ALL_UDPS = [ + BufferWrites, + WBufferTrigger, + BufferReads, + RBufferTrigger, + ReadSwacc, + WriteSwacc, +] diff --git a/src/peakrdl_regblock/udps/extended_swacc.py b/src/peakrdl_regblock/udps/extended_swacc.py new file mode 100644 index 0000000..c982eda --- /dev/null +++ b/src/peakrdl_regblock/udps/extended_swacc.py @@ -0,0 +1,23 @@ +from typing import TYPE_CHECKING, Any + +from systemrdl.udp import UDPDefinition +from systemrdl.component import Field + +if TYPE_CHECKING: + from systemrdl.node import Node + +class ReadSwacc(UDPDefinition): + name = "rd_swacc" + valid_components = {Field} + valid_type = bool + + def get_unassigned_default(self, node: 'Node') -> Any: + return False + +class WriteSwacc(UDPDefinition): + name = "wr_swacc" + valid_components = {Field} + valid_type = bool + + def get_unassigned_default(self, node: 'Node') -> Any: + return False diff --git a/src/peakrdl_regblock/udps.py b/src/peakrdl_regblock/udps/rw_buffering.py similarity index 97% rename from src/peakrdl_regblock/udps.py rename to src/peakrdl_regblock/udps/rw_buffering.py index dc754a5..5c794fc 100644 --- a/src/peakrdl_regblock/udps.py +++ b/src/peakrdl_regblock/udps/rw_buffering.py @@ -118,12 +118,3 @@ def get_unassigned_default(self, node: 'Node') -> Any: if node.get_property('buffer_reads'): return node return None - - - -ALL_UDPS = [ - BufferWrites, - WBufferTrigger, - BufferReads, - RBufferTrigger, -] diff --git a/tests/test_extended_swacc_swmod/__init__.py b/tests/test_extended_swacc_swmod/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_extended_swacc_swmod/regblock.rdl b/tests/test_extended_swacc_swmod/regblock.rdl new file mode 100644 index 0000000..de8be5c --- /dev/null +++ b/tests/test_extended_swacc_swmod/regblock.rdl @@ -0,0 +1,17 @@ +addrmap top { + default regwidth = 8; + + reg { + field { + sw=r; hw=w; + rd_swacc; + } f[8]; + } r1; + + reg { + field { + sw=rw; hw=r; + wr_swacc; + } f[8] = 20; + } r2; +}; diff --git a/tests/test_extended_swacc_swmod/tb_template.sv b/tests/test_extended_swacc_swmod/tb_template.sv new file mode 100644 index 0000000..ba724e6 --- /dev/null +++ b/tests/test_extended_swacc_swmod/tb_template.sv @@ -0,0 +1,68 @@ +{% extends "lib/tb_base.sv" %} + +{% block seq %} + {% sv_line_anchor %} + logic [7:0] counter; + logic [7:0] rd_data; + logic [7:0] latched_data; + int event_count; + latched_data = 'x; + + ##1; + cb.rst <= '0; + ##1; + + // Verify that hwif gets sampled at the same cycle as rd_swacc strobe + counter = 'h10; + cb.hwif_in.r1.f.next <= counter; + @cb; + event_count = 0; + fork + begin + ##0; + forever begin + counter++; + cb.hwif_in.r1.f.next <= counter; + @cb; + if(cb.hwif_out.r1.f.rd_swacc) begin + latched_data = counter; + event_count++; + end + end + end + + begin + cpuif.read('h0, rd_data); + @cb; + end + join_any + disable fork; + assert(rd_data == latched_data) else $error("Read returned 0x%0x but rd_swacc strobed during 0x%0x", rd_data, latched_data); + assert(event_count == 1) else $error("Observed excess rd_swacc events: %0d", event_count); + + + // Verify that hwif changes 1 cycle after wr_swacc + fork + begin + ##0; + forever begin + assert(cb.hwif_out.r2.f.value == 20); + if(cb.hwif_out.r2.f.wr_swacc) break; + @cb; + end + @cb; + forever begin + assert(cb.hwif_out.r2.f.value == 21); + assert(cb.hwif_out.r2.f.wr_swacc == 0); + @cb; + end + end + + begin + cpuif.write('h1, 21); + @cb; + end + join_any + disable fork; + +{% endblock %} diff --git a/tests/test_extended_swacc_swmod/testcase.py b/tests/test_extended_swacc_swmod/testcase.py new file mode 100644 index 0000000..835b5ef --- /dev/null +++ b/tests/test_extended_swacc_swmod/testcase.py @@ -0,0 +1,5 @@ +from ..lib.sim_testcase import SimTestCase + +class Test(SimTestCase): + def test_dut(self): + self.run_test()