execute1: Handle interrupts during sequences of load/store operations

At present the logic prevents any interrupts from being handled while
there is a load/store instruction (one that has unit=LDST) being
executed.  However, load/store instructions can still get sent to
loadstore1.  Thus an instruction which should generate an interrupt
such as a floating-point unavailable interrupt will instead get
executed.

To fix this, when we detect that an interrupt should be generated but
loadstore1 is still executing a previous instruction, we don't execute
any new instructions, and set a new r.intr_pending flag.  That results
in busy_out being asserted (meaning that no further instructions will
come in from decode2).  When loadstore1 has finished the instructions
it has, the interrupt gets sent to writeback.  If one of the
instructions in loadstore1 generates an interrupt in the meantime, the
l_in.interrupt signal gets asserted and that clears r.intr_pending, so
the interrupt we detected gets discarded.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
pull/313/head
Paul Mackerras 4 years ago
parent c198b2b82e
commit 64e3ce7134

@ -369,6 +369,7 @@ package common is
type Loadstore1ToExecute1Type is record
busy : std_ulogic;
in_progress : std_ulogic;
interrupt : std_ulogic;
end record;

type Loadstore1ToDcacheType is record

@ -58,6 +58,7 @@ architecture behaviour of execute1 is
cur_instr : Decode2ToExecute1Type;
busy: std_ulogic;
terminate: std_ulogic;
intr_pending : std_ulogic;
fp_exception_next : std_ulogic;
trace_next : std_ulogic;
prev_op : insn_type_t;
@ -71,7 +72,7 @@ architecture behaviour of execute1 is
constant reg_type_init : reg_type :=
(e => Execute1ToWritebackInit,
cur_instr => Decode2ToExecute1Init,
busy => '0', terminate => '0',
busy => '0', terminate => '0', intr_pending => '0',
fp_exception_next => '0', trace_next => '0', prev_op => OP_ILLEGAL, br_taken => '0',
mul_in_progress => '0', mul_finish => '0', div_in_progress => '0', cntz_in_progress => '0',
others => (others => '0'));
@ -655,8 +656,6 @@ begin

execute1_1: process(all)
variable v : reg_type;
variable lo, hi : integer;
variable sh, mb, me : std_ulogic_vector(5 downto 0);
variable bo, bi : std_ulogic_vector(4 downto 0);
variable overflow : std_ulogic;
variable lv : Execute1ToLoadstore1Type;
@ -702,18 +701,7 @@ begin
ctrl_tmp.tb <= std_ulogic_vector(unsigned(ctrl.tb) + 1);
ctrl_tmp.dec <= std_ulogic_vector(unsigned(ctrl.dec) - 1);

irq_valid := '0';
if ctrl.msr(MSR_EE) = '1' then
if ctrl.dec(63) = '1' then
v.e.intr_vec := 16#900#;
report "IRQ valid: DEC";
irq_valid := '1';
elsif ext_irq_in = '1' then
v.e.intr_vec := 16#500#;
report "IRQ valid: External";
irq_valid := '1';
end if;
end if;
irq_valid := ctrl.msr(MSR_EE) and (ctrl.dec(63) or ext_irq_in);

v.terminate := '0';
icache_inval <= '0';
@ -728,9 +716,11 @@ begin
rot_clear_right <= '1' when e_in.insn_type = OP_RLC or e_in.insn_type = OP_RLCR else '0';
rot_sign_ext <= '1' when e_in.insn_type = OP_EXTSWSLI else '0';

v.e.srr1 := (others => '0');
exception := '0';
illegal := '0';
if r.intr_pending = '1' then
v.e.srr1 := r.e.srr1;
v.e.intr_vec := r.e.intr_vec;
end if;
if valid_in = '1' then
v.e.last_nia := e_in.nia;
else
@ -742,12 +732,14 @@ begin

do_trace := valid_in and ctrl.msr(MSR_SE);
if valid_in = '1' then
v.cur_instr := e_in;
v.prev_op := e_in.insn_type;
end if;

-- Determine if there is any exception to be taken
-- Determine if there is any interrupt to be taken
-- before/instead of executing this instruction
if valid_in = '1' and e_in.second = '0' and l_in.in_progress = '0' then
exception := r.intr_pending;
if valid_in = '1' and e_in.second = '0' and r.intr_pending = '0' then
if HAS_FPU and r.fp_exception_next = '1' then
-- This is used for FP-type program interrupts that
-- become pending due to MSR[FE0,FE1] changing from 00 to non-zero.
@ -771,6 +763,13 @@ begin
elsif irq_valid = '1' then
-- Don't deliver the interrupt until we have a valid instruction
-- coming in, so we have a valid NIA to put in SRR0.
if ctrl.dec(63) = '1' then
v.e.intr_vec := 16#900#;
report "IRQ valid: DEC";
elsif ext_irq_in = '1' then
v.e.intr_vec := 16#500#;
report "IRQ valid: External";
end if;
exception := '1';

elsif ctrl.msr(MSR_PR) = '1' and instr_is_privileged(e_in.insn_type, e_in.insn) then
@ -792,9 +791,17 @@ begin
report "FP unavailable interrupt";
end if;
end if;
if exception = '1' and l_in.in_progress = '1' then
-- We can't send this interrupt to writeback yet because there are
-- still instructions in loadstore1 that haven't completed.
v.intr_pending := '1';
v.busy := '1';
end if;
if l_in.interrupt = '1' then
v.intr_pending := '0';
end if;

if valid_in = '1' and exception = '0' and illegal = '0' and e_in.unit = ALU then
v.cur_instr := e_in;
v.e.valid := '1';

case_0: case e_in.insn_type is
@ -1136,7 +1143,10 @@ begin
report "illegal";
end if;

v.e.interrupt := exception;
v.e.interrupt := exception and not (l_in.in_progress or l_in.interrupt);
if v.e.interrupt = '1' then
v.intr_pending := '0';
end if;

if do_trace = '1' then
v.trace_next := '1';
@ -1157,6 +1167,7 @@ begin
ctrl_tmp.msr(MSR_LE) <= '1';
v.trace_next := '0';
v.fp_exception_next := '0';
v.intr_pending := '0';
end if;

if hold_wr_data = '0' then

@ -944,6 +944,7 @@ begin
-- update busy signal back to execute1
e_out.busy <= busy;
e_out.in_progress <= in_progress;
e_out.interrupt <= r3.interrupt;

-- Busy calculation.
stage3_busy_next <= r2.req.valid and not (complete or part_done or exception);

Loading…
Cancel
Save