OpenCores
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; 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.