diff --git a/hdl/arith/hazard3_onehot_priority.v b/hdl/arith/hazard3_onehot_priority.v index 65cba6d..238fac2 100644 --- a/hdl/arith/hazard3_onehot_priority.v +++ b/hdl/arith/hazard3_onehot_priority.v @@ -21,7 +21,7 @@ always @ (*) begin: select integer i; for (i = 0; i < W_REQ; i = i + 1) begin gnt[i] = req[i] && ~|(req & ( - HIGHEST_WINS ? ~({W_REQ{1'b1}} >> i) : ~({W_REQ{1'b1}} << i) + HIGHEST_WINS ? ~({W_REQ{1'b1}} >> (W_REQ - 1 - i)) : ~({W_REQ{1'b1}} << i) )); end end diff --git a/hdl/arith/hazard3_onehot_priority_dynamic.v b/hdl/arith/hazard3_onehot_priority_dynamic.v index 5dedc07..808e528 100644 --- a/hdl/arith/hazard3_onehot_priority_dynamic.v +++ b/hdl/arith/hazard3_onehot_priority_dynamic.v @@ -19,7 +19,7 @@ module hazard3_onehot_priority_dynamic #( // Do not modify: parameter W_PRIORITY = $clog2(N_PRIORITIES) ) ( - input wire [W_REQ*W_PRIORITY-1:0] priority, + input wire [W_REQ*W_PRIORITY-1:0] pri, input wire [W_REQ-1:0] req, output wire [W_REQ-1:0] gnt ); @@ -32,7 +32,7 @@ always @ (*) begin: stratify integer i, j; for (i = 0; i < N_PRIORITIES; i = i + 1) begin for (j = 0; j < W_REQ; j = j + 1) begin - req_stratified[i][j] = req[j] && priority[W_PRIORITY * j +: W_PRIORITY] == i; + req_stratified[i][j] = req[j] && pri[W_PRIORITY * j +: W_PRIORITY] == i; end level_has_req[i] = |req_stratified[i]; end diff --git a/hdl/hazard3_csr.v b/hdl/hazard3_csr.v index d099309..a907d81 100644 --- a/hdl/hazard3_csr.v +++ b/hdl/hazard3_csr.v @@ -313,7 +313,7 @@ end localparam MAX_IRQS = 512; localparam [MAX_IRQS-1:0] IRQ_IMPL_MASK = ~({MAX_IRQS{1'b1}} << NUM_IRQS); -localparam IRQ_PRIORITY_MASK = ~(4'hf >> IRQ_PRIORITY_BITS); +localparam [3:0] IRQ_PRIORITY_MASK = ~(4'hf >> IRQ_PRIORITY_BITS); // Assigned later: wire [MAX_IRQS-1:0] meipa; @@ -471,9 +471,9 @@ hazard3_onehot_priority_dynamic #( .PRIORITY_HIGHEST_WINS (1), .TIEBREAK_HIGHEST_WINS (0) ) eirq_priority_u ( - .priority (meipra[NUM_IRQS-1:0] & {NUM_IRQS{IRQ_PRIORITY_MASK}}), - .req (eirq_active_above_ppreempt), - .gnt (highest_eirq_onehot) + .pri (meipra[4*NUM_IRQS-1:0] & {NUM_IRQS{IRQ_PRIORITY_MASK}}), + .req (eirq_active_above_ppreempt), + .gnt (highest_eirq_onehot) ); always @ (*) begin: get_highest_eirq_priority diff --git a/test/sim/common/irq_dispatch.S b/test/sim/common/irq_dispatch.S index 9758ba4..6347f4a 100644 --- a/test/sim/common/irq_dispatch.S +++ b/test/sim/common/irq_dispatch.S @@ -23,6 +23,14 @@ isr_external_irq: sw t5, 56(sp) sw t6, 60(sp) + // Update a count of the number of external IRQ vector entries (just for + // use in tests) + la a0, _external_irq_entry_count + lw a1, (a0) + addi a1, a1, 1 + sw a1, (a0) + // Make sure to delete the above ^^^ if you use this code for real! + csrr a0, mepc sw a0, 64(sp) // Make sure to set meicontext.clearts to clear and save mie.msie/mtie @@ -117,3 +125,7 @@ _external_irq_table: ref_eirq i .equ i, i + 1 .endr + +.global _external_irq_entry_count +_external_irq_entry_count: +.word 0 diff --git a/test/sim/sw_testcases/Makefile b/test/sim/sw_testcases/Makefile index 97f4ac3..7119393 100644 --- a/test/sim/sw_testcases/Makefile +++ b/test/sim/sw_testcases/Makefile @@ -5,5 +5,6 @@ MAX_CYCLES := 1000000 INCDIR := include ../common EXTRA_SRCS_irq_preempt_set_in_irq := ../common/irq_dispatch.S +EXTRA_SRCS_irq_set_all_with_pri := ../common/irq_dispatch.S include ../common/src_only_app.mk diff --git a/test/sim/sw_testcases/irq_preempt_set_in_irq.c b/test/sim/sw_testcases/irq_preempt_set_in_irq.c index b4b3603..4482a91 100644 --- a/test/sim/sw_testcases/irq_preempt_set_in_irq.c +++ b/test/sim/sw_testcases/irq_preempt_set_in_irq.c @@ -36,7 +36,7 @@ Exiting IRQ 3 Exiting IRQ 2 Exiting IRQ 1 Exiting IRQ 0 -Done. +EIRQ vector was entered 16 times *******************************************************************************/ @@ -44,6 +44,7 @@ Done. #define NUM_IRQS 64 extern uintptr_t _external_irq_table[NUM_IRQS]; +extern uint32_t _external_irq_entry_count; void handler(void); @@ -64,7 +65,7 @@ int main() { tb_set_irq_masked(0x1); asm ("wfi"); - tb_puts("Done.\n"); + tb_printf("EIRQ vector was entered %d times\n", _external_irq_entry_count); return 0; } diff --git a/test/sim/sw_testcases/irq_set_all_with_pri.c b/test/sim/sw_testcases/irq_set_all_with_pri.c new file mode 100644 index 0000000..0e852cf --- /dev/null +++ b/test/sim/sw_testcases/irq_set_all_with_pri.c @@ -0,0 +1,91 @@ +#include "tb_cxxrtl_io.h" +#include "hazard3_irq.h" + +// Similar to the irq_preempt_set_in_irq test, but there are two IRQs at each +// priority level, and rather than posting the high-priority IRQs from within +// lower priority IRQs, we post every single IRQ at once. Check that: +// +// - The IRQS run from highest to lowest priority order (which happens to be +// opposite to the default tiebreak order for these IRQs) +// +// - Within each priority level, the IRQs are tiebroken lowest-first +// +// - Each IRQ enters and exits before any lower IRQ enters +// +// - The EIRQ vector was entered exactly once (save/restore was shared between +// all the IRQs at their different priority levels) + +/*EXPECTED-OUTPUT*************************************************************** + +Posting IRQs +Entered IRQ 15 +Exiting IRQ 15 +Entered IRQ 14 +Exiting IRQ 14 +Entered IRQ 13 +Exiting IRQ 13 +Entered IRQ 12 +Exiting IRQ 12 +Entered IRQ 11 +Exiting IRQ 11 +Entered IRQ 10 +Exiting IRQ 10 +Entered IRQ 9 +Exiting IRQ 9 +Entered IRQ 8 +Exiting IRQ 8 +Entered IRQ 7 +Exiting IRQ 7 +Entered IRQ 6 +Exiting IRQ 6 +Entered IRQ 5 +Exiting IRQ 5 +Entered IRQ 4 +Exiting IRQ 4 +Entered IRQ 3 +Exiting IRQ 3 +Entered IRQ 2 +Exiting IRQ 2 +Entered IRQ 1 +Exiting IRQ 1 +Entered IRQ 0 +Exiting IRQ 0 +EIRQ vector was entered 1 times +gstat +*******************************************************************************/ + +#define PRIORITY_LEVELS 16 +#define NUM_IRQS 32 + +extern uintptr_t _external_irq_table[NUM_IRQS]; +extern uint32_t _external_irq_entry_count; + +void handler(void); + +int main() { + // Enable external IRQs globally + set_csr(mstatus, 0x8); + set_csr(mie, 0x800); + // Enable one external IRQ at each priority level + for (int i = 0; i < PRIORITY_LEVELS * 2; ++i) { + h3irq_enable(i, true); + h3irq_set_priority(i, i / 2); + _external_irq_table[i] = (uintptr_t)handler; + } + // Set off the lowest-priority IRQ. The IRQ handler will then set the + // next-lowest, which will preempt it. So on, up to the highest level, + // then return all the way back down through the nested frames. + tb_puts("Posting IRQs\n"); + tb_set_irq_masked(0xffffffffu); + asm ("wfi"); + + tb_printf("EIRQ vector was entered %d times\n", _external_irq_entry_count); + return 0; +} + +void handler(void) { + int irqnum = h3irq_get_current_irq(); + tb_printf("Entered IRQ %d\n", irqnum); + tb_clr_irq_masked(1u << irqnum); + tb_printf("Exiting IRQ %d\n", irqnum); +}