Implement interrupts for prefixed instructions
authorPaul Mackerras <paulus@ozlabs.org>
Mon, 3 Jul 2023 08:19:38 +0000 (18:19 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Thu, 6 Jul 2023 09:41:51 +0000 (19:41 +1000)
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 <paulus@ozlabs.org>
common.vhdl
decode1.vhdl
decode2.vhdl
execute1.vhdl
loadstore1.vhdl

index 838179b1720e8ac842680eab1d196d6de44cf4b5..59c855ed7b3555f0968d1d1f44777f7aef20b94d 100644 (file)
@@ -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'));
 
index 138e483113f9fdfe2dbdaa8a7d66752ac3af74eb..0aa2feec66a9faac96bb0d55e672e1f8ad5262d6 100644 (file)
@@ -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)));
index fa3b54d45f3ad9d711fcd36b5830b42c9ec8e190..338a80a0ab444c62e2f5daa52eab966f47eceea5 100644 (file)
@@ -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.
index db1159dc0a0e957990548eea404f5f83376775f2..e6cfd3e609e4604f7a4e37654a6d8ac583eaa948 100644 (file)
@@ -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;
index 01babc3f583f8c225cac1446792be91bdb9d347a..fc8c158189b9601018cc474d9c4a5e0afbcaaecc 100644 (file)
@@ -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#;