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;