Line 1... |
Line 1... |
-- #################################################################################################
|
-- #################################################################################################
|
-- # << NEORV32 - CPU Co-Processor: Integer Multiplier/Divider Unit (RISC-V "M" Extension) >> #
|
-- # << NEORV32 - CPU Co-Processor: Integer Multiplier/Divider Unit (RISC-V "M" Extension) >> #
|
-- # ********************************************************************************************* #
|
-- # ********************************************************************************************* #
|
-- # Multiplier and Divider unit. Implements the RISC-V M CPU extension. #
|
-- # Multiplier and Divider unit. Implements the RISC-V M & Zmmul CPU extensions. #
|
-- # #
|
|
-- # Multiplier core (signed/unsigned) uses classical serial algorithm. Unit latency: 31+3 cycles #
|
|
-- # Divider core (unsigned) uses classical serial algorithm. Unit latency: 32+4 cycles #
|
|
-- # #
|
-- # #
|
|
-- # Multiplier core (signed/unsigned) uses classical serial algorithm. Latency = 31+3 cycles. #
|
-- # Multiplications can be mapped to DSP blocks (faster!) when FAST_MUL_EN = true. #
|
-- # Multiplications can be mapped to DSP blocks (faster!) when FAST_MUL_EN = true. #
|
|
-- # Divider core (unsigned-only) uses classical serial algorithm. latency = 32+4 cycles. #
|
-- # ********************************************************************************************* #
|
-- # ********************************************************************************************* #
|
-- # BSD 3-Clause License #
|
-- # BSD 3-Clause License #
|
-- # #
|
-- # #
|
-- # Copyright (c) 2022, Stephan Nolting. All rights reserved. #
|
-- # Copyright (c) 2022, Stephan Nolting. All rights reserved. #
|
-- # #
|
-- # #
|
Line 88... |
Line 87... |
signal start_mul : std_ulogic;
|
signal start_mul : std_ulogic;
|
signal operation : std_ulogic;
|
signal operation : std_ulogic;
|
signal div_opy : std_ulogic_vector(data_width_c-1 downto 0);
|
signal div_opy : std_ulogic_vector(data_width_c-1 downto 0);
|
signal rs1_is_signed : std_ulogic;
|
signal rs1_is_signed : std_ulogic;
|
signal rs2_is_signed : std_ulogic;
|
signal rs2_is_signed : std_ulogic;
|
signal opy_is_zero : std_ulogic;
|
|
signal div_res_corr : std_ulogic;
|
signal div_res_corr : std_ulogic;
|
signal out_en : std_ulogic;
|
signal out_en : std_ulogic;
|
|
|
-- divider core --
|
-- divider core --
|
signal remainder : std_ulogic_vector(data_width_c-1 downto 0);
|
signal remainder : std_ulogic_vector(data_width_c-1 downto 0);
|
Line 123... |
Line 121... |
cp_op_ff <= (others => def_rst_val_c);
|
cp_op_ff <= (others => def_rst_val_c);
|
start_div <= '0';
|
start_div <= '0';
|
out_en <= '0';
|
out_en <= '0';
|
valid_o <= '0';
|
valid_o <= '0';
|
div_res_corr <= def_rst_val_c;
|
div_res_corr <= def_rst_val_c;
|
opy_is_zero <= def_rst_val_c;
|
|
elsif rising_edge(clk_i) then
|
elsif rising_edge(clk_i) then
|
-- defaults --
|
-- defaults --
|
start_div <= '0';
|
start_div <= '0';
|
out_en <= '0';
|
out_en <= '0';
|
valid_o <= '0';
|
valid_o <= '0';
|
Line 151... |
Line 148... |
end if;
|
end if;
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
when DIV_PREPROCESS =>
|
when DIV_PREPROCESS =>
|
-- check relevant input signs --
|
-- check relevant input signs for result sign compensation --
|
if (cp_op = cp_op_div_c) then -- result sign compensation for div?
|
if (cp_op = cp_op_div_c) then -- signed div operation
|
div_res_corr <= rs1_i(rs1_i'left) xor rs2_i(rs2_i'left);
|
div_res_corr <= (rs1_i(rs1_i'left) xor rs2_i(rs2_i'left)) and or_reduce_f(rs2_i); -- different signs AND rs2 not zero
|
elsif (cp_op = cp_op_rem_c) then -- result sign compensation for rem?
|
elsif (cp_op = cp_op_rem_c) then -- signed rem operation
|
div_res_corr <= rs1_i(rs1_i'left);
|
div_res_corr <= rs1_i(rs1_i'left);
|
else
|
else
|
div_res_corr <= '0';
|
div_res_corr <= '0';
|
end if;
|
end if;
|
-- divide by zero? --
|
|
opy_is_zero <= not or_reduce_f(rs2_i); -- set if rs2 = 0
|
|
-- abs(rs2) --
|
-- abs(rs2) --
|
if ((rs2_i(rs2_i'left) and rs2_is_signed) = '1') then -- signed division?
|
if ((rs2_i(rs2_i'left) and rs2_is_signed) = '1') then -- signed division?
|
div_opy <= std_ulogic_vector(0 - unsigned(rs2_i)); -- make positive
|
div_opy <= std_ulogic_vector(0 - unsigned(rs2_i)); -- make positive
|
else
|
else
|
div_opy <= rs2_i;
|
div_opy <= rs2_i;
|
end if;
|
end if;
|
--
|
|
state <= PROCESSING;
|
state <= PROCESSING;
|
|
|
when PROCESSING =>
|
when PROCESSING =>
|
cnt <= std_ulogic_vector(unsigned(cnt) - 1);
|
cnt <= std_ulogic_vector(unsigned(cnt) - 1);
|
if (cnt = "00000") or (ctrl_i(ctrl_trap_c) = '1') then -- abort on trap
|
if (cnt = "00000") or (ctrl_i(ctrl_trap_c) = '1') then -- abort on trap
|
Line 295... |
Line 289... |
|
|
-- try another subtraction --
|
-- try another subtraction --
|
div_sub <= std_ulogic_vector(unsigned('0' & remainder(30 downto 0) & quotient(31)) - unsigned('0' & div_opy));
|
div_sub <= std_ulogic_vector(unsigned('0' & remainder(30 downto 0) & quotient(31)) - unsigned('0' & div_opy));
|
|
|
-- result sign compensation --
|
-- result sign compensation --
|
div_sign_comp_in <= quotient when (cp_op = cp_op_div_c) else remainder;
|
div_sign_comp_in <= quotient when (cp_op = cp_op_div_c) or (cp_op = cp_op_divu_c) else remainder;
|
div_sign_comp <= std_ulogic_vector(0 - unsigned(div_sign_comp_in));
|
div_sign_comp <= std_ulogic_vector(0 - unsigned(div_sign_comp_in));
|
div_res <= div_sign_comp when (div_res_corr = '1') and (opy_is_zero = '0') else div_sign_comp_in;
|
div_res <= div_sign_comp when (div_res_corr = '1') else div_sign_comp_in;
|
end generate;
|
end generate;
|
|
|
-- no divider --
|
-- no divider --
|
divider_core_serial_none:
|
divider_core_serial_none:
|
if (DIVISION_EN = false) generate
|
if (DIVISION_EN = false) generate
|
remainder <= (others => '0');
|
remainder <= (others => '0');
|
quotient <= (others => '0');
|
quotient <= (others => '0');
|
|
div_sub <= (others => '0');
|
|
div_sign_comp_in <= (others => '0');
|
|
div_sign_comp <= (others => '0');
|
div_res <= (others => '0');
|
div_res <= (others => '0');
|
end generate;
|
end generate;
|
|
|
|
|
-- Data Output ----------------------------------------------------------------------------
|
-- Data Output ----------------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
operation_result: process(out_en, cp_op_ff, mul_product, div_res, quotient, opy_is_zero, rs1_i, remainder)
|
operation_result: process(out_en, cp_op_ff, mul_product, div_res, div_sign_comp_in)
|
begin
|
begin
|
if (out_en = '1') then
|
if (out_en = '1') then
|
case cp_op_ff is
|
case cp_op_ff is
|
when cp_op_mul_c =>
|
when cp_op_mul_c =>
|
res_o <= mul_product(31 downto 00);
|
res_o <= mul_product(31 downto 00);
|
when cp_op_mulh_c | cp_op_mulhsu_c | cp_op_mulhu_c =>
|
when cp_op_mulh_c | cp_op_mulhsu_c | cp_op_mulhu_c =>
|
res_o <= mul_product(63 downto 32);
|
res_o <= mul_product(63 downto 32);
|
when cp_op_div_c =>
|
when cp_op_div_c | cp_op_rem_c =>
|
res_o <= div_res;
|
|
when cp_op_divu_c =>
|
|
res_o <= quotient;
|
|
when cp_op_rem_c =>
|
|
if (opy_is_zero = '0') then
|
|
res_o <= div_res;
|
res_o <= div_res;
|
else
|
when others => -- cp_op_divu_c | cp_op_remu_c
|
res_o <= rs1_i;
|
res_o <= div_sign_comp_in;
|
end if;
|
|
when others => -- cp_op_remu_c
|
|
res_o <= remainder;
|
|
end case;
|
end case;
|
else
|
else
|
res_o <= (others => '0');
|
res_o <= (others => '0');
|
end if;
|
end if;
|
end process operation_result;
|
end process operation_result;
|