OpenCores
URL https://opencores.org/ocsvn/serial_div_uu/serial_div_uu/trunk

Subversion Repositories serial_div_uu

[/] [serial_div_uu/] [trunk/] [serial_divide_uu.vhd] - Rev 7

Compare with Previous | Blame | View Log

-----------------------------------------------------------------------------
-- serial_divide_uu.v  -- Serial division module
--
--
-- Description: See description below (which suffices for IP core
--                                     specification document.)
--
-- Copyright (C) 2002 John Clayton and OPENCORES.ORG (this Verilog version)
--
-- 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 source file is free software; you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as published
-- by the Free Software Foundation;  either version 2.1 of the License, or
-- (at your option) any later version.
--
-- This source is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-- FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-- License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this source.
-- If not, download it from http:--www.opencores.org/lgpl.shtml
--
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date  : Jan. 30, 2003
-- Update: Jan. 30, 2003  Copied this file from "vga_crosshair.v"
--                        Stripped out extraneous stuff.
-- Update: Mar. 14, 2003  Added S_PP parameter, made some simple changes to
--                        implement quotient leading zero "skip" feature.
-- Update: Mar. 24, 2003  Updated comments to improve readability.
-- Update: Jul. 29, 2009  Verilog to VHDL translation (by David Sala)
--
-------------------------------------------------------------------------------
-- Description:
--
-- This module performs a division operation serially, producing one bit of the
-- answer per clock cycle.  The dividend and the divisor are both taken to be
-- unsigned quantities.  The divider is conceived as an integer divider (as
-- opposed to a divider for fractional quantities) but the user can configure
-- the divider to divide fractional quantities as long as the position of the
-- binary point is carefully monitored.
--
-- The widths of the signals are configurable by parameters, as follows:
--
-- M_PP = Bit width of the dividend
-- N_PP = Bit width of the divisor
-- R_PP = Remainder bits desired
-- S_PP = Skipped quotient bits
--
-- The skipped quotient bits parameter provides a way to prevent the divider
-- from calculating the full M_PP+R_PP output bits, in case some of the leading
-- bits are already known to be zero.  This is the case, for example, when
-- dividing two quantities to obtain a result that is a fraction between 0 and 1
-- (as when measuring PWM signals).  In that case the integer portion of the
-- quotient is always zero, and therefore it need not be calculated.
--
-- The divide operation is begun by providing a pulse on the divide_i input.
-- The quotient is provided (M_PP+R_PP-S_PP) clock cycles later.
-- The divide_i pulse stores the input parameters in registers, so they do
-- not need to be maintained at the inputs throughout the operation of the module.
-- If a divide_i pulse is given to the serial_divide_uu module during the time
-- when it is already working on a previous divide operation, it will abort the
-- operation it was doing, and begin working on the new one.
--
-- The user is responsible for treating the results correctly.  The position
-- of the binary point is not given, but it is understood that the integer part
-- of the result is the M_PP most significant bits of the quotient output.
-- The remaining R_PP least significant bits are the fractional part.
--
-- This is illustrated graphically:
--
--     [ M_PP bits ][    R_PP bits]
--     [ S_PP bits    ][quotient_o]
--
-- The quotient will consist of whatever bits are left after removing the S_PP
-- most significant bits from the (M_PP+R_PP) result bits.
--
-- Attempting to divide by zero will simply produce a result of all ones.
-- This core is so simple, that no checking for this condition is provided.
-- If the user is concerned about a possible divide by zero condition, he should
-- compare the divisor to zero and flag that condition himself!
--
-- The COUNT_WIDTH_PP parameter must be sized so that 2^COUNT_WIDTH_PP-1 is >=
-- M_PP+R_PP-S_PP-1.  The unit terminates the divide operation when the count
-- is equal to M_PP+R_PP-S_PP-1.
--
-- The HELD_OUTPUT_PP parameter causes the unit to keep its output result in
-- a register other than the one which it uses to compute the quotient.  This
-- is useful for applications where the divider is used repeatedly and the
-- previous divide result (quotient) must be stable during the computation of the
-- next divide result.  Using the additional output register does incur some
-- additional utilization of resources.
--
-------------------------------------------------------------------------------
library ieee;
 
