Add counter support
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
#recursive-include peakrdl/regblock/XXX *
|
recursive-include peakrdl/regblock *.sv
|
||||||
recursive-exclude test *
|
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
|
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
|
- derive the CPU bus width based on the largest regwidth
|
||||||
this seems like a reasonable & easy thing to implement
|
this seems like a reasonable & easy thing to implement
|
||||||
- CPUIF should make sure to always present an aligned address!
|
- 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!
|
Codify this in the internal specification!
|
||||||
- address decode may produce multiple strobes if registers are packed.
|
- 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,
|
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 not so much in other protocols...
|
||||||
Maybe add some words to the "clarifications" section
|
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
|
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
|
- Link more functions to the dereferencer
|
||||||
I shouldn't have to go to the hwif or whatever
|
I shouldn't have to go to the hwif or whatever
|
||||||
dereferencer should have all the query functions
|
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
|
endianness controls byte order of the CPU bus
|
||||||
controls byteswap at the CPUIF layer
|
controls byteswap at the CPUIF layer
|
||||||
Internally, use little endian ordering.
|
Internally, use little endian ordering.
|
||||||
@@ -155,16 +98,3 @@ endianness controls byte order of the CPU bus
|
|||||||
Do something about cpuif byte strobes?
|
Do something about cpuif byte strobes?
|
||||||
Remove for now?
|
Remove for now?
|
||||||
Demote to APB3?
|
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
|
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!)
|
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
|
! hwclr/hwset/we/wel probably shouldn't be able to reference itself
|
||||||
y->hwclr = y;
|
y->hwclr = y;
|
||||||
y->we = y;
|
y->we = y;
|
||||||
... it works, but should it be allowed? Seems like user-error
|
... 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
|
Things that need validation by this exporter
|
||||||
|
|||||||
@@ -23,10 +23,6 @@ bigendian/littleendian
|
|||||||
----------------------
|
----------------------
|
||||||
|NO|
|
|NO|
|
||||||
|
|
||||||
bridge
|
|
||||||
------
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
rsvdset
|
rsvdset
|
||||||
-------
|
-------
|
||||||
|NO|
|
|NO|
|
||||||
|
|||||||
@@ -19,6 +19,16 @@ singlepulse
|
|||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|OK|
|
|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
|
sw
|
||||||
^^^
|
^^^
|
||||||
|OK|
|
|OK|
|
||||||
@@ -143,93 +153,228 @@ Counter Properties
|
|||||||
|
|
||||||
counter
|
counter
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|NO|
|
|OK|
|
||||||
|
|
||||||
decr
|
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.
|
||||||
reference
|
If any of the properties associated with an up-counter are used, then up-counting
|
||||||
|NO|
|
capabilities will be implemented. The same is true for down-counters and up/down
|
||||||
|
counters.
|
||||||
|
|
||||||
decrthreshold
|
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
|
||||||
boolean
|
of counter described.
|
||||||
|NO|
|
|
||||||
|
|
||||||
bit
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
reference
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
decrsaturate
|
|
||||||
^^^^^^^^^^^^
|
|
||||||
boolean
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
bit
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
reference
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
decrvalue
|
|
||||||
^^^^^^^^^
|
|
||||||
bit
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
reference
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
decrwidth
|
|
||||||
^^^^^^^^^
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
incr
|
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
|
incrsaturate/saturate
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
boolean
|
If assigned, indicates that the counter will saturate instead of wrapping.
|
||||||
|NO|
|
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
|
boolean
|
||||||
|NO|
|
|OK|
|
||||||
|
|
||||||
|
If true, saturation point is at the counter's maximum count value. (2^width - 1)
|
||||||
|
|
||||||
|
integer
|
||||||
|
|OK|
|
||||||
|
|
||||||
|
Specify a static saturation value.
|
||||||
|
|
||||||
reference
|
reference
|
||||||
|NO|
|
|OK|
|
||||||
|
|
||||||
|
Specify a dynamic saturation value.
|
||||||
|
|
||||||
|
|
||||||
incrthreshold/threshold
|
incrthreshold/threshold
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
boolean
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
bit
|
If assigned, infers a ``hwif_out..incrthreshold`` output signal. This signal is
|
||||||
|NO|
|
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
|
reference
|
||||||
|NO|
|
|EX|
|
||||||
|
|
||||||
|
Specify a dynamic threshold value.
|
||||||
|
|
||||||
|
|
||||||
incrvalue
|
incrvalue
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
bit
|
Override the counter's increment step size.
|
||||||
|NO|
|
|
||||||
|
integer
|
||||||
|
|OK|
|
||||||
|
|
||||||
reference
|
reference
|
||||||
|NO|
|
|OK|
|
||||||
|
|
||||||
incrwidth
|
incrwidth
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|NO|
|
|OK|
|
||||||
|
|
||||||
|
If assigned, infers an input signal ``hwif_in..incrvalue``. The value of this
|
||||||
|
property defines the signal's width.
|
||||||
|
|
||||||
|
|
||||||
overflow
|
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
|
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
|
reset
|
||||||
^^^^^
|
^^^^^
|
||||||
bit
|
integer
|
||||||
|OK|
|
|OK|
|
||||||
|
|
||||||
reference
|
reference
|
||||||
|
|||||||
@@ -13,7 +13,3 @@ Only ``accesswidth`` that is equal to the ``regwidth`` is supported (default if
|
|||||||
regwidth
|
regwidth
|
||||||
--------
|
--------
|
||||||
|OK|
|
|OK|
|
||||||
|
|
||||||
shared
|
|
||||||
------
|
|
||||||
|NO|
|
|
||||||
|
|||||||
@@ -18,63 +18,65 @@ The text below describes the interpretations used for this exporter.
|
|||||||
Field
|
Field
|
||||||
-----
|
-----
|
||||||
|
|
||||||
swacc
|
field -> swacc
|
||||||
^^^^^
|
^^^^^^^^^^^^^^
|
||||||
|EX|
|
|EX|
|
||||||
|
|
||||||
Single-cycle strobe that indicates the field is being sampled during a software
|
Single-cycle strobe that indicates the field is being sampled during a software
|
||||||
read operation.
|
read operation.
|
||||||
|
|
||||||
|
|
||||||
swmod
|
field -> swmod
|
||||||
^^^^^
|
^^^^^^^^^^^^^^^
|
||||||
|EX|
|
|EX|
|
||||||
|
|
||||||
Single-cycle strobe that indicates the field is being modified during a software
|
Single-cycle strobe that indicates the field is being modified during a software
|
||||||
access operation.
|
access operation.
|
||||||
|
|
||||||
|
|
||||||
swwe/swwel
|
field -> swwe/swwel
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|OK|
|
|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|
|
|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|
|
|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|
|
|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|
|
|EX|
|
||||||
|
|
||||||
next
|
field -> reset
|
||||||
^^^^
|
^^^^^^^^^^^^^^
|
||||||
|EX|
|
|EX|
|
||||||
|
|
||||||
reset
|
field -> resetsignal
|
||||||
^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|EX|
|
|
||||||
|
|
||||||
resetsignal
|
|
||||||
^^^^^^^^^^^
|
|
||||||
|EX|
|
|EX|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
@@ -82,67 +84,118 @@ resetsignal
|
|||||||
Field Counter Properties
|
Field Counter Properties
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
Represents the signal that controls the owning field's we/wel behavior.
|
field -> incr
|
||||||
|
|
||||||
decr
|
|
||||||
^^^^
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
decrthreshold
|
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|NO|
|
|
||||||
|
|
||||||
decrsaturate
|
|
||||||
^^^^^^^^^^^^
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
decrvalue
|
|
||||||
^^^^^^^^^
|
|
||||||
|EX|
|
|EX|
|
||||||
|
|
||||||
incr
|
Represents the signal that controls the field's counter increment control.
|
||||||
^^^^
|
|
||||||
|NO|
|
|
||||||
|
|
||||||
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|
|
|EX|
|
||||||
|
|
||||||
overflow
|
Represents the internal 1-bit event signal that indicates whether the counter is saturated
|
||||||
^^^^^^^^
|
at its saturation value.
|
||||||
|NO|
|
|
||||||
|
|
||||||
underflow
|
.. wavedrom::
|
||||||
^^^^^^^^^
|
|
||||||
|NO|
|
{
|
||||||
|
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
|
Field Interrupt Properties
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
enable
|
field -> enable
|
||||||
^^^^^^
|
^^^^^^^^^^^^^^^
|
||||||
|EX|
|
|EX|
|
||||||
|
|
||||||
haltenable
|
field -> haltenable
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|EX|
|
|EX|
|
||||||
|
|
||||||
haltmask
|
field -> haltmask
|
||||||
^^^^^^^^
|
^^^^^^^^^^^^^^^^^
|
||||||
|EX|
|
|EX|
|
||||||
|
|
||||||
mask
|
field -> mask
|
||||||
^^^^
|
^^^^^^^^^^^^^
|
||||||
|EX|
|
|EX|
|
||||||
|
|
||||||
|
|
||||||
@@ -151,10 +204,10 @@ mask
|
|||||||
Register
|
Register
|
||||||
--------
|
--------
|
||||||
|
|
||||||
intr
|
reg -> intr
|
||||||
^^^^
|
^^^^^^^^^^^
|
||||||
|NO|
|
|NO|
|
||||||
|
|
||||||
halt
|
reg -> halt
|
||||||
^^^^
|
^^^^^^^^^^^
|
||||||
|NO|
|
|NO|
|
||||||
|
|||||||
@@ -60,8 +60,12 @@ class Dereferencer:
|
|||||||
return self.get_value(reset_value)
|
return self.get_value(reset_value)
|
||||||
else:
|
else:
|
||||||
# No reset value defined!
|
# No reset value defined!
|
||||||
# Callers shall ensure this is impossible
|
obj.env.msg.warning(
|
||||||
raise RuntimeError
|
"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):
|
if isinstance(obj, SignalNode):
|
||||||
# Signals are always inputs from the hwif
|
# Signals are always inputs from the hwif
|
||||||
@@ -106,28 +110,12 @@ class Dereferencer:
|
|||||||
}:
|
}:
|
||||||
return self.get_value(field.get_property(prop_name))
|
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
|
# Field Next
|
||||||
if prop_name == "next":
|
if prop_name == "next":
|
||||||
prop_value = field.get_property(prop_name)
|
prop_value = field.get_property(prop_name)
|
||||||
if prop_value is None:
|
if prop_value is None:
|
||||||
# unset by the user, points to the implied internal signal
|
# 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:
|
else:
|
||||||
return self.get_value(prop_value)
|
return self.get_value(prop_value)
|
||||||
|
|
||||||
@@ -175,19 +163,31 @@ class Dereferencer:
|
|||||||
if prop_name == "swmod":
|
if prop_name == "swmod":
|
||||||
return self.field_logic.get_swmod_identifier(field)
|
return self.field_logic.get_swmod_identifier(field)
|
||||||
|
|
||||||
raise RuntimeError("Unhandled reference to: %s->%s" % (field, prop_name))
|
|
||||||
|
|
||||||
"""
|
# translate aliases
|
||||||
TODO:
|
aliases = {
|
||||||
Resolves to an internal signal used in the field's logic
|
"saturate": "incrsaturate",
|
||||||
decrsaturate
|
"threshold": "incrthreshold",
|
||||||
decrthreshold
|
}
|
||||||
incrsaturate
|
prop_name = aliases.get(prop_name, prop_name)
|
||||||
incrthreshold
|
|
||||||
overflow
|
# Counter properties
|
||||||
saturate
|
if prop_name == 'incr':
|
||||||
threshold
|
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:
|
def get_reg_propref_value(self, reg: RegNode, prop_name: str) -> str:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from systemrdl.rdltypes import PropertyReference, PrecedenceType
|
from systemrdl.rdltypes import PropertyReference, PrecedenceType
|
||||||
|
from systemrdl.node import Node
|
||||||
|
|
||||||
from .bases import AssignmentPrecedence, NextStateConditional
|
from .bases import AssignmentPrecedence, NextStateConditional
|
||||||
from . import sw_onread
|
from . import sw_onread
|
||||||
@@ -61,41 +62,105 @@ class FieldLogic:
|
|||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Field utility functions
|
# 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
|
Returns the Verilog string that represents the storage register element
|
||||||
for the referenced field
|
for the referenced field
|
||||||
"""
|
"""
|
||||||
assert node.implements_storage
|
assert field.implements_storage
|
||||||
path = get_indexed_path(self.top_node, node)
|
path = get_indexed_path(self.top_node, field)
|
||||||
return f"field_storage.{path}"
|
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.
|
Returns a Verilog string that represents a field's internal combinational
|
||||||
This is specifically for use in Field->next property references.
|
signal.
|
||||||
"""
|
"""
|
||||||
assert node.implements_storage
|
assert field.implements_storage
|
||||||
path = get_indexed_path(self.top_node, node)
|
path = get_indexed_path(self.top_node, field)
|
||||||
return f"field_combo.{path}.next"
|
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.
|
Return the Verilog string that represents the field's incr 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.
|
|
||||||
"""
|
"""
|
||||||
# TODO: Implement this
|
prop_value = field.get_property('incr')
|
||||||
raise NotImplementedError
|
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.
|
Return the string that represents the field's increment value
|
||||||
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.
|
|
||||||
"""
|
"""
|
||||||
# TODO: Implement this
|
incrvalue = field.get_property('incrvalue')
|
||||||
raise NotImplementedError
|
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:
|
def get_swacc_identifier(self, field: 'FieldNode') -> str:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -39,8 +39,26 @@ class CombinationalStructGenerator(RDLStructGenerator):
|
|||||||
self.add_member("load_next")
|
self.add_member("load_next")
|
||||||
for signal in extra_combo_signals.values():
|
for signal in extra_combo_signals.values():
|
||||||
self.add_member(signal.name, signal.width)
|
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()
|
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):
|
class FieldStorageStructGenerator(RDLStructGenerator):
|
||||||
|
|
||||||
@@ -92,10 +110,12 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
|||||||
'node': node,
|
'node': node,
|
||||||
'reset': reset_value_str,
|
'reset': reset_value_str,
|
||||||
'field_path': get_indexed_path(self.exp.top_node, node),
|
'field_path': get_indexed_path(self.exp.top_node, node),
|
||||||
|
'field_logic': self.field_logic,
|
||||||
'extra_combo_signals': extra_combo_signals,
|
'extra_combo_signals': extra_combo_signals,
|
||||||
'conditionals': conditionals,
|
'conditionals': conditionals,
|
||||||
'resetsignal': resetsignal,
|
'resetsignal': resetsignal,
|
||||||
'get_always_ff_event': get_always_ff_event,
|
'get_always_ff_event': get_always_ff_event,
|
||||||
|
'get_value': self.exp.dereferencer.get_value,
|
||||||
}
|
}
|
||||||
self.add_content(self.field_storage_template.render(context))
|
self.add_content(self.field_storage_template.render(context))
|
||||||
|
|
||||||
@@ -144,3 +164,16 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
|||||||
self.add_content(
|
self.add_content(
|
||||||
f"assign {output_identifier} = {value};"
|
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"
|
next_val = "'1"
|
||||||
|
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = {next_val};",
|
f"next_c = {next_val};",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -71,6 +71,6 @@ class HWClear(NextStateConditional):
|
|||||||
next_val = "'0"
|
next_val = "'0"
|
||||||
|
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = {next_val};",
|
f"next_c = {next_val};",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ class AlwaysWrite(NextStateConditional):
|
|||||||
next_val = self.exp.hwif.get_input_identifier(field)
|
next_val = self.exp.hwif.get_input_identifier(field)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = {next_val};",
|
f"next_c = {next_val};",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
class WEWrite(AlwaysWrite):
|
class WEWrite(AlwaysWrite):
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ class ClearOnRead(_OnRead):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = '0;",
|
f"next_c = '0;",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -36,6 +36,6 @@ class SetOnRead(_OnRead):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = '1;",
|
f"next_c = '1;",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ class WriteOneSet(_OnWrite):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = field_storage.{field_path} | {self._wr_data(field)};",
|
f"next_c = field_storage.{field_path} | {self._wr_data(field)};",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
class WriteOneClear(_OnWrite):
|
class WriteOneClear(_OnWrite):
|
||||||
@@ -51,8 +51,8 @@ class WriteOneClear(_OnWrite):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = field_storage.{field_path} & ~{self._wr_data(field)};",
|
f"next_c = field_storage.{field_path} & ~{self._wr_data(field)};",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
class WriteOneToggle(_OnWrite):
|
class WriteOneToggle(_OnWrite):
|
||||||
@@ -62,8 +62,8 @@ class WriteOneToggle(_OnWrite):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = field_storage.{field_path} ^ {self._wr_data(field)};",
|
f"next_c = field_storage.{field_path} ^ {self._wr_data(field)};",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
class WriteZeroSet(_OnWrite):
|
class WriteZeroSet(_OnWrite):
|
||||||
@@ -73,8 +73,8 @@ class WriteZeroSet(_OnWrite):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = field_storage.{field_path} | ~{self._wr_data(field)};",
|
f"next_c = field_storage.{field_path} | ~{self._wr_data(field)};",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
class WriteZeroClear(_OnWrite):
|
class WriteZeroClear(_OnWrite):
|
||||||
@@ -84,8 +84,8 @@ class WriteZeroClear(_OnWrite):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = field_storage.{field_path} & {self._wr_data(field)};",
|
f"next_c = field_storage.{field_path} & {self._wr_data(field)};",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
class WriteZeroToggle(_OnWrite):
|
class WriteZeroToggle(_OnWrite):
|
||||||
@@ -95,8 +95,8 @@ class WriteZeroToggle(_OnWrite):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = field_storage.{field_path} ^ ~{self._wr_data(field)};",
|
f"next_c = field_storage.{field_path} ^ ~{self._wr_data(field)};",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
class WriteClear(_OnWrite):
|
class WriteClear(_OnWrite):
|
||||||
@@ -106,8 +106,8 @@ class WriteClear(_OnWrite):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = '0;",
|
f"next_c = '0;",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
class WriteSet(_OnWrite):
|
class WriteSet(_OnWrite):
|
||||||
@@ -117,8 +117,8 @@ class WriteSet(_OnWrite):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = '1;",
|
f"next_c = '1;",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|
||||||
class Write(_OnWrite):
|
class Write(_OnWrite):
|
||||||
@@ -128,6 +128,6 @@ class Write(_OnWrite):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = {self._wr_data(field)};",
|
f"next_c = {self._wr_data(field)};",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
f"load_next_c = '1;",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ class Singlepulse(NextStateConditional):
|
|||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
field_path = self.get_field_path(field)
|
field_path = self.get_field_path(field)
|
||||||
return [
|
return [
|
||||||
f"field_combo.{field_path}.next = '0;",
|
f"next_c = '0;",
|
||||||
f"field_combo.{field_path}.load_next = '1;",
|
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()}}
|
// Field: {{node.get_path()}}
|
||||||
always_comb begin
|
always_comb begin
|
||||||
field_combo.{{field_path}}.next = field_storage.{{field_path}};
|
automatic logic [{{node.width-1}}:0] next_c = field_storage.{{field_path}};
|
||||||
field_combo.{{field_path}}.load_next = '0;
|
automatic logic load_next_c = '0;
|
||||||
{%- for signal in extra_combo_signals %}
|
{%- for signal in extra_combo_signals %}
|
||||||
field_combo.{{field_path}}.{{signal.name}} = {{signal.default_assignment}};
|
field_combo.{{field_path}}.{{signal.name}} = {{signal.default_assignment}};
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
@@ -12,6 +13,14 @@ always_comb begin
|
|||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
end
|
end
|
||||||
{%- endfor %}
|
{%- 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
|
end
|
||||||
always_ff {{get_always_ff_event(resetsignal)}} begin
|
always_ff {{get_always_ff_event(resetsignal)}} begin
|
||||||
{% if resetsignal is not none -%}
|
{% if resetsignal is not none -%}
|
||||||
|
|||||||
@@ -159,21 +159,23 @@ class Hwif:
|
|||||||
contents.append(f"logic {prop_name};")
|
contents.append(f"logic {prop_name};")
|
||||||
|
|
||||||
# Generate any implied counter inputs
|
# Generate any implied counter inputs
|
||||||
if node.get_property('counter'):
|
if node.is_up_counter:
|
||||||
if not node.get_property('incr'):
|
if not node.get_property('incr'):
|
||||||
# User did not provide their own incr component reference.
|
# User did not provide their own incr component reference.
|
||||||
# Imply an input
|
# Imply an input
|
||||||
contents.append("logic incr;")
|
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')
|
width = node.get_property('incrwidth')
|
||||||
if width:
|
if width:
|
||||||
# Implies a corresponding incrvalue input
|
# Implies a corresponding incrvalue input
|
||||||
contents.append(f"logic [{width-1}:0] incrvalue;")
|
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')
|
width = node.get_property('decrwidth')
|
||||||
if width:
|
if width:
|
||||||
# Implies a corresponding decrvalue input
|
# Implies a corresponding decrvalue input
|
||||||
@@ -198,10 +200,15 @@ class Hwif:
|
|||||||
contents.append(f"logic [{node.width-1}:0] value;")
|
contents.append(f"logic [{node.width-1}:0] value;")
|
||||||
|
|
||||||
# Generate output bit signals enabled via property
|
# 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):
|
if node.get_property(prop_name):
|
||||||
contents.append(f"logic {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
|
return contents
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@@ -252,7 +259,10 @@ class Hwif:
|
|||||||
|
|
||||||
|
|
||||||
def get_implied_prop_input_identifier(self, field: FieldNode, prop: str) -> str:
|
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)
|
path = get_indexed_path(self.top_node, field)
|
||||||
return "hwif_in." + path + "." + prop
|
return "hwif_in." + path + "." + prop
|
||||||
|
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -23,7 +23,7 @@ setuptools.setup(
|
|||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
python_requires='>=3.6',
|
python_requires='>=3.6',
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"systemrdl-compiler>=1.21.0",
|
"systemrdl-compiler>=1.22.0",
|
||||||
"Jinja2>=2.11",
|
"Jinja2>=2.11",
|
||||||
],
|
],
|
||||||
classifiers=(
|
classifiers=(
|
||||||
|
|||||||
@@ -91,9 +91,10 @@ interface apb3_intf_driver #(
|
|||||||
reset();
|
reset();
|
||||||
endtask
|
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;
|
logic [DATA_WIDTH-1:0] data;
|
||||||
read(addr, 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);
|
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ class ModelSim(Simulator):
|
|||||||
cmd = [
|
cmd = [
|
||||||
"vlog", "-sv", "-quiet", "-l", "build.log",
|
"vlog", "-sv", "-quiet", "-l", "build.log",
|
||||||
|
|
||||||
|
"+incdir+%s" % os.path.join(os.path.dirname(__file__), ".."),
|
||||||
|
|
||||||
# Free version of ModelSim throws errors if generate/endgenerate
|
# Free version of ModelSim throws errors if generate/endgenerate
|
||||||
# blocks are not used.
|
# blocks are not used.
|
||||||
# These have been made optional long ago. Modern versions of SystemVerilog do
|
# 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.
|
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
|
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...
|
Keeping this here in case someday it works better...
|
||||||
"""
|
"""
|
||||||
def compile(self) -> None:
|
def compile(self) -> None:
|
||||||
cmd = [
|
cmd = [
|
||||||
"xvlog", "--sv"
|
"xvlog", "--sv",
|
||||||
|
"--include", os.path.join(os.path.dirname(__file__), ".."),
|
||||||
]
|
]
|
||||||
cmd.extend(self.tb_files)
|
cmd.extend(self.tb_files)
|
||||||
subprocess.run(cmd, check=True)
|
subprocess.run(cmd, check=True)
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ module tb;
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
{% sv_line_anchor %}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Test Sequence
|
// Test Sequence
|
||||||
@@ -82,7 +83,7 @@ module tb;
|
|||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
{%- endfilter %}
|
{%- endfilter %}
|
||||||
end
|
end
|
||||||
|
{% sv_line_anchor %}
|
||||||
##5;
|
##5;
|
||||||
$finish();
|
$finish();
|
||||||
end
|
end
|
||||||
@@ -90,7 +91,6 @@ module tb;
|
|||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Monitor for timeout
|
// Monitor for timeout
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
{% sv_line_anchor %}
|
|
||||||
initial begin
|
initial begin
|
||||||
##{{cls.timeout_clk_cycles}};
|
##{{cls.timeout_clk_cycles}};
|
||||||
$fatal(1, "Test timed out after {{cls.timeout_clk_cycles}} clock 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 %}
|
{% block seq %}
|
||||||
{% sv_line_anchor %}
|
{% 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 %}
|
{% block seq %}
|
||||||
{% sv_line_anchor %}
|
{% sv_line_anchor %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% extends "lib/templates/tb_base.sv" %}
|
{% extends "lib/tb_base.sv" %}
|
||||||
|
|
||||||
{% block seq %}
|
{% block seq %}
|
||||||
{% sv_line_anchor %}
|
{% sv_line_anchor %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% extends "lib/templates/tb_base.sv" %}
|
{% extends "lib/tb_base.sv" %}
|
||||||
|
|
||||||
{% block seq %}
|
{% block seq %}
|
||||||
{% sv_line_anchor %}
|
{% sv_line_anchor %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% extends "lib/templates/tb_base.sv" %}
|
{% extends "lib/tb_base.sv" %}
|
||||||
|
|
||||||
{%- block declarations %}
|
{%- block declarations %}
|
||||||
{% sv_line_anchor %}
|
{% sv_line_anchor %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% extends "lib/templates/tb_base.sv" %}
|
{% extends "lib/tb_base.sv" %}
|
||||||
|
|
||||||
{% block seq %}
|
{% block seq %}
|
||||||
{% sv_line_anchor %}
|
{% sv_line_anchor %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% extends "lib/templates/tb_base.sv" %}
|
{% extends "lib/tb_base.sv" %}
|
||||||
|
|
||||||
{% block seq %}
|
{% block seq %}
|
||||||
{% sv_line_anchor %}
|
{% sv_line_anchor %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% extends "lib/templates/tb_base.sv" %}
|
{% extends "lib/tb_base.sv" %}
|
||||||
|
|
||||||
{% block seq %}
|
{% block seq %}
|
||||||
{% sv_line_anchor %}
|
{% sv_line_anchor %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% extends "lib/templates/tb_base.sv" %}
|
{% extends "lib/tb_base.sv" %}
|
||||||
|
|
||||||
{% block seq %}
|
{% block seq %}
|
||||||
{% sv_line_anchor %}
|
{% sv_line_anchor %}
|
||||||
|
|||||||
Reference in New Issue
Block a user