From c4492c843a6d4c61df7e0134c3b13342599b1102 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 3 Jul 2023 18:19:38 +1000 Subject: [PATCH] Implement interrupts for prefixed instructions This arranges to generate an illegal instruction type program interrupt for illegal prefixed instructions, that is, those where the suffix is not a legal value given the prefix, or the prefix has a reserved value in the subtype field. This implementation doesn't generate an interrupt for the invalid 8LS:D and MLS:D instruction forms where R = 1 and RA != 0. (In those cases it uses (RA) as the addend, i.e. it ignores the R bit.) This detects the case where the address of an instruction prefix is equal mod 64 to 60, and generates an alignment interrupt in that case. This also arranges to set bit 34 of SRR1 when an interrupt occurs due to a prefixed instruction, for those interrupts where that is required (i.e. trace, alignment, floating-point unavailable, data storage, data segment, and most cases of program interrupt). Signed-off-by: Paul Mackerras --- common.vhdl | 10 ++++++++-- decode1.vhdl | 8 +++++++- decode2.vhdl | 33 +++++++++++++++++++++------------ execute1.vhdl | 28 ++++++++++++++++++++++++++-- loadstore1.vhdl | 7 ++++++- 5 files changed, 68 insertions(+), 18 deletions(-) diff --git a/common.vhdl b/common.vhdl index 838179b..59c855e 100644 --- a/common.vhdl +++ b/common.vhdl @@ -266,6 +266,7 @@ package common is prefixed: std_ulogic; prefix: std_ulogic_vector(25 downto 0); illegal_suffix: std_ulogic; + misaligned_prefix: std_ulogic; insn: std_ulogic_vector(31 downto 0); decode: decode_rom_t; br_pred: std_ulogic; -- Branch was predicted to be taken @@ -279,7 +280,7 @@ package common is constant Decode1ToDecode2Init : Decode1ToDecode2Type := (valid => '0', stop_mark => '0', nia => (others => '0'), prefixed => '0', prefix => (others => '0'), insn => (others => '0'), - illegal_suffix => '0', + illegal_suffix => '0', misaligned_prefix => '0', decode => decode_rom_init, br_pred => '0', big_endian => '0', spr_info => spr_id_init, ram_spr => ram_spr_info_init, reg_a => (others => '0'), reg_b => (others => '0'), reg_c => (others => '0')); @@ -364,6 +365,9 @@ package common is ramspr_write_odd : std_ulogic; dbg_spr_access : std_ulogic; dec_ctr : std_ulogic; + prefixed : std_ulogic; + illegal_suffix : std_ulogic; + misaligned_prefix : std_ulogic; end record; constant Decode2ToExecute1Init : Decode2ToExecute1Type := (valid => '0', unit => ALU, fac => NONE, insn_type => OP_ILLEGAL, instr_tag => instr_tag_init, @@ -383,6 +387,7 @@ package common is ramspr_wraddr => (others => '0'), ramspr_write_even => '0', ramspr_write_odd => '0', dbg_spr_access => '0', dec_ctr => '0', + prefixed => '0', illegal_suffix => '0', misaligned_prefix => '0', others => (others => '0')); type MultiplyInputType is record @@ -505,6 +510,7 @@ package common is priv_mode : std_ulogic; -- privileged mode (MSR[PR] = 0) mode_32bit : std_ulogic; -- trim addresses to 32 bits is_32bit : std_ulogic; + prefixed : std_ulogic; repeat : std_ulogic; second : std_ulogic; e2stall : std_ulogic; @@ -519,7 +525,7 @@ package common is addr1 => (others => '0'), addr2 => (others => '0'), data => (others => '0'), write_reg => (others => '0'), length => (others => '0'), - mode_32bit => '0', is_32bit => '0', + mode_32bit => '0', is_32bit => '0', prefixed => '0', repeat => '0', second => '0', e2stall => '0', msr => (others => '0')); diff --git a/decode1.vhdl b/decode1.vhdl index 138e483..0aa2fee 100644 --- a/decode1.vhdl +++ b/decode1.vhdl @@ -572,7 +572,13 @@ begin pv.prefixed := '1'; pv.pref_ia := f_in.nia(5 downto 2); pv.prefix := f_in.insn(25 downto 0); - v.valid := '0'; + -- Check if the address of the prefix mod 64 is 60; + -- if so we need to arrange to generate an alignment interrupt + if f_in.nia(5 downto 2) = "1111" then + v.misaligned_prefix := '1'; + else + v.valid := '0'; + end if; end if; decode_rom_addr <= insn_code'val(to_integer(unsigned(icode_bits))); diff --git a/decode2.vhdl b/decode2.vhdl index fa3b54d..338a80a 100644 --- a/decode2.vhdl +++ b/decode2.vhdl @@ -371,21 +371,27 @@ begin c_out.read <= d_in.decode.input_cr; decode2_addrs: process(all) + variable dec_a, dec_b, dec_c : decode_input_reg_t; + variable dec_o : decode_output_reg_t; begin - decoded_reg_a <= decode_input_reg_init; - decoded_reg_b <= decode_input_reg_init; - decoded_reg_c <= decode_input_reg_init; - decoded_reg_o <= decode_output_reg_init; - if d_in.valid = '1' then - decoded_reg_a <= decode_input_reg_a (d_in.decode.input_reg_a, d_in.insn, d_in.prefix, d_in.nia); - decoded_reg_b <= decode_input_reg_b (d_in.decode.input_reg_b, d_in.insn, d_in.prefix); - decoded_reg_c <= decode_input_reg_c (d_in.decode.input_reg_c, d_in.insn); - decoded_reg_o <= decode_output_reg (d_in.decode.output_reg_a, d_in.insn); + dec_a := decode_input_reg_a (d_in.decode.input_reg_a, d_in.insn, d_in.prefix, d_in.nia); + dec_b := decode_input_reg_b (d_in.decode.input_reg_b, d_in.insn, d_in.prefix); + dec_c := decode_input_reg_c (d_in.decode.input_reg_c, d_in.insn); + dec_o := decode_output_reg (d_in.decode.output_reg_a, d_in.insn); + if d_in.valid = '0' or d_in.illegal_suffix = '1' then + dec_a.reg_valid := '0'; + dec_b.reg_valid := '0'; + dec_c.reg_valid := '0'; + dec_o.reg_valid := '0'; end if; - r_out.read1_enable <= decoded_reg_a.reg_valid; - r_out.read2_enable <= decoded_reg_b.reg_valid; - r_out.read3_enable <= decoded_reg_c.reg_valid; + decoded_reg_a <= dec_a; + decoded_reg_b <= dec_b; + decoded_reg_c <= dec_c; + decoded_reg_o <= dec_o; + r_out.read1_enable <= dec_a.reg_valid; + r_out.read2_enable <= dec_b.reg_valid; + r_out.read3_enable <= dec_c.reg_valid; end process; @@ -592,6 +598,9 @@ begin v.e.result_sel := "001"; -- logical_result end if; end if; + v.e.prefixed := d_in.prefixed; + v.e.illegal_suffix := d_in.illegal_suffix; + v.e.misaligned_prefix := d_in.misaligned_prefix; elsif dc2.e.valid = '1' then -- dc2.busy = 1 and dc2.e.valid = 1, thus this must be a repeated instruction. diff --git a/execute1.vhdl b/execute1.vhdl index db1159d..e6cfd3e 100644 --- a/execute1.vhdl +++ b/execute1.vhdl @@ -118,6 +118,7 @@ architecture behaviour of execute1 is fp_exception_next : std_ulogic; trace_next : std_ulogic; prev_op : insn_type_t; + prev_prefixed : std_ulogic; oe : std_ulogic; mul_select : std_ulogic_vector(1 downto 0); res2_sel : std_ulogic_vector(1 downto 0); @@ -141,6 +142,7 @@ architecture behaviour of execute1 is (e => Execute1ToWritebackInit, se => side_effect_init, busy => '0', fp_exception_next => '0', trace_next => '0', prev_op => OP_ILLEGAL, + prev_prefixed => '0', oe => '0', mul_select => "00", res2_sel => "00", spr_select => spr_id_init, pmu_spr_num => 5x"0", mul_in_progress => '0', mul_finish => '0', div_in_progress => '0', @@ -978,6 +980,7 @@ begin variable bo, bi : std_ulogic_vector(4 downto 0); variable illegal : std_ulogic; variable privileged : std_ulogic; + variable misaligned : std_ulogic; variable slow_op : std_ulogic; variable owait : std_ulogic; variable srr1 : std_ulogic_vector(63 downto 0); @@ -1021,10 +1024,13 @@ begin illegal := '0'; privileged := '0'; + misaligned := e_in.misaligned_prefix; slow_op := '0'; owait := '0'; - if ex1.msr(MSR_PR) = '1' and instr_is_privileged(e_in.insn_type, e_in.insn) then + if e_in.illegal_suffix = '1' then + illegal := '1'; + elsif ex1.msr(MSR_PR) = '1' and instr_is_privileged(e_in.insn_type, e_in.insn) then privileged := '1'; end if; @@ -1315,9 +1321,22 @@ begin end if; end case; - if privileged = '1' then + if misaligned = '1' then + -- generate an alignment interrupt + -- This is higher priority than illegal because a misaligned + -- prefix will come down as an OP_ILLEGAL instruction. + v.exception := '1'; + v.e.intr_vec := 16#600#; + v.e.srr1(47 - 35) := '1'; + v.e.srr1(47 - 34) := '1'; + if e_in.valid = '1' then + report "misaligned prefixed instruction interrupt"; + end if; + + elsif privileged = '1' then -- generate a program interrupt v.exception := '1'; + v.e.srr1(47 - 34) := e_in.prefixed; -- set bit 45 to indicate privileged instruction type interrupt v.e.srr1(47 - 45) := '1'; if e_in.valid = '1' then @@ -1326,6 +1345,7 @@ begin elsif illegal = '1' then v.exception := '1'; + v.e.srr1(47 - 34) := e_in.prefixed; -- Since we aren't doing Hypervisor emulation assist (0xe40) we -- set bit 44 to indicate we have an illegal v.e.srr1(47 - 44) := '1'; @@ -1336,6 +1356,7 @@ begin elsif HAS_FPU and ex1.msr(MSR_FP) = '0' and e_in.fac = FPU then -- generate a floating-point unavailable interrupt v.exception := '1'; + v.e.srr1(47 - 34) := e_in.prefixed; v.e.intr_vec := 16#800#; if e_in.valid = '1' then report "FP unavailable interrupt"; @@ -1401,6 +1422,7 @@ begin if valid_in = '1' then v.prev_op := e_in.insn_type; + v.prev_prefixed := e_in.prefixed; end if; -- Determine if there is any interrupt to be taken @@ -1422,6 +1444,7 @@ begin v.e.intr_vec := 16#d00#; v.e.srr1 := (others => '0'); v.e.srr1(47 - 33) := '1'; + v.e.srr1(47 - 34) := ex1.prev_prefixed; if ex1.prev_op = OP_LOAD or ex1.prev_op = OP_ICBI or ex1.prev_op = OP_ICBT or ex1.prev_op = OP_DCBT or ex1.prev_op = OP_DCBST or ex1.prev_op = OP_DCBF then v.e.srr1(47 - 35) := '1'; @@ -1584,6 +1607,7 @@ begin lv.priv_mode := not ex1.msr(MSR_PR); lv.mode_32bit := not ex1.msr(MSR_SF); lv.is_32bit := e_in.is_32bit; + lv.prefixed := e_in.prefixed; lv.repeat := e_in.repeat; lv.second := e_in.second; lv.e2stall := fp_in.f2stall; diff --git a/loadstore1.vhdl b/loadstore1.vhdl index 01babc3..fc8c158 100644 --- a/loadstore1.vhdl +++ b/loadstore1.vhdl @@ -69,6 +69,7 @@ architecture behave of loadstore1 is instr_fault : std_ulogic; do_update : std_ulogic; mode_32bit : std_ulogic; + prefixed : std_ulogic; addr : std_ulogic_vector(63 downto 0); byte_sel : std_ulogic_vector(7 downto 0); second_bytes : std_ulogic_vector(7 downto 0); @@ -99,7 +100,8 @@ architecture behave of loadstore1 is constant request_init : request_t := (valid => '0', dc_req => '0', load => '0', store => '0', tlbie => '0', dcbz => '0', read_spr => '0', write_spr => '0', mmu_op => '0', instr_fault => '0', do_update => '0', - mode_32bit => '0', addr => (others => '0'), + mode_32bit => '0', prefixed => '0', + addr => (others => '0'), byte_sel => x"00", second_bytes => x"00", store_data => (others => '0'), instr_tag => instr_tag_init, write_reg => 6x"00", length => x"0", @@ -411,6 +413,7 @@ begin v.valid := l_in.valid; v.instr_tag := l_in.instr_tag; v.mode_32bit := l_in.mode_32bit; + v.prefixed := l_in.prefixed; v.write_reg := l_in.write_reg; v.length := l_in.length; v.elt_length := l_in.length; @@ -906,8 +909,10 @@ begin if exception = '1' then if r2.req.align_intr = '1' then v.intr_vec := 16#600#; + v.srr1(47 - 34) := r2.req.prefixed; v.dar := r2.req.addr; elsif r2.req.instr_fault = '0' then + v.srr1(47 - 34) := r2.req.prefixed; v.dar := r2.req.addr; if m_in.segerr = '0' then v.intr_vec := 16#300#;