Add counter support

This commit is contained in:
Alex Mykyta
2021-12-11 20:41:49 -08:00
parent f5b12253ad
commit 9eddc9b60f
40 changed files with 1133 additions and 349 deletions

View File

@@ -1,2 +1,2 @@
#recursive-include peakrdl/regblock/XXX *
recursive-exclude test *
recursive-include peakrdl/regblock *.sv
prune test

View File

@@ -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

View File

@@ -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

View File

@@ -23,10 +23,6 @@ bigendian/littleendian
----------------------
|NO|
bridge
------
|NO|
rsvdset
-------
|NO|

View File

@@ -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

View File

@@ -13,7 +13,3 @@ Only ``accesswidth`` that is equal to the ``regwidth`` is supported (default if
regwidth
--------
|OK|
shared
------
|NO|

View File

@@ -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|

View File

@@ -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:

View File

@@ -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:
"""

View File

@@ -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};"
)

View File

@@ -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;",
]

View File

@@ -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):

View File

@@ -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;",
]

View File

@@ -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;",
]

View File

@@ -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;",
]

View 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 %}

View File

@@ -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 -%}

View File

@@ -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

View File

@@ -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=(

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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");

View File

@@ -1,4 +1,4 @@
{% extends "lib/templates/tb_base.sv" %}
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}

View File

View 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;
};

View 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 %}

View File

@@ -0,0 +1,5 @@
from ..lib.regblock_testcase import RegblockTestCase
class Test(RegblockTestCase):
def test_dut(self):
self.run_test()

View File

View 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;
};

View 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 %}

View File

@@ -0,0 +1,5 @@
from ..lib.regblock_testcase import RegblockTestCase
class Test(RegblockTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,4 +1,4 @@
{% extends "lib/templates/tb_base.sv" %}
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}

View File

@@ -1,4 +1,4 @@
{% extends "lib/templates/tb_base.sv" %}
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}

View File

@@ -1,4 +1,4 @@
{% extends "lib/templates/tb_base.sv" %}
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}

View File

@@ -1,4 +1,4 @@
{% extends "lib/templates/tb_base.sv" %}
{% extends "lib/tb_base.sv" %}
{%- block declarations %}
{% sv_line_anchor %}

View File

@@ -1,4 +1,4 @@
{% extends "lib/templates/tb_base.sv" %}
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}

View File

@@ -1,4 +1,4 @@
{% extends "lib/templates/tb_base.sv" %}
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}

View File

@@ -1,4 +1,4 @@
{% extends "lib/templates/tb_base.sv" %}
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}

View File

@@ -1,4 +1,4 @@
{% extends "lib/templates/tb_base.sv" %}
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}