mirror of
https://github.com/antonblanchard/microwatt.git
synced 2026-03-10 12:28:45 +00:00
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>
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user