Change the multiplier interface to support signed multipliers
authorPaul Mackerras <paulus@ozlabs.org>
Mon, 8 Aug 2022 12:26:39 +0000 (22:26 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Tue, 9 Aug 2022 23:17:55 +0000 (09:17 +1000)
This adds an 'is_signed' signal to MultiplyInputType to indicate
whether the data1 and data2 fields are to be interpreted as signed or
unsigned numbers.

The 'not_result' field is replaced by a 'subtract' field which
provides a more intuitive interface for requesting that the product be
subtracted from the addend rather than added, i.e. subtract = 1 gives
C - A * B, vs. subtract = 0 giving C + A * B.  (Previously the users
of the multipliers got the same effect by complementing the addend and
setting not_result = 1.)

The is_32bit field is removed because it is no longer used now that we
have a separate 32-bit multiplier.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
common.vhdl
execute1.vhdl
fpu.vhdl
multiply-32s.vhdl
multiply.vhdl
xilinx-mult-32s.vhdl
xilinx-mult.vhdl

index 6287be58babb6af97714e4d48a5c09fedfdeca54..7c7a8d5ee93792d6aebb581e285c9aba7d05c597 100644 (file)
@@ -385,12 +385,11 @@ package common is
        data1: std_ulogic_vector(63 downto 0);
        data2: std_ulogic_vector(63 downto 0);
         addend: std_ulogic_vector(127 downto 0);
-       is_32bit: std_ulogic;
-        not_result: std_ulogic;
+        is_signed: std_ulogic;
+        subtract: std_ulogic;   -- 0 => addend + data1 * data2, 1 => addend - data1 * data2
     end record;
-    constant MultiplyInputInit : MultiplyInputType := (valid => '0',
-                                                       is_32bit => '0', not_result => '0',
-                                                       others => (others => '0'));
+    constant MultiplyInputInit : MultiplyInputType := (data1 => 64x"0", data2 => 64x"0",
+                                                       addend => 128x"0", others => '0');
 
     type MultiplyOutputType is record
        valid: std_ulogic;
index 948bdd613760e3f40ae7593b80df7ce9bd0804a1..721d4936445549fc993183080329cf101bee99cd 100644 (file)
@@ -695,7 +695,22 @@ begin
         overflow_32 <= calc_ov(a_inv(31), b_in(31), carry_32, sum_with_carry(31));
         overflow_64 <= calc_ov(a_inv(63), b_in(63), carry_64, sum_with_carry(63));
 
-        -- signals to multiply and divide units
+        -- signals to multiplier
+        addend := (others => '0');
+        if e_in.reg_valid3 = '1' then
+            -- integer multiply-add, major op 4 (if it is a multiply)
+            addend(63 downto 0) := c_in;
+            if e_in.is_signed = '1' then
+                addend(127 downto 64) := (others => c_in(63));
+            end if;
+        end if;
+        x_to_multiply.data1 <= std_ulogic_vector(a_in);
+        x_to_multiply.data2 <= std_ulogic_vector(b_in);
+        x_to_multiply.is_signed <= e_in.is_signed;
+        x_to_multiply.subtract <= '0';
+        x_to_multiply.addend <= addend;
+
+        -- Interface to divide unit
         sign1 := '0';
         sign2 := '0';
         if e_in.is_signed = '1' then
@@ -719,7 +734,6 @@ begin
             abs2 := - signed(b_in);
         end if;
 
-        -- Interface to multiply and divide units
         x_to_divider.is_signed <= e_in.is_signed;
        x_to_divider.is_32bit <= e_in.is_32bit;
         x_to_divider.is_extended <= '0';
@@ -728,24 +742,6 @@ begin
             x_to_divider.is_modulus <= '1';
         end if;
         x_to_divider.flush <= flush_in;
-
-        addend := (others => '0');
-        if e_in.reg_valid3 = '1' then
-            -- integer multiply-add, major op 4 (if it is a multiply)
-            addend(63 downto 0) := c_in;
-            if e_in.is_signed = '1' then
-                addend(127 downto 64) := (others => c_in(63));
-            end if;
-        end if;
-        if (sign1 xor sign2) = '1' then
-            addend := not addend;
-        end if;
-
-        x_to_multiply.data1 <= std_ulogic_vector(abs1);
-        x_to_multiply.data2 <= std_ulogic_vector(abs2);
-       x_to_multiply.is_32bit <= e_in.is_32bit;
-        x_to_multiply.not_result <= sign1 xor sign2;
-        x_to_multiply.addend <= addend;
         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
@@ -766,11 +762,11 @@ begin
         end if;
 
         -- signals to 32-bit multiplier
-        x_to_mult_32s.data1 <= 31x"0" & (a_in(31) and e_in.is_signed) & a_in(31 downto 0);
-        x_to_mult_32s.data2 <= 31x"0" & (b_in(31) and e_in.is_signed) & b_in(31 downto 0);
+        x_to_mult_32s.data1 <= 32x"0" & a_in(31 downto 0);
+        x_to_mult_32s.data2 <= 32x"0" & b_in(31 downto 0);
+        x_to_mult_32s.is_signed <= e_in.is_signed;
         -- The following are unused, but set here to avoid X states
-        x_to_mult_32s.is_32bit <= '1';
-        x_to_mult_32s.not_result <= '0';
+        x_to_mult_32s.subtract <= '0';
         x_to_mult_32s.addend <= (others => '0');
 
         shortmul_result <= std_ulogic_vector(resize(signed(mshort_p), 64));
index 417a318d8540503f7d8da6eace0f40a36f697a9d..eaa4cf2b028e8aad57f82914c4e8a7dfed375329 100644 (file)
--- a/fpu.vhdl
+++ b/fpu.vhdl
@@ -1071,7 +1071,7 @@ begin
         set_b_mant := '0';
         set_c := '0';
         set_s := '0';
-        f_to_multiply.is_32bit <= '0';
+        f_to_multiply.is_signed <= '0';
         f_to_multiply.valid <= '0';
         msel_1 <= MUL1_A;
         msel_2 <= MUL2_C;
@@ -3227,12 +3227,8 @@ begin
                 maddend(UNIT_BIT - 1 downto 0) := r.s;
             when others =>
         end case;
-        if msel_inv = '1' then
-            f_to_multiply.addend <= not maddend;
-        else
-            f_to_multiply.addend <= maddend;
-        end if;
-        f_to_multiply.not_result <= msel_inv;
+        f_to_multiply.addend <= maddend;
+        f_to_multiply.subtract <= msel_inv;
         if set_y = '1' then
             v.y := f_to_multiply.data2;
         end if;
index 0639dbfc6ca69ff89b0106fb563d46e084d1e7a2..ea3e2a8210571edc127e1a7c62bd5170c1bb55dc 100644 (file)
@@ -40,7 +40,8 @@ begin
        variable ov : std_ulogic;
     begin
         v.valid := m_in.valid;
-        v.data := signed(m_in.data1(32 downto 0)) * signed(m_in.data2(32 downto 0));
+        v.data := signed((m_in.is_signed and m_in.data1(31)) & m_in.data1(31 downto 0)) *
+                  signed((m_in.is_signed and m_in.data2(31)) & m_in.data2(31 downto 0));
 
         d := std_ulogic_vector(r.data(63 downto 0));
 
index c09fc22c4783de7aa1639834d29f3629ba2da403..615ceeac9a0988961ea199b99076124fae8c7786 100644 (file)
@@ -7,7 +7,7 @@ use work.common.all;
 
 entity multiply is
     generic (
-        PIPELINE_DEPTH : natural := 4
+        PIPELINE_DEPTH : natural := 3
         );
     port (
         clk   : in std_logic;
@@ -23,11 +23,8 @@ architecture behaviour of multiply is
     type multiply_pipeline_stage is record
         valid     : std_ulogic;
         data      : unsigned(127 downto 0);
-       is_32bit  : std_ulogic;
-        not_res   : std_ulogic;
     end record;
     constant MultiplyPipelineStageInit : multiply_pipeline_stage := (valid => '0',
-                                                                    is_32bit => '0', not_res => '0',
                                                                     data => (others => '0'));
 
     type multiply_pipeline_type is array(0 to PIPELINE_DEPTH-1) of multiply_pipeline_stage;
@@ -52,31 +49,29 @@ begin
 
     multiply_1: process(all)
         variable v : reg_type;
+        variable a, b : std_ulogic_vector(64 downto 0);
+        variable prod : 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;
+        a := (m.is_signed and m.data1(63)) & m.data1;
+        b := (m.is_signed and m.data2(63)) & m.data2;
+        prod := std_ulogic_vector(signed(a) * signed(b));
         v.multiply_pipeline(0).valid := m.valid;
-        v.multiply_pipeline(0).data := (unsigned(m.data1) * unsigned(m.data2)) + unsigned(m.addend);
-        v.multiply_pipeline(0).is_32bit := m.is_32bit;
-        v.multiply_pipeline(0).not_res := m.not_result;
+        if m.subtract = '1' then
+            v.multiply_pipeline(0).data := unsigned(m.addend) - unsigned(prod(127 downto 0));
+        else
+            v.multiply_pipeline(0).data := unsigned(m.addend) + unsigned(prod(127 downto 0));
+        end if;
 
         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);
-        if v.multiply_pipeline(PIPELINE_DEPTH-1).not_res = '1' then
-            d := not d;
-        end if;
-
-        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;
+        ov := (or d(127 downto 63)) and not (and d(127 downto 63));
         ovf_in <= ov;
 
         m_out.result <= d;
index fde19ae2b948c430005752cd9d8a9f0fa5b711a7..cacc22d1510a7f1f3338e6a15379e057a8a6ab4b 100644 (file)
@@ -33,9 +33,11 @@ architecture behaviour of multiply_32s is
     signal product_lo : std_ulogic_vector(22 downto 0);
 
 begin
-    -- sign extend
-    data1 <= std_ulogic_vector(resize(signed(m_in.data1(32 downto 0)), 53));
-    data2 <= std_ulogic_vector(resize(signed(m_in.data2(32 downto 0)), 35));
+    -- sign extend if signed
+    data1(31 downto 0)  <= m_in.data1(31 downto 0);
+    data1(52 downto 32) <= (others => m_in.is_signed and m_in.data1(31));
+    data2(31 downto 0)  <= m_in.data2(31 downto 0);
+    data2(34 downto 32) <= (others => m_in.is_signed and m_in.data2(31));
 
     clocken <= m_in.valid and not stall;
 
index 608810ea576d775248dd3392121253e5e56b1e3b..26ba5d7947f209e07d244c4ed0ba810e0f530081 100644 (file)
@@ -18,6 +18,8 @@ entity multiply is
 end entity multiply;
 
 architecture behaviour of multiply is
+    signal d1sign : std_ulogic_vector(13 downto 0);
+    signal d2sign : std_ulogic_vector(4 downto 0);
     signal m00_p, m01_p, m02_p, m03_p : std_ulogic_vector(47 downto 0);
     signal m00_pc, m02_pc : std_ulogic_vector(47 downto 0);
     signal m10_p, m11_p, m12_p, m13_p : std_ulogic_vector(47 downto 0);
@@ -39,7 +41,9 @@ architecture behaviour of multiply is
     signal overflow : std_ulogic;
 
 begin
-    addend <= m_in.addend;
+    addend <= m_in.addend when m_in.subtract = '0' else not m_in.addend;
+    d1sign <= (others => m_in.data1(63) and m_in.is_signed);
+    d2sign <= (others => m_in.data2(63) and m_in.is_signed);
 
     m00: DSP48E1
         generic map (
@@ -233,7 +237,7 @@ begin
             A => 6x"0" & m_in.data1(23 downto 0),
             ACIN => (others => '0'),
             ALUMODE => "0000",
-            B => "00000" & m_in.data2(63 downto 51),
+            B => d2sign & m_in.data2(63 downto 51),
             BCIN => (others => '0'),
             C => (others => '0'),
             CARRYCASCIN => '0',
@@ -463,7 +467,7 @@ begin
             A => 6x"0" & m_in.data1(47 downto 24),
             ACIN => (others => '0'),
             ALUMODE => "0000",
-            B => "00000" & m_in.data2(63 downto 51),
+            B => d2sign & m_in.data2(63 downto 51),
             BCIN => (others => '0'),
             C => (others => '0'),
             CARRYCASCIN => '0',
@@ -517,7 +521,7 @@ begin
             PREG => 1
             )
         port map (
-            A => 14x"0" & m_in.data1(63 downto 48),
+            A => d1sign & m_in.data1(63 downto 48),
             ACIN => (others => '0'),
             ALUMODE => "0000",
             B => '0' & m_in.data2(16 downto 0),
@@ -575,7 +579,7 @@ begin
             PREG => 0
             )
         port map (
-            A => 14x"0" & m_in.data1(63 downto 48),
+            A => d1sign & m_in.data1(63 downto 48),
             ACIN => (others => '0'),
             ALUMODE => "0000",
             B => '0' & m_in.data2(33 downto 17),
@@ -632,7 +636,7 @@ begin
             PREG => 1
             )
         port map (
-            A => 14x"0" & m_in.data1(63 downto 48),
+            A => d1sign & m_in.data1(63 downto 48),
             ACIN => (others => '0'),
             ALUMODE => "0000",
             B => '0' & m_in.data2(50 downto 34),
@@ -690,10 +694,10 @@ begin
             PREG => 0
             )
         port map (
-            A => 14x"0" & m_in.data1(63 downto 48),
+            A => d1sign & m_in.data1(63 downto 48),
             ACIN => (others => '0'),
             ALUMODE => "0000",
-            B => "00000" & m_in.data2(63 downto 51),
+            B => d2sign & m_in.data2(63 downto 51),
             BCIN => (others => '0'),
             C => (others => '0'),
             CARRYCASCIN => '0',
@@ -996,7 +1000,7 @@ begin
             end if;
             m_out.valid <= valid_1;
             valid_1 <= m_in.valid;
-            rnot_1 <= m_in.not_result;
+            rnot_1 <= m_in.subtract;
             overflow <= not ((p1_pat and p0_pat) or (p1_patb and p0_patb));
         end if;
     end process;