Add a second execute stage to the pipeline
authorPaul Mackerras <paulus@ozlabs.org>
Thu, 30 Jun 2022 10:33:33 +0000 (20:33 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Fri, 22 Jul 2022 12:19:05 +0000 (22:19 +1000)
This adds a second execute stage to the pipeline, in order to match up
the length of the pipeline through loadstore and dcache with the
length through execute1.  This will ultimately enable us to get rid of
the 1-cycle bubble that we currently have when issuing ALU
instructions after one or more LSU instructions.

Most ALU instructions execute in the first stage, except for
count-zeroes and popcount instructions (which take two cycles and do
some of their work in the second stage) and mfspr/mtspr to "slow" SPRs
(TB, DEC, PVR, LOGA/LOGD, CFAR).  Multiply and divide/mod instructions
take several cycles but the instruction stays in the first stage (ex1)
and ex1.busy is asserted until the operation is complete.

There is currently a bypass from the first stage but not the second
stage.  Performance is down somewhat because of that and because this
doesn't yet eliminate the bubble between LSU and ALU instructions.

The forwarding of XER common bits has been changed somewhat because
now there is another pipeline stage between ex1 and the committed
state in cr_file.  The simplest thing for now is to record the last
value written and use that, unless there has been a flush, in which
case the committed state (obtained via e_in.xerc) is used.

Note that this fixes what was previously a benign bug in control.vhdl,
where it was possible for control to forget an instructions dependency
on a value from a previous instruction (a GPR or the CR) if this
instruction writes the value and the instruction gets to the point
where it could issue but is blocked by the busy signal from execute1.
In that situation, control may incorrectly not indicate that a bypass
should be used.  That didn't matter previously because, for ALU and
FPU instructions, there was only one previous instruction in flight
and once the current instruction could issue, the previous instruction
was completing and the correct value would be obtained from
register_file or cr_file.  For loadstore instructions there could be
two being executed, but because there are no bypass paths, failing to
indicate use of a bypass path is fine.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
common.vhdl
control.vhdl
countbits_tb.vhdl
decode2.vhdl
divider.vhdl
execute1.vhdl

index 7ecf4e299d754ba8a9a2302e900df87cc0bd30b7..6cbf1810cae3e7cc1556271f3bb5cfa526b86236 100644 (file)
@@ -356,6 +356,7 @@ package common is
 
     type Execute1ToDividerType is record
        valid: std_ulogic;
+        flush: std_ulogic;
        dividend: std_ulogic_vector(63 downto 0);
        divisor: std_ulogic_vector(63 downto 0);
        is_signed: std_ulogic;
@@ -364,9 +365,8 @@ package common is
        is_modulus: std_ulogic;
         neg_result: std_ulogic;
     end record;
-    constant Execute1ToDividerInit: Execute1ToDividerType := (valid => '0', is_signed => '0', is_32bit => '0',
-                                                              is_extended => '0', is_modulus => '0',
-                                                              neg_result => '0', others => (others => '0'));
+    constant Execute1ToDividerInit: Execute1ToDividerType := (
+        dividend => 64x"0", divisor => 64x"0", others => '0');
 
     type PMUEventType is record
         no_instr_avail      : std_ulogic;
index 1d5551782ede97bbed74fd1dde1e319eeaae6995..0bbe9ad16658dce9654ac34d00cfbaaf64005bd2 100644 (file)
@@ -104,7 +104,8 @@ begin
                         tag_regs(i).wr_cr <= '0';
                         report "tag " & integer'image(i) & " not valid";
                     end if;
-                    if gpr_write_valid = '1' and tag_regs(i).reg = gpr_write_in then
+                    if instr_tag.valid = '1' and gpr_write_valid = '1' and
+                        tag_regs(i).reg = gpr_write_in then
                         tag_regs(i).recent <= '0';
                         if tag_regs(i).recent = '1' and tag_regs(i).wr_gpr = '1' then
                             report "tag " & integer'image(i) & " not recent";
@@ -126,7 +127,7 @@ begin
                 curr_cr_tag <= 0;
             else
                 curr_tag <= next_tag;
-                if cr_write_valid = '1' then
+                if instr_tag.valid = '1' and cr_write_valid = '1' then
                     curr_cr_tag <= instr_tag.tag;
                 end if;
             end if;
index c00a6b6116195489eee0127dc02a842265dc560a..c945c573b773e476b56bb92cb462979adc8b18e2 100644 (file)
@@ -26,6 +26,7 @@ begin
     bitcounter_0: entity work.bit_counter
         port map (
             clk => clk,
+            stall => '0',
             rs => rs,
             result => res,
             count_right => count_right,
index 8998f2b6acd82b72592f214e3f09ac9dd6dcd830..af0c27dc14e3057cf4ab713a2b91855727ec8bea 100644 (file)
@@ -228,7 +228,6 @@ architecture behaviour of decode2 is
         OP_SHR      => "010",
         OP_EXTSWSLI => "010",
         OP_MUL_L64  => "011",           -- muldiv_result
-        OP_MFSPR    => "101",           -- spr_result
         OP_B        => "110",           -- next_nia
         OP_BC       => "110",
         OP_BCREG    => "110",
index 3f9b312801d165bed69e9caa22394bcd6fba9e9e..55e3c5d2c60c59a5fcb383c57bfd3b583361dbae 100644 (file)
@@ -36,7 +36,7 @@ begin
     divider_0: process(clk)
     begin
         if rising_edge(clk) then
-            if rst = '1' then
+            if rst = '1' or d_in.flush = '1' then
                 dend <= (others => '0');
                 div <= (others => '0');
                 quot <= (others => '0');
index 7bd091393ae710d2bb02a3376214b24f433625aa..ebcdfebf0dd6c8204b420d75cadc4308ca577869 100644 (file)
@@ -60,65 +60,90 @@ entity execute1 is
 end entity execute1;
 
 architecture behaviour of execute1 is
-    type reg_type is record
+    type side_effect_type is record
+        terminate : std_ulogic;
+        icache_inval : std_ulogic;
+        write_msr : std_ulogic;
+        write_xerlow : std_ulogic;
+        write_dec : std_ulogic;
+        write_cfar : std_ulogic;
+        write_loga : std_ulogic;
+        inc_loga : std_ulogic;
+        write_pmuspr : std_ulogic;
+    end record;
+    constant side_effect_init : side_effect_type := (others => '0');
+
+    type actions_type is record
+        e : Execute1ToWritebackType;
+        se : side_effect_type;
+        complete : std_ulogic;
+        exception : std_ulogic;
+        trap : std_ulogic;
+        new_msr : std_ulogic_vector(63 downto 0);
+        take_branch : std_ulogic;
+        direct_branch : std_ulogic;
+        start_mul : std_ulogic;
+        start_div : std_ulogic;
+        do_trace : std_ulogic;
+        fp_intr : std_ulogic;
+        res2_sel : std_ulogic_vector(1 downto 0);
+        bypass_valid : std_ulogic;
+    end record;
+    constant actions_type_init : actions_type :=
+        (e => Execute1ToWritebackInit, se => side_effect_init,
+         new_msr => (others => '0'), res2_sel => "00", others => '0');
+
+    type reg_stage1_type is record
        e : Execute1ToWritebackType;
+        se : side_effect_type;
         busy: std_ulogic;
-        terminate: std_ulogic;
-        intr_pending : std_ulogic;
         fp_exception_next : std_ulogic;
         trace_next : std_ulogic;
         prev_op : insn_type_t;
         br_taken : std_ulogic;
         oe : std_ulogic;
         mul_select : std_ulogic_vector(1 downto 0);
+        res2_sel : std_ulogic_vector(1 downto 0);
+        spr_select : spr_id;
+        pmu_spr_num : std_ulogic_vector(4 downto 0);
        mul_in_progress : std_ulogic;
         mul_finish : std_ulogic;
         div_in_progress : std_ulogic;
-        cntz_in_progress : std_ulogic;
         no_instr_avail : std_ulogic;
         instr_dispatch : std_ulogic;
         ext_interrupt : std_ulogic;
         taken_branch_event : std_ulogic;
         br_mispredict : std_ulogic;
-        log_addr_spr : std_ulogic_vector(31 downto 0);
+        msr : std_ulogic_vector(63 downto 0);
+        xerc : xer_common_t;
+        xerc_valid : std_ulogic;
     end record;
-    constant reg_type_init : reg_type :=
-        (e => Execute1ToWritebackInit,
-         busy => '0', terminate => '0', intr_pending => '0',
+    constant reg_stage1_type_init : reg_stage1_type :=
+        (e => Execute1ToWritebackInit, se => side_effect_init,
+         busy => '0',
          fp_exception_next => '0', trace_next => '0', prev_op => OP_ILLEGAL, br_taken => '0',
-         oe => '0', mul_select => "00",
-         mul_in_progress => '0', mul_finish => '0', div_in_progress => '0', cntz_in_progress => '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',
          no_instr_avail => '0', instr_dispatch => '0', ext_interrupt => '0',
          taken_branch_event => '0', br_mispredict => '0',
-         others => (others => '0'));
+         msr => 64x"0",
+         xerc => xerc_init, xerc_valid => '0');
 
-    type actions_type is record
+    type reg_stage2_type is record
        e : Execute1ToWritebackType;
-        complete : std_ulogic;
-        exception : std_ulogic;
-        trap : std_ulogic;
-        terminate : std_ulogic;
-        write_msr : std_ulogic;
-        new_msr : std_ulogic_vector(63 downto 0);
-        write_xerlow : std_ulogic;
-        write_pmuspr : std_ulogic;
-        write_dec : std_ulogic;
-        write_loga : std_ulogic;
-        inc_loga : std_ulogic;
-        write_cfar : std_ulogic;
-        take_branch : std_ulogic;
-        direct_branch : std_ulogic;
-        start_mul : std_ulogic;
-        start_div : std_ulogic;
-        start_cntz : std_ulogic;
-        do_trace : std_ulogic;
-        fp_intr : std_ulogic;
-        icache_inval : std_ulogic;
+        se : side_effect_type;
+        ext_interrupt : std_ulogic;
+        taken_branch_event : std_ulogic;
+        br_mispredict : std_ulogic;
+        log_addr_spr : std_ulogic_vector(31 downto 0);
     end record;
-    constant actions_type_init : actions_type :=
-        (e => Execute1ToWritebackInit, new_msr => (others => '0'), others => '0');
+    constant reg_stage2_type_init : reg_stage2_type :=
+        (e => Execute1ToWritebackInit, se => side_effect_init,
+         log_addr_spr => 32x"0", others => '0');
 
-    signal ex1, ex1in : reg_type;
+    signal ex1, ex1in : reg_stage1_type;
+    signal ex2, ex2in : reg_stage2_type;
     signal actions : actions_type;
 
     signal a_in, b_in, c_in : std_ulogic_vector(63 downto 0);
@@ -142,7 +167,9 @@ architecture behaviour of execute1 is
     signal muldiv_result: std_ulogic_vector(63 downto 0);
     signal shortmul_result: std_ulogic_vector(63 downto 0);
     signal spr_result: std_ulogic_vector(63 downto 0);
+    signal ex_result: std_ulogic_vector(63 downto 0);
     signal next_nia : std_ulogic_vector(63 downto 0);
+    signal s1_sel : std_ulogic_vector(2 downto 0);
 
     signal carry_32 : std_ulogic;
     signal carry_64 : std_ulogic;
@@ -372,7 +399,7 @@ begin
     end generate;
 
     dbg_ctrl_out <= ctrl;
-    log_rd_addr <= ex1.log_addr_spr;
+    log_rd_addr <= ex2.log_addr_spr;
 
     a_in <= e_in.read_data1;
     b_in <= e_in.read_data2;
@@ -393,15 +420,15 @@ begin
                        itlb_miss_resolved => ic_events.itlb_miss_resolved,
                        no_instr_avail => ex1.no_instr_avail,
                        dispatch => ex1.instr_dispatch,
-                       ext_interrupt => ex1.ext_interrupt,
-                       br_taken_complete => ex1.taken_branch_event,
-                       br_mispredict => ex1.br_mispredict,
+                       ext_interrupt => ex2.ext_interrupt,
+                       br_taken_complete => ex2.taken_branch_event,
+                       br_mispredict => ex2.br_mispredict,
                        others => '0');
     x_to_pmu.nia <= e_in.nia;
     x_to_pmu.addr <= (others => '0');
     x_to_pmu.addr_v <= '0';
-    x_to_pmu.spr_num <= e_in.insn(20 downto 16);
-    x_to_pmu.spr_val <= c_in;
+    x_to_pmu.spr_num <= ex1.pmu_spr_num;
+    x_to_pmu.spr_val <= ex1.e.write_data;
     x_to_pmu.run <= '1';
 
     -- XER forwarding. To avoid having to track XER hazards, we use
@@ -409,35 +436,23 @@ begin
     -- (SO, OV[32] and CA[32]) are only modified by instructions that are
     -- handled here, we can just forward the result being sent to
     -- writeback.
-    xerc_in <= ex1.e.xerc when (ex1.e.write_xerc_enable and ex1.e.valid) = '1' else e_in.xerc;
+    xerc_in <= ex1.xerc when ex1.xerc_valid = '1' else e_in.xerc;
 
     with e_in.unit select busy_out <=
-        l_in.busy or ex1.busy or fp_in.busy when LDST,
+        l_in.busy or ex1.e.valid or ex1.busy or fp_in.busy when LDST,
+        l_in.busy or l_in.in_progress or ex1.e.valid or ex1.busy or fp_in.busy when FPU,
         l_in.busy or l_in.in_progress or ex1.busy or fp_in.busy when others;
 
-    valid_in <= e_in.valid and not busy_out and not flush_in;
+    valid_in <= e_in.valid and not (busy_out or flush_in or ex1.e.redirect or ex1.e.interrupt);
 
-    terminate_out <= ex1.terminate;
-
-    -- Slow SPR read mux
-    with e_in.spr_select.sel select spr_result <=
-        ctrl.tb when SPRSEL_TB,
-        32x"0" & ctrl.tb(63 downto 32) when SPRSEL_TBU,
-        ctrl.dec when SPRSEL_DEC,
-        32x"0" & PVR_MICROWATT when SPRSEL_PVR,
-        log_wr_addr & ex1.log_addr_spr when SPRSEL_LOGA,
-        log_rd_data when SPRSEL_LOGD,
-        ctrl.cfar when SPRSEL_CFAR,
-        assemble_xer(xerc_in, ctrl.xer_low) when others;
-
-    -- Result mux
-    with e_in.result_sel select alu_result <=
+    -- First stage result mux
+    s1_sel <= e_in.result_sel when ex1.busy = '0' else "100";
+    with s1_sel select alu_result <=
         adder_result       when "000",
         logical_result     when "001",
         rotator_result     when "010",
         shortmul_result    when "011",
-        pmu_to_x.spr_val   when "100",
-        spr_result         when "101",
+        muldiv_result      when "100",
         next_nia           when "110",
         misc_result        when others;
 
@@ -445,22 +460,31 @@ begin
     begin
        if rising_edge(clk) then
             if rst = '1' then
-                ex1 <= reg_type_init;
+                ex1 <= reg_stage1_type_init;
+                ex2 <= reg_stage2_type_init;
                 ctrl <= ctrl_t_init;
                 ctrl.msr <= (MSR_SF => '1', MSR_LE => '1', others => '0');
+                ex1.msr <= (MSR_SF => '1', MSR_LE => '1', others => '0');
             else
                 ex1 <= ex1in;
+                ex2 <= ex2in;
                 ctrl <= ctrl_tmp;
                 if valid_in = '1' then
                     report "execute " & to_hstring(e_in.nia) & " op=" & insn_type_t'image(e_in.insn_type) &
                         " wr=" & to_hstring(ex1in.e.write_reg) & " we=" & std_ulogic'image(ex1in.e.write_enable) &
                         " tag=" & integer'image(ex1in.e.instr_tag.tag) & std_ulogic'image(ex1in.e.instr_tag.valid);
                 end if;
+                -- We mustn't get stalled on a cycle where execute2 is
+                -- completing an instruction or generating an interrupt
+                if ex2.e.valid = '1' or ex2.e.interrupt = '1' then
+                    assert (l_in.busy or fp_in.busy) = '0'
+                        severity failure;
+                end if;
             end if;
        end if;
     end process;
 
-    -- Data path for integer instructions
+    -- Data path for integer instructions (first execute stage)
     execute1_dp: process(all)
        variable a_inv : std_ulogic_vector(63 downto 0);
        variable b_or_m1 : std_ulogic_vector(63 downto 0);
@@ -543,6 +567,7 @@ begin
         if e_in.insn_type = OP_MOD then
             x_to_divider.is_modulus <= '1';
         end if;
+        x_to_divider.flush <= flush_in;
 
         addend := (others => '0');
         if e_in.insn(26) = '0' then
@@ -638,7 +663,7 @@ begin
                 misc_result <= darn;
             when "100" =>
                 -- mfmsr
-               misc_result <= ctrl.msr;
+               misc_result <= ex1.msr;
             when "101" =>
                if e_in.insn(20) = '0' then
                    -- mfcr
@@ -792,6 +817,7 @@ begin
         variable illegal : std_ulogic;
         variable privileged : std_ulogic;
         variable slow_op : std_ulogic;
+        variable owait : std_ulogic;
     begin
         v := actions_type_init;
         v.e.write_data := alu_result;
@@ -803,12 +829,11 @@ begin
         v.e.write_cr_enable := e_in.output_cr;
         v.e.write_xerc_enable := e_in.output_xer;
         v.e.xerc := xerc_in;
-        v.new_msr := ctrl.msr;
-        v.e.write_xerc_enable := e_in.output_xer;
-        v.e.redir_mode := ctrl.msr(MSR_IR) & not ctrl.msr(MSR_PR) &
-                          not ctrl.msr(MSR_LE) & not ctrl.msr(MSR_SF);
+        v.new_msr := ex1.msr;
+        v.e.redir_mode := ex1.msr(MSR_IR) & not ex1.msr(MSR_PR) &
+                          not ex1.msr(MSR_LE) & not ex1.msr(MSR_SF);
         v.e.intr_vec := 16#700#;
-        v.e.mode_32bit := not ctrl.msr(MSR_SF);
+        v.e.mode_32bit := not ex1.msr(MSR_SF);
         v.e.instr_tag := e_in.instr_tag;
         v.e.last_nia := e_in.nia;
         v.e.br_offset := 64x"4";
@@ -827,8 +852,9 @@ begin
         illegal := '0';
         privileged := '0';
         slow_op := '0';
+        owait := '0';
 
-        if ctrl.msr(MSR_PR) = '1' and instr_is_privileged(e_in.insn_type, e_in.insn) then
+        if ex1.msr(MSR_PR) = '1' and instr_is_privileged(e_in.insn_type, e_in.insn) then
             privileged := '1';
         end if;
 
@@ -837,7 +863,7 @@ begin
             illegal := '1';
         end if;
 
-        v.do_trace := ctrl.msr(MSR_SE);
+        v.do_trace := ex1.msr(MSR_SE);
         case_0: case e_in.insn_type is
            when OP_ILLEGAL =>
                illegal := '1';
@@ -858,7 +884,7 @@ begin
                 -- check bits 1-10 of the instruction to make sure it's attn
                 -- if not then it is illegal
                 if e_in.insn(10 downto 1) = "0100000000" then
-                    v.terminate := '1';
+                    v.se.terminate := '1';
                     if e_in.valid = '1' then
                         report "ATTN";
                     end if;
@@ -909,10 +935,10 @@ begin
                     -- should never happen
                     v.e.redirect := '1';
                 end if;
-                if ctrl.msr(MSR_BE) = '1' then
+                if ex1.msr(MSR_BE) = '1' then
                     v.do_trace := '1';
                 end if;
-                v.write_cfar := '1';
+                v.se.write_cfar := '1';
             when OP_BC =>
                 -- read_data1 is CTR
                 -- If this instruction updates both CTR and LR, then it is
@@ -938,10 +964,10 @@ begin
                     v.direct_branch := '1';
                     v.e.br_last := '1';
                     v.e.br_taken := v.take_branch;
-                    if ctrl.msr(MSR_BE) = '1' then
+                    if ex1.msr(MSR_BE) = '1' then
                         v.do_trace := '1';
                     end if;
-                    v.write_cfar := v.take_branch;
+                    v.se.write_cfar := v.take_branch;
                 end if;
             when OP_BCREG =>
                 -- read_data1 is CTR, read_data2 is target register (CTR, LR or TAR)
@@ -964,10 +990,10 @@ begin
                     -- Indirect branches are never predicted taken
                     v.e.redirect := v.take_branch;
                     v.e.br_taken := v.take_branch;
-                    if ctrl.msr(MSR_BE) = '1' then
+                    if ex1.msr(MSR_BE) = '1' then
                         v.do_trace := '1';
                     end if;
-                    v.write_cfar := v.take_branch;
+                    v.se.write_cfar := v.take_branch;
                 end if;
 
            when OP_RFID =>
@@ -983,11 +1009,11 @@ begin
                     v.new_msr(MSR_IR) := '1';
                     v.new_msr(MSR_DR) := '1';
                 end if;
-                v.write_msr := '1';
+                v.se.write_msr := '1';
                 v.e.br_offset := b_in;
                 v.e.abs_br := '1';
                 v.e.redirect := '1';
-                v.write_cfar := '1';
+                v.se.write_cfar := '1';
                 if HAS_FPU then
                     v.fp_intr := fp_in.exception and
                                  (a_in(MSR_FE0) or a_in(MSR_FE1));
@@ -995,8 +1021,8 @@ begin
                 v.do_trace := '0';
 
             when OP_CNTZ | OP_POPCNT =>
+                v.res2_sel := "01";
                 slow_op := '1';
-                v.start_cntz := '1';
            when OP_ISEL =>
             when OP_CROP =>
             when OP_MCRXRX =>
@@ -1010,14 +1036,19 @@ begin
                     end if;
                elsif e_in.spr_select.valid = '1' then
                     if e_in.valid = '1' then
-                        report "MFSPR to SPR " & integer'image(decode_spr_num(e_in.insn)) &
-                            "=" & to_hstring(spr_result);
+                        report "MFSPR to slow SPR " & integer'image(decode_spr_num(e_in.insn));
+                    end if;
+                    slow_op := '1';
+                    if e_in.spr_select.ispmu = '0' then
+                        case e_in.spr_select.sel is
+                            when SPRSEL_LOGD =>
+                                v.se.inc_loga := '1';
+                            when others =>
+                        end case;
+                        v.res2_sel := "10";
+                    else
+                        v.res2_sel := "11";
                     end if;
-                    case e_in.spr_select.sel is
-                       when SPRSEL_LOGD =>
-                           v.inc_loga := '1';
-                           when others =>
-                    end case;
                 else
                     -- mfspr from unimplemented SPRs should be a nop in
                     -- supervisor mode and a program interrupt for user mode
@@ -1025,7 +1056,7 @@ begin
                         report "MFSPR to SPR " & integer'image(decode_spr_num(e_in.insn)) &
                             " invalid";
                     end if;
-                    if ctrl.msr(MSR_PR) = '1' then
+                    if ex1.msr(MSR_PR) = '1' then
                         illegal := '1';
                     end if;
                 end if;
@@ -1033,7 +1064,7 @@ begin
            when OP_MFCR =>
            when OP_MTCRF =>
             when OP_MTMSRD =>
-                v.write_msr := '1';
+                v.se.write_msr := '1';
                 if e_in.insn(16) = '1' then
                     -- just update EE and RI
                     v.new_msr(MSR_EE) := c_in(MSR_EE);
@@ -1062,7 +1093,7 @@ begin
                     report "MTSPR to SPR " & integer'image(decode_spr_num(e_in.insn)) &
                         "=" & to_hstring(c_in);
                 end if;
-                v.write_pmuspr := e_in.spr_select.ispmu;
+                v.se.write_pmuspr := e_in.spr_select.ispmu;
                 if e_in.spr_select.valid = '1' and e_in.spr_select.ispmu = '0' then
                     case e_in.spr_select.sel is
                         when SPRSEL_XER =>
@@ -1071,17 +1102,17 @@ begin
                             v.e.xerc.ca := c_in(63-34);
                             v.e.xerc.ov32 := c_in(63-44);
                             v.e.xerc.ca32 := c_in(63-45);
-                            v.write_xerlow := '1';
+                            v.se.write_xerlow := '1';
                         when SPRSEL_DEC =>
-                            v.write_dec := '1';
+                            v.se.write_dec := '1';
                         when SPRSEL_LOGA =>
-                            v.write_loga := '1';
+                            v.se.write_loga := '1';
                         when others =>
                     end case;
                elsif is_fast_spr(e_in.write_reg) = '0' then
                     -- mtspr to unimplemented SPRs should be a nop in
                     -- supervisor mode and a program interrupt for user mode
-                    if ctrl.msr(MSR_PR) = '1' then
+                    if ex1.msr(MSR_PR) = '1' then
                         illegal := '1';
                     end if;
                end if;
@@ -1095,7 +1126,7 @@ begin
                v.e.redirect := '1';
 
            when OP_ICBI =>
-               v.icache_inval := '1';
+               v.se.icache_inval := '1';
 
            when OP_MUL_L64 =>
                 if HAS_SHORT_MULT and e_in.insn(26) = '1' and
@@ -1109,15 +1140,18 @@ begin
                     -- Use standard multiplier
                     v.start_mul := '1';
                     slow_op := '1';
+                    owait := '1';
                 end if;
 
            when OP_MUL_H64 | OP_MUL_H32 =>
                 v.start_mul := '1';
                 slow_op := '1';
+                owait := '1';
 
            when OP_DIV | OP_DIVE | OP_MOD =>
                 v.start_div := '1';
                 slow_op := '1';
+                owait := '1';
 
             when OP_FETCH_FAILED =>
                 -- Handling an ITLB miss doesn't count as having executed an instruction
@@ -1147,7 +1181,7 @@ begin
                 report "illegal instruction";
             end if;
 
-        elsif HAS_FPU and ctrl.msr(MSR_FP) = '0' and e_in.fac = FPU then
+        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.intr_vec := 16#800#;
@@ -1157,26 +1191,33 @@ begin
         end if;
 
         if e_in.unit = ALU then
-            v.complete := e_in.valid and not v.exception and not slow_op;
+            v.complete := e_in.valid and not v.exception and not owait;
+            v.bypass_valid := e_in.valid and not v.exception and not slow_op;
         end if;
 
         actions <= v;
     end process;
 
+    -- First execute stage
     execute1_1: process(all)
-       variable v : reg_type;
+       variable v : reg_stage1_type;
        variable overflow : std_ulogic;
         variable lv : Execute1ToLoadstore1Type;
        variable irq_valid : std_ulogic;
        variable exception : std_ulogic;
         variable fv : Execute1ToFPUType;
         variable go : std_ulogic;
+        variable bypass_valid : std_ulogic;
     begin
        v := ex1;
-        if ex1.busy = '0' then
+        if (ex1.busy or l_in.busy or fp_in.busy) = '0' then
             v.e := actions.e;
+            v.e.valid := '0';
             v.oe := e_in.oe;
+            v.spr_select := e_in.spr_select;
+            v.pmu_spr_num := e_in.insn(20 downto 16);
             v.mul_select := e_in.sub_select(1 downto 0);
+            v.se := side_effect_init;
         end if;
 
         lv := Execute1ToLoadstore1Init;
@@ -1184,33 +1225,13 @@ begin
 
         x_to_multiply.valid <= '0';
         x_to_divider.valid <= '0';
-       v.mul_in_progress := '0';
-        v.div_in_progress := '0';
-        v.cntz_in_progress := '0';
-        v.mul_finish := '0';
         v.ext_interrupt := '0';
         v.taken_branch_event := '0';
         v.br_mispredict := '0';
+        v.busy := '0';
+        bypass_valid := '0';
 
-        x_to_pmu.mfspr <= '0';
-        x_to_pmu.mtspr <= '0';
-        x_to_pmu.tbbits(3) <= ctrl.tb(63 - 47);
-        x_to_pmu.tbbits(2) <= ctrl.tb(63 - 51);
-        x_to_pmu.tbbits(1) <= ctrl.tb(63 - 55);
-        x_to_pmu.tbbits(0) <= ctrl.tb(63 - 63);
-        x_to_pmu.pmm_msr <= ctrl.msr(MSR_PMM);
-        x_to_pmu.pr_msr <= ctrl.msr(MSR_PR);
-
-       ctrl_tmp <= ctrl;
-       -- FIXME: run at 512MHz not core freq
-       ctrl_tmp.tb <= std_ulogic_vector(unsigned(ctrl.tb) + 1);
-       ctrl_tmp.dec <= std_ulogic_vector(unsigned(ctrl.dec) - 1);
-
-        irq_valid := ctrl.msr(MSR_EE) and (pmu_to_x.intr or ctrl.dec(63) or ext_irq_in);
-
-       v.terminate := '0';
-       icache_inval <= '0';
-       v.busy := '0';
+        irq_valid := ex1.msr(MSR_EE) and (pmu_to_x.intr or ctrl.dec(63) or ext_irq_in);
 
        -- Next insn adder used in a couple of places
        next_nia <= std_ulogic_vector(unsigned(e_in.nia) + 4);
@@ -1223,19 +1244,14 @@ begin
 
         do_popcnt <= '1' when e_in.insn_type = OP_POPCNT else '0';
 
-        if ex1.intr_pending = '1' then
-            v.e.srr1 := ex1.e.srr1;
-            v.e.intr_vec := ex1.e.intr_vec;
-        end if;
-
         if valid_in = '1' then
             v.prev_op := e_in.insn_type;
         end if;
 
         -- Determine if there is any interrupt to be taken
         -- before/instead of executing this instruction
-        exception := ex1.intr_pending or (valid_in and actions.exception);
-        if valid_in = '1' and e_in.second = '0' and ex1.intr_pending = '0' then
+        exception := valid_in and actions.exception;
+        if valid_in = '1' and e_in.second = '0' then
             if HAS_FPU and ex1.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.
@@ -1278,54 +1294,37 @@ begin
 
             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;
 
-        v.no_instr_avail := not (e_in.valid or l_in.busy or l_in.in_progress or ex1.busy or fp_in.busy);
+        v.no_instr_avail := not (e_in.valid or l_in.busy or l_in.in_progress or
+                                 ex1.busy or fp_in.busy);
 
         go := valid_in and not exception;
         v.instr_dispatch := go;
 
        if go = '1' then
+            v.se := actions.se;
             v.e.valid := actions.complete;
+            bypass_valid := actions.bypass_valid;
             v.taken_branch_event := actions.take_branch;
             v.br_taken := actions.take_branch;
             v.trace_next := actions.do_trace;
             v.fp_exception_next := actions.fp_intr;
-            v.cntz_in_progress := actions.start_cntz;
-
-            if actions.write_msr = '1' then
-                ctrl_tmp.msr <= actions.new_msr;
-            end if;
-            if actions.write_xerlow = '1' then
-                ctrl_tmp.xer_low <= c_in(17 downto 0);
-            end if;
-            if actions.write_dec = '1' then
-                ctrl_tmp.dec <= c_in;
-            end if;
-            if actions.write_cfar = '1' then
-                ctrl_tmp.cfar <= e_in.nia;
-            end if;
-            if actions.write_loga = '1' then
-                v.log_addr_spr := c_in(31 downto 0);
-            elsif actions.inc_loga = '1' then
-                v.log_addr_spr := std_ulogic_vector(unsigned(ex1.log_addr_spr) + 1);
-            end if;
-            x_to_pmu.mtspr <= actions.write_pmuspr;
-            icache_inval <= actions.icache_inval;
+            v.res2_sel := actions.res2_sel;
+            v.msr := actions.new_msr;
             x_to_multiply.valid <= actions.start_mul;
             v.mul_in_progress := actions.start_mul;
             x_to_divider.valid <= actions.start_div;
             v.div_in_progress := actions.start_div;
-            v.terminate := actions.terminate;
             v.br_mispredict := v.e.redirect and actions.direct_branch;
-            v.busy := actions.start_cntz or actions.start_mul or actions.start_div;
             exception := actions.trap;
 
+            -- Go busy while division is happening because the
+            -- divider is not pipelined.  Also go busy while a
+            -- multiply is happening in order to stop following
+            -- instructions from using the wrong XER value
+            -- (and for simplicity in the OE=0 case).
+            v.busy := actions.start_div or actions.start_mul;
+
             -- instruction for other units, i.e. LDST
             if e_in.unit = LDST then
                 lv.valid := '1';
@@ -1335,86 +1334,74 @@ begin
             end if;
         end if;
 
-        -- The following cases all occur when ex1.busy = 1 and therefore
-        -- valid_in = 0.  Hence they don't happen in the same cycle as any of
-        -- the cases above which depend on valid_in = 1.
-        if ex1.cntz_in_progress = '1' then
-            -- cnt[lt]z and popcnt* always take two cycles
-            v.e.valid := '1';
-            v.e.write_data := countbits_result;
-        end if;
-       if ex1.div_in_progress = '1' then
-           if divider_to_x.valid = '1' then
-                v.e.write_data := muldiv_result;
-                overflow := divider_to_x.overflow;
-                -- We must test oe because the RC update code in writeback
-                -- will use the xerc value to set CR0:SO so we must not clobber
-                -- xerc if OE wasn't set.
-                if ex1.oe = '1' then
-                    v.e.xerc.ov := overflow;
-                    v.e.xerc.ov32 := overflow;
-                    if overflow = '1' then
-                        v.e.xerc.so := '1';
-                    end if;
+        if ex1.div_in_progress = '1' then
+            v.div_in_progress := not divider_to_x.valid;
+            v.busy := not divider_to_x.valid;
+            if divider_to_x.valid = '1' and ex1.oe = '1' then
+                v.e.xerc.ov := divider_to_x.overflow;
+                v.e.xerc.ov32 := divider_to_x.overflow;
+                if divider_to_x.overflow = '1' then
+                    v.e.xerc.so := '1';
                 end if;
-                v.e.valid := '1';
-           else
-               v.busy := '1';
-               v.div_in_progress := '1';
-           end if;
+            end if;
+            v.e.valid := divider_to_x.valid;
+            v.e.write_data := alu_result;
+            bypass_valid := v.e.valid;
         end if;
-       if ex1.mul_in_progress = '1' then
-           if multiply_to_x.valid = '1' then
-                v.e.write_data := muldiv_result;
-                if ex1.oe = '1' then
-                    -- have to wait until next cycle for overflow indication
-                    v.mul_finish := '1';
-                    v.busy := '1';
-                else
-                    v.e.valid := '1';
-                end if;
-           else
-               v.busy := '1';
-               v.mul_in_progress := '1';
-           end if;
+        if ex1.mul_in_progress = '1' then
+            v.mul_in_progress := not multiply_to_x.valid;
+            v.mul_finish := multiply_to_x.valid and ex1.oe;
+            v.e.valid := multiply_to_x.valid and not ex1.oe;
+            v.busy := not v.e.valid;
+            v.e.write_data := alu_result;
+            bypass_valid := v.e.valid;
         end if;
         if ex1.mul_finish = '1' then
+            v.mul_finish := '0';
             v.e.xerc.ov := multiply_to_x.overflow;
             v.e.xerc.ov32 := multiply_to_x.overflow;
             if multiply_to_x.overflow = '1' then
                 v.e.xerc.so := '1';
             end if;
             v.e.valid := '1';
-       end if;
+        end if;
 
-        v.e.interrupt := exception and not (l_in.in_progress or l_in.interrupt);
-        if v.e.interrupt = '1' then
-            v.intr_pending := '0';
+        if v.e.write_xerc_enable = '1' and v.e.valid = '1' then
+            v.xerc := v.e.xerc;
+            v.xerc_valid := '1';
         end if;
 
-       if interrupt_in = '1' then
-            ctrl_tmp.msr(MSR_SF) <= '1';
-            ctrl_tmp.msr(MSR_EE) <= '0';
-            ctrl_tmp.msr(MSR_PR) <= '0';
-            ctrl_tmp.msr(MSR_SE) <= '0';
-            ctrl_tmp.msr(MSR_BE) <= '0';
-            ctrl_tmp.msr(MSR_FP) <= '0';
-            ctrl_tmp.msr(MSR_FE0) <= '0';
-            ctrl_tmp.msr(MSR_FE1) <= '0';
-            ctrl_tmp.msr(MSR_IR) <= '0';
-            ctrl_tmp.msr(MSR_DR) <= '0';
-            ctrl_tmp.msr(MSR_RI) <= '0';
-            ctrl_tmp.msr(MSR_LE) <= '1';
+        if (ex1.busy or l_in.busy or fp_in.busy) = '0' then
+            v.e.interrupt := exception;
+        end if;
+        if v.e.valid = '0' then
+            v.e.redirect := '0';
+            v.e.br_last := '0';
+        end if;
+        if flush_in = '1' then
+            v.e.valid := '0';
+            v.e.interrupt := '0';
+            v.e.redirect := '0';
+            v.e.br_last := '0';
+            v.busy := '0';
+            v.div_in_progress := '0';
+            v.mul_in_progress := '0';
+            v.mul_finish := '0';
+            v.xerc_valid := '0';
+        end if;
+        if flush_in = '1' or interrupt_in = '1' then
+            v.msr := ctrl_tmp.msr;
+        end if;
+        if interrupt_in = '1' then
             v.trace_next := '0';
             v.fp_exception_next := '0';
-            v.intr_pending := '0';
         end if;
 
-        bypass_data.tag.valid <= v.e.write_enable and v.e.valid;
+        bypass_data.tag.valid <= v.e.write_enable and bypass_valid;
         bypass_data.tag.tag <= v.e.instr_tag.tag;
-        bypass_data.data <= v.e.write_data;
+        bypass_data.data <= alu_result;
 
-        bypass_cr_data.tag.valid <= v.e.write_cr_enable and v.e.valid;
+        bypass_cr_data.tag.valid <= v.e.write_cr_enable and bypass_valid;
         bypass_cr_data.tag.tag <= v.e.instr_tag.tag;
         bypass_cr_data.data <= v.e.write_cr_data;
 
@@ -1427,7 +1414,7 @@ begin
         lv.data := c_in;
         lv.write_reg := e_in.write_reg;
         lv.length := e_in.data_len;
-        lv.byte_reverse := e_in.byte_reverse xnor ctrl.msr(MSR_LE);
+        lv.byte_reverse := e_in.byte_reverse xnor ex1.msr(MSR_LE);
         lv.sign_extend := e_in.sign_extend;
         lv.update := e_in.update;
         lv.xerc := xerc_in;
@@ -1439,9 +1426,9 @@ begin
             e_in.insn(5 downto 1) = "10101" then
             lv.ci := '1';
         end if;
-        lv.virt_mode := ctrl.msr(MSR_DR);
-        lv.priv_mode := not ctrl.msr(MSR_PR);
-        lv.mode_32bit := not ctrl.msr(MSR_SF);
+        lv.virt_mode := ex1.msr(MSR_DR);
+        lv.priv_mode := not ex1.msr(MSR_PR);
+        lv.mode_32bit := not ex1.msr(MSR_SF);
         lv.is_32bit := e_in.is_32bit;
         lv.repeat := e_in.repeat;
         lv.second := e_in.second;
@@ -1452,7 +1439,7 @@ begin
         fv.insn := e_in.insn;
         fv.itag := e_in.instr_tag;
         fv.single := e_in.is_32bit;
-        fv.fe_mode := ctrl.msr(MSR_FE0) & ctrl.msr(MSR_FE1);
+        fv.fe_mode := ex1.msr(MSR_FE0) & ex1.msr(MSR_FE1);
         fv.fra := a_in;
         fv.frb := b_in;
         fv.frc := c_in;
@@ -1465,19 +1452,124 @@ begin
 
        -- update outputs
         l_out <= lv;
-       e_out <= ex1.e;
-        if ex1.e.valid = '0' then
-            e_out.write_enable <= '0';
-            e_out.write_cr_enable <= '0';
-            e_out.write_xerc_enable <= '0';
-            e_out.redirect <= '0';
-            e_out.br_last <= '0';
+        fp_out <= fv;
+        irq_valid_log <= irq_valid;
+    end process;
+
+    -- Slow SPR read mux
+    with ex1.spr_select.sel select spr_result <=
+        ctrl.tb when SPRSEL_TB,
+        32x"0" & ctrl.tb(63 downto 32) when SPRSEL_TBU,
+        ctrl.dec when SPRSEL_DEC,
+        32x"0" & PVR_MICROWATT when SPRSEL_PVR,
+        log_wr_addr & ex2.log_addr_spr when SPRSEL_LOGA,
+        log_rd_data when SPRSEL_LOGD,
+        ctrl.cfar when SPRSEL_CFAR,
+        assemble_xer(ex1.e.xerc, ctrl.xer_low) when others;
+
+    -- Second stage result mux
+    with ex1.res2_sel select ex_result <=
+        countbits_result  when "01",
+        spr_result        when "10",
+        pmu_to_x.spr_val  when "11",
+        ex1.e.write_data  when others;
+
+    -- Second execute stage control
+    execute2_1: process(all)
+       variable v : reg_stage2_type;
+       variable overflow : std_ulogic;
+        variable lv : Execute1ToLoadstore1Type;
+        variable fv : Execute1ToFPUType;
+        variable k : integer;
+        variable go : std_ulogic;
+    begin
+       v := ex2;
+        if (l_in.busy or fp_in.busy) = '0' then
+            v.e := ex1.e;
+            v.se := ex1.se;
+            v.e.write_data := ex_result;
+            v.ext_interrupt := ex1.ext_interrupt;
+            v.taken_branch_event := ex1.taken_branch_event;
+            v.br_mispredict := ex1.br_mispredict;
+        end if;
+
+       ctrl_tmp <= ctrl;
+       -- FIXME: run at 512MHz not core freq
+       ctrl_tmp.tb <= std_ulogic_vector(unsigned(ctrl.tb) + 1);
+       ctrl_tmp.dec <= std_ulogic_vector(unsigned(ctrl.dec) - 1);
+
+        x_to_pmu.mfspr <= '0';
+        x_to_pmu.mtspr <= '0';
+        x_to_pmu.tbbits(3) <= ctrl.tb(63 - 47);
+        x_to_pmu.tbbits(2) <= ctrl.tb(63 - 51);
+        x_to_pmu.tbbits(1) <= ctrl.tb(63 - 55);
+        x_to_pmu.tbbits(0) <= ctrl.tb(63 - 63);
+        x_to_pmu.pmm_msr <= ctrl.msr(MSR_PMM);
+        x_to_pmu.pr_msr <= ctrl.msr(MSR_PR);
+
+        if v.e.valid = '0' or flush_in = '1' then
+            v.e.write_enable := '0';
+            v.e.write_cr_enable := '0';
+            v.e.write_xerc_enable := '0';
+            v.e.redirect := '0';
+            v.e.br_last := '0';
+            v.se := side_effect_init;
+            v.taken_branch_event := '0';
+            v.br_mispredict := '0';
+        end if;
+        if flush_in = '1' then
+            v.e.valid := '0';
+            v.e.interrupt := '0';
+            v.ext_interrupt := '0';
+        end if;
+
+       if (l_in.busy or fp_in.busy) = '0' then
+            if ex1.se.write_msr = '1' then
+                ctrl_tmp.msr <= ex1.msr;
+            end if;
+            if ex1.se.write_xerlow = '1' then
+                ctrl_tmp.xer_low <= ex1.e.write_data(17 downto 0);
+            end if;
+            if ex1.se.write_dec = '1' then
+                ctrl_tmp.dec <= ex1.e.write_data;
+            end if;
+            if ex1.se.write_cfar = '1' then
+                ctrl_tmp.cfar <= ex1.e.last_nia;
+            end if;
+            if ex1.se.write_loga = '1' then
+                v.log_addr_spr := ex1.e.write_data(31 downto 0);
+            elsif ex1.se.inc_loga = '1' then
+                v.log_addr_spr := std_ulogic_vector(unsigned(ex2.log_addr_spr) + 1);
+            end if;
+            x_to_pmu.mtspr <= ex1.se.write_pmuspr;
         end if;
+
+       if interrupt_in = '1' then
+            ctrl_tmp.msr(MSR_SF) <= '1';
+            ctrl_tmp.msr(MSR_EE) <= '0';
+            ctrl_tmp.msr(MSR_PR) <= '0';
+            ctrl_tmp.msr(MSR_SE) <= '0';
+            ctrl_tmp.msr(MSR_BE) <= '0';
+            ctrl_tmp.msr(MSR_FP) <= '0';
+            ctrl_tmp.msr(MSR_FE0) <= '0';
+            ctrl_tmp.msr(MSR_FE1) <= '0';
+            ctrl_tmp.msr(MSR_IR) <= '0';
+            ctrl_tmp.msr(MSR_DR) <= '0';
+            ctrl_tmp.msr(MSR_RI) <= '0';
+            ctrl_tmp.msr(MSR_LE) <= '1';
+        end if;
+
+       -- Update registers
+       ex2in <= v;
+
+       -- update outputs
+       e_out <= ex2.e;
         e_out.msr <= msr_copy(ctrl.msr);
-        fp_out <= fv;
 
-        exception_log <= exception;
-        irq_valid_log <= irq_valid;
+        terminate_out <= ex2.se.terminate;
+        icache_inval <= ex2.se.icache_inval;
+
+        exception_log <= v.e.interrupt;
     end process;
 
     e1_log: if LOG_LENGTH > 0 generate
@@ -1492,9 +1584,9 @@ begin
                             irq_valid_log &
                             interrupt_in &
                             "000" &
-                            ex1.e.write_enable &
-                            ex1.e.valid &
-                            ((ex1.e.redirect and ex1.e.valid) or ex1.e.interrupt) &
+                            ex2.e.write_enable &
+                            ex2.e.valid &
+                            (ex2.e.redirect or ex2.e.interrupt) &
                             ex1.busy &
                             flush_in;
             end if;