URL
https://opencores.org/ocsvn/rv01_riscv_core/rv01_riscv_core/trunk
Subversion Repositories rv01_riscv_core
[/] [rv01_riscv_core/] [trunk/] [VHDL/] [RV01_divider_r2.vhd] - Rev 2
Compare with Previous | Blame | View Log
----------------------------------------------------------------- -- -- ----------------------------------------------------------------- -- -- -- Copyright (C) 2015 Stefano Tonello -- -- -- -- This source file may be used and distributed without -- -- restriction provided that this copyright statement is not -- -- removed from the file and that any derivative work contains -- -- the original copyright notice and the associated disclaimer.-- -- -- -- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY -- -- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -- -- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -- -- FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR -- -- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -- -- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -- -- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -- -- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -- -- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -- -- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -- -- OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -- -- POSSIBILITY OF SUCH DAMAGE. -- -- -- ----------------------------------------------------------------- --------------------------------------------------------------- -- RISC-V Integer divider --------------------------------------------------------------- ------------------------------------------------------------------ -- Divider FSM ------------------------------------------------------------------ library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; entity RV01_DIVFSM is port( CLK_i : in std_logic; RST_i : in std_logic; STRT_i : in std_logic; STOP_i : in std_logic; DBZ_i : in std_logic; SOVF_i : in std_logic; DRLE_o : out std_logic; DDULE_o : out std_logic; DRULE_o : out std_logic; DDZLE_o : out std_logic; DRZLE_o : out std_logic; DDNLE_o : out std_logic; DRNLE_o : out std_logic; QTXLE_o : out std_logic; QV_o : out std_logic; BSY_o : out std_logic ); end RV01_DIVFSM; architecture ARC of RV01_DIVFSM is -- Note: a division operation requires the following steps: -- 1) DD sign check (and optional change) -- 2) DR sign check (and optional change) -- 3) DD leading 0's calculation -- 4) DR leading 0's calculation -- 5) DD normalization -- 6) DR normalization -- 7) unsigned division (up to 32 cycles) -- 8) result sign adjustment type DIV_STATE is ( DS_DDSA, DS_DRSA, DS_DDLZ, DS_DRLZ, DS_DDNR, DS_DRNR, DS_DIVX, DS_QTSA, DS_WAIT1, DS_WAIT2 ); signal DRLE : std_logic; signal DDULE : std_logic; signal DRULE : std_logic; signal DDZLE : std_logic; signal DRZLE : std_logic; signal DDNLE : std_logic; signal DRNLE : std_logic; signal QTXLE : std_logic; signal QV : std_logic; signal BSY : std_logic; signal DS,DS_q : DIV_STATE; begin process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then DS_q <= DS_DDSA; else DS_q <= DS; end if; end if; end process; process(DS_q,DBZ_i,SOVF_i,STRT_i,STOP_i) begin DRLE <= '0'; DDULE <= '0'; DRULE <= '0'; DDZLE <= '0'; DRZLE <= '0'; DDNLE <= '0'; DRNLE <= '0'; QTXLE <= '0'; QV <= '0'; BSY <= '0'; case DS_q is -- DD sign adjustment when DS_DDSA => if(STRT_i = '1') then if(DBZ_i = '1' or SOVF_i = '1') then -- division by zero or signed overflow, end operation DDULE <= '1'; -- latch DD DS <= DS_WAIT1; else DDULE <= '1'; -- latch unsigned DD DRLE <= '1'; -- latch signed DR DS <= DS_DRSA; end if; else DS <= DS_DDSA; end if; -- wait state when DS_WAIT1 => BSY <= '1'; DS <= DS_WAIT2; -- wait state when DS_WAIT2 => --BSY <= '1'; QV <= '1'; -- flag operation end DS <= DS_DDSA; -- DR sign adjustment when DS_DRSA => BSY <= '1'; DRULE <= '1'; -- latch unsigned DR DS <= DS_DDLZ; -- DD leading 0's calculation when DS_DDLZ => BSY <= '1'; DDZLE <= '1'; -- latch LZD(DD) DS <= DS_DRLZ; -- DR leading 0's calculation when DS_DRLZ => BSY <= '1'; DRZLE <= '1'; -- latch LZD(DR) DS <= DS_DDNR; -- DD normalization (left shift) when DS_DDNR => BSY <= '1'; DDNLE <= '1'; -- latch normalized DD DS <= DS_DRNR; -- DR normalization (left shift) when DS_DRNR => BSY <= '1'; DRNLE <= '1'; -- latch normalized DR DS <= DS_DIVX; -- division loop when DS_DIVX => QTXLE <= not(STOP_i); -- latch intermediate quotient BSY <= '1'; if(STOP_i = '1') then DS <= DS_QTSA; else DS <= DS_DIVX; end if; ---- quotient sign adjustment when DS_QTSA => --BSY <= '1'; QV <= '1'; DS <= DS_DDSA; -- invalid state when others => DS <= DS_DDSA; end case; end process; DRLE_o <= DRLE; DDULE_o <= DDULE; DRULE_o <= DRULE; DDZLE_o <= DDZLE; DRZLE_o <= DRZLE; DDNLE_o <= DDNLE; DRNLE_o <= DRNLE; QTXLE_o <= QTXLE; QV_o <= QV; BSY_o <= BSY; end ARC; ------------------------------------------------------------------ -- Divider ------------------------------------------------------------------ library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; library WORK; use WORK.RV01_CONSTS_PKG.all; use WORK.RV01_TYPES_PKG.all; use WORK.RV01_OP_PKG.all; use WORK.RV01_FUNCS_PKG.all; use WORK.RV01_ARITH_PKG.all; use WORK.RV01_DIV_FUNCS_PKG.all; entity RV01_DIVIDER_R2 is port( CLK_i : in std_logic; RST_i : in std_logic; STRT_i : in std_logic; SU_i : in std_logic; -- '0' -> unsigned div., '1' -> signed div. QS_i : in std_logic; -- '1' -> quotient, '0' -> reminder DD_i : in SDWORD_T; DR_i : in SDWORD_T; CLRD_i : in std_logic; CLRV_i : in std_logic; Q_o : out SDWORD_T; QV_o : out std_logic; BSY_o : out std_logic ); end RV01_DIVIDER_R2; architecture ARC of RV01_DIVIDER_R2 is -- zero constant constant ZERO : SDWORD_T := (others => '0'); -- min. signed integer constant constant MIN_SINT : SDWORD_T := (SDLEN-1 => '1',others => '0'); -- -1 constant constant MINUS_ONE : SDWORD_T := (others => '1'); constant L2SDLEN : natural := log2(SDLEN); component RV01_DIVFSM is port( CLK_i : in std_logic; RST_i : in std_logic; STRT_i : in std_logic; STOP_i : in std_logic; DBZ_i : in std_logic; SOVF_i : in std_logic; DRLE_o : out std_logic; DDULE_o : out std_logic; DRULE_o : out std_logic; DDZLE_o : out std_logic; DRZLE_o : out std_logic; DDNLE_o : out std_logic; DRNLE_o : out std_logic; QTXLE_o : out std_logic; QV_o : out std_logic; BSY_o : out std_logic ); end component; component RV01_LZD32 is generic( WIDTH : natural := 32 ); port( A_i : in std_logic_vector(WIDTH-1 downto 0); CNT_o : out std_logic_vector(6-1 downto 0) ); end component; signal DRLE,DDULE,DRULE,DDZLE,DRZLE,DDNLE,DRNLE,QTXLE,QV,BSY : std_logic; signal DBZ,SOVF,SGNDD,SGNDR,SGNOP1 : std_logic; signal DBZ_q,SOVF_q : std_logic; signal UOP1,OP2,OP3 : unsigned(SDLEN-1 downto 0); signal SOP3 : unsigned(SDLEN-1 downto 0); signal LZD : std_logic_vector(log2(SDLEN+1)-1 downto 0); -- see note below signal SHFT : unsigned(log2(SDLEN+1)-1 downto 0); -- see note below signal STOP : std_logic; signal IRST : std_logic; signal IDD : unsigned(SDLEN downto 0); signal IQT : unsigned(SDLEN-1 downto 0); signal SGNDD_q,SGNDR_q : std_logic; signal LZDD_q,LZDR_q : unsigned(log2(SDLEN+1)-1 downto 0); -- see note below signal RM_SHFT,RM_SHFT_q : unsigned(log2(SDLEN+1)-1 downto 0); signal CNT_q : unsigned(L2SDLEN downto 0); signal IDD_q : unsigned(SDLEN downto 0); signal IDR_q,IQT_q : unsigned(SDLEN-1 downto 0); signal QT,QT_q : signed(SDLEN-1 downto 0); signal URM : unsigned(SDLEN downto 0); signal RM,RM_q : signed(SDLEN-1 downto 0); signal Q_q : signed(SDLEN-1 downto 0); signal SU_q,QS_q,QV_q : std_logic; -- Note: -- leading zero's detector (shifter) with data input of SDLEN N -- generates a count output (needs an shift amount input) in the -- range 0:N. When N is a power-of-2, output (input) variable SDLEN -- must be 1 bit greater than log2(N). This special case can be managed -- setting SDLEN to log2(N+1) for all cases. -- synthesis translate_off signal CHK_QT : unsigned(SDLEN-1 downto 0); signal CHK_RM : unsigned(SDLEN-1 downto 0); signal QT_ERROR : std_logic; -- synthesis translate_on begin --------------------------------------- -- Notes: -- -- Division execution consists of the -- following steps: -- -- 1) dividend (DD) and divisor (DR are -- converted to unsigned numbers, storing -- their original sign in SGNDD_q and SGNDR_q. -- -- 2) unsigned DD and DR are normalized, -- to insure their MSb is '1'. -- -- 3) actual division is performed using -- the unsigned and normalized DD and DR, -- generating a 32-bit quotient/reminder. -- -- 4) if the quotient number of integer bits -- is negative, the quotient is right shifted -- to force it to zero. -- -- 5) the quotient sign is adjusted according -- to SGNDD_q and SGNDR_q. --------------------------------------- IRST <= RST_i or CLRD_i; -- divide-by-zero flag DBZ <= '1' when (DR_i = ZERO) else '0'; -- signed overflow flag SOVF <= SU_i when (DD_i = MIN_SINT and DR_i = MINUS_ONE) else '0'; U_DIVFSM : RV01_DIVFSM port map( CLK_i => CLK_i, RST_i => IRST, --RST_i, STRT_i => STRT_i, STOP_i => STOP, DBZ_i => DBZ, SOVF_i => SOVF, DRLE_o => DRLE, DDULE_o => DDULE, DRULE_o => DRULE, DDZLE_o => DDZLE, DRZLE_o => DRZLE, DDNLE_o => DDNLE, DRNLE_o => DRNLE, QTXLE_o => QTXLE, QV_o => QV, BSY_o => BSY ); --------------------------------------- -- Convert DD/DR to unsigned --------------------------------------- -- DD sign SGNDD <= DD_i(SDLEN-1) when SU_i = '1' else '0'; -- DR sign (note the use of SU_q) SGNDR <= IDR_q(SDLEN-1) when SU_q = '1' else '0'; process(DDULE,SGNDD,SGNDR,DD_i,IDR_q) variable SGN : std_logic; variable OP1 : unsigned(SDLEN-1 downto 0); begin -- extract DD/DR sign and extend DD/DR to SDLEN+1 bits if(DDULE = '1') then SGN := SGNDD; OP1 := to_unsigned(DD_i); else SGN := SGNDR; OP1 := IDR_q(SDLEN-1 downto 0); end if; -- change DD/DR sign, if needed if(SGN = '1') then UOP1 <= not(OP1)+1; else UOP1 <= OP1; end if; end process; --------------------------------------- -- Calculate unsigned DD/DR leading 0's number --------------------------------------- -- This information is needed to normalize DD/DR and -- is stored into LZDD_q/LZDR_q register. -- Same hardware is used for both operands in different cycles. -- select LZD unit input OP2 <= IDD_q(SDLEN-1 downto 0) when (DDZLE = '1') else IDR_q; U_LZD : RV01_LZD32 generic map( WIDTH => SDLEN ) port map( A_i => to_std_logic_vector(OP2), CNT_o => LZD ); --------------------------------------- -- Normalize unsigned DD/DR --------------------------------------- -- Unsigned DD/DR is normalized by left-shifting it -- by the amount stored in LZDD_q/LZDR_q, result is -- stored into IDD_q/IDR_q register. -- Same hardware is used for both operands in different cycles. -- select shifter inputs SHFT <= LZDD_q when DDNLE = '1' else LZDR_q; OP3 <= IDD_q(SDLEN-1 downto 0) when DDNLE = '1' else IDR_q; SOP3 <= div_shift_left32(OP3,to_integer(SHFT)); --------------------------------------- -- Division core loop --------------------------------------- -- division cycles counter process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(DRNLE = '1') then if(LZDR_q >= LZDD_q) then CNT_q <= (LZDR_q - LZDD_q + 1); else CNT_q <= (others => '0'); end if; else CNT_q <= CNT_q-1; end if; end if; end process; -- end division flag STOP <= '1' when (CNT_q = 0) else '0'; -- This divider implement a basic restoring division -- algorithm. -- IDD_q register is (SDLEN+1)-bit long in order to -- accomodate any possible result of 1-bit left-shift -- performed on IDD_q itself or DIFF (see below). process(IDD_q,IDR_q,IQT_q) variable XIDD,XIDR,DIFF : unsigned(SDLEN+1 downto 0); variable SGN : std_logic; begin -- zero-extend IDD by 1 bit XIDD := '0' & IDD_q; -- zero-extend IDR by 2 bits XIDR := "00" & IDR_q; -- perform subtraction DIFF := XIDD - XIDR; -- difference sign SGN := DIFF(SDLEN+1); -- updated dividend value if(SGN = '1') then IDD <= (IDD_q sll 1); else IDD <= (DIFF(SDLEN downto 0) sll 1); end if; -- updated quotient IQT <= IQT_q(SDLEN-2 downto 0) & not(SGN); end process; --------------------------------------- -- Quotient/Reminder sign adjustment --------------------------------------- QT <= to_signed(IQT_q) when (SGNDD_q = SGNDR_q) else -to_signed(IQT_q); RM_SHFT <= LZDR_q+1 when (LZDR_q >= LZDD_q) else LZDD_q; --URM <= div_shift_right32(IDD_q(SDLEN-1 downto 0),to_integer(LZDR_q)+1); -- to be fixed!!!! --URM <= div_shift_right32(IDD_q(SDLEN-1 downto 0),to_integer(RM_SHFT_q)); URM <= div_shift_right32(IDD_q,to_integer(RM_SHFT_q)); RM <= to_signed(URM(SDLEN-1 downto 0)) when (SGNDD_q = '0') else -to_signed(URM(SDLEN-1 downto 0)); --------------------------------------- -- Output --------------------------------------- BSY_o <= BSY; Q_o <= Q_q; QV_o <= QV_q; --------------------------------------- -- Registers --------------------------------------- -- DD/DR sign register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(STRT_i = '1') then SU_q <= SU_i; QS_q <= QS_i; DBZ_q <= DBZ; SOVF_q <= SOVF; end if; end if; end process; -- DD/DR sign register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(DDULE = '1') then SGNDD_q <= SGNDD; end if; if(DRULE = '1') then SGNDR_q <= SGNDR; end if; end if; end process; -- DD register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(DDULE = '1') then if(SOVF = '1' or DBZ = '1') then IDD_q <= '0' & to_unsigned(DD_i); else IDD_q <= '0' & UOP1; end if; elsif(DDNLE = '1') then IDD_q <= '0' & SOP3; elsif(QTXLE = '1') then IDD_q <= IDD; end if; end if; end process; -- DR register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(DRLE = '1') then IDR_q <= to_unsigned(DR_i); elsif(DRULE = '1') then IDR_q <= UOP1; elsif(DRNLE = '1') then IDR_q <= SOP3; end if; end if; end process; -- DD leading 0's count register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(DDZLE = '1') then LZDD_q <= to_unsigned(LZD); end if; end if; end process; -- DR leading 0's count register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(DRZLE = '1') then LZDR_q <= to_unsigned(LZD); end if; end if; end process; -- reminder shift amount register -- (timing purpose only) process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(DDNLE = '1') then RM_SHFT_q <= RM_SHFT; end if; end if; end process; -- quotient register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(DRNLE = '1') then IQT_q <= to_unsigned(0,IQT_q'HIGH+1); elsif(QTXLE = '1') then IQT_q <= IQT; end if; end if; end process; -- end operation flag registers process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then --if(RST_i = '1' or CLRV_i = '1') then if(IRST = '1' or CLRV_i = '1') then QV_q <= '0'; elsif(QV = '1') then QV_q <= '1'; end if; end if; end process; process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(QV = '1') then if(QS_q = '1') then -- it's a division operation... if(DBZ_q = '1') then -- div-by-zero: set to result to all-1 Q_q <= MINUS_ONE; elsif(SOVF_q = '1') then -- signed overflow: set result to dividend Q_q <= to_signed(IDD_q(SDLEN-1 downto 0)); else -- regular result Q_q <= QT_q; end if; else -- it's a remainder operation... if(DBZ_q = '1') then -- div-by-zero: set to result to dividend Q_q <= to_signed(IDD_q(SDLEN-1 downto 0)); elsif(SOVF_q = '1') then -- signed overflow: set result to zero Q_q <= ZERO; else -- regular result Q_q <= RM_q; end if; end if; end if; end if; end process; -- final result and reminder registers process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then --if(QRLE = '1') then QT_q <= QT; RM_q <= RM; --end if; end if; end process; -- synthesis translate_off process(CLK_i) variable TMP1,TMP2 : unsigned(SDLEN*2-2 downto 0); begin if(CLK_i = '1' and CLK_i'event) then if(DRNLE = '1') then TMP1(SDLEN*2-2 downto SDLEN-1) := IDD_q(SDLEN-1 downto 0); TMP1(SDLEN-2 downto 0) := (others => '0'); TMP2 := (TMP1 / SOP3); CHK_QT <= TMP2(SDLEN-1 downto 0); --CHK_RM <= (TMP1 - TMP2(SDLEN-1 downto 0) * SOP3); end if; end if; end process; process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then QT_ERROR <= '0'; elsif(STOP = '1') then if(CHK_QT /= IQT_q) then QT_ERROR <= '1'; else QT_ERROR <= '0'; end if; end if; end if; end process; -- synthesis translate_on end ARC;