Add counter support
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
#recursive-include peakrdl/regblock/XXX *
|
||||
recursive-exclude test *
|
||||
recursive-include peakrdl/regblock *.sv
|
||||
prune test
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,10 +23,6 @@ bigendian/littleendian
|
||||
----------------------
|
||||
|NO|
|
||||
|
||||
bridge
|
||||
------
|
||||
|NO|
|
||||
|
||||
rsvdset
|
||||
-------
|
||||
|NO|
|
||||
|
||||
@@ -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: '<swmod>', 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: '<counter>', 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: '<counter>', 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: '<counter>', 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: '<counter>', 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
|
||||
|
||||
@@ -13,7 +13,3 @@ Only ``accesswidth`` that is equal to the ``regwidth`` is supported (default if
|
||||
regwidth
|
||||
--------
|
||||
|OK|
|
||||
|
||||
shared
|
||||
------
|
||||
|NO|
|
||||
|
||||
@@ -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: '<counter>', wave: '=.=....', data: [1,0]},
|
||||
{name: '<decrsaturate>', 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: '<counter>', wave: '=.=....', data: [14,15]},
|
||||
{name: '<incrsaturate>', 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|
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
"""
|
||||
|
||||
@@ -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};"
|
||||
)
|
||||
|
||||
@@ -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;",
|
||||
]
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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;",
|
||||
]
|
||||
|
||||
@@ -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;",
|
||||
]
|
||||
|
||||
@@ -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;",
|
||||
]
|
||||
|
||||
56
peakrdl/regblock/field_logic/templates/counter_macros.sv
Normal file
56
peakrdl/regblock/field_logic/templates/counter_macros.sv
Normal file
@@ -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 %}
|
||||
@@ -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 -%}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
2
setup.py
2
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=(
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "lib/templates/tb_base.sv" %}
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
{% block seq %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
0
test/test_counter_basics/__init__.py
Normal file
0
test/test_counter_basics/__init__.py
Normal file
68
test/test_counter_basics/regblock.rdl
Normal file
68
test/test_counter_basics/regblock.rdl
Normal file
@@ -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;
|
||||
};
|
||||
93
test/test_counter_basics/tb_template.sv
Normal file
93
test/test_counter_basics/tb_template.sv
Normal file
@@ -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 %}
|
||||
5
test/test_counter_basics/testcase.py
Normal file
5
test/test_counter_basics/testcase.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from ..lib.regblock_testcase import RegblockTestCase
|
||||
|
||||
class Test(RegblockTestCase):
|
||||
def test_dut(self):
|
||||
self.run_test()
|
||||
0
test/test_counter_saturate/__init__.py
Normal file
0
test/test_counter_saturate/__init__.py
Normal file
88
test/test_counter_saturate/regblock.rdl
Normal file
88
test/test_counter_saturate/regblock.rdl
Normal file
@@ -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;
|
||||
};
|
||||
214
test/test_counter_saturate/tb_template.sv
Normal file
214
test/test_counter_saturate/tb_template.sv
Normal file
@@ -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 = <ref 255>; decrsaturate = <ref 0>;
|
||||
//--------------------------------------------------------------------------
|
||||
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 = <ref 250>; decrsaturate = <ref 5>;
|
||||
//--------------------------------------------------------------------------
|
||||
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 %}
|
||||
5
test/test_counter_saturate/testcase.py
Normal file
5
test/test_counter_saturate/testcase.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from ..lib.regblock_testcase import RegblockTestCase
|
||||
|
||||
class Test(RegblockTestCase):
|
||||
def test_dut(self):
|
||||
self.run_test()
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "lib/templates/tb_base.sv" %}
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
{% block seq %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "lib/templates/tb_base.sv" %}
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
{% block seq %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "lib/templates/tb_base.sv" %}
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
{% block seq %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "lib/templates/tb_base.sv" %}
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
{%- block declarations %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "lib/templates/tb_base.sv" %}
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
{% block seq %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "lib/templates/tb_base.sv" %}
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
{% block seq %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "lib/templates/tb_base.sv" %}
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
{% block seq %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "lib/templates/tb_base.sv" %}
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
{% block seq %}
|
||||
{% sv_line_anchor %}
|
||||
|
||||
Reference in New Issue
Block a user