Search code examples
vhdl

Output is always zeros (quotient and remainder) in divider code VHDL


Output is always zeros (quotient and remainder) in the code shown below. Even if I assign value of b to remainder,it is giving 0. I have checked for many times but I am not able to understand what the issue is. While compiling, it is showing 2 warnings:

- Initial value of "b" depends on value of signal "divisor".

What is the problem?

-- DIVIDER

library ieee;  
use ieee.numeric_bit.all;  

entity unsigned_divider is  
port(
-- the two inputs  
dividend: in bit_vector(15 downto 0);  
divisor : in bit_vector(15 downto 0);  
-- the two outputs  
quotient : out bit_vector(15 downto 0);  
remainder : out bit_vector(15 downto 0)  
);  
end entity unsigned_divider;  

architecture behave of unsigned_divider is  

begin  
process  

variable a : bit_vector(15 downto 0):=dividend;  
variable b : bit_vector(15 downto 0):=divisor;  

variable p : bit_vector(15 downto 0):= (others => '0');  
variable i : integer:=0;  
begin  

for i in 0 to 15 loop  
p(15 downto 1) := p(14 downto 0);  
p(0) := a(15);  
a(15 downto 1) := a(14 downto 0);  
p := bit_vector(unsigned(p) -  unsigned(b));  

if(p(15) ='1') then  
a(0) :='0';  
p := bit_vector(unsigned(p) +  unsigned(b));  
else  
a(0) :='1';  
end if;  
wait for 1 ns;  

end loop;

quotient <= a after 1 ns;  
remainder <= p  after 1 ns;  

end process;
end behave;

Solution

  • You should have explicit assignments to the variables a and b inside the process statement part (as sequential signal assignments). The declarations:

            variable a : bit_vector(15 downto 0):=dividend;  
            variable b : bit_vector(15 downto 0):=divisor;  
    

    Should be:

            variable a : bit_vector(15 downto 0);  
            variable b : bit_vector(15 downto 0);  
    

    And in the process statement part (following the begin in the process):

            a := dividend;
            b := divisor;
    

    These overcome the issue natipar mentions, that the values are only assigned to a and b during initialization.

    Further should you desire to have a 1 ns delay you should have an explicit wait statement as the last sequential statement of the process statement process statement part:

    wait on dividend, divisor;
    

    These make your process statement look something like this (with indentation added):

        process  
    
            variable a : bit_vector(15 downto 0); -- := dividend;  
            variable b : bit_vector(15 downto 0); -- := divisor;  
    
            variable p : bit_vector(15 downto 0) :=  (others => '0');  
            variable i : integer := 0;  
        begin  
    
            a := dividend;
            b := divisor;
    
            for i in 0 to 15 loop  
                p(15 downto 1) := p(14 downto 0);  
                p(0) := a(15);  
                a(15 downto 1) := a(14 downto 0);  
                p := bit_vector(unsigned(p) -  unsigned(b));  
    
                if  p(15)  = '1'  then  
                    a(0) :='0';  
                    p := bit_vector(unsigned(p) +  unsigned(b));  
                else  
                    a(0) := '1';
                end if;  
                wait for 1 ns;  
    
            end loop;
    
            quotient <= a after 1 ns;  
            remainder <= p  after 1 ns;  
    
            wait on dividend, divisor;
    
        end process;
    

    (Note the space between the numeric literal and the units, required by IEEE Std 1076-2008, 15.3 Lexical elements, separators and delimiters paragraph 4, the last sentence "At least one separator is required between an identifier or an abstract literal and an adjacent identifier or abstract literal.", despite Modelsim not requiring it).

    Writing a simple testbench we find at least one error in your restoring division algorithm:

    entity unsigned_divider_tb is
    end entity;
    
    architecture foo of unsigned_divider_tb is
        signal dividend, divisor: bit_vector (15 downto 0) := (others => '0');
        signal quotient, remainder: bit_vector (15 downto 0);
    
        function to_string(inp: bit_vector) return string is
            variable image_str: string (1 to inp'length);
            alias input_str:  bit_vector (1 to inp'length) is inp;
        begin
            for i in input_str'range loop
                image_str(i) := character'VALUE(BIT'IMAGE(input_str(i)));
            end loop;
            return image_str;
        end;
    
    begin
    DUT:
        entity work.unsigned_divider
            port map (
                dividend,
                divisor,
                quotient,
                remainder
            );
    MONITOR:
        process (quotient, remainder)
        begin
            report "quotient = " & to_string (quotient) severity NOTE;
            report "remainder = " & to_string (remainder) severity NOTE;
        end process;
    
    end architecture;
    

    ghdl -a unsigned_divider.vhdl
    ghdl -e unsigned_divider_tb
    ghdl -r unsigned_divider_tb
    unsigned_divider.vhdl:83:9:@0ms:(report note): quotient = 0000000000000000
    unsigned_divider.vhdl:84:9:@0ms:(report note): remainder = 0000000000000000
    unsigned_divider.vhdl:83:9:@17ns:(report note): quotient = 1111111111111111
    unsigned_divider.vhdl:84:9:@17ns:(report note): remainder = 0000000000000000

    (And a note on interpretation, the transactions reported at time 0 ms are the default assignments performed as a result of elaboration).

    Your algorithm gives a wrong answer for division by 0.

    Adding a stimulus process to the testbench:

    STIMULUS:
        process
        begin
            wait for 20 ns;
            dividend <= x"ffff";
            divisor <= x"000f";
        end process;
    

    Shows it can get the right answer too:

    unsigned_divider.vhdl:83:9:@37ns:(report note): quotient = 0001000100010001
    unsigned_divider.vhdl:84:9:@37ns:(report note): remainder = 0000000000000000

    And with the testbench and added wait statements and assignments in the stimulus process you can explore further.

    I've always been a fan of non-restoring division myself, because the adds or subtracts take a clock in a clocked divider.