You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
microwatt/divider.vhdl

138 lines
4.7 KiB
VHDL

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.common.all;
use work.decode_types.all;
entity divider is
port (
clk : in std_logic;
rst : in std_logic;
d_in : in Execute1ToDividerType;
d_out : out DividerToExecute1Type
);
end entity divider;
architecture behaviour of divider is
5 years ago
signal dend : std_ulogic_vector(128 downto 0);
signal div : unsigned(63 downto 0);
signal quot : std_ulogic_vector(63 downto 0);
signal result : std_ulogic_vector(63 downto 0);
signal sresult : std_ulogic_vector(64 downto 0);
signal oresult : std_ulogic_vector(63 downto 0);
signal running : std_ulogic;
signal count : unsigned(6 downto 0);
signal neg_result : std_ulogic;
signal is_modulus : std_ulogic;
signal is_32bit : std_ulogic;
signal extended : std_ulogic;
5 years ago
signal is_signed : std_ulogic;
signal overflow : std_ulogic;
signal ovf32 : std_ulogic;
5 years ago
signal did_ovf : std_ulogic;
begin
divider_0: process(clk)
begin
if rising_edge(clk) then
Add a second execute stage to the pipeline 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>
2 years ago
if rst = '1' or d_in.flush = '1' then
dend <= (others => '0');
div <= (others => '0');
quot <= (others => '0');
running <= '0';
count <= "0000000";
is_32bit <= '0';
overflow <= '0';
elsif d_in.valid = '1' then
if d_in.is_extended = '1' then
5 years ago
dend <= '0' & d_in.dividend & x"0000000000000000";
else
5 years ago
dend <= '0' & x"0000000000000000" & d_in.dividend;
end if;
div <= unsigned(d_in.divisor);
quot <= (others => '0');
neg_result <= d_in.neg_result;
is_modulus <= d_in.is_modulus;
extended <= d_in.is_extended;
is_32bit <= d_in.is_32bit;
5 years ago
is_signed <= d_in.is_signed;
count <= "1111111";
running <= '1';
5 years ago
overflow <= '0';
ovf32 <= '0';
elsif running = '1' then
if count = "0111111" then
running <= '0';
end if;
5 years ago
overflow <= quot(63);
if dend(128) = '1' or unsigned(dend(127 downto 64)) >= div then
ovf32 <= ovf32 or quot(31);
5 years ago
dend <= std_ulogic_vector(unsigned(dend(127 downto 64)) - div) &
dend(63 downto 0) & '0';
quot <= quot(62 downto 0) & '1';
count <= count + 1;
5 years ago
elsif dend(128 downto 57) = x"000000000000000000" and count(6 downto 3) /= "0111" then
-- consume 8 bits of zeroes in one cycle
ovf32 <= or (ovf32 & quot(31 downto 24));
5 years ago
dend <= dend(120 downto 0) & x"00";
quot <= quot(55 downto 0) & x"00";
count <= count + 8;
else
ovf32 <= ovf32 or quot(31);
5 years ago
dend <= dend(127 downto 0) & '0';
quot <= quot(62 downto 0) & '0';
count <= count + 1;
end if;
else
count <= "0000000";
end if;
end if;
end process;
divider_1: process(all)
begin
if is_modulus = '1' then
5 years ago
result <= dend(128 downto 65);
else
result <= quot;
end if;
if neg_result = '1' then
sresult <= std_ulogic_vector(- signed('0' & result));
else
sresult <= '0' & result;
end if;
5 years ago
did_ovf <= '0';
if is_32bit = '0' then
did_ovf <= overflow or (is_signed and (sresult(64) xor sresult(63)));
5 years ago
elsif is_signed = '1' then
if ovf32 = '1' or sresult(32) /= sresult(31) then
5 years ago
did_ovf <= '1';
end if;
else
did_ovf <= ovf32;
5 years ago
end if;
if did_ovf = '1' then
oresult <= (others => '0');
5 years ago
elsif (is_32bit = '1') and (is_modulus = '0') then
-- 32-bit divisions set the top 32 bits of the result to 0
oresult <= x"00000000" & sresult(31 downto 0);
5 years ago
else
oresult <= sresult(63 downto 0);
5 years ago
end if;
end process;
divider_out: process(clk)
begin
if rising_edge(clk) then
d_out.valid <= '0';
d_out.write_reg_data <= oresult;
d_out.overflow <= did_ovf;
if count = "1000000" then
d_out.valid <= '1';
end if;
end if;
end process;
end architecture behaviour;