From ac6244181a531b86a8f5f54e5b98ac85e97d64f1 Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Mon, 21 Dec 2020 13:26:46 +0000 Subject: [PATCH] PLIC: Allow multiple interrupts to be claimed but not completed Whilst the current PLIC spec is hard to decipher, the older v1.9 privileged spec is clearer. The intent is that each interrupt is independent and so claims and completions can be overlapped (e.g. as done by FreeBSD if deferring an interrupt handler). This is approximately expressed in the spec by: The PLIC can perform a claim at any time and the claim operation is not affected by the setting of the priority threshold register. ... The PLIC does not check whether the completion ID is the same as the last claim ID for that target. If the completion ID does not match an interrupt source that is currently enabled for the target, the completion is silently ignored. Thost last two sentences make it sound like the completion ID not matching the last claim ID is a thing to ignore, but in fact they are entirely independent statements. This can be confirmed by reading the source for the Rocket and QEMU PLICs, which have none of the stricter requirements of the Bluespec PLIC implementation. This should fix running FreeBSD after r362977, which started to correctly delay the completion of interrupts until after deferred handlers had completed (as opposed to after they had been queued). --- src_Core/PLIC/PLIC.bsv | 46 ++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/src_Core/PLIC/PLIC.bsv b/src_Core/PLIC/PLIC.bsv index 18e7a5b..9f0e7a2 100644 --- a/src_Core/PLIC/PLIC.bsv +++ b/src_Core/PLIC/PLIC.bsv @@ -158,8 +158,6 @@ module mkPLIC (PLIC_IFC #(t_n_external_sources, t_n_targets, t_max_priority)) // Threshold: interrupts at or below threshold should be masked out for target Vector #(t_n_targets, Reg #(Bit #(t_wd_priority))) vrg_target_threshold <- replicateM (mkReg ('1)); - // Target has claimed interrupt for source and is servicing it - Vector #(t_n_targets, Reg #(Bit #(TLog #(t_n_sources)))) vrg_servicing_source <- replicateM (mkReg (0)); // ---------------- // Per-target, per-source state @@ -212,8 +210,8 @@ module mkPLIC (PLIC_IFC #(t_n_external_sources, t_n_targets, t_max_priority)) for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1) $write (" %0d", vvrg_ie [target_id][source_id]); match { .max_prio, .max_id } = fn_target_max_prio_and_max_id (fromInteger (target_id)); - $display (" MaxPri %0d, Thresh %0d, MaxId %0d, Svcing %0d", - max_prio, vrg_target_threshold [target_id], max_id, vrg_servicing_source [target_id]); + $display (" MaxPri %0d, Thresh %0d, MaxId %0d", + max_prio, vrg_target_threshold [target_id], max_id); end endaction endfunction @@ -236,7 +234,6 @@ module mkPLIC (PLIC_IFC #(t_n_external_sources, t_n_targets, t_max_priority)) for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1) begin // Mask all interrupts with highest threshold vrg_target_threshold [target_id] <= '1; - vrg_servicing_source [target_id] <= 0; end for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1) @@ -362,26 +359,15 @@ module mkPLIC (PLIC_IFC #(t_n_external_sources, t_n_targets, t_max_priority)) match { .max_prio, .max_id } = fn_target_max_prio_and_max_id (target_id); Bool eip = (max_prio > vrg_target_threshold [target_id]); if (target_id <= fromInteger (n_targets - 1)) begin - if (vrg_servicing_source [target_id] != 0) begin - $display ("%0d: ERROR: PLIC: target %0d claiming without prior completion", - cur_cycle, target_id); - $display (" Still servicing interrupt from source %0d", vrg_servicing_source [target_id]); - $display (" Trying to claim service for source %0d", max_id); - $display (" Ignoring."); - rresp = SLVERR; + if (max_id != 0) begin + vrg_source_ip [max_id] <= False; + vrg_source_busy [max_id] <= True; + rdata = changeWidth (max_id); end - else begin - if (max_id != 0) begin - vrg_source_ip [max_id] <= False; - vrg_source_busy [max_id] <= True; - vrg_servicing_source [target_id] <= truncate (max_id); - rdata = changeWidth (max_id); - if (cfg_verbosity > 0) - $display ("%0d: PLIC.rl_process_rd_req: reading Claim for target %0d = 0x%0h", - cur_cycle, target_id, rdata); - end - end + if (cfg_verbosity > 0) + $display ("%0d: PLIC.rl_process_rd_req: reading Claim for target %0d = 0x%0h", + cur_cycle, target_id, rdata); end else rresp = SLVERR; @@ -515,22 +501,20 @@ module mkPLIC (PLIC_IFC #(t_n_external_sources, t_n_targets, t_max_priority)) // Actual memory-write-data is irrelevant. else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0004) begin Bit #(T_wd_target_id) target_id = truncate (addr_offset [20:12]); - Bit #(T_wd_source_id) source_id = zeroExtend (vrg_servicing_source [target_id]); + Bit #(T_wd_source_id) source_id = truncate (wdata32); if (target_id <= fromInteger (n_targets - 1)) begin - if (vrg_source_busy [source_id]) begin + if ( (source_id <= fromInteger (n_sources)) + && vvrg_ie [target_id][source_id]) begin vrg_source_busy [source_id] <= False; - vrg_servicing_source [target_id] <= 0; if (cfg_verbosity > 0) $display ("%0d: PLIC.rl_process_wr_req: writing completion for target %0d for source 0x%0h", cur_cycle, target_id, source_id); end else begin - $display ("%0d: ERROR: PLIC: interrupt completion to source that is not being serviced", - cur_cycle); - $display (" Completion message from target %0d to source %0d", target_id, source_id); - $display (" Ignoring"); - bresp = SLVERR; + if (cfg_verbosity > 0) + $display ("%0d: PLIC.rl_process_wr_req: ignoring completion for target %0d for source 0x%0h", + cur_cycle, target_id, source_id); end end else