diff --git a/docs/addressing.rst b/docs/cpuif/addressing.rst similarity index 100% rename from docs/addressing.rst rename to docs/cpuif/addressing.rst diff --git a/docs/dev_notes/Validation Needed b/docs/dev_notes/Validation Needed index 078660e..5d14ac7 100644 --- a/docs/dev_notes/Validation Needed +++ b/docs/dev_notes/Validation Needed @@ -105,12 +105,13 @@ X sticky=true + "(posedge|negedge|bothedge) intr" X we/wel + implied or explicit "sticky"/"stickybit" we/wel modifier doesn't make sense here. -! hwclr/hwset/we/wel probably shouldn't be able to reference itself - y->hwclr = y; - y->we = y; - ... it works, but should it be allowed? Seems like user-error +X sticky/stickybit shall be hw writable -! 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; decrsaturate; underflow; @@ -119,11 +120,10 @@ X we/wel + implied or explicit "sticky"/"stickybit" Same goes to prop references to overflow/underflow -! incrwidth/decrwidth must be between 1 and the width of the counter - -! Illegal to use enable/mask/haltenable/haltmask on non-intr fields - -! sticky/stickybit shall be hw writable +! hwclr/hwset/we/wel probably shouldn't be able to reference itself + y->hwclr = y; + y->we = y; + ... it works, but should it be allowed? Seems like user-error ================================================================================ 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 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 ! Error if a property references a non-signal component, or property reference from diff --git a/docs/dev_notes/template-layers/4-fields b/docs/dev_notes/template-layers/4-fields index ed03318..3721266 100644 --- a/docs/dev_notes/template-layers/4-fields +++ b/docs/dev_notes/template-layers/4-fields @@ -53,7 +53,7 @@ TODO: Provide a mechanism for users to extend/override field behavior TODO: - Does the endinness the user sets matter anywhere? + Does the endianness the user sets matter anywhere? Implementation Makes sense to use a listener class @@ -63,7 +63,7 @@ Be sure to skip alias registers -------------------------------------------------------------------------------- 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: if() begin @@ -110,22 +110,22 @@ FieldBuilder Class NextStateConditional objects are stored in a dictionary as follows: _conditionals { assignment_precedence: [ - conditional_option_3, - conditional_option_2, conditional_option_1, + conditional_option_2, + conditional_option_3, ] } add_conditional(self, conditional, assignment_precedence): 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: Called from __init__. loads all possible conditionals into self.conditionals list 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) If user wants to extend this class, they can pile onto the bins of conditionals freely! diff --git a/docs/index.rst b/docs/index.rst index a967987..135eb23 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -44,6 +44,7 @@ Links :hidden: :caption: CPU Interfaces + cpuif/addressing cpuif/apb3 cpuif/advanced diff --git a/docs/props/field.rst b/docs/props/field.rst index 4b6f03f..ae438bf 100644 --- a/docs/props/field.rst +++ b/docs/props/field.rst @@ -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. 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 - |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 - |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 - |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 - |EX| + |OK| 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 ^^^^ -|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 ^^^^^^^^^^ -|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 ^^^^^^^^ -|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 ^^^^^^^^^ -|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: '', 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: '', wave: '2.3...', data: [0, 10]} + ] + } -------------------------------------------------------------------------------- diff --git a/docs/props/rhs_props.rst b/docs/props/rhs_props.rst index dc4f3c7..aed4e00 100644 --- a/docs/props/rhs_props.rst +++ b/docs/props/rhs_props.rst @@ -206,8 +206,12 @@ Register reg -> intr ^^^^^^^^^^^ -|EX| +|OK| + +References the register's ``hwif_out..intr`` signal. reg -> halt ^^^^^^^^^^^ -|EX| +|OK| + +References the register's ``hwif_out..halt`` signal. diff --git a/peakrdl/regblock/field_logic/__init__.py b/peakrdl/regblock/field_logic/__init__.py index cc870ed..2fbf9be 100644 --- a/peakrdl/regblock/field_logic/__init__.py +++ b/peakrdl/regblock/field_logic/__init__.py @@ -235,7 +235,6 @@ class FieldLogic: If multiple conditionals of the same precedence are registered, they are 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: self._hw_conditionals[precedence] = [] @@ -253,7 +252,6 @@ class FieldLogic: If multiple conditionals of the same precedence are registered, they are 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: 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.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.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_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.NegedgeStickybit(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.NegedgeNonsticky(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) @@ -308,6 +306,7 @@ class FieldLogic: for conditional in conditionals[precedence]: if conditional.is_match(field): result.append(conditional) + break return result diff --git a/test/README.md b/test/README.md index 1c0133f..02b8c07 100644 --- a/test/README.md +++ b/test/README.md @@ -18,8 +18,8 @@ python3 -m pip install test/requirements.txt # Running tests -Tests can be launched from the test directory using `pytest``. -Use ``pytest -n auto`` to run tests in parallel. +Tests can be launched from the test directory using `pytest`. +Use `pytest -n auto` to run tests in parallel. To run all tests: ```bash @@ -36,12 +36,12 @@ pytest # 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 SystemVerilog testbenches. 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. diff --git a/test/test_interrupts/__init__.py b/test/test_interrupts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_interrupts/regblock.rdl b/test/test_interrupts/regblock.rdl new file mode 100644 index 0000000..8cf563d --- /dev/null +++ b/test/test_interrupts/regblock.rdl @@ -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; + +}; diff --git a/test/test_interrupts/tb_template.sv b/test/test_interrupts/tb_template.sv new file mode 100644 index 0000000..7ff0bd5 --- /dev/null +++ b/test/test_interrupts/tb_template.sv @@ -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 %} diff --git a/test/test_interrupts/testcase.py b/test/test_interrupts/testcase.py new file mode 100644 index 0000000..b458d3c --- /dev/null +++ b/test/test_interrupts/testcase.py @@ -0,0 +1,5 @@ +from ..lib.regblock_testcase import RegblockTestCase + +class Test(RegblockTestCase): + def test_dut(self): + self.run_test()