[rv_plic] Latch to CC for pending interrupts
This is related to #577. @GregAC reported this bug while reviewing the
RV_PLIC specification.
Error:
RISC-V PLIC assumes the Claim/Complete register can be polled by the
software even the interrupt pending bit (mip) isn't set. The RV_PLIC
design only set the CC0 register when the priority is greater than
the threshold value.
From the RISCV PLIC Spec:
> It is always legal for a hart to perform a claim even if the EIP is
> not set. In particular, a hart could set the threshold value to
> maximum to disable interrupt notifications and instead poll for active
> interrupts using periodic claim requests, though a simpler approach to
> implement polling would be to clear the external interrupt enable in
> the corresponding xie register for privilege mode x.
https://github.com/riscv/riscv-isa-manual/blob/master/src/plic.tex#L329-L334
For instance, if the `threshold` value is set to 7h while the MAX_PRIO
is set to 7 and ID 1 interrupt is pending, the `irq_o` of the RV_PLIC IP
will never be asserted. In this case, `CC0` register of the previous
version showed value `0` always. What spec expects is to be visible of
the most highest priority pending interrupt, which is, in this case, ID
1.
Solution:
Revise the logic not to consider threshold to calculate `irq_id` in
the target module. But `irq` should consider the threshold value
comparing with max priority calculated.
diff --git a/hw/ip/rv_plic/rtl/rv_plic_target.sv b/hw/ip/rv_plic/rtl/rv_plic_target.sv
index 9902a6b..17f19a5 100644
--- a/hw/ip/rv_plic/rtl/rv_plic_target.sv
+++ b/hw/ip/rv_plic/rtl/rv_plic_target.sv
@@ -43,16 +43,17 @@
logic irq_next;
logic [SRCW-1:0] irq_id_next;
always_comb begin
- max_prio = threshold + 1'b1; // Priority strictly greater than threshold
+ // Threshold doesn't matter for interrupt claim, it only factors into
+ // whether irq is raised for a target
+ max_prio = 1'b0;
irq_id_next = '0; // default: No Interrupt
- irq_next = 1'b0;
for (int i = N_SOURCE-1 ; i >= 0 ; i--) begin
if ((ip[i] & ie[i]) == 1'b1 && prio[i] >= max_prio) begin
max_prio = MAX_PRIOW'(prio[i]);
irq_id_next = SRCW'(i+1);
- irq_next = 1'b1;
end
end // for i
+ irq_next = (max_prio > threshold) ? 1'b1 : 1'b0;
end
always_ff @(posedge clk_i or negedge rst_ni) begin
@@ -74,19 +75,19 @@
// So if above approach(ALGORITHM 1) meets timing, don't use this algorithm.
logic [N_SOURCE-1:0] is;
- logic [N_SOURCE-1:0][N_SOURCE-1:0] mat;
logic [N_SOURCE-1:0] merged_row;
assign is = ip & ie;
always_comb begin
- merged_row[N_SOURCE-1] = is[N_SOURCE-1] & (prio[N_SOURCE-1] > threshold);
+ merged_row[N_SOURCE-1] = is[N_SOURCE-1] ;
for (int i = 0 ; i < N_SOURCE-1 ; i++) begin
merged_row[i] = 1'b1;
for (int j = i+1 ; j < N_SOURCE ; j++) begin
- mat[i][j] = (prio[i] <= threshold) ? 1'b0 : // No compare if less than TH
- (is[i] & is[j]) ? prio[i] >= prio[j] :
- (is[i]) ? 1'b 1 : 1'b 0 ;
- merged_row[i] = merged_row[i] & mat[i][j]; // all should be 1
+ if (is[i] && is[j]) begin
+ merged_row[i] = merged_row[i] & (prio[i] >= prio[j]);
+ end else if (!is[i]) begin
+ merged_row[i] = 1'b0;
+ end
end // for j
end // for i
end // always_comb
@@ -103,7 +104,7 @@
// so, safely run for loop
for (int i = N_SOURCE-1 ; i >= 0 ; i--) begin
if (lod[i] == 1'b1) begin
- irq <= 1'b 1;
+ if (prio[i] > threshold) irq <= 1'b 1;
irq_id <= SRCW'(i + 1);
end
end // for