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).
This commit is contained in:
Jessica Clarke
2020-12-21 13:26:46 +00:00
parent 6b55d30a8e
commit ac6244181a

View File

@@ -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