From 9eddc9b60f0ef24947356b36942cacb4b074d112 Mon Sep 17 00:00:00 2001 From: Alex Mykyta Date: Sat, 11 Dec 2021 20:41:49 -0800 Subject: [PATCH] Add counter support --- MANIFEST.in | 4 +- doc/logbooks/000-Main-Logbook | 142 +++------- doc/logbooks/Validation Needed | 16 +- doc/props/addrmap.rst | 4 - doc/props/field.rst | 255 ++++++++++++++---- doc/props/reg.rst | 4 - doc/props/rhs_props.rst | 195 +++++++++----- peakrdl/regblock/dereferencer.py | 62 ++--- peakrdl/regblock/field_logic/__init__.py | 107 ++++++-- peakrdl/regblock/field_logic/generators.py | 33 +++ peakrdl/regblock/field_logic/hw_set_clr.py | 8 +- peakrdl/regblock/field_logic/hw_write.py | 4 +- peakrdl/regblock/field_logic/sw_onread.py | 8 +- peakrdl/regblock/field_logic/sw_onwrite.py | 36 +-- .../regblock/field_logic/sw_singlepulse.py | 4 +- .../field_logic/templates/counter_macros.sv | 56 ++++ .../field_logic/templates/field_storage.sv | 13 +- peakrdl/regblock/hwif.py | 24 +- setup.py | 2 +- test/lib/cpuifs/apb3/apb3_intf_driver.sv | 3 +- test/lib/simulators/modelsim.py | 2 + test/lib/simulators/xilinx.py | 5 +- test/lib/{templates => }/tb_base.sv | 4 +- test/test_bitwise_reduce/tb_template.sv | 2 +- test/test_counter_basics/__init__.py | 0 test/test_counter_basics/regblock.rdl | 68 +++++ test/test_counter_basics/tb_template.sv | 93 +++++++ test/test_counter_basics/testcase.py | 5 + test/test_counter_saturate/__init__.py | 0 test/test_counter_saturate/regblock.rdl | 88 ++++++ test/test_counter_saturate/tb_template.sv | 214 +++++++++++++++ test/test_counter_saturate/testcase.py | 5 + test/test_field_types/tb_template.sv | 2 +- test/test_hw_access/tb_template.sv | 2 +- test/test_onread_onwrite/tb_template.sv | 2 +- test/test_read_fanin/tb_template.sv | 2 +- test/test_singlepulse/tb_template.sv | 2 +- test/test_structural_sw_rw/tb_template.sv | 2 +- test/test_swacc_swmod/tb_template.sv | 2 +- test/test_swwe/tb_template.sv | 2 +- 40 files changed, 1133 insertions(+), 349 deletions(-) create mode 100644 peakrdl/regblock/field_logic/templates/counter_macros.sv rename test/lib/{templates => }/tb_base.sv (98%) create mode 100644 test/test_counter_basics/__init__.py create mode 100644 test/test_counter_basics/regblock.rdl create mode 100644 test/test_counter_basics/tb_template.sv create mode 100644 test/test_counter_basics/testcase.py create mode 100644 test/test_counter_saturate/__init__.py create mode 100644 test/test_counter_saturate/regblock.rdl create mode 100644 test/test_counter_saturate/tb_template.sv create mode 100644 test/test_counter_saturate/testcase.py diff --git a/MANIFEST.in b/MANIFEST.in index cf582ac..d627dcd 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ -#recursive-include peakrdl/regblock/XXX * -recursive-exclude test * +recursive-include peakrdl/regblock *.sv +prune test diff --git a/doc/logbooks/000-Main-Logbook b/doc/logbooks/000-Main-Logbook index a8d60d3..efd00be 100644 --- a/doc/logbooks/000-Main-Logbook +++ b/doc/logbooks/000-Main-Logbook @@ -1,33 +1,4 @@ -================================================================================ -Overarching philosophy -================================================================================ -Encourage users to be able to tweak the design -Templating: - Design templates to be small bite-size snippets, each with clear intent - Templates shall be extendable - Templates shall be easy/intuitive -Output: - Output shall be beautiful. Consistent and clean formatting/whitespace builds trust - Output has comments. Users will be looking at it. - -================================================================================ -On templating... -================================================================================ -Everything should be written out to a single file -The only exception is if a struct port interface is used, then the appropriate -struct package is also written out (or lumped into the same file?) - -Each layer should actually be its own separate template. - -Use helper functions to abstract away details about identifier implementation. -Similarly, some fields will end up with additional port-level signals inferred. -For example, if the user set the field's "anded=true" property -the template would do something like: - {{output_signal(field, "anded")}} = &{{field_value(field)}}; - -Basically, i'd define a ton of helper functions that return the signal identifier. - ================================================================================ Accesswidth vs Regwidth ================================================================================ @@ -46,7 +17,7 @@ Changes to this tool this new understanding imposes: - derive the CPU bus width based on the largest regwidth this seems like a reasonable & easy thing to implement - CPUIF should make sure to always present an aligned address! - if bus width is 32-bits, decoder logic shall recieve an address with bits [1:0] ALWAYS zeroed + if bus width is 32-bits, decoder logic shall receive an address with bits [1:0] ALWAYS zeroed Codify this in the internal specification! - address decode may produce multiple strobes if registers are packed. Eg: if bus width is 32, and there is a region of 8-bit registers that are tightly packed, @@ -75,77 +46,49 @@ Write about this in the SystemRDL errata? Maybe not so much in other protocols... Maybe add some words to the "clarifications" section - -================================================================================ -Unit Testing -================================================================================ -I NEED to start building a suite of unit-tests! -Goal: -- Small easy-to-understand testcases -- Parameterized testcases to rerun testcases with different cpuifs, etc. -- coverage - -Split it into the following components: - Common testbench SV infrastructure - Make a generic SV framework that can be re-used everywhere - Use SV interfaces/classes and even `include preprocessor tricks to make it possible - to swap out for specific testcases: - - cpuif abstraction layer - Need to be able to swap out to different CPU interfaces easily - - Clocks/resets - - DUT instantiation - Will need to account for minor variations in port list somehow - Maybe a good time to use the .* method? - - Helper functions, assertion library, etc. - - Testcase-specific - SV sequence file that issues transactions and asserts things - -Dispatch tests completely through pytest - - Each testcase has its own folder with: - testcase-specific SV file(s) - RDL file - pytest entry point .py file - - build up py utility functions that will: - Export the testcase-specific RDL --> SV - Compile and run the simulation - need to deal with timeouts if the RTL deadlocks somehow. Limit of how many uS to run? - Query sim result for pass/fail - - Each testcase folder will likely have multiple subtests - - Variations to RDL export: - - different cpuif - - pipe stages - - etc. - - Different test sequences - may be necessary to test the same compilation in different ways - I can imagine it may not be possible to do everything from a single test sequence. - May require the sim to reset to T-0 for fresh-slate. - - Handle these variations using pytest testcases & parameterizations as appropriate. - Possibly something like: - - Each pytest class --> unique compilation/elaboration - pytest parameters to expand this for export variations - - Each pytest class method --> simulation sequence - - Collect coverage! - install the tool in a venv, collect exporter coverage, etc. - TBD if i want to deal with SV coverage (is that even allowed in modelsim free?) - - ================================================================================ Dev Todo list ================================================================================ +- Signals - clean them up and add proper support + Generate these in the hwif_in struct? I forget what I decided + - FIXME: cpuif reset inside top-level addrmap results in two input signals: + - one popped out to top + - another inside the input struct + +- Interrupt properties + i think my docs are missing a property or something... + +- Rework TB CPUIF driver a bit + Split test API into a class + class receives a vif to the driver + make this more proper w.r.t extending stuff. + +- Add more CPUIF protocols + - AXI-Lite + - cpuif interface passthrough? + +- Add synthesis tests + Create a new testcase base class + Similar concept as before with testcases & parameterization. + Launch Vivado and do out-of-context synthesis + Override message severities to highlight: + - undriven nets + - multi-driven nets + - combo loops + Figure out a way to test these separately from other testcases + maybe rearrange folders so: + test/test_behav/... + test/test_synth/... + +- break out 'next' and singlepulse into a separate section of their own. + these should always be lowest priority regardless of precedence + and always end up as the "else" clause in the conditional list. + - Link more functions to the dereferencer I shouldn't have to go to the hwif or whatever dereferencer should have all the query functions -- Start a sphinx docs thing - I need to keep better track of everything! - Diagrams of each layer in an architecture overview - transcribe logbook into dev notes - - Define strict interface expectations for each layer! - Including latency/etc - endianness controls byte order of the CPU bus controls byteswap at the CPUIF layer Internally, use little endian ordering. @@ -155,16 +98,3 @@ endianness controls byte order of the CPU bus Do something about cpuif byte strobes? Remove for now? Demote to APB3? - - -- Other field output assignments - -- HWIF layer - - User Signals - Generate these in the io struct? I forget what I decided - -- dereferencer has some remaining todos that depend on field logic - -- FIXME: cpuif reset inside top-level addrmap results in two input signals: - - one popped out to top - - another inside the input struct diff --git a/doc/logbooks/Validation Needed b/doc/logbooks/Validation Needed index d252a31..5cc6ae6 100644 --- a/doc/logbooks/Validation Needed +++ b/doc/logbooks/Validation Needed @@ -93,15 +93,29 @@ X Flag illegal sw actions if not readable/writable X Signals marked as field_reset or cpuif_reset need to have activehigh/activelow specified. (8.2.1-d states that activehigh/low does not have an implied default state if unset!) - Also aplies to signals referenced by resetsignal + Also applies to signals referenced by resetsignal ! hwclr/hwset/we/wel probably shouldn't be able to reference itself y->hwclr = y; y->we = y; ... it works, but should it be allowed? Seems like user-error +X incrvalue/decrvalue needs to be the same or narrower than counter itself +! singlepulse and next properties cannot both be used. + Both are hard overrides of the field's next value. + singlepulse is basically an alias for next=0 that takes lowest priority +! counter field that saturates should not set overflow + counter; incrsaturate; overflow; + counter; decrsaturate; underflow; + + Flag this as an error on the overflow/underflow property. + overflow/underflow property is meaningless since it can never happen. + + Same goes to prop references to overflow/underflow + +! incrwidth/decrwidth must be between 1 and the width of the counter ================================================================================ Things that need validation by this exporter diff --git a/doc/props/addrmap.rst b/doc/props/addrmap.rst index 614711d..770cd3f 100644 --- a/doc/props/addrmap.rst +++ b/doc/props/addrmap.rst @@ -23,10 +23,6 @@ bigendian/littleendian ---------------------- |NO| -bridge ------- -|NO| - rsvdset ------- |NO| diff --git a/doc/props/field.rst b/doc/props/field.rst index 181faa2..f4a0ca3 100644 --- a/doc/props/field.rst +++ b/doc/props/field.rst @@ -19,6 +19,16 @@ singlepulse ^^^^^^^^^^^ |OK| +If set, field will get cleared back to zero after being written. + +.. wavedrom:: + + {signal: [ + {name: 'clk', wave: 'p.....'}, + {name: '', wave: '0.10..'}, + {name: 'hwif_out..value', wave: '0..10.'} + ]} + sw ^^^ |OK| @@ -143,93 +153,228 @@ Counter Properties counter ^^^^^^^ -|NO| +|OK| -decr -^^^^ -reference - |NO| +If true, marks this field as a counter. The counter direction is inferred based +based on which properties are assigned. By default, an up-counter is implemented. +If any of the properties associated with an up-counter are used, then up-counting +capabilities will be implemented. The same is true for down-counters and up/down +counters. -decrthreshold -^^^^^^^^^^^^^ -boolean - |NO| +Unless alternate control signals are specified, the existence of input signals +``hwif_in..incr`` and ``hwif_in..decr`` will be inferred depending on the type +of counter described. -bit - |NO| - -reference - |NO| - -decrsaturate -^^^^^^^^^^^^ -boolean - |NO| - -bit - |NO| - -reference - |NO| - -decrvalue -^^^^^^^^^ -bit - |NO| - -reference - |NO| - -decrwidth -^^^^^^^^^ -|NO| incr ^^^^ -|NO| +|OK| + +Assign a reference to an alternate control signal to increment the counter. +If assigned, the inferred ``hwif_in..incr`` input will not be generated. incrsaturate/saturate ^^^^^^^^^^^^^^^^^^^^^ -boolean - |NO| +If assigned, indicates that the counter will saturate instead of wrapping. +If an alternate saturation point is specified, the counter value will be +adjusted so that it does not exceed that limit, even after non-increment actions. -bit - |NO| +boolean + |OK| + + If true, saturation point is at the counter's maximum count value. (2^width - 1) + +integer + |OK| + + Specify a static saturation value. reference - |NO| + |OK| + + Specify a dynamic saturation value. + incrthreshold/threshold ^^^^^^^^^^^^^^^^^^^^^^^ -boolean - |NO| -bit - |NO| +If assigned, infers a ``hwif_out..incrthreshold`` output signal. This signal is +asserted if the counter value is greater or equal to the threshold. + +.. wavedrom:: + + { + signal: [ + {name: 'clk', wave: 'p......'}, + {name: 'hwif_in..incr', wave: '01...0.'}, + {name: '', wave: '=.=3==..', data: [4,5,6,7,8,9]}, + {name: 'hwif_out..incrthreshold', wave: '0..1....'} + ], + foot: { + text: "Example where incrthreshold = 6" + } + } + + +boolean + |EX| + + If true, threshold is the counter's maximum count value. (2^width - 1) + +integer + |EX| + + Specify a static threshold value. reference - |NO| + |EX| + + Specify a dynamic threshold value. + incrvalue ^^^^^^^^^ -bit - |NO| +Override the counter's increment step size. + +integer + |OK| reference - |NO| + |OK| incrwidth ^^^^^^^^^ -|NO| +|OK| + +If assigned, infers an input signal ``hwif_in..incrvalue``. The value of this +property defines the signal's width. + overflow ^^^^^^^^ -|NO| +|EX| + +If true, infers an output signal ``hwif_out..overflow`` that is asserted when +the counter is about to wrap. + +.. wavedrom:: + + { + signal: [ + {name: 'clk', wave: 'p.......'}, + {name: 'hwif_in..incr', wave: '0101010.'}, + {name: '', wave: '=.=.=.=.', data: [14,15,0,1]}, + {name: 'hwif_out..overflow', wave: '0..10...'} + ], + foot: { + text: "A 4-bit counter overflowing" + } + } + + +decr +^^^^ +|OK| + +Assign a reference to an alternate control signal to decrement the counter. +If assigned, the inferred ``hwif_in..decr`` input will not be generated. + +decrsaturate +^^^^^^^^^^^^ +If assigned, indicates that the counter will saturate instead of wrapping. +If an alternate saturation point is specified, the counter value will be +adjusted so that it does not exceed that limit, even after non-decrement actions. + +boolean + |OK| + + If true, saturation point is when the counter reaches 0. + +integer + |OK| + + Specify a static saturation value. + +reference + |OK| + + Specify a dynamic saturation value. + + +decrthreshold +^^^^^^^^^^^^^ + +If assigned, infers a ``hwif_out..decrthreshold`` output signal. This signal is +asserted if the counter value is less than or equal to the threshold. + +.. wavedrom:: + + { + signal: [ + {name: 'clk', wave: 'p......'}, + {name: 'hwif_in..decr', wave: '01...0.'}, + {name: '', wave: '=.=3==..', data: [9,8,7,6,5,4]}, + {name: 'hwif_out..decrthreshold', wave: '0..1....'} + ], + foot: { + text: "Example where incrthreshold = 7" + } + } + + +boolean + |EX| + + If true, threshold is 0. + +integer + |EX| + + Specify a static threshold value. + +reference + |EX| + + Specify a dynamic threshold value. + + +decrvalue +^^^^^^^^^ +Override the counter's decrement step size. + +integer + |OK| + +reference + |OK| + +decrwidth +^^^^^^^^^ +|OK| + +If assigned, infers an input signal ``hwif_in..decrvalue``. The value of this +property defines the signal's width. underflow ^^^^^^^^^ -|NO| +|EX| +If true, infers an output signal ``hwif_out..underflow`` that is asserted when +the counter is about to wrap. + +.. wavedrom:: + + { + signal: [ + {name: 'clk', wave: 'p.......'}, + {name: 'hwif_in..decr', wave: '0101010.'}, + {name: '', wave: '=.=.=.=.', data: [1,0,15,14]}, + {name: 'hwif_out..underflow', wave: '0..10...'} + ], + foot: { + text: "A 4-bit counter underflowing" + } + } -------------------------------------------------------------------------------- @@ -288,7 +433,7 @@ precedence reset ^^^^^ -bit +integer |OK| reference diff --git a/doc/props/reg.rst b/doc/props/reg.rst index cb094fa..933681a 100644 --- a/doc/props/reg.rst +++ b/doc/props/reg.rst @@ -13,7 +13,3 @@ Only ``accesswidth`` that is equal to the ``regwidth`` is supported (default if regwidth -------- |OK| - -shared ------- -|NO| diff --git a/doc/props/rhs_props.rst b/doc/props/rhs_props.rst index 8c1f1b8..51e33ef 100644 --- a/doc/props/rhs_props.rst +++ b/doc/props/rhs_props.rst @@ -6,7 +6,7 @@ property assignment expressions: .. code-block:: systemrdl - some_property = my_reg.my_field->some_property; + some_property = my_reg.my_field -> some_property; The official SystemRDL spec refers to these as "Ref targets" in Table G1, but unfortunately does not describe their semantics in much detail. @@ -18,63 +18,65 @@ The text below describes the interpretations used for this exporter. Field ----- -swacc -^^^^^ +field -> swacc +^^^^^^^^^^^^^^ |EX| Single-cycle strobe that indicates the field is being sampled during a software read operation. -swmod -^^^^^ +field -> swmod +^^^^^^^^^^^^^^^ |EX| Single-cycle strobe that indicates the field is being modified during a software access operation. -swwe/swwel -^^^^^^^^^^ +field -> swwe/swwel +^^^^^^^^^^^^^^^^^^^ |OK| -Represents the signal that controls the owning field's swwe/swwel behavior. +Represents the signal that controls the field's swwe/swwel behavior. -anded/ored/xored -^^^^^^^^^^^^^^^^ +field -> anded/ored/xored +^^^^^^^^^^^^^^^^^^^^^^^^^ |EX| -Represents the current and/or/xor reduction of the owning field's value. +Represents the current and/or/xor reduction of the field's value. -hwclr/hwset -^^^^^^^^^^^ +field -> hwclr/hwset +^^^^^^^^^^^^^^^^^^^^ |EX| -Represents the signal that controls the owning field's hwclr/hwset behavior. +Represents the signal that controls the field's hwclr/hwset behavior. -hwenable/hwmask +field -> hwenable/hwmask +^^^^^^^^^^^^^^^^^^^^^^^^ +|EX| + +Represents the signal that controls the field's hwenable/hwmask behavior. + +field -> we/wel ^^^^^^^^^^^^^^^ |EX| -Represents the signal that controls the owning field's hwenable/hwmask behavior. +Represents the signal that controls the field's we/wel behavior. -we/wel -^^^^^^ +field -> next +^^^^^^^^^^^^^ |EX| -next -^^^^ +field -> reset +^^^^^^^^^^^^^^ |EX| -reset -^^^^^ -|EX| - -resetsignal -^^^^^^^^^^^ +field -> resetsignal +^^^^^^^^^^^^^^^^^^^^ |EX| -------------------------------------------------------------------------------- @@ -82,67 +84,118 @@ resetsignal Field Counter Properties ------------------------ -Represents the signal that controls the owning field's we/wel behavior. - -decr -^^^^ -|NO| - -decrthreshold +field -> incr ^^^^^^^^^^^^^ -|NO| - -decrsaturate -^^^^^^^^^^^^ -|NO| - -decrvalue -^^^^^^^^^ |EX| -incr -^^^^ -|NO| +Represents the signal that controls the field's counter increment control. -incrsaturate/saturate +field -> incrsaturate/saturate +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +|EX| + +Represents the internal 1-bit event signal that indicates whether the counter is saturated +at its saturation value. + +.. wavedrom:: + + { + signal: [ + {name: 'clk', wave: 'p......'}, + {name: 'hwif_in..decr', wave: '0101010'}, + {name: '', wave: '=.=....', data: [1,0]}, + {name: '', wave: '0.1....'} + ], + foot: { + text: "A 4-bit counter saturating" + } + } + + +field -> incrthreshold/threshold +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +|EX| + +Represents the 1-bit event signal that indicates whether the counter has met or +exceeded its incrthreshold. + +field -> incrvalue +^^^^^^^^^^^^^^^^^^ +|EX| + +Represents the value that was assigned to this property. + +field -> overflow +^^^^^^^^^^^^^^^^^ +|OK| + +Represents the event signal that is asserted when the counter is about to wrap. + +field -> decr +^^^^^^^^^^^^^ +|EX| + +Represents the signal that controls the field's counter decrement control. + +field -> decrsaturate ^^^^^^^^^^^^^^^^^^^^^ -|NO| - -incrthreshold/threshold -^^^^^^^^^^^^^^^^^^^^^^^ -|NO| - -incrvalue -^^^^^^^^^ |EX| -overflow -^^^^^^^^ -|NO| +Represents the internal 1-bit event signal that indicates whether the counter is saturated +at its saturation value. -underflow -^^^^^^^^^ -|NO| +.. wavedrom:: + + { + signal: [ + {name: 'clk', wave: 'p......'}, + {name: 'hwif_in..incr', wave: '0101010'}, + {name: '', wave: '=.=....', data: [14,15]}, + {name: '', wave: '0.1....'} + ], + foot: { + text: "A 4-bit counter saturating" + } + } + +field -> decrthreshold +^^^^^^^^^^^^^^^^^^^^^^ +|EX| + +Represents the 1-bit event signal that indicates whether the counter has met or +exceeded its incrthreshold. + +field -> decrvalue +^^^^^^^^^^^^^^^^^^ +|EX| + +Represents the value that was assigned to this property. + +field -> underflow +^^^^^^^^^^^^^^^^^^ +|OK| + +Represents the event signal that is asserted when the counter is about to wrap. -------------------------------------------------------------------------------- Field Interrupt Properties -------------------------- -enable -^^^^^^ +field -> enable +^^^^^^^^^^^^^^^ |EX| -haltenable -^^^^^^^^^^ +field -> haltenable +^^^^^^^^^^^^^^^^^^^ |EX| -haltmask -^^^^^^^^ +field -> haltmask +^^^^^^^^^^^^^^^^^ |EX| -mask -^^^^ +field -> mask +^^^^^^^^^^^^^ |EX| @@ -151,10 +204,10 @@ mask Register -------- -intr -^^^^ +reg -> intr +^^^^^^^^^^^ |NO| -halt -^^^^ +reg -> halt +^^^^^^^^^^^ |NO| diff --git a/peakrdl/regblock/dereferencer.py b/peakrdl/regblock/dereferencer.py index 9953889..e475f9b 100644 --- a/peakrdl/regblock/dereferencer.py +++ b/peakrdl/regblock/dereferencer.py @@ -60,8 +60,12 @@ class Dereferencer: return self.get_value(reset_value) else: # No reset value defined! - # Callers shall ensure this is impossible - raise RuntimeError + obj.env.msg.warning( + "Field '%s' is a constant but does not have a known value (missing reset). Assigning it a value of X." + % obj.inst_name, + obj.inst.inst_src_ref + ) + return "'X" if isinstance(obj, SignalNode): # Signals are always inputs from the hwif @@ -106,28 +110,12 @@ class Dereferencer: }: return self.get_value(field.get_property(prop_name)) - # Counter properties - if prop_name == 'incr': - prop_value = field.get_property(prop_name) - if prop_value is None: - # unset by the user, points to the implied internal signal - return self.field_logic.get_counter_incr_identifier(field) - else: - return self.get_value(prop_value) - elif prop_name == 'decr': - prop_value = field.get_property(prop_name) - if prop_value is None: - # unset by the user, points to the implied internal signal - return self.field_logic.get_counter_decr_identifier(field) - else: - return self.get_value(prop_value) - # Field Next if prop_name == "next": prop_value = field.get_property(prop_name) if prop_value is None: # unset by the user, points to the implied internal signal - return self.field_logic.get_field_next_identifier(field) + return self.field_logic.get_field_combo_identifier(field, "next") else: return self.get_value(prop_value) @@ -175,19 +163,31 @@ class Dereferencer: if prop_name == "swmod": return self.field_logic.get_swmod_identifier(field) - raise RuntimeError("Unhandled reference to: %s->%s" % (field, prop_name)) - """ - TODO: - Resolves to an internal signal used in the field's logic - decrsaturate - decrthreshold - incrsaturate - incrthreshold - overflow - saturate - threshold - """ + # translate aliases + aliases = { + "saturate": "incrsaturate", + "threshold": "incrthreshold", + } + prop_name = aliases.get(prop_name, prop_name) + + # Counter properties + if prop_name == 'incr': + return self.field_logic.get_counter_incr_strobe(field) + if prop_name == 'decr': + return self.field_logic.get_counter_decr_strobe(field) + + if prop_name in { + 'decrsaturate', + 'decrthreshold', + 'incrsaturate', + 'incrthreshold', + 'overflow', + 'underflow', + }: + return self.field_logic.get_field_combo_identifier(field, prop_name) + + raise RuntimeError("Unhandled reference to: %s->%s" % (field, prop_name)) def get_reg_propref_value(self, reg: RegNode, prop_name: str) -> str: diff --git a/peakrdl/regblock/field_logic/__init__.py b/peakrdl/regblock/field_logic/__init__.py index 2e6bf6a..f43c9a7 100644 --- a/peakrdl/regblock/field_logic/__init__.py +++ b/peakrdl/regblock/field_logic/__init__.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from systemrdl.rdltypes import PropertyReference, PrecedenceType +from systemrdl.node import Node from .bases import AssignmentPrecedence, NextStateConditional from . import sw_onread @@ -61,41 +62,105 @@ class FieldLogic: #--------------------------------------------------------------------------- # Field utility functions #--------------------------------------------------------------------------- - def get_storage_identifier(self, node: 'FieldNode') -> str: + def get_storage_identifier(self, field: 'FieldNode') -> str: """ Returns the Verilog string that represents the storage register element for the referenced field """ - assert node.implements_storage - path = get_indexed_path(self.top_node, node) + assert field.implements_storage + path = get_indexed_path(self.top_node, field) return f"field_storage.{path}" - def get_field_next_identifier(self, node: 'FieldNode') -> str: + def get_field_combo_identifier(self, field: 'FieldNode', name: str) -> str: """ - Returns a Verilog string that represents the field's next-state. - This is specifically for use in Field->next property references. + Returns a Verilog string that represents a field's internal combinational + signal. """ - assert node.implements_storage - path = get_indexed_path(self.top_node, node) - return f"field_combo.{path}.next" + assert field.implements_storage + path = get_indexed_path(self.top_node, field) + return f"field_combo.{path}.{name}" - def get_counter_incr_identifier(self, field: 'FieldNode') -> str: + def get_counter_incr_strobe(self, field: 'FieldNode') -> str: """ - Return the Veriog string that represents the field's inferred incr/decr strobe signal. - prop_ref will be either an incr or decr property reference, and it is already known that - the incr/decr properties are not explicitly set by the user and are therefore inferred. + Return the Verilog string that represents the field's incr strobe signal. """ - # TODO: Implement this - raise NotImplementedError + prop_value = field.get_property('incr') + if prop_value: + return self.exp.dereferencer.get_value(prop_value) - def get_counter_decr_identifier(self, field: 'FieldNode') -> str: + # unset by the user, points to the implied input signal + return self.exp.hwif.get_implied_prop_input_identifier(field, "incr") + + def get_counter_incrvalue(self, field: 'FieldNode') -> str: """ - Return the Veriog string that represents the field's inferred incr/decr strobe signal. - prop_ref will be either an incr or decr property reference, and it is already known that - the incr/decr properties are not explicitly set by the user and are therefore inferred. + Return the string that represents the field's increment value """ - # TODO: Implement this - raise NotImplementedError + incrvalue = field.get_property('incrvalue') + if incrvalue is not None: + return self.exp.dereferencer.get_value(incrvalue) + if field.get_property('incrwidth'): + return self.exp.hwif.get_implied_prop_input_identifier(field, "incrvalue") + return "1'b1" + + def get_counter_incrsaturate_value(self, field: 'FieldNode') -> str: + prop_value = field.get_property('incrsaturate') + if prop_value is True: + return self.exp.dereferencer.get_value(2**field.width - 1) + return self.exp.dereferencer.get_value(prop_value) + + def counter_incrsaturates(self, field: 'FieldNode') -> bool: + """ + Returns True if the counter saturates + """ + return field.get_property('incrsaturate') is not False + + def get_counter_incrthreshold_value(self, field: 'FieldNode') -> str: + prop_value = field.get_property('incrthreshold') + if isinstance(prop_value, bool): + # No explicit value set. use max + return self.exp.dereferencer.get_value(2**field.width - 1) + return self.exp.dereferencer.get_value(prop_value) + + def get_counter_decr_strobe(self, field: 'FieldNode') -> str: + """ + Return the Verilog string that represents the field's incr strobe signal. + """ + prop_value = field.get_property('decr') + if prop_value: + return self.exp.dereferencer.get_value(prop_value) + + # unset by the user, points to the implied input signal + return self.exp.hwif.get_implied_prop_input_identifier(field, "decr") + + def get_counter_decrvalue(self, field: 'FieldNode') -> str: + """ + Return the string that represents the field's decrement value + """ + decrvalue = field.get_property('decrvalue') + if decrvalue is not None: + return self.exp.dereferencer.get_value(decrvalue) + if field.get_property('decrwidth'): + return self.exp.hwif.get_implied_prop_input_identifier(field, "decrvalue") + return "1'b1" + + def get_counter_decrsaturate_value(self, field: 'FieldNode') -> str: + prop_value = field.get_property('decrsaturate') + if prop_value is True: + return "'d0" + return self.exp.dereferencer.get_value(prop_value) + + def counter_decrsaturates(self, field: 'FieldNode') -> bool: + """ + Returns True if the counter saturates + """ + return field.get_property('decrsaturate') is not False + + def get_counter_decrthreshold_value(self, field: 'FieldNode') -> str: + prop_value = field.get_property('decrthreshold') + if isinstance(prop_value, bool): + # No explicit value set. use min + return "'d0" + return self.exp.dereferencer.get_value(prop_value) def get_swacc_identifier(self, field: 'FieldNode') -> str: """ diff --git a/peakrdl/regblock/field_logic/generators.py b/peakrdl/regblock/field_logic/generators.py index c60b624..5781667 100644 --- a/peakrdl/regblock/field_logic/generators.py +++ b/peakrdl/regblock/field_logic/generators.py @@ -39,8 +39,26 @@ class CombinationalStructGenerator(RDLStructGenerator): self.add_member("load_next") for signal in extra_combo_signals.values(): self.add_member(signal.name, signal.width) + if node.is_up_counter: + self.add_up_counter_members(node) + if node.is_down_counter: + self.add_down_counter_members(node) self.pop_struct() + def add_up_counter_members(self, node: 'FieldNode') -> None: + self.add_member('incrthreshold') + if self.field_logic.counter_incrsaturates(node): + self.add_member('incrsaturate') + else: + self.add_member('overflow') + + def add_down_counter_members(self, node: 'FieldNode') -> None: + self.add_member('decrthreshold') + if self.field_logic.counter_decrsaturates(node): + self.add_member('decrsaturate') + else: + self.add_member('underflow') + class FieldStorageStructGenerator(RDLStructGenerator): @@ -92,10 +110,12 @@ class FieldLogicGenerator(RDLForLoopGenerator): 'node': node, 'reset': reset_value_str, 'field_path': get_indexed_path(self.exp.top_node, node), + 'field_logic': self.field_logic, 'extra_combo_signals': extra_combo_signals, 'conditionals': conditionals, 'resetsignal': resetsignal, 'get_always_ff_event': get_always_ff_event, + 'get_value': self.exp.dereferencer.get_value, } self.add_content(self.field_storage_template.render(context)) @@ -144,3 +164,16 @@ class FieldLogicGenerator(RDLForLoopGenerator): self.add_content( f"assign {output_identifier} = {value};" ) + + 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') + self.add_content( + f"assign {output_identifier} = {value};" + ) + if node.get_property('decrthreshold') is not False: # (explicitly not False. Not 0) + output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "decrthreshold") + value = self.field_logic.get_field_combo_identifier(node, 'decrthreshold') + self.add_content( + f"assign {output_identifier} = {value};" + ) diff --git a/peakrdl/regblock/field_logic/hw_set_clr.py b/peakrdl/regblock/field_logic/hw_set_clr.py index b5321fd..bb2cf38 100644 --- a/peakrdl/regblock/field_logic/hw_set_clr.py +++ b/peakrdl/regblock/field_logic/hw_set_clr.py @@ -36,8 +36,8 @@ class HWSet(NextStateConditional): next_val = "'1" return [ - f"field_combo.{field_path}.next = {next_val};", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = {next_val};", + f"load_next_c = '1;", ] @@ -71,6 +71,6 @@ class HWClear(NextStateConditional): next_val = "'0" return [ - f"field_combo.{field_path}.next = {next_val};", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = {next_val};", + f"load_next_c = '1;", ] diff --git a/peakrdl/regblock/field_logic/hw_write.py b/peakrdl/regblock/field_logic/hw_write.py index 55bf4ed..e4f4073 100644 --- a/peakrdl/regblock/field_logic/hw_write.py +++ b/peakrdl/regblock/field_logic/hw_write.py @@ -39,8 +39,8 @@ class AlwaysWrite(NextStateConditional): next_val = self.exp.hwif.get_input_identifier(field) return [ - f"field_combo.{field_path}.next = {next_val};", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = {next_val};", + f"load_next_c = '1;", ] class WEWrite(AlwaysWrite): diff --git a/peakrdl/regblock/field_logic/sw_onread.py b/peakrdl/regblock/field_logic/sw_onread.py index 02e20b1..0678160 100644 --- a/peakrdl/regblock/field_logic/sw_onread.py +++ b/peakrdl/regblock/field_logic/sw_onread.py @@ -24,8 +24,8 @@ class ClearOnRead(_OnRead): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = '0;", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = '0;", + f"load_next_c = '1;", ] @@ -36,6 +36,6 @@ class SetOnRead(_OnRead): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = '1;", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = '1;", + f"load_next_c = '1;", ] diff --git a/peakrdl/regblock/field_logic/sw_onwrite.py b/peakrdl/regblock/field_logic/sw_onwrite.py index 4211a63..8471774 100644 --- a/peakrdl/regblock/field_logic/sw_onwrite.py +++ b/peakrdl/regblock/field_logic/sw_onwrite.py @@ -40,8 +40,8 @@ class WriteOneSet(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = field_storage.{field_path} | {self._wr_data(field)};", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = field_storage.{field_path} | {self._wr_data(field)};", + f"load_next_c = '1;", ] class WriteOneClear(_OnWrite): @@ -51,8 +51,8 @@ class WriteOneClear(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = field_storage.{field_path} & ~{self._wr_data(field)};", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = field_storage.{field_path} & ~{self._wr_data(field)};", + f"load_next_c = '1;", ] class WriteOneToggle(_OnWrite): @@ -62,8 +62,8 @@ class WriteOneToggle(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = field_storage.{field_path} ^ {self._wr_data(field)};", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = field_storage.{field_path} ^ {self._wr_data(field)};", + f"load_next_c = '1;", ] class WriteZeroSet(_OnWrite): @@ -73,8 +73,8 @@ class WriteZeroSet(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = field_storage.{field_path} | ~{self._wr_data(field)};", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = field_storage.{field_path} | ~{self._wr_data(field)};", + f"load_next_c = '1;", ] class WriteZeroClear(_OnWrite): @@ -84,8 +84,8 @@ class WriteZeroClear(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = field_storage.{field_path} & {self._wr_data(field)};", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = field_storage.{field_path} & {self._wr_data(field)};", + f"load_next_c = '1;", ] class WriteZeroToggle(_OnWrite): @@ -95,8 +95,8 @@ class WriteZeroToggle(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = field_storage.{field_path} ^ ~{self._wr_data(field)};", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = field_storage.{field_path} ^ ~{self._wr_data(field)};", + f"load_next_c = '1;", ] class WriteClear(_OnWrite): @@ -106,8 +106,8 @@ class WriteClear(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = '0;", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = '0;", + f"load_next_c = '1;", ] class WriteSet(_OnWrite): @@ -117,8 +117,8 @@ class WriteSet(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = '1;", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = '1;", + f"load_next_c = '1;", ] class Write(_OnWrite): @@ -128,6 +128,6 @@ class Write(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = {self._wr_data(field)};", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = {self._wr_data(field)};", + f"load_next_c = '1;", ] diff --git a/peakrdl/regblock/field_logic/sw_singlepulse.py b/peakrdl/regblock/field_logic/sw_singlepulse.py index 0b35e8c..56d8c4b 100644 --- a/peakrdl/regblock/field_logic/sw_singlepulse.py +++ b/peakrdl/regblock/field_logic/sw_singlepulse.py @@ -18,6 +18,6 @@ class Singlepulse(NextStateConditional): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = '0;", - f"field_combo.{field_path}.load_next = '1;", + f"next_c = '0;", + f"load_next_c = '1;", ] diff --git a/peakrdl/regblock/field_logic/templates/counter_macros.sv b/peakrdl/regblock/field_logic/templates/counter_macros.sv new file mode 100644 index 0000000..4cba925 --- /dev/null +++ b/peakrdl/regblock/field_logic/templates/counter_macros.sv @@ -0,0 +1,56 @@ +{% macro up_counter(field) -%} + if({{field_logic.get_counter_incr_strobe(node)}}) begin // increment + {%- if field_logic.counter_incrsaturates(node) %} + if((({{node.width+1}})'(next_c) + {{field_logic.get_counter_incrvalue(node)}}) > {{field_logic.get_counter_incrsaturate_value(node)}}) begin // up-counter saturated + next_c = {{field_logic.get_counter_incrsaturate_value(node)}}; + end else begin + next_c = next_c + {{field_logic.get_counter_incrvalue(node)}}; + end + {%- else %} + field_combo.{{field_path}}.overflow = ((({{node.width+1}})'(next_c) + {{field_logic.get_counter_incrvalue(node)}}) > {{get_value(2**node.width - 1)}}); + next_c = next_c + {{field_logic.get_counter_incrvalue(node)}}; + {%- endif %} + load_next_c = '1; + {%- if not field_logic.counter_incrsaturates(node) %} + end else begin + field_combo.{{field_path}}.overflow = '0; + {%- endif %} + end + field_combo.{{field_path}}.incrthreshold = (field_storage.{{field_path}} >= {{field_logic.get_counter_incrthreshold_value(node)}}); + {%- if field_logic.counter_incrsaturates(node) %} + field_combo.{{field_path}}.incrsaturate = (field_storage.{{field_path}} >= {{field_logic.get_counter_incrsaturate_value(node)}}); + if(next_c > {{field_logic.get_counter_incrsaturate_value(node)}}) begin + next_c = {{field_logic.get_counter_incrsaturate_value(node)}}; + load_next_c = '1; + end + {%- endif %} +{%- endmacro %} + + +{% macro down_counter(field) -%} + if({{field_logic.get_counter_decr_strobe(node)}}) begin // decrement + {%- if field_logic.counter_decrsaturates(node) %} + if(({{node.width+1}})'(next_c) < ({{field_logic.get_counter_decrvalue(node)}} + {{field_logic.get_counter_decrsaturate_value(node)}})) begin // down-counter saturated + next_c = {{field_logic.get_counter_decrsaturate_value(node)}}; + end else begin + next_c = next_c - {{field_logic.get_counter_decrvalue(node)}}; + end + {%- else %} + field_combo.{{field_path}}.underflow = (next_c < ({{field_logic.get_counter_decrvalue(node)}})); + next_c = next_c - {{field_logic.get_counter_decrvalue(node)}}; + {%- endif %} + load_next_c = '1; + {%- if not field_logic.counter_decrsaturates(node) %} + end else begin + field_combo.{{field_path}}.underflow = '0; + {%- endif %} + end + field_combo.{{field_path}}.decrthreshold = (field_storage.{{field_path}} <= {{field_logic.get_counter_decrthreshold_value(node)}}); + {%- if field_logic.counter_decrsaturates(node) %} + field_combo.{{field_path}}.decrsaturate = (field_storage.{{field_path}} <= {{field_logic.get_counter_decrsaturate_value(node)}}); + if(next_c < {{field_logic.get_counter_decrsaturate_value(node)}}) begin + next_c = {{field_logic.get_counter_decrsaturate_value(node)}}; + load_next_c = '1; + end + {%- endif %} +{%- endmacro %} diff --git a/peakrdl/regblock/field_logic/templates/field_storage.sv b/peakrdl/regblock/field_logic/templates/field_storage.sv index 541c75c..8536e2f 100644 --- a/peakrdl/regblock/field_logic/templates/field_storage.sv +++ b/peakrdl/regblock/field_logic/templates/field_storage.sv @@ -1,7 +1,8 @@ +{%- import 'field_logic/templates/counter_macros.sv' as counter_macros with context -%} // Field: {{node.get_path()}} always_comb begin - field_combo.{{field_path}}.next = field_storage.{{field_path}}; - field_combo.{{field_path}}.load_next = '0; + automatic logic [{{node.width-1}}:0] next_c = field_storage.{{field_path}}; + automatic logic load_next_c = '0; {%- for signal in extra_combo_signals %} field_combo.{{field_path}}.{{signal.name}} = {{signal.default_assignment}}; {%- endfor %} @@ -12,6 +13,14 @@ always_comb begin {%- endfor %} end {%- endfor %} + {%- if node.is_up_counter %} + {{counter_macros.up_counter(node)}} + {%- endif %} + {%- if node.is_down_counter %} + {{counter_macros.down_counter(node)}} + {%- endif %} + field_combo.{{field_path}}.next = next_c; + field_combo.{{field_path}}.load_next = load_next_c; end always_ff {{get_always_ff_event(resetsignal)}} begin {% if resetsignal is not none -%} diff --git a/peakrdl/regblock/hwif.py b/peakrdl/regblock/hwif.py index faafbe0..62e1edd 100644 --- a/peakrdl/regblock/hwif.py +++ b/peakrdl/regblock/hwif.py @@ -159,21 +159,23 @@ class Hwif: contents.append(f"logic {prop_name};") # Generate any implied counter inputs - if node.get_property('counter'): + if node.is_up_counter: if not node.get_property('incr'): # User did not provide their own incr component reference. # Imply an input contents.append("logic incr;") - if not node.get_property('decr'): - # User did not provide their own decr component reference. - # Imply an input - contents.append("logic decr;") width = node.get_property('incrwidth') if width: # Implies a corresponding incrvalue input contents.append(f"logic [{width-1}:0] incrvalue;") + if node.is_down_counter: + if not node.get_property('decr'): + # User did not provide their own decr component reference. + # Imply an input + contents.append("logic decr;") + width = node.get_property('decrwidth') if width: # Implies a corresponding decrvalue input @@ -198,10 +200,15 @@ class Hwif: contents.append(f"logic [{node.width-1}:0] value;") # Generate output bit signals enabled via property - for prop_name in ["anded", "ored", "xored", "swmod", "swacc"]: + for prop_name in ["anded", "ored", "xored", "swmod", "swacc", "overflow", "underflow"]: if node.get_property(prop_name): contents.append(f"logic {prop_name};") + if node.get_property('incrthreshold') is not False: # (explicitly not False. Not 0) + contents.append("logic incrthreshold;") + if node.get_property('decrthreshold') is not False: # (explicitly not False. Not 0) + contents.append("logic decrthreshold;") + return contents #--------------------------------------------------------------------------- @@ -252,7 +259,10 @@ class Hwif: def get_implied_prop_input_identifier(self, field: FieldNode, prop: str) -> str: - assert prop in {'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel'} + assert prop in { + 'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel', + 'incr', 'decr', 'incrvalue', 'decrvalue' + } path = get_indexed_path(self.top_node, field) return "hwif_in." + path + "." + prop diff --git a/setup.py b/setup.py index 96cfe24..435774a 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ setuptools.setup( include_package_data=True, python_requires='>=3.6', install_requires=[ - "systemrdl-compiler>=1.21.0", + "systemrdl-compiler>=1.22.0", "Jinja2>=2.11", ], classifiers=( diff --git a/test/lib/cpuifs/apb3/apb3_intf_driver.sv b/test/lib/cpuifs/apb3/apb3_intf_driver.sv index 082907b..97b449c 100644 --- a/test/lib/cpuifs/apb3/apb3_intf_driver.sv +++ b/test/lib/cpuifs/apb3/apb3_intf_driver.sv @@ -91,9 +91,10 @@ interface apb3_intf_driver #( reset(); endtask - task assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data); + task assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1); logic [DATA_WIDTH-1:0] data; read(addr, data); + data &= mask; assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data); endtask diff --git a/test/lib/simulators/modelsim.py b/test/lib/simulators/modelsim.py index 51aced0..10e4128 100644 --- a/test/lib/simulators/modelsim.py +++ b/test/lib/simulators/modelsim.py @@ -9,6 +9,8 @@ class ModelSim(Simulator): cmd = [ "vlog", "-sv", "-quiet", "-l", "build.log", + "+incdir+%s" % os.path.join(os.path.dirname(__file__), ".."), + # Free version of ModelSim throws errors if generate/endgenerate # blocks are not used. # These have been made optional long ago. Modern versions of SystemVerilog do diff --git a/test/lib/simulators/xilinx.py b/test/lib/simulators/xilinx.py index 793f31b..80171ab 100644 --- a/test/lib/simulators/xilinx.py +++ b/test/lib/simulators/xilinx.py @@ -8,13 +8,14 @@ class Xilinx(Simulator): """ Don't bother using the Xilinx simulator... Its buggy and extraordinarily slow. As observed in v2021.1, clocking block assignments do not seem to actually simulate - correctly - assignemnt statements get ignored or the values get mangled. + correctly - assignment statements get ignored or the values get mangled. Keeping this here in case someday it works better... """ def compile(self) -> None: cmd = [ - "xvlog", "--sv" + "xvlog", "--sv", + "--include", os.path.join(os.path.dirname(__file__), ".."), ] cmd.extend(self.tb_files) subprocess.run(cmd, check=True) diff --git a/test/lib/templates/tb_base.sv b/test/lib/tb_base.sv similarity index 98% rename from test/lib/templates/tb_base.sv rename to test/lib/tb_base.sv index 31464f9..b64fa83 100644 --- a/test/lib/templates/tb_base.sv +++ b/test/lib/tb_base.sv @@ -66,6 +66,7 @@ module tb; end end {%- endif %} + {% sv_line_anchor %} //-------------------------------------------------------------------------- // Test Sequence @@ -82,7 +83,7 @@ module tb; {%- endblock %} {%- endfilter %} end - + {% sv_line_anchor %} ##5; $finish(); end @@ -90,7 +91,6 @@ module tb; //-------------------------------------------------------------------------- // Monitor for timeout //-------------------------------------------------------------------------- - {% sv_line_anchor %} initial begin ##{{cls.timeout_clk_cycles}}; $fatal(1, "Test timed out after {{cls.timeout_clk_cycles}} clock cycles"); diff --git a/test/test_bitwise_reduce/tb_template.sv b/test/test_bitwise_reduce/tb_template.sv index 85752ab..9222491 100644 --- a/test/test_bitwise_reduce/tb_template.sv +++ b/test/test_bitwise_reduce/tb_template.sv @@ -1,4 +1,4 @@ -{% extends "lib/templates/tb_base.sv" %} +{% extends "lib/tb_base.sv" %} {% block seq %} {% sv_line_anchor %} diff --git a/test/test_counter_basics/__init__.py b/test/test_counter_basics/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_counter_basics/regblock.rdl b/test/test_counter_basics/regblock.rdl new file mode 100644 index 0000000..47e55d5 --- /dev/null +++ b/test/test_counter_basics/regblock.rdl @@ -0,0 +1,68 @@ +addrmap top { + reg { + field { + sw=r; hw=na; counter; + } implied_up[3:0] = 0xD; + field { + sw=r; hw=na; counter; + incrvalue=1; + } up[7:4] = 0xD; + field { + sw=r; hw=na; counter; + decrvalue=1; + } down[11:8] = 0x4; + field { + sw=r; hw=na; counter; + incrvalue=1; + decrvalue=1; + } updown[15:12] = 0; + + field { + sw=r; hw=na; counter; + incrvalue=3; + decrvalue=3; + } updown2[19:16] = 0; + + field { + sw=r; hw=na; counter; + incrwidth=2; + decrwidth=2; + } updown3[23:20] = 0; + + field { + sw=r; hw=na; counter; + } updown4[27:24] = 0; + + field { + sw=rw; hw=na; + } step[29:28] = 0; + updown4->incrvalue = step; + updown4->decrvalue = step; + + field { + sw=w; hw=r; singlepulse; + } do_count_up[30:30] = 0; + field { + sw=w; hw=r; singlepulse; + } do_count_down[31:31] = 0; + updown2->incr = do_count_up; + updown2->decr = do_count_down; + updown3->incr = do_count_up; + updown3->decr = do_count_down; + updown4->incr = do_count_up; + updown4->decr = do_count_down; + } simple @ 0x0; + + + reg { + field { + sw=r; hw=na; rclr; counter; + } overflow_count[8] = 0; + + field { + sw=r; hw=na; rclr; counter; + } underflow_count[8] = 0; + } wrap_counter @ 0x4; + wrap_counter.overflow_count->incr = simple.updown3->overflow; + wrap_counter.underflow_count->incr = simple.updown3->underflow; +}; diff --git a/test/test_counter_basics/tb_template.sv b/test/test_counter_basics/tb_template.sv new file mode 100644 index 0000000..55fa61c --- /dev/null +++ b/test/test_counter_basics/tb_template.sv @@ -0,0 +1,93 @@ +{% extends "lib/tb_base.sv" %} + +{% block seq %} + {% sv_line_anchor %} + ##1; + cb.rst <= '0; + ##1; + + //-------------------------------------------------------------------------- + // Test simple counters + //-------------------------------------------------------------------------- + // up + cpuif.assert_read('h0, 'hD, 'h000F); + cb.hwif_in.simple.implied_up.incr <= '1; + repeat(4) @cb; + cb.hwif_in.simple.implied_up.incr <= '0; + cpuif.assert_read('h0, 'h1, 'h000F); + + // up + cpuif.assert_read('h0, 'hD0, 'h00F0); + cb.hwif_in.simple.up.incr <= '1; + repeat(4) @cb; + cb.hwif_in.simple.up.incr <= '0; + cpuif.assert_read('h0, 'h10, 'h00F0); + + // down + cpuif.assert_read('h0, 'h400, 'h0F00); + cb.hwif_in.simple.down.decr <= '1; + repeat(6) @cb; + cb.hwif_in.simple.down.decr <= '0; + cpuif.assert_read('h0, 'hE00, 'h0F00); + + // up/down via hw + cpuif.assert_read('h0, 'h0000, 'hF000); + cb.hwif_in.simple.updown.incr <= '1; + repeat(6) @cb; + cb.hwif_in.simple.updown.incr <= '0; + cpuif.assert_read('h0, 'h6000, 'hF000); + cb.hwif_in.simple.updown.decr <= '1; + repeat(6) @cb; + cb.hwif_in.simple.updown.decr <= '0; + cpuif.assert_read('h0, 'h0000, 'hF000); + cb.hwif_in.simple.updown.decr <= '1; + repeat(6) @cb; + cb.hwif_in.simple.updown.decr <= '0; + cpuif.assert_read('h0, 'hA000, 'hF000); + cb.hwif_in.simple.updown.incr <= '1; + repeat(6) @cb; + cb.hwif_in.simple.updown.incr <= '0; + cpuif.assert_read('h0, 'h0000, 'hF000); + + // up/down via sw + cpuif.assert_read('h0, 'h00000, 'hF0000); + repeat(3) cpuif.write('h0, 'h4000_0000); // incr + cpuif.assert_read('h0, 'h90000, 'hF0000); + repeat(3) cpuif.write('h0, 'h8000_0000); // decr + cpuif.assert_read('h0, 'h00000, 'hF0000); + repeat(3) cpuif.write('h0, 'h8000_0000); // decr + cpuif.assert_read('h0, 'h70000, 'hF0000); + repeat(3) cpuif.write('h0, 'h4000_0000); // incr + cpuif.assert_read('h0, 'h00000, 'hF0000); + + // up/down via hw + external dynamic stepsize + cpuif.assert_read('h0, 'h000000, 'hF00000); + cb.hwif_in.simple.updown3.incrvalue <= 'h2; + repeat(3) cpuif.write('h0, 'h4000_0000); // incr + cpuif.assert_read('h0, 'h600000, 'hF00000); + cpuif.assert_read('h4, 'h00_00); // no overflows or underflows + cb.hwif_in.simple.updown3.decrvalue <= 'h3; + repeat(3) cpuif.write('h0, 'h8000_0000); // decr + cpuif.assert_read('h0, 'hD00000, 'hF00000); + cpuif.assert_read('h4, 'h01_00); // one underflow + cb.hwif_in.simple.updown3.incrvalue <= 'h1; + repeat(2) cpuif.write('h0, 'h4000_0000); // incr + cpuif.assert_read('h0, 'hF00000, 'hF00000); + cpuif.assert_read('h4, 'h00_00); // no overflows or underflows + repeat(1) cpuif.write('h0, 'h4000_0000); // incr + cpuif.assert_read('h0, 'h000000, 'hF00000); + cpuif.assert_read('h4, 'h00_01); // one overflow + repeat(32) cpuif.write('h0, 'h4000_0000); // incr + cpuif.assert_read('h0, 'h000000, 'hF00000); + cpuif.assert_read('h4, 'h00_02); // overflow + + // up/down via hw + referenced dynamic stepsize + cpuif.assert_read('h0, 'h0000000, 'hF000000); + repeat(4) cpuif.write('h0, 'h4000_0000 + (2'h3 << 28)); // + 3 + cpuif.assert_read('h0, 'hC000000, 'hF000000); + repeat(4) cpuif.write('h0, 'h8000_0000 + (2'h1 << 28)); // - 1 + cpuif.assert_read('h0, 'h8000000, 'hF000000); + repeat(2) cpuif.write('h0, 'h8000_0000 + (2'h3 << 28)); // - 3 + cpuif.assert_read('h0, 'h2000000, 'hF000000); + +{% endblock %} diff --git a/test/test_counter_basics/testcase.py b/test/test_counter_basics/testcase.py new file mode 100644 index 0000000..b458d3c --- /dev/null +++ b/test/test_counter_basics/testcase.py @@ -0,0 +1,5 @@ +from ..lib.regblock_testcase import RegblockTestCase + +class Test(RegblockTestCase): + def test_dut(self): + self.run_test() diff --git a/test/test_counter_saturate/__init__.py b/test/test_counter_saturate/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_counter_saturate/regblock.rdl b/test/test_counter_saturate/regblock.rdl new file mode 100644 index 0000000..f42a27c --- /dev/null +++ b/test/test_counter_saturate/regblock.rdl @@ -0,0 +1,88 @@ +addrmap top { + field strobe_t { + sw=w; hw=r; singlepulse; + }; + + reg { + field { + sw=r; hw=r; counter; + incrsaturate; + decrsaturate; + } count[8] = 0; + + strobe_t increment[9:9] = 0; + strobe_t decrement[10:10] = 0; + strobe_t clear[11:11] = 0; + strobe_t set[12:12] = 0; + + field { + sw=rw; hw=na; + } step[23:16] = 1; + + count->incr = increment; + count->decr = decrement; + count->hwclr = clear; + count->hwset = set; + count->incrvalue = step; + count->decrvalue = step; + } saturate_via_bool @ 0x0; + + + reg { + field { + sw=r; hw=r; counter; + incrsaturate = 250; + decrsaturate = 5; + } count[8] = 0; + + strobe_t increment[9:9] = 0; + strobe_t decrement[10:10] = 0; + strobe_t clear[11:11] = 0; + strobe_t set[12:12] = 0; + + field { + sw=rw; hw=na; + } step[23:16] = 1; + + count->incr = increment; + count->decr = decrement; + count->hwclr = clear; + count->hwset = set; + count->incrvalue = step; + count->decrvalue = step; + } saturate_via_const @ 0x4; + + + reg { + field { + sw=r; hw=r; counter; + } count[8] = 0; + + strobe_t increment[9:9] = 0; + strobe_t decrement[10:10] = 0; + strobe_t clear[11:11] = 0; + strobe_t set[12:12] = 0; + + field { + sw=rw; hw=na; + } step[23:16] = 1; + + count->incr = increment; + count->decr = decrement; + count->hwclr = clear; + count->hwset = set; + count->incrvalue = step; + count->decrvalue = step; + } saturate_via_ref @ 0x8; + + reg { + field { + sw=rw; hw=na; + } min[8] = 0x00; + field { + sw=rw; hw=na; + } max[8] = 0xFF; + } saturate_control @ 0xC; + saturate_via_ref.count -> decrsaturate = saturate_control.min; + saturate_via_ref.count -> incrsaturate = saturate_control.max; +}; diff --git a/test/test_counter_saturate/tb_template.sv b/test/test_counter_saturate/tb_template.sv new file mode 100644 index 0000000..bc3f2ea --- /dev/null +++ b/test/test_counter_saturate/tb_template.sv @@ -0,0 +1,214 @@ +{% extends "lib/tb_base.sv" %} + +{% block seq %} + {% sv_line_anchor %} + ##1; + cb.rst <= '0; + ##1; + + // counter controls are the same for each sub-test + `define incr (1<<9) + `define decr (1<<10) + `define clr (1<<11) + `define set (1<<12) + `define step(n) (n<<16) + + //-------------------------------------------------------------------------- + // Test incrsaturate = true; decrsaturate = true; + //-------------------------------------------------------------------------- + cpuif.assert_read('h0, 'h00, 'hFF); + + // incrsaturate via +1 + cpuif.write('h0, `set); + cpuif.assert_read('h0, 'hFF, 'hFF); + cpuif.write('h0, `decr + `step(1)); + cpuif.assert_read('h0, 'hFE, 'hFF); + cpuif.write('h0, `incr + `step(1)); + cpuif.assert_read('h0, 'hFF, 'hFF); + cpuif.write('h0, `incr + `step(1)); + cpuif.assert_read('h0, 'hFF, 'hFF); + + // decrsaturate via +1 + cpuif.write('h0, `clr); + cpuif.assert_read('h0, 'h00, 'hFF); + cpuif.write('h0, `incr + `step(1)); + cpuif.assert_read('h0, 'h01, 'hFF); + cpuif.write('h0, `decr + `step(1)); + cpuif.assert_read('h0, 'h00, 'hFF); + cpuif.write('h0, `decr + `step(1)); + cpuif.assert_read('h0, 'h00, 'hFF); + + // incrsaturate via larger steps + cpuif.write('h0, `set); + cpuif.assert_read('h0, 'hFF, 'hFF); + cpuif.write('h0, `decr + `step(1)); + cpuif.assert_read('h0, 'hFE, 'hFF); + cpuif.write('h0, `incr + `step(2)); + cpuif.assert_read('h0, 'hFF, 'hFF); + cpuif.write('h0, `incr + `step(3)); + cpuif.assert_read('h0, 'hFF, 'hFF); + cpuif.write('h0, `incr + `step(255)); + cpuif.assert_read('h0, 'hFF, 'hFF); + + // decrsaturate via larger steps + cpuif.write('h0, `clr); + cpuif.assert_read('h0, 'h00, 'hFF); + cpuif.write('h0, `incr + `step(1)); + cpuif.assert_read('h0, 'h01, 'hFF); + cpuif.write('h0, `decr + `step(2)); + cpuif.assert_read('h0, 'h00, 'hFF); + cpuif.write('h0, `decr + `step(3)); + cpuif.assert_read('h0, 'h00, 'hFF); + cpuif.write('h0, `decr + `step(255)); + cpuif.assert_read('h0, 'h00, 'hFF); + + //-------------------------------------------------------------------------- + // Test incrsaturate = 250; decrsaturate = 5; + //-------------------------------------------------------------------------- + cpuif.assert_read('h4, 'h05, 'hFF); + + // incrsaturate via +1 + cpuif.write('h4, `set); + cpuif.assert_read('h4, 'hFA, 'hFF); + cpuif.write('h4, `decr + `step(1)); + cpuif.assert_read('h4, 'hF9, 'hFF); + cpuif.write('h4, `incr + `step(1)); + cpuif.assert_read('h4, 'hFA, 'hFF); + cpuif.write('h4, `incr + `step(1)); + cpuif.assert_read('h4, 'hFA, 'hFF); + + // decrsaturate via +1 + cpuif.write('h4, `clr); + cpuif.assert_read('h4, 'h05, 'hFF); + cpuif.write('h4, `incr + `step(1)); + cpuif.assert_read('h4, 'h06, 'hFF); + cpuif.write('h4, `decr + `step(1)); + cpuif.assert_read('h4, 'h05, 'hFF); + cpuif.write('h4, `decr + `step(1)); + cpuif.assert_read('h4, 'h05, 'hFF); + + // incrsaturate via larger steps + cpuif.write('h4, `set); + cpuif.assert_read('h4, 'hFA, 'hFF); + cpuif.write('h4, `decr + `step(1)); + cpuif.assert_read('h4, 'hF9, 'hFF); + cpuif.write('h4, `incr + `step(2)); + cpuif.assert_read('h4, 'hFA, 'hFF); + cpuif.write('h4, `incr + `step(3)); + cpuif.assert_read('h4, 'hFA, 'hFF); + cpuif.write('h4, `incr + `step(255)); + cpuif.assert_read('h4, 'hFA, 'hFF); + + // decrsaturate via larger steps + cpuif.write('h4, `clr); + cpuif.assert_read('h4, 'h05, 'hFF); + cpuif.write('h4, `incr + `step(1)); + cpuif.assert_read('h4, 'h06, 'hFF); + cpuif.write('h4, `decr + `step(2)); + cpuif.assert_read('h4, 'h05, 'hFF); + cpuif.write('h4, `decr + `step(3)); + cpuif.assert_read('h4, 'h05, 'hFF); + cpuif.write('h4, `decr + `step(255)); + cpuif.assert_read('h4, 'h05, 'hFF); + + //-------------------------------------------------------------------------- + // Test incrsaturate = ; decrsaturate = ; + //-------------------------------------------------------------------------- + cpuif.assert_read('h8, 'h00, 'hFF); + + // incrsaturate via +1 + cpuif.write('h8, `set); + cpuif.assert_read('h8, 'hFF, 'hFF); + cpuif.write('h8, `decr + `step(1)); + cpuif.assert_read('h8, 'hFE, 'hFF); + cpuif.write('h8, `incr + `step(1)); + cpuif.assert_read('h8, 'hFF, 'hFF); + cpuif.write('h8, `incr + `step(1)); + cpuif.assert_read('h8, 'hFF, 'hFF); + + // decrsaturate via +1 + cpuif.write('h8, `clr); + cpuif.assert_read('h8, 'h00, 'hFF); + cpuif.write('h8, `incr + `step(1)); + cpuif.assert_read('h8, 'h01, 'hFF); + cpuif.write('h8, `decr + `step(1)); + cpuif.assert_read('h8, 'h00, 'hFF); + cpuif.write('h8, `decr + `step(1)); + cpuif.assert_read('h8, 'h00, 'hFF); + + // incrsaturate via larger steps + cpuif.write('h8, `set); + cpuif.assert_read('h8, 'hFF, 'hFF); + cpuif.write('h8, `decr + `step(1)); + cpuif.assert_read('h8, 'hFE, 'hFF); + cpuif.write('h8, `incr + `step(2)); + cpuif.assert_read('h8, 'hFF, 'hFF); + cpuif.write('h8, `incr + `step(3)); + cpuif.assert_read('h8, 'hFF, 'hFF); + cpuif.write('h8, `incr + `step(255)); + cpuif.assert_read('h8, 'hFF, 'hFF); + + // decrsaturate via larger steps + cpuif.write('h8, `clr); + cpuif.assert_read('h8, 'h00, 'hFF); + cpuif.write('h8, `incr + `step(1)); + cpuif.assert_read('h8, 'h01, 'hFF); + cpuif.write('h8, `decr + `step(2)); + cpuif.assert_read('h8, 'h00, 'hFF); + cpuif.write('h8, `decr + `step(3)); + cpuif.assert_read('h8, 'h00, 'hFF); + cpuif.write('h8, `decr + `step(255)); + cpuif.assert_read('h8, 'h00, 'hFF); + + //-------------------------------------------------------------------------- + // Test incrsaturate = ; decrsaturate = ; + //-------------------------------------------------------------------------- + cpuif.write('hc, 'hFA_05); + + cpuif.assert_read('h4, 'h05, 'hFF); + + // incrsaturate via +1 + cpuif.write('h8, `set); + cpuif.assert_read('h8, 'hFA, 'hFF); + cpuif.write('h8, `decr + `step(1)); + cpuif.assert_read('h8, 'hF9, 'hFF); + cpuif.write('h8, `incr + `step(1)); + cpuif.assert_read('h8, 'hFA, 'hFF); + cpuif.write('h8, `incr + `step(1)); + cpuif.assert_read('h8, 'hFA, 'hFF); + + // decrsaturate via +1 + cpuif.write('h8, `clr); + cpuif.assert_read('h8, 'h05, 'hFF); + cpuif.write('h8, `incr + `step(1)); + cpuif.assert_read('h8, 'h06, 'hFF); + cpuif.write('h8, `decr + `step(1)); + cpuif.assert_read('h8, 'h05, 'hFF); + cpuif.write('h8, `decr + `step(1)); + cpuif.assert_read('h8, 'h05, 'hFF); + + // incrsaturate via larger steps + cpuif.write('h8, `set); + cpuif.assert_read('h8, 'hFA, 'hFF); + cpuif.write('h8, `decr + `step(1)); + cpuif.assert_read('h8, 'hF9, 'hFF); + cpuif.write('h8, `incr + `step(2)); + cpuif.assert_read('h8, 'hFA, 'hFF); + cpuif.write('h8, `incr + `step(3)); + cpuif.assert_read('h8, 'hFA, 'hFF); + cpuif.write('h8, `incr + `step(255)); + cpuif.assert_read('h8, 'hFA, 'hFF); + + // decrsaturate via larger steps + cpuif.write('h8, `clr); + cpuif.assert_read('h8, 'h05, 'hFF); + cpuif.write('h8, `incr + `step(1)); + cpuif.assert_read('h8, 'h06, 'hFF); + cpuif.write('h8, `decr + `step(2)); + cpuif.assert_read('h8, 'h05, 'hFF); + cpuif.write('h8, `decr + `step(3)); + cpuif.assert_read('h8, 'h05, 'hFF); + cpuif.write('h8, `decr + `step(255)); + cpuif.assert_read('h8, 'h05, 'hFF); + +{% endblock %} diff --git a/test/test_counter_saturate/testcase.py b/test/test_counter_saturate/testcase.py new file mode 100644 index 0000000..b458d3c --- /dev/null +++ b/test/test_counter_saturate/testcase.py @@ -0,0 +1,5 @@ +from ..lib.regblock_testcase import RegblockTestCase + +class Test(RegblockTestCase): + def test_dut(self): + self.run_test() diff --git a/test/test_field_types/tb_template.sv b/test/test_field_types/tb_template.sv index d096ae4..e54f631 100644 --- a/test/test_field_types/tb_template.sv +++ b/test/test_field_types/tb_template.sv @@ -1,4 +1,4 @@ -{% extends "lib/templates/tb_base.sv" %} +{% extends "lib/tb_base.sv" %} {% block seq %} {% sv_line_anchor %} diff --git a/test/test_hw_access/tb_template.sv b/test/test_hw_access/tb_template.sv index 6128039..4e190f8 100644 --- a/test/test_hw_access/tb_template.sv +++ b/test/test_hw_access/tb_template.sv @@ -1,4 +1,4 @@ -{% extends "lib/templates/tb_base.sv" %} +{% extends "lib/tb_base.sv" %} {% block seq %} {% sv_line_anchor %} diff --git a/test/test_onread_onwrite/tb_template.sv b/test/test_onread_onwrite/tb_template.sv index 3d5731f..f3308e7 100644 --- a/test/test_onread_onwrite/tb_template.sv +++ b/test/test_onread_onwrite/tb_template.sv @@ -1,4 +1,4 @@ -{% extends "lib/templates/tb_base.sv" %} +{% extends "lib/tb_base.sv" %} {% block seq %} {% sv_line_anchor %} diff --git a/test/test_read_fanin/tb_template.sv b/test/test_read_fanin/tb_template.sv index 3497728..519a1e5 100644 --- a/test/test_read_fanin/tb_template.sv +++ b/test/test_read_fanin/tb_template.sv @@ -1,4 +1,4 @@ -{% extends "lib/templates/tb_base.sv" %} +{% extends "lib/tb_base.sv" %} {%- block declarations %} {% sv_line_anchor %} diff --git a/test/test_singlepulse/tb_template.sv b/test/test_singlepulse/tb_template.sv index 762c6c9..27ff6dd 100644 --- a/test/test_singlepulse/tb_template.sv +++ b/test/test_singlepulse/tb_template.sv @@ -1,4 +1,4 @@ -{% extends "lib/templates/tb_base.sv" %} +{% extends "lib/tb_base.sv" %} {% block seq %} {% sv_line_anchor %} diff --git a/test/test_structural_sw_rw/tb_template.sv b/test/test_structural_sw_rw/tb_template.sv index fd03814..c91144e 100644 --- a/test/test_structural_sw_rw/tb_template.sv +++ b/test/test_structural_sw_rw/tb_template.sv @@ -1,4 +1,4 @@ -{% extends "lib/templates/tb_base.sv" %} +{% extends "lib/tb_base.sv" %} {% block seq %} {% sv_line_anchor %} diff --git a/test/test_swacc_swmod/tb_template.sv b/test/test_swacc_swmod/tb_template.sv index 98bfe39..d5b926c 100644 --- a/test/test_swacc_swmod/tb_template.sv +++ b/test/test_swacc_swmod/tb_template.sv @@ -1,4 +1,4 @@ -{% extends "lib/templates/tb_base.sv" %} +{% extends "lib/tb_base.sv" %} {% block seq %} {% sv_line_anchor %} diff --git a/test/test_swwe/tb_template.sv b/test/test_swwe/tb_template.sv index ac89f98..114816f 100644 --- a/test/test_swwe/tb_template.sv +++ b/test/test_swwe/tb_template.sv @@ -1,4 +1,4 @@ -{% extends "lib/templates/tb_base.sv" %} +{% extends "lib/tb_base.sv" %} {% block seq %} {% sv_line_anchor %}