use ieee.std_logic_1164.all;
use ieee.std_logic_signed.all;
use ieee.numeric_std.all;
use ieee.math_real.log;
use ieee.math_real.ceil;
 
 
entity serial_divide_uu is
  generic ( M_PP : integer := 16;           -- Size of dividend
            N_PP : integer := 8;            -- Size of divisor
            R_PP : integer := 0;            -- Size of remainder
            S_PP : integer := 0;            -- Skip this many bits (known leading zeros)
--            COUNT_WIDTH_PP : integer := 5;  -- 2^COUNT_WIDTH_PP-1 >= (M_PP+R_PP-S_PP-1)
            HELD_OUTPUT_PP : integer := 0); -- Set to 1 if stable output should be held
                                            -- from previous operation, during current
                                            -- operation.  Using this option will increase
                                            -- the resource utilization (costs extra d-flip-flops.)
    port(   clk_i      : in  std_logic;
            clk_en_i   : in  std_logic;
            rst_i      : in  std_logic;
            divide_i   : in  std_logic;
            dividend_i : in  std_logic_vector(M_PP-1 downto 0);
            divisor_i  : in  std_logic_vector(N_PP-1 downto 0);
            quotient_o : out std_logic_vector(M_PP+R_PP-S_PP-1 downto 0);
            done_o     : out std_logic
    );
end serial_divide_uu;
 
 
architecture behavior of serial_divide_uu is
 
   constant COUNT_WIDTH_PP    : integer := integer(ceil(log(real(M_PP+R_PP-S_PP),2.0)));
 
   signal done_s         : std_logic;
 
-- Internal signal declarations
   signal grand_dividend : std_logic_vector(M_PP+R_PP-1 downto 0)     ;
   signal grand_divisor  : std_logic_vector(M_PP+N_PP+R_PP-2 downto 0);
   signal quotient       : std_logic_vector(M_PP+R_PP-S_PP-1 downto 0);
   signal quotient_reg   : std_logic_vector(M_PP+R_PP-1 downto 0)     ;
   signal divide_count   : std_logic_vector(COUNT_WIDTH_PP-1 downto 0);
 
   signal subtract_node  : std_logic_vector(M_PP+N_PP+R_PP-1 downto 0); -- Subtract node has extra "sign" bit
   signal quotient_node  : std_logic_vector(M_PP+R_PP-1 downto 0)     ; -- Shifted version of quotient
   signal divisor_node   : std_logic_vector(M_PP+N_PP+R_PP-2 downto 0); -- Shifted version of grand divisor
 
begin
 
   done_o <= done_s;
 
----------------------------------------------------------------------------
-- Module code
 
   P_SERIAL_DIVIDING_MODULE: process (clk_i)
      begin
         if rising_edge(clk_i) then
            if rst_i='1' then
                grand_dividend <= (others=>'0');
                grand_divisor <= (others=>'0');
                divide_count <= (others=>'0');
                quotient <= (others=>'0');
                done_s <= '0';
            elsif clk_en_i='1' then
                done_s <= '0';
                if divide_i='1' then       -- Start a new division
                    quotient <= (others=>'0');
                    divide_count <= (others=>'0');
                    -- dividend placed initially so that remainder bits are zero...
                    grand_dividend <= (others=>'0');
                    grand_dividend (M_PP+R_PP-1 downto R_PP)<= dividend_i;
                    -- divisor placed initially for a 1 bit overlap with dividend...
                    -- But adjust it back by S_PP, to account for bits that are known
                    -- to be leading zeros in the quotient.
                    grand_divisor <= (others=>'0');
                    grand_divisor (M_PP+N_PP+R_PP-2 downto M_PP+R_PP-1) <= divisor_i;
                elsif (divide_count = M_PP+R_PP-S_PP-1) then
                    done_s <= '1';                    -- Indicate done, just sit
                    if done_s='0' then
                        quotient <= quotient_node;      -- final shift...
                        quotient_reg <= quotient_node;  -- final shift (held output)
                    end if;
                else                -- Division in progress
                    -- If the subtraction yields a positive result, then store that result
                    if subtract_node(M_PP+N_PP+R_PP-1)='0' then
                        grand_dividend <= subtract_node(M_PP+R_PP-1 downto 0);
                    end if;
                    -- If the subtraction yields a positive result, then a 1 bit goes into
                    -- the quotient, via a shift register
                    quotient <= quotient_node;
                    -- shift the grand divisor to the right, to cut it in half next clock cycle
                    grand_divisor <= divisor_node;
                    -- Advance the counter
                    divide_count <= divide_count + 1;
                end if;
            end if;
         end if;
     end process;
 
    subtract_node <= ('0' & grand_dividend) - ('0' & grand_divisor);
    quotient_node <= quotient(M_PP+R_PP-S_PP-2 downto 0) & not(subtract_node(M_PP+N_PP+R_PP-1));
    divisor_node  <= '0' & grand_divisor(M_PP+N_PP+R_PP-2 downto 1);
    quotient_o    <= quotient when HELD_OUTPUT_PP = 0 else quotient_reg;
 
end behavior;
 

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.