Add interrupt tests!
This commit is contained in:
@@ -105,12 +105,13 @@ X sticky=true + "(posedge|negedge|bothedge) intr"
|
|||||||
X we/wel + implied or explicit "sticky"/"stickybit"
|
X we/wel + implied or explicit "sticky"/"stickybit"
|
||||||
we/wel modifier doesn't make sense here.
|
we/wel modifier doesn't make sense here.
|
||||||
|
|
||||||
! hwclr/hwset/we/wel probably shouldn't be able to reference itself
|
X sticky/stickybit shall be hw writable
|
||||||
y->hwclr = y;
|
|
||||||
y->we = y;
|
|
||||||
... it works, but should it be allowed? Seems like user-error
|
|
||||||
|
|
||||||
! counter field that saturates should not set overflow
|
X Illegal to use enable/mask/haltenable/haltmask on non-intr fields
|
||||||
|
|
||||||
|
X incrwidth/decrwidth must be between 1 and the width of the counter
|
||||||
|
|
||||||
|
X counter field that saturates should not set overflow
|
||||||
counter; incrsaturate; overflow;
|
counter; incrsaturate; overflow;
|
||||||
counter; decrsaturate; underflow;
|
counter; decrsaturate; underflow;
|
||||||
|
|
||||||
@@ -119,11 +120,10 @@ X we/wel + implied or explicit "sticky"/"stickybit"
|
|||||||
|
|
||||||
Same goes to prop references to overflow/underflow
|
Same goes to prop references to overflow/underflow
|
||||||
|
|
||||||
! incrwidth/decrwidth must be between 1 and the width of the counter
|
! hwclr/hwset/we/wel probably shouldn't be able to reference itself
|
||||||
|
y->hwclr = y;
|
||||||
! Illegal to use enable/mask/haltenable/haltmask on non-intr fields
|
y->we = y;
|
||||||
|
... it works, but should it be allowed? Seems like user-error
|
||||||
! sticky/stickybit shall be hw writable
|
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Things that need validation by this exporter
|
Things that need validation by this exporter
|
||||||
@@ -148,7 +148,7 @@ X Warn/error on any signal with cpuif_reset set, that is not in the top-level
|
|||||||
|
|
||||||
! async data signals
|
! async data signals
|
||||||
Only supporting async signals if they are exclusively used in resets.
|
Only supporting async signals if they are exclusively used in resets.
|
||||||
Anyhting else declared as "async" shall emit a warning that it is ignored
|
Anything else declared as "async" shall emit a warning that it is ignored
|
||||||
I have zero interest in implementing resynchronizers
|
I have zero interest in implementing resynchronizers
|
||||||
|
|
||||||
! Error if a property references a non-signal component, or property reference from
|
! Error if a property references a non-signal component, or property reference from
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ TODO:
|
|||||||
Provide a mechanism for users to extend/override field behavior
|
Provide a mechanism for users to extend/override field behavior
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
Does the endinness the user sets matter anywhere?
|
Does the endianness the user sets matter anywhere?
|
||||||
|
|
||||||
Implementation
|
Implementation
|
||||||
Makes sense to use a listener class
|
Makes sense to use a listener class
|
||||||
@@ -63,7 +63,7 @@ Be sure to skip alias registers
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
NextStateConditional Class
|
NextStateConditional Class
|
||||||
Decribes a single conditional action that determines the next state of a field
|
Describes a single conditional action that determines the next state of a field
|
||||||
Provides information to generate the following content:
|
Provides information to generate the following content:
|
||||||
if(<conditional>) begin
|
if(<conditional>) begin
|
||||||
<assignments>
|
<assignments>
|
||||||
@@ -110,22 +110,22 @@ FieldBuilder Class
|
|||||||
NextStateConditional objects are stored in a dictionary as follows:
|
NextStateConditional objects are stored in a dictionary as follows:
|
||||||
_conditionals {
|
_conditionals {
|
||||||
assignment_precedence: [
|
assignment_precedence: [
|
||||||
conditional_option_3,
|
|
||||||
conditional_option_2,
|
|
||||||
conditional_option_1,
|
conditional_option_1,
|
||||||
|
conditional_option_2,
|
||||||
|
conditional_option_3,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
add_conditional(self, conditional, assignment_precedence):
|
add_conditional(self, conditional, assignment_precedence):
|
||||||
Inserts the NextStateConditional into the given assignment precedence bin
|
Inserts the NextStateConditional into the given assignment precedence bin
|
||||||
The last one added to a precedence bin is first in that bin's search order
|
The first one added to a precedence bin is first in that bin's search order
|
||||||
|
|
||||||
init_conditionals(self) -> None:
|
init_conditionals(self) -> None:
|
||||||
Called from __init__.
|
Called from __init__.
|
||||||
loads all possible conditionals into self.conditionals list
|
loads all possible conditionals into self.conditionals list
|
||||||
This function is to provide a hook for the user to add their own.
|
This function is to provide a hook for the user to add their own.
|
||||||
|
|
||||||
Do not do fancy class intospection. Load them explicitly by name like so:
|
Do not do fancy class introspection. Load them explicitly by name like so:
|
||||||
self.add_conditional(MyNextState(), AssignmentPrecedence.SW_ACCESS)
|
self.add_conditional(MyNextState(), AssignmentPrecedence.SW_ACCESS)
|
||||||
|
|
||||||
If user wants to extend this class, they can pile onto the bins of conditionals freely!
|
If user wants to extend this class, they can pile onto the bins of conditionals freely!
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ Links
|
|||||||
:hidden:
|
:hidden:
|
||||||
:caption: CPU Interfaces
|
:caption: CPU Interfaces
|
||||||
|
|
||||||
|
cpuif/addressing
|
||||||
cpuif/apb3
|
cpuif/apb3
|
||||||
cpuif/advanced
|
cpuif/advanced
|
||||||
|
|
||||||
|
|||||||
@@ -402,46 +402,113 @@ that an interrupt is active. This is an or-reduction of all interrupt fields
|
|||||||
after applying the appropriate ``enable`` or ``mask`` to the field value.
|
after applying the appropriate ``enable`` or ``mask`` to the field value.
|
||||||
|
|
||||||
level (default)
|
level (default)
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
Interrupt is level-sensitive.
|
Interrupt is level-sensitive. If a bit on the field's ``hwif_in..next`` input
|
||||||
|
is '1', it will trigger an interrupt event.
|
||||||
|
|
||||||
posedge
|
posedge
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
If a bit on the field's ``hwif_in..next`` input transitions from '0' to '1',
|
||||||
|
it will trigger an interrupt event. This transition shall still be synchronous
|
||||||
|
to the register block's clock.
|
||||||
|
|
||||||
negedge
|
negedge
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
If a bit on the field's ``hwif_in..next`` input transitions from '1' to '0',
|
||||||
|
it will trigger an interrupt event. This transition shall still be synchronous
|
||||||
|
to the register block's clock.
|
||||||
|
|
||||||
bothedge
|
bothedge
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
If a bit on the field's ``hwif_in..next`` input transitions from '0' to '1' or '1' to '0',
|
||||||
|
it will trigger an interrupt event. This transition shall still be synchronous
|
||||||
|
to the register block's clock.
|
||||||
|
|
||||||
nonsticky
|
nonsticky
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
|
||||||
enable
|
enable
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
Reference to a field or signal that, if set to 1, define which bits in the field
|
||||||
|
are used to assert an interrupt.
|
||||||
|
|
||||||
|
|
||||||
mask
|
mask
|
||||||
^^^^
|
^^^^
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
Reference to a field or signal that, if set to 1, define which bits in the field
|
||||||
|
are *not* used to assert an interrupt.
|
||||||
|
|
||||||
|
|
||||||
haltenable
|
haltenable
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
Reference to a field or signal that, if set to 1, define which bits in the field
|
||||||
|
are used to assert the halt output.
|
||||||
|
|
||||||
|
If this property is set, the enclosing register will infer a ``hwif_out..halt`` output.
|
||||||
|
|
||||||
|
|
||||||
haltmask
|
haltmask
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
Reference to a field or signal that, if set to 1, define which bits in the field
|
||||||
|
are *not* used to assert the halt output.
|
||||||
|
|
||||||
|
If this property is set, the enclosing register will infer a ``hwif_out..halt`` output.
|
||||||
|
|
||||||
sticky
|
|
||||||
^^^^^^
|
|
||||||
|EX|
|
|
||||||
|
|
||||||
stickybit
|
stickybit
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
When an interrupt trigger occurs, a stickybit field will set the corresponding
|
||||||
|
bit to '1' and hold it until it is cleared by a software access.
|
||||||
|
|
||||||
|
The interrupt trigger depends on the interrupt type. By default, interrupts are
|
||||||
|
level-sensitive, but the interrupt modifiers allow for edge-sensitive triggers as
|
||||||
|
well.
|
||||||
|
|
||||||
|
The waveform below demonstrates a level-sensitive interrupt:
|
||||||
|
|
||||||
|
.. wavedrom::
|
||||||
|
|
||||||
|
{
|
||||||
|
signal: [
|
||||||
|
{name: 'clk', wave: 'p.....'},
|
||||||
|
{name: 'hwif_in..next', wave: '010...'},
|
||||||
|
{name: '<field value>', wave: '0.1...'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sticky
|
||||||
|
^^^^^^
|
||||||
|
|OK|
|
||||||
|
|
||||||
|
Unlike ``stickybit`` fields, a sticky field will latch an entire value. The
|
||||||
|
value is latched as soon as ``hwif_in..next`` is nonzero, and is held until the
|
||||||
|
field contents are cleared back to 0 by a software access.
|
||||||
|
|
||||||
|
.. wavedrom::
|
||||||
|
|
||||||
|
{
|
||||||
|
signal: [
|
||||||
|
{name: 'clk', wave: 'p.....'},
|
||||||
|
{name: 'hwif_in..next', wave: '23.22.', data: [0,10,20,30]},
|
||||||
|
{name: '<field value>', wave: '2.3...', data: [0, 10]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -206,8 +206,12 @@ Register
|
|||||||
|
|
||||||
reg -> intr
|
reg -> intr
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
References the register's ``hwif_out..intr`` signal.
|
||||||
|
|
||||||
reg -> halt
|
reg -> halt
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
|
References the register's ``hwif_out..halt`` signal.
|
||||||
|
|||||||
@@ -235,7 +235,6 @@ class FieldLogic:
|
|||||||
|
|
||||||
If multiple conditionals of the same precedence are registered, they are
|
If multiple conditionals of the same precedence are registered, they are
|
||||||
searched sequentially and only the first to match the given field is used.
|
searched sequentially and only the first to match the given field is used.
|
||||||
Conditionals are searched in reverse order that they were registered.
|
|
||||||
"""
|
"""
|
||||||
if precedence not in self._hw_conditionals:
|
if precedence not in self._hw_conditionals:
|
||||||
self._hw_conditionals[precedence] = []
|
self._hw_conditionals[precedence] = []
|
||||||
@@ -253,7 +252,6 @@ class FieldLogic:
|
|||||||
|
|
||||||
If multiple conditionals of the same precedence are registered, they are
|
If multiple conditionals of the same precedence are registered, they are
|
||||||
searched sequentially and only the first to match the given field is used.
|
searched sequentially and only the first to match the given field is used.
|
||||||
Conditionals are searched in reverse order that they were registered.
|
|
||||||
"""
|
"""
|
||||||
if precedence not in self._sw_conditionals:
|
if precedence not in self._sw_conditionals:
|
||||||
self._sw_conditionals[precedence] = []
|
self._sw_conditionals[precedence] = []
|
||||||
@@ -272,29 +270,29 @@ class FieldLogic:
|
|||||||
self.add_sw_conditional(sw_onread.ClearOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
|
self.add_sw_conditional(sw_onread.ClearOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
|
||||||
self.add_sw_conditional(sw_onread.SetOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
|
self.add_sw_conditional(sw_onread.SetOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
|
||||||
|
|
||||||
self.add_sw_conditional(sw_onwrite.WriteOneSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_sw_conditional(sw_onwrite.WriteOneClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_sw_conditional(sw_onwrite.WriteOneToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_sw_conditional(sw_onwrite.WriteZeroSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_sw_conditional(sw_onwrite.WriteZeroClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_sw_conditional(sw_onwrite.WriteZeroToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_sw_conditional(sw_onwrite.WriteClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_sw_conditional(sw_onwrite.WriteSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_sw_conditional(sw_onwrite.Write(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
self.add_sw_conditional(sw_onwrite.Write(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteZeroToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteZeroClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteZeroSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteOneToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteOneClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteOneSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
|
||||||
self.add_sw_conditional(sw_singlepulse.Singlepulse(self.exp), AssignmentPrecedence.SW_SINGLEPULSE)
|
self.add_sw_conditional(sw_singlepulse.Singlepulse(self.exp), AssignmentPrecedence.SW_SINGLEPULSE)
|
||||||
|
|
||||||
self.add_hw_conditional(hw_write.AlwaysWrite(self.exp), AssignmentPrecedence.HW_WRITE)
|
|
||||||
self.add_hw_conditional(hw_write.WELWrite(self.exp), AssignmentPrecedence.HW_WRITE)
|
|
||||||
self.add_hw_conditional(hw_write.WEWrite(self.exp), AssignmentPrecedence.HW_WRITE)
|
|
||||||
self.add_hw_conditional(hw_interrupts.Sticky(self.exp), AssignmentPrecedence.HW_WRITE)
|
|
||||||
self.add_hw_conditional(hw_interrupts.Stickybit(self.exp), AssignmentPrecedence.HW_WRITE)
|
|
||||||
self.add_hw_conditional(hw_interrupts.PosedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
|
self.add_hw_conditional(hw_interrupts.PosedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
self.add_hw_conditional(hw_interrupts.NegedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
|
self.add_hw_conditional(hw_interrupts.NegedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
self.add_hw_conditional(hw_interrupts.BothedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
|
self.add_hw_conditional(hw_interrupts.BothedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
self.add_hw_conditional(hw_interrupts.PosedgeNonsticky(self.exp), AssignmentPrecedence.HW_WRITE)
|
self.add_hw_conditional(hw_interrupts.PosedgeNonsticky(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
self.add_hw_conditional(hw_interrupts.NegedgeNonsticky(self.exp), AssignmentPrecedence.HW_WRITE)
|
self.add_hw_conditional(hw_interrupts.NegedgeNonsticky(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
self.add_hw_conditional(hw_interrupts.BothedgeNonsticky(self.exp), AssignmentPrecedence.HW_WRITE)
|
self.add_hw_conditional(hw_interrupts.BothedgeNonsticky(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
|
self.add_hw_conditional(hw_interrupts.Sticky(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
|
self.add_hw_conditional(hw_interrupts.Stickybit(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
|
self.add_hw_conditional(hw_write.WEWrite(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
|
self.add_hw_conditional(hw_write.WELWrite(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
|
self.add_hw_conditional(hw_write.AlwaysWrite(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
|
|
||||||
self.add_hw_conditional(hw_set_clr.HWClear(self.exp), AssignmentPrecedence.HWCLR)
|
self.add_hw_conditional(hw_set_clr.HWClear(self.exp), AssignmentPrecedence.HWCLR)
|
||||||
|
|
||||||
@@ -308,6 +306,7 @@ class FieldLogic:
|
|||||||
for conditional in conditionals[precedence]:
|
for conditional in conditionals[precedence]:
|
||||||
if conditional.is_match(field):
|
if conditional.is_match(field):
|
||||||
result.append(conditional)
|
result.append(conditional)
|
||||||
|
break
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ python3 -m pip install test/requirements.txt
|
|||||||
|
|
||||||
# Running tests
|
# Running tests
|
||||||
|
|
||||||
Tests can be launched from the test directory using `pytest``.
|
Tests can be launched from the test directory using `pytest`.
|
||||||
Use ``pytest -n auto`` to run tests in parallel.
|
Use `pytest -n auto` to run tests in parallel.
|
||||||
|
|
||||||
To run all tests:
|
To run all tests:
|
||||||
```bash
|
```bash
|
||||||
@@ -36,12 +36,12 @@ pytest
|
|||||||
|
|
||||||
# Test organization
|
# Test organization
|
||||||
|
|
||||||
The goal for this test infrastructre is to make it easy to add small-standalone
|
The goal for this test infrastructure is to make it easy to add small-standalone
|
||||||
testcases, with minimal repetition/boilerplate code that is usually present in
|
testcases, with minimal repetition/boilerplate code that is usually present in
|
||||||
SystemVerilog testbenches.
|
SystemVerilog testbenches.
|
||||||
|
|
||||||
To accomplish this, Jinja templates are used extensively to generate the
|
To accomplish this, Jinja templates are used extensively to generate the
|
||||||
resulting ``tb.sv`` file, as well as assist in dynamic testcase parameterization.
|
resulting `tb.sv` file, as well as assist in dynamic testcase parameterization.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
0
test/test_interrupts/__init__.py
Normal file
0
test/test_interrupts/__init__.py
Normal file
106
test/test_interrupts/regblock.rdl
Normal file
106
test/test_interrupts/regblock.rdl
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
addrmap top {
|
||||||
|
//---------------------------------
|
||||||
|
reg {
|
||||||
|
field ctrl_t {
|
||||||
|
sw=rw; hw=na;
|
||||||
|
};
|
||||||
|
ctrl_t irq0[8] = 0;
|
||||||
|
ctrl_t irq1[1] = 0;
|
||||||
|
}
|
||||||
|
ctrl_enable @ 0x100,
|
||||||
|
ctrl_mask @ 0x104,
|
||||||
|
ctrl_haltenable @ 0x108,
|
||||||
|
ctrl_haltmask @ 0x10c;
|
||||||
|
//---------------------------------
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field intr_t {
|
||||||
|
sw=rw; hw=w;
|
||||||
|
level intr;
|
||||||
|
woclr;
|
||||||
|
};
|
||||||
|
|
||||||
|
intr_t irq0[8] = 0;
|
||||||
|
intr_t irq1[1] = 0;
|
||||||
|
}
|
||||||
|
level_irqs_1 @ 0x0,
|
||||||
|
level_irqs_2 @ 0x4,
|
||||||
|
level_irqs_3 @ 0x8;
|
||||||
|
|
||||||
|
level_irqs_2.irq0->enable = ctrl_enable.irq0;
|
||||||
|
level_irqs_2.irq1->enable = ctrl_enable.irq1;
|
||||||
|
level_irqs_2.irq0->haltenable = ctrl_haltenable.irq0;
|
||||||
|
level_irqs_2.irq1->haltenable = ctrl_haltenable.irq1;
|
||||||
|
level_irqs_3.irq0->mask = ctrl_mask.irq0;
|
||||||
|
level_irqs_3.irq1->mask = ctrl_mask.irq1;
|
||||||
|
level_irqs_3.irq0->haltmask = ctrl_haltmask.irq0;
|
||||||
|
level_irqs_3.irq1->haltmask = ctrl_haltmask.irq1;
|
||||||
|
//---------------------------------
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field intr_t {
|
||||||
|
sw=rw; hw=w;
|
||||||
|
posedge intr;
|
||||||
|
woclr;
|
||||||
|
};
|
||||||
|
|
||||||
|
intr_t irq0[8] = 0;
|
||||||
|
intr_t irq1[1] = 0;
|
||||||
|
} posedge_irqs @ 0x10;
|
||||||
|
|
||||||
|
//---------------------------------
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field intr_t {
|
||||||
|
sw=rw; hw=w;
|
||||||
|
negedge intr;
|
||||||
|
woclr;
|
||||||
|
};
|
||||||
|
|
||||||
|
intr_t irq0[8] = 0;
|
||||||
|
intr_t irq1[1] = 0;
|
||||||
|
} negedge_irqs @ 0x20;
|
||||||
|
|
||||||
|
//---------------------------------
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field intr_t {
|
||||||
|
sw=rw; hw=w;
|
||||||
|
bothedge intr;
|
||||||
|
woclr;
|
||||||
|
};
|
||||||
|
|
||||||
|
intr_t irq0[8] = 0;
|
||||||
|
intr_t irq1[1] = 0;
|
||||||
|
} bothedge_irqs @ 0x30;
|
||||||
|
|
||||||
|
//---------------------------------
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field intr_t {
|
||||||
|
sw=r; hw=w;
|
||||||
|
nonsticky intr;
|
||||||
|
};
|
||||||
|
|
||||||
|
intr_t level_active[1];
|
||||||
|
intr_t posedge_active[1];
|
||||||
|
intr_t negedge_active[1];
|
||||||
|
intr_t bothedge_active[1];
|
||||||
|
intr_t level_halt_active[1];
|
||||||
|
} top_irq @ 0x40;
|
||||||
|
|
||||||
|
top_irq.level_active->next = level_irqs_1->intr;
|
||||||
|
top_irq.posedge_active->next = posedge_irqs->intr;
|
||||||
|
top_irq.negedge_active->next = negedge_irqs->intr;
|
||||||
|
top_irq.bothedge_active->next = bothedge_irqs->intr;
|
||||||
|
top_irq.level_halt_active->next = level_irqs_2->halt;
|
||||||
|
|
||||||
|
//---------------------------------
|
||||||
|
reg {
|
||||||
|
field {
|
||||||
|
sw=rw; hw=w;
|
||||||
|
sticky;
|
||||||
|
} stickyfield[8] = 0;
|
||||||
|
} stickyreg @ 0x50;
|
||||||
|
|
||||||
|
};
|
||||||
237
test/test_interrupts/tb_template.sv
Normal file
237
test/test_interrupts/tb_template.sv
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
{% extends "lib/tb_base.sv" %}
|
||||||
|
|
||||||
|
{% block seq %}
|
||||||
|
{% sv_line_anchor %}
|
||||||
|
##1;
|
||||||
|
cb.rst <= '0;
|
||||||
|
##1;
|
||||||
|
|
||||||
|
// Enable all interrupts
|
||||||
|
cpuif.write('h100, 'h1FF); // ctrl_enable
|
||||||
|
cpuif.write('h104, 'h000); // ctrl_mask
|
||||||
|
cpuif.write('h108, 'h1FF); // ctrl_haltenable
|
||||||
|
cpuif.write('h10C, 'h000); // ctrl_haltmask
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Test level_irqs_1
|
||||||
|
cpuif.assert_read('h0, 'h000);
|
||||||
|
assert(cb.hwif_out.level_irqs_1.intr == 1'b0);
|
||||||
|
cb.hwif_in.level_irqs_1.irq0.next <= 'h0F;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.level_irqs_1.irq0.next <= 'h00;
|
||||||
|
cpuif.assert_read('h0, 'h00F);
|
||||||
|
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
|
||||||
|
cpuif.write('h0, 'h3);
|
||||||
|
cpuif.assert_read('h0, 'h00C);
|
||||||
|
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
|
||||||
|
cpuif.write('h0, 'hC);
|
||||||
|
cpuif.assert_read('h0, 'h000);
|
||||||
|
assert(cb.hwif_out.level_irqs_1.intr == 1'b0);
|
||||||
|
|
||||||
|
cb.hwif_in.level_irqs_1.irq1.next <= 'b1;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.level_irqs_1.irq1.next <= 'b0;
|
||||||
|
cpuif.assert_read('h0, 'h100);
|
||||||
|
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
|
||||||
|
cpuif.write('h0, 'h100);
|
||||||
|
@cb;
|
||||||
|
assert(cb.hwif_out.level_irqs_1.intr == 1'b0);
|
||||||
|
cpuif.assert_read('h0, 'h0);
|
||||||
|
|
||||||
|
cb.hwif_in.level_irqs_1.irq1.next <= 'b1;
|
||||||
|
cpuif.assert_read('h0, 'h100);
|
||||||
|
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
|
||||||
|
cpuif.write('h0, 'h100);
|
||||||
|
cpuif.assert_read('h0, 'h100);
|
||||||
|
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
|
||||||
|
cb.hwif_in.level_irqs_1.irq1.next <= 'b0;
|
||||||
|
cpuif.assert_read('h0, 'h100);
|
||||||
|
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
|
||||||
|
cpuif.write('h0, 'h100);
|
||||||
|
cpuif.assert_read('h0, 'h000);
|
||||||
|
assert(cb.hwif_out.level_irqs_1.intr == 1'b0);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Test level_irqs_2
|
||||||
|
cpuif.assert_read('h4, 'h000);
|
||||||
|
assert(cb.hwif_out.level_irqs_2.intr == 1'b0);
|
||||||
|
assert(cb.hwif_out.level_irqs_2.halt == 1'b0);
|
||||||
|
cb.hwif_in.level_irqs_2.irq0.next <= 'h0F;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.level_irqs_2.irq0.next <= 'h00;
|
||||||
|
cpuif.assert_read('h4, 'h00F);
|
||||||
|
assert(cb.hwif_out.level_irqs_2.intr == 1'b1);
|
||||||
|
assert(cb.hwif_out.level_irqs_2.halt == 1'b1);
|
||||||
|
cpuif.write('h100, 'h0); // ctrl_enable
|
||||||
|
@cb;
|
||||||
|
assert(cb.hwif_out.level_irqs_2.intr == 1'b0);
|
||||||
|
assert(cb.hwif_out.level_irqs_2.halt == 1'b1);
|
||||||
|
cpuif.write('h108, 'h0); // ctrl_haltenable
|
||||||
|
@cb;
|
||||||
|
assert(cb.hwif_out.level_irqs_2.intr == 1'b0);
|
||||||
|
assert(cb.hwif_out.level_irqs_2.halt == 1'b0);
|
||||||
|
cpuif.write('h100, 'h1FF); // ctrl_enable
|
||||||
|
cpuif.write('h108, 'h1FF); // ctrl_haltenable
|
||||||
|
@cb;
|
||||||
|
assert(cb.hwif_out.level_irqs_2.intr == 1'b1);
|
||||||
|
assert(cb.hwif_out.level_irqs_2.halt == 1'b1);
|
||||||
|
cpuif.write('h4, 'h1FF);
|
||||||
|
@cb;
|
||||||
|
assert(cb.hwif_out.level_irqs_2.intr == 1'b0);
|
||||||
|
assert(cb.hwif_out.level_irqs_2.halt == 1'b0);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Test level_irqs_3
|
||||||
|
cpuif.assert_read('h8, 'h000);
|
||||||
|
assert(cb.hwif_out.level_irqs_3.intr == 1'b0);
|
||||||
|
assert(cb.hwif_out.level_irqs_3.halt == 1'b0);
|
||||||
|
cb.hwif_in.level_irqs_3.irq0.next <= 'h0F;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.level_irqs_3.irq0.next <= 'h00;
|
||||||
|
cpuif.assert_read('h8, 'h00F);
|
||||||
|
assert(cb.hwif_out.level_irqs_3.intr == 1'b1);
|
||||||
|
assert(cb.hwif_out.level_irqs_3.halt == 1'b1);
|
||||||
|
cpuif.write('h104, 'h0F); // ctrl_mask
|
||||||
|
@cb;
|
||||||
|
assert(cb.hwif_out.level_irqs_3.intr == 1'b0);
|
||||||
|
assert(cb.hwif_out.level_irqs_3.halt == 1'b1);
|
||||||
|
cpuif.write('h10C, 'hF); // ctrl_haltmask
|
||||||
|
@cb;
|
||||||
|
assert(cb.hwif_out.level_irqs_3.intr == 1'b0);
|
||||||
|
assert(cb.hwif_out.level_irqs_3.halt == 1'b0);
|
||||||
|
cpuif.write('h104, 'h0); // ctrl_mask
|
||||||
|
cpuif.write('h10C, 'h0); // ctrl_haltmask
|
||||||
|
@cb;
|
||||||
|
assert(cb.hwif_out.level_irqs_3.intr == 1'b1);
|
||||||
|
assert(cb.hwif_out.level_irqs_3.halt == 1'b1);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Test posedge_irqs
|
||||||
|
cpuif.assert_read('h10, 'h000);
|
||||||
|
assert(cb.hwif_out.posedge_irqs.intr == 1'b0);
|
||||||
|
cb.hwif_in.posedge_irqs.irq1.next <= 1'b1;
|
||||||
|
@cb;
|
||||||
|
cpuif.assert_read('h10, 'h100);
|
||||||
|
assert(cb.hwif_out.posedge_irqs.intr == 1'b1);
|
||||||
|
cpuif.write('h10, 'h100);
|
||||||
|
cpuif.assert_read('h10, 'h000);
|
||||||
|
assert(cb.hwif_out.posedge_irqs.intr == 1'b0);
|
||||||
|
cpuif.assert_read('h10, 'h000);
|
||||||
|
|
||||||
|
cb.hwif_in.posedge_irqs.irq1.next <= 1'b0;
|
||||||
|
cpuif.assert_read('h10, 'h000);
|
||||||
|
assert(cb.hwif_out.posedge_irqs.intr == 1'b0);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Test negedge_irqs
|
||||||
|
cpuif.assert_read('h20, 'h000);
|
||||||
|
assert(cb.hwif_out.negedge_irqs.intr == 1'b0);
|
||||||
|
cb.hwif_in.negedge_irqs.irq1.next <= 1'b1;
|
||||||
|
@cb;
|
||||||
|
cpuif.assert_read('h20, 'h000);
|
||||||
|
assert(cb.hwif_out.negedge_irqs.intr == 1'b0);
|
||||||
|
cb.hwif_in.negedge_irqs.irq1.next <= 1'b0;
|
||||||
|
cpuif.assert_read('h20, 'h100);
|
||||||
|
assert(cb.hwif_out.negedge_irqs.intr == 1'b1);
|
||||||
|
cpuif.write('h20, 'h100);
|
||||||
|
cpuif.assert_read('h20, 'h000);
|
||||||
|
assert(cb.hwif_out.negedge_irqs.intr == 1'b0);
|
||||||
|
cpuif.assert_read('h20, 'h000);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Test bothedge_irqs
|
||||||
|
cpuif.assert_read('h30, 'h000);
|
||||||
|
assert(cb.hwif_out.bothedge_irqs.intr == 1'b0);
|
||||||
|
|
||||||
|
cb.hwif_in.bothedge_irqs.irq1.next <= 1'b1;
|
||||||
|
cpuif.assert_read('h30, 'h100);
|
||||||
|
assert(cb.hwif_out.bothedge_irqs.intr == 1'b1);
|
||||||
|
cpuif.write('h30, 'h100);
|
||||||
|
cpuif.assert_read('h30, 'h000);
|
||||||
|
assert(cb.hwif_out.bothedge_irqs.intr == 1'b0);
|
||||||
|
cpuif.assert_read('h30, 'h000);
|
||||||
|
|
||||||
|
cb.hwif_in.bothedge_irqs.irq1.next <= 1'b0;
|
||||||
|
cpuif.assert_read('h30, 'h100);
|
||||||
|
assert(cb.hwif_out.bothedge_irqs.intr == 1'b1);
|
||||||
|
cpuif.write('h30, 'h100);
|
||||||
|
cpuif.assert_read('h30, 'h000);
|
||||||
|
assert(cb.hwif_out.bothedge_irqs.intr == 1'b0);
|
||||||
|
cpuif.assert_read('h30, 'h000);
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
cpuif.assert_read('h40, 'h000);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b0);
|
||||||
|
|
||||||
|
cb.hwif_in.level_irqs_1.irq0.next <= 'h01;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.level_irqs_1.irq0.next <= 'h00;
|
||||||
|
cpuif.assert_read('h40, 'b0001);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b1);
|
||||||
|
cpuif.write('h0, 'h01);
|
||||||
|
cpuif.assert_read('h40, 'b0000);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b0);
|
||||||
|
|
||||||
|
cb.hwif_in.posedge_irqs.irq0.next <= 'h01;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.posedge_irqs.irq0.next <= 'h00;
|
||||||
|
cpuif.assert_read('h40, 'b0010);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b1);
|
||||||
|
cpuif.write('h10, 'h01);
|
||||||
|
cpuif.assert_read('h40, 'b0000);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b0);
|
||||||
|
|
||||||
|
cb.hwif_in.negedge_irqs.irq0.next <= 'h01;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.negedge_irqs.irq0.next <= 'h00;
|
||||||
|
@cb;
|
||||||
|
cpuif.assert_read('h40, 'b0100);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b1);
|
||||||
|
cpuif.write('h20, 'h01);
|
||||||
|
cpuif.assert_read('h40, 'b0000);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b0);
|
||||||
|
|
||||||
|
cb.hwif_in.bothedge_irqs.irq0.next <= 'h01;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.bothedge_irqs.irq0.next <= 'h00;
|
||||||
|
cpuif.assert_read('h40, 'b1000);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b1);
|
||||||
|
cpuif.write('h30, 'h01);
|
||||||
|
cpuif.assert_read('h40, 'b0000);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b0);
|
||||||
|
|
||||||
|
cpuif.write('h108, 'h000); // ctrl_haltenable
|
||||||
|
cb.hwif_in.level_irqs_2.irq0.next <= 'h01;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.level_irqs_2.irq0.next <= 'h00;
|
||||||
|
@cb;
|
||||||
|
cpuif.assert_read('h40, 'b00000);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b0);
|
||||||
|
|
||||||
|
cpuif.write('h108, 'h001); // ctrl_haltenable
|
||||||
|
cpuif.assert_read('h40, 'b10000);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b1);
|
||||||
|
|
||||||
|
cpuif.write('h4, 'h01);
|
||||||
|
cpuif.assert_read('h40, 'b00000);
|
||||||
|
assert(cb.hwif_out.top_irq.intr == 1'b0);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Test multi-bit sticky reg
|
||||||
|
cpuif.assert_read('h50, 'h00);
|
||||||
|
cb.hwif_in.stickyreg.stickyfield.next <= 'h12;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.stickyreg.stickyfield.next <= 'h34;
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.stickyreg.stickyfield.next <= 'h56;
|
||||||
|
@cb;
|
||||||
|
cpuif.assert_read('h50, 'h12);
|
||||||
|
cpuif.write('h50, 'h00);
|
||||||
|
@cb;
|
||||||
|
cb.hwif_in.stickyreg.stickyfield.next <= 'h78;
|
||||||
|
@cb;
|
||||||
|
cpuif.assert_read('h50, 'h56);
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
5
test/test_interrupts/testcase.py
Normal file
5
test/test_interrupts/testcase.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from ..lib.regblock_testcase import RegblockTestCase
|
||||||
|
|
||||||
|
class Test(RegblockTestCase):
|
||||||
|
def test_dut(self):
|
||||||
|
self.run_test()
|
||||||
Reference in New Issue
Block a user