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-include peakrdl/regblock *.sv
recursive-exclude test * 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 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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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()}} // 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 -%}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 %} {% block seq %}
{% sv_line_anchor %} {% sv_line_anchor %}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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