From 9880fc743551a15aa2dd834b2472180e5280796e Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 21 May 2020 13:42:46 +1000 Subject: [PATCH] multiply: Move selection of result bits into execute1 This puts the logic that selects which bits of the multiplier result get written into the destination GPR into execute1, moved out from multiply. The multiplier is now expected to do an unsigned multiplication of 64-bit operands, optionally negate the result, detect 32-bit or 64-bit signed overflow of the result, and return a full 128-bit result. Signed-off-by: Paul Mackerras --- common.vhdl | 12 +++--- execute1.vhdl | 54 ++++++++++++-------------- multiply.vhdl | 59 ++++++++++------------------- multiply_tb.vhdl | 99 +++++++++++++++++++++++++++--------------------- 4 files changed, 107 insertions(+), 117 deletions(-) diff --git a/common.vhdl b/common.vhdl index 7236a56..82b3242 100644 --- a/common.vhdl +++ b/common.vhdl @@ -158,13 +158,13 @@ package common is type Execute1ToMultiplyType is record valid: std_ulogic; - insn_type: insn_type_t; - data1: std_ulogic_vector(64 downto 0); - data2: std_ulogic_vector(64 downto 0); + data1: std_ulogic_vector(63 downto 0); + data2: std_ulogic_vector(63 downto 0); is_32bit: std_ulogic; + neg_result: std_ulogic; end record; - constant Execute1ToMultiplyInit : Execute1ToMultiplyType := (valid => '0', insn_type => OP_ILLEGAL, - is_32bit => '0', + constant Execute1ToMultiplyInit : Execute1ToMultiplyType := (valid => '0', + is_32bit => '0', neg_result => '0', others => (others => '0')); type Execute1ToDividerType is record @@ -356,7 +356,7 @@ package common is type MultiplyToExecute1Type is record valid: std_ulogic; - write_reg_data: std_ulogic_vector(63 downto 0); + result: std_ulogic_vector(127 downto 0); overflow : std_ulogic; end record; constant MultiplyToExecute1Init : MultiplyToExecute1Type := (valid => '0', overflow => '0', diff --git a/execute1.vhdl b/execute1.vhdl index 9066aa0..0009699 100644 --- a/execute1.vhdl +++ b/execute1.vhdl @@ -53,6 +53,7 @@ architecture behaviour of execute1 is mul_in_progress : std_ulogic; div_in_progress : std_ulogic; cntz_in_progress : std_ulogic; + slow_op_insn : insn_type_t; slow_op_dest : gpr_index_t; slow_op_rc : std_ulogic; slow_op_oe : std_ulogic; @@ -63,7 +64,7 @@ architecture behaviour of execute1 is constant reg_type_init : reg_type := (e => Execute1ToWritebackInit, lr_update => '0', mul_in_progress => '0', div_in_progress => '0', cntz_in_progress => '0', - slow_op_rc => '0', slow_op_oe => '0', slow_op_xerc => xerc_init, + slow_op_insn => OP_ILLEGAL, slow_op_rc => '0', slow_op_oe => '0', slow_op_xerc => xerc_init, next_lr => (others => '0'), ldst_nia => (others => '0'), others => (others => '0')); signal r, rin : reg_type; @@ -346,32 +347,7 @@ begin v.div_in_progress := '0'; v.cntz_in_progress := '0'; - -- signals to multiply unit - x_to_multiply <= Execute1ToMultiplyInit; - x_to_multiply.insn_type <= e_in.insn_type; - x_to_multiply.is_32bit <= e_in.is_32bit; - - if e_in.is_32bit = '1' then - if e_in.is_signed = '1' then - x_to_multiply.data1 <= (others => a_in(31)); - x_to_multiply.data1(31 downto 0) <= a_in(31 downto 0); - x_to_multiply.data2 <= (others => b_in(31)); - x_to_multiply.data2(31 downto 0) <= b_in(31 downto 0); - else - x_to_multiply.data1 <= '0' & x"00000000" & a_in(31 downto 0); - x_to_multiply.data2 <= '0' & x"00000000" & b_in(31 downto 0); - end if; - else - if e_in.is_signed = '1' then - x_to_multiply.data1 <= a_in(63) & a_in; - x_to_multiply.data2 <= b_in(63) & b_in; - else - x_to_multiply.data1 <= '0' & a_in; - x_to_multiply.data2 <= '0' & b_in; - end if; - end if; - - -- signals to divide unit + -- signals to multiply and divide units sign1 := '0'; sign2 := '0'; if e_in.is_signed = '1' then @@ -395,15 +371,22 @@ begin abs2 := - signed(b_in); end if; + x_to_multiply <= Execute1ToMultiplyInit; + x_to_multiply.is_32bit <= e_in.is_32bit; + x_to_divider <= Execute1ToDividerInit; x_to_divider.is_signed <= e_in.is_signed; x_to_divider.is_32bit <= e_in.is_32bit; if e_in.insn_type = OP_MOD then x_to_divider.is_modulus <= '1'; end if; + + x_to_multiply.neg_result <= sign1 xor sign2; x_to_divider.neg_result <= sign1 xor (sign2 and not x_to_divider.is_modulus); if e_in.is_32bit = '0' then -- 64-bit forms + x_to_multiply.data1 <= std_ulogic_vector(abs1); + x_to_multiply.data2 <= std_ulogic_vector(abs2); if e_in.insn_type = OP_DIVE then x_to_divider.is_extended <= '1'; end if; @@ -411,6 +394,8 @@ begin x_to_divider.divisor <= std_ulogic_vector(abs2); else -- 32-bit forms + x_to_multiply.data1 <= x"00000000" & std_ulogic_vector(abs1(31 downto 0)); + x_to_multiply.data2 <= x"00000000" & std_ulogic_vector(abs2(31 downto 0)); x_to_divider.is_extended <= '0'; if e_in.insn_type = OP_DIVE then -- extended forms x_to_divider.dividend <= std_ulogic_vector(abs1(31 downto 0)) & x"00000000"; @@ -505,6 +490,7 @@ begin v.e.valid := '1'; v.e.write_reg := e_in.write_reg; + v.slow_op_insn := e_in.insn_type; v.slow_op_dest := gspr_to_gpr(e_in.write_reg); v.slow_op_rc := e_in.rc; v.slow_op_oe := e_in.oe; @@ -950,8 +936,18 @@ begin if (r.mul_in_progress = '1' and multiply_to_x.valid = '1') or (r.div_in_progress = '1' and divider_to_x.valid = '1') then if r.mul_in_progress = '1' then - result := multiply_to_x.write_reg_data; - overflow := multiply_to_x.overflow; + overflow := '0'; + case r.slow_op_insn is + when OP_MUL_H32 => + result := multiply_to_x.result(63 downto 32) & + multiply_to_x.result(63 downto 32); + when OP_MUL_H64 => + result := multiply_to_x.result(127 downto 64); + when others => + -- i.e. OP_MUL_L64 + result := multiply_to_x.result(63 downto 0); + overflow := multiply_to_x.overflow; + end case; else result := divider_to_x.write_reg_data; overflow := divider_to_x.overflow; diff --git a/multiply.vhdl b/multiply.vhdl index 959c114..7a4c81b 100644 --- a/multiply.vhdl +++ b/multiply.vhdl @@ -4,11 +4,10 @@ use ieee.numeric_std.all; library work; use work.common.all; -use work.decode_types.all; entity multiply is generic ( - PIPELINE_DEPTH : natural := 16 + PIPELINE_DEPTH : natural := 4 ); port ( clk : in std_logic; @@ -19,17 +18,16 @@ entity multiply is end entity multiply; architecture behaviour of multiply is - signal m: Execute1ToMultiplyType; + signal m: Execute1ToMultiplyType := Execute1ToMultiplyInit; type multiply_pipeline_stage is record valid : std_ulogic; - insn_type : insn_type_t; - data : signed(129 downto 0); + data : unsigned(127 downto 0); is_32bit : std_ulogic; + neg_res : std_ulogic; end record; constant MultiplyPipelineStageInit : multiply_pipeline_stage := (valid => '0', - insn_type => OP_ILLEGAL, - is_32bit => '0', + is_32bit => '0', neg_res => '0', data => (others => '0')); type multiply_pipeline_type is array(0 to PIPELINE_DEPTH-1) of multiply_pipeline_stage; @@ -51,50 +49,35 @@ begin multiply_1: process(all) variable v : reg_type; - variable d : std_ulogic_vector(129 downto 0); + variable d : std_ulogic_vector(127 downto 0); variable d2 : std_ulogic_vector(63 downto 0); variable ov : std_ulogic; begin - v := r; - - m_out <= MultiplyToExecute1Init; - v.multiply_pipeline(0).valid := m.valid; - v.multiply_pipeline(0).insn_type := m.insn_type; - v.multiply_pipeline(0).data := signed(m.data1) * signed(m.data2); + v.multiply_pipeline(0).data := unsigned(m.data1) * unsigned(m.data2); v.multiply_pipeline(0).is_32bit := m.is_32bit; + v.multiply_pipeline(0).neg_res := m.neg_result; loop_0: for i in 1 to PIPELINE_DEPTH-1 loop v.multiply_pipeline(i) := r.multiply_pipeline(i-1); end loop; - d := std_ulogic_vector(v.multiply_pipeline(PIPELINE_DEPTH-1).data); - ov := '0'; + if v.multiply_pipeline(PIPELINE_DEPTH-1).neg_res = '0' then + d := std_ulogic_vector(v.multiply_pipeline(PIPELINE_DEPTH-1).data); + else + d := std_ulogic_vector(- signed(v.multiply_pipeline(PIPELINE_DEPTH-1).data)); + end if; - -- TODO: Handle overflows - case_0: case v.multiply_pipeline(PIPELINE_DEPTH-1).insn_type is - when OP_MUL_L64 => - d2 := d(63 downto 0); - if v.multiply_pipeline(PIPELINE_DEPTH-1).is_32bit = '1' then - ov := (or d(63 downto 31)) and not (and d(63 downto 31)); - else - ov := (or d(127 downto 63)) and not (and d(127 downto 63)); - end if; - when OP_MUL_H32 => - d2 := d(63 downto 32) & d(63 downto 32); - when OP_MUL_H64 => - d2 := d(127 downto 64); - when others => - --report "Illegal insn type in multiplier"; - d2 := (others => '0'); - end case; + ov := '0'; + if v.multiply_pipeline(PIPELINE_DEPTH-1).is_32bit = '1' then + ov := (or d(63 downto 31)) and not (and d(63 downto 31)); + else + ov := (or d(127 downto 63)) and not (and d(127 downto 63)); + end if; - m_out.write_reg_data <= d2; + m_out.result <= d; m_out.overflow <= ov; - - if v.multiply_pipeline(PIPELINE_DEPTH-1).valid = '1' then - m_out.valid <= '1'; - end if; + m_out.valid <= v.multiply_pipeline(PIPELINE_DEPTH-1).valid; rin <= v; end process; diff --git a/multiply_tb.vhdl b/multiply_tb.vhdl index ee80de0..87f029d 100644 --- a/multiply_tb.vhdl +++ b/multiply_tb.vhdl @@ -17,8 +17,18 @@ architecture behave of multiply_tb is constant pipeline_depth : integer := 4; - signal m1 : Execute1ToMultiplyType; + signal m1 : Execute1ToMultiplyType := Execute1ToMultiplyInit; signal m2 : MultiplyToExecute1Type; + + function absval(x: std_ulogic_vector) return std_ulogic_vector is + begin + if x(x'left) = '1' then + return std_ulogic_vector(- signed(x)); + else + return x; + end if; + end; + begin multiply_0: entity work.multiply generic map (PIPELINE_DEPTH => pipeline_depth) @@ -39,9 +49,8 @@ begin wait for clk_period; m1.valid <= '1'; - m1.insn_type <= OP_MUL_L64; - m1.data1 <= '0' & x"0000000000001000"; - m1.data2 <= '0' & x"0000000000001111"; + m1.data1 <= x"0000000000001000"; + m1.data2 <= x"0000000000001111"; wait for clk_period; assert m2.valid = '0'; @@ -56,7 +65,7 @@ begin wait for clk_period; assert m2.valid = '1'; - assert m2.write_reg_data = x"0000000001111000"; + assert m2.result = x"00000000000000000000000001111000"; wait for clk_period; assert m2.valid = '0'; @@ -70,7 +79,7 @@ begin wait for clk_period * (pipeline_depth-1); assert m2.valid = '1'; - assert m2.write_reg_data = x"0000000001111000"; + assert m2.result = x"00000000000000000000000001111000"; -- test mulld mulld_loop : for i in 0 to 1000 loop @@ -79,10 +88,10 @@ begin behave_rt := ppc_mulld(ra, rb); - m1.data1 <= '0' & ra; - m1.data2 <= '0' & rb; + m1.data1 <= absval(ra); + m1.data2 <= absval(rb); + m1.neg_result <= ra(63) xor rb(63); m1.valid <= '1'; - m1.insn_type <= OP_MUL_L64; wait for clk_period; @@ -92,8 +101,8 @@ begin assert m2.valid = '1'; - assert to_hstring(behave_rt) = to_hstring(m2.write_reg_data) - report "bad mulld expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.write_reg_data); + assert to_hstring(behave_rt) = to_hstring(m2.result(63 downto 0)) + report "bad mulld expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.result(63 downto 0)); end loop; -- test mulhdu @@ -103,10 +112,10 @@ begin behave_rt := ppc_mulhdu(ra, rb); - m1.data1 <= '0' & ra; - m1.data2 <= '0' & rb; + m1.data1 <= ra; + m1.data2 <= rb; + m1.neg_result <= '0'; m1.valid <= '1'; - m1.insn_type <= OP_MUL_H64; wait for clk_period; @@ -116,8 +125,8 @@ begin assert m2.valid = '1'; - assert to_hstring(behave_rt) = to_hstring(m2.write_reg_data) - report "bad mulhdu expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.write_reg_data); + assert to_hstring(behave_rt) = to_hstring(m2.result(127 downto 64)) + report "bad mulhdu expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.result(127 downto 64)); end loop; -- test mulhd @@ -127,10 +136,10 @@ begin behave_rt := ppc_mulhd(ra, rb); - m1.data1 <= ra(63) & ra; - m1.data2 <= rb(63) & rb; + m1.data1 <= absval(ra); + m1.data2 <= absval(rb); + m1.neg_result <= ra(63) xor rb(63); m1.valid <= '1'; - m1.insn_type <= OP_MUL_H64; wait for clk_period; @@ -140,8 +149,8 @@ begin assert m2.valid = '1'; - assert to_hstring(behave_rt) = to_hstring(m2.write_reg_data) - report "bad mulhd expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.write_reg_data); + assert to_hstring(behave_rt) = to_hstring(m2.result(127 downto 64)) + report "bad mulhd expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.result(127 downto 64)); end loop; -- test mullw @@ -151,12 +160,12 @@ begin behave_rt := ppc_mullw(ra, rb); - m1.data1 <= (others => ra(31)); - m1.data1(31 downto 0) <= ra(31 downto 0); - m1.data2 <= (others => rb(31)); - m1.data2(31 downto 0) <= rb(31 downto 0); + m1.data1 <= (others => '0'); + m1.data1(31 downto 0) <= absval(ra(31 downto 0)); + m1.data2 <= (others => '0'); + m1.data2(31 downto 0) <= absval(rb(31 downto 0)); + m1.neg_result <= ra(31) xor rb(31); m1.valid <= '1'; - m1.insn_type <= OP_MUL_L64; wait for clk_period; @@ -166,8 +175,8 @@ begin assert m2.valid = '1'; - assert to_hstring(behave_rt) = to_hstring(m2.write_reg_data) - report "bad mullw expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.write_reg_data); + assert to_hstring(behave_rt) = to_hstring(m2.result(63 downto 0)) + report "bad mullw expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.result(63 downto 0)); end loop; -- test mulhw @@ -177,12 +186,12 @@ begin behave_rt := ppc_mulhw(ra, rb); - m1.data1 <= (others => ra(31)); - m1.data1(31 downto 0) <= ra(31 downto 0); - m1.data2 <= (others => rb(31)); - m1.data2(31 downto 0) <= rb(31 downto 0); + m1.data1 <= (others => '0'); + m1.data1(31 downto 0) <= absval(ra(31 downto 0)); + m1.data2 <= (others => '0'); + m1.data2(31 downto 0) <= absval(rb(31 downto 0)); + m1.neg_result <= ra(31) xor rb(31); m1.valid <= '1'; - m1.insn_type <= OP_MUL_H32; wait for clk_period; @@ -192,8 +201,9 @@ begin assert m2.valid = '1'; - assert to_hstring(behave_rt) = to_hstring(m2.write_reg_data) - report "bad mulhw expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.write_reg_data); + assert to_hstring(behave_rt) = to_hstring(m2.result(63 downto 32) & m2.result(63 downto 32)) + report "bad mulhw expected " & to_hstring(behave_rt) & " got " & + to_hstring(m2.result(63 downto 32) & m2.result(63 downto 32)); end loop; -- test mulhwu @@ -207,8 +217,8 @@ begin m1.data1(31 downto 0) <= ra(31 downto 0); m1.data2 <= (others => '0'); m1.data2(31 downto 0) <= rb(31 downto 0); + m1.neg_result <= '0'; m1.valid <= '1'; - m1.insn_type <= OP_MUL_H32; wait for clk_period; @@ -218,8 +228,9 @@ begin assert m2.valid = '1'; - assert to_hstring(behave_rt) = to_hstring(m2.write_reg_data) - report "bad mulhwu expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.write_reg_data); + assert to_hstring(behave_rt) = to_hstring(m2.result(63 downto 32) & m2.result(63 downto 32)) + report "bad mulhwu expected " & to_hstring(behave_rt) & " got " & + to_hstring(m2.result(63 downto 32) & m2.result(63 downto 32)); end loop; -- test mulli @@ -229,11 +240,11 @@ begin behave_rt := ppc_mulli(ra, si); - m1.data1 <= ra(63) & ra; - m1.data2 <= (others => si(15)); - m1.data2(15 downto 0) <= si; + m1.data1 <= absval(ra); + m1.data2 <= (others => '0'); + m1.data2(15 downto 0) <= absval(si); + m1.neg_result <= ra(63) xor si(15); m1.valid <= '1'; - m1.insn_type <= OP_MUL_L64; wait for clk_period; @@ -243,8 +254,8 @@ begin assert m2.valid = '1'; - assert to_hstring(behave_rt) = to_hstring(m2.write_reg_data) - report "bad mulli expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.write_reg_data); + assert to_hstring(behave_rt) = to_hstring(m2.result(63 downto 0)) + report "bad mulli expected " & to_hstring(behave_rt) & " got " & to_hstring(m2.result(63 downto 0)); end loop; std.env.finish;