Search code examples
vhdlalu

Issues in compact 1-bit ALU behavior


I tried to write a compact code for a 1-bit ALU that implements logic operations, a full adder, and a full subtractor. The compilation looks fine, but it does not assert the message "Test done." at the end of the testbench. Moreover, the change of logic values in variables, such as A, B and F that should lead to error in the testbench are unvariedness to the program as it does not report any error. Something is certainly wrong in the main design, but I couldn't find the problem.

In the testbench, I just tested some cases of each function.

library IEEE;
use IEEE.std_logic_1164.all;

entity ALU is
port(A,B :  in bit;                         -- operands
    S :       in bit_vector(2 downto 0);  
    F:        out bit;                      -- output
    carryIn:  in bit;   
    carryOut: out bit);
end ALU;


architecture behavior of ALU is
begin 
    process(S)
    begin   
    case (S) is
    when "000" => if carryIn = '1' then F <= A XOR B XOR carryIn;       -- Full Adder
                                        carryOut <= (A AND B) OR (carryIn AND A) OR (carryIn AND B);  
                                        end if;
    when "001" => if carryIn = '1' then F <=  (A XOR B) XOR carryIn;    -- Full Subtractor
                                        carryOut <= ((NOT A) AND (B OR carryIn)) OR (B AND carryIn);  
                                        end if; 
    when "010" => F <= A AND B;
    when "011" => F <= A OR B;
    when "100" => F <= A NAND B;
    when "101" => F <= A NOR B;
    when "110" => F <= A XOR B;
    when "111" => F <= A XNOR B;
    end case;   
    end process;

end behavior ;

Testbench

library IEEE;
use IEEE.std_logic_1164.all;

entity testbench is             
end testbench;

architecture tb of testbench is
component ALU is
port(A,B :    in bit;                                                        
    S :       in bit_vector(2 downto 0);     
    F:        out bit;                        
    carryIn:  in bit;   
    carryOut: out bit);
end component;

signal A, B, F, carryIn, carryOut: bit;
signal   S : bit_vector(2 downto 0);

begin
DUT: ALU port map (A => A, B => B, F => F, carryIn => carryIn, carryOut => carryOut, S => S);
process
begin

-- AND
S <= "010"; 
A <= '0';
B <= '0';
carryIn <= '0';
assert(F ='0' and carryOut = '0') report "Fail AND1" severity error;
wait;
S <= "010"; 
A <= '0';
B <= '1';
carryIn <= '0';
assert(F ='0' and carryOut = '0') report "Fail AND2" severity error;
wait;

-- OR
S <= "011"; 
A <= '0';
B <= '0';
carryIn <= '0';
assert(F ='0' and carryOut = '0') report "Fail OR1" severity error;
wait;
S <= "011"; 
A <= '0';
B <= '1';
carryIn <= '0';
assert(F ='1' and carryOut = '0') report "Fail OR2" severity error;
wait;

-- NAND
S <= "100"; 
A <= '0';
B <= '0';
carryIn <= '0';
assert(F ='1' and carryOut = '0') report "Fail NAND1" severity error;
wait;
S <= "100"; 
A <= '1';
B <= '1';
carryIn <= '0';
assert(F ='0' and carryOut = '0') report "Fail NAND2" severity error;
wait;

-- NOR
S <= "101"; 
A <= '0';
B <= '0';
carryIn <= '0';
assert(F ='1' and carryOut = '0') report "Fail NOR1" severity error;
wait;
S <= "101"; 
A <= '1';
B <= '0';
carryIn <= '0';
assert(F ='0' and carryOut = '0') report "Fail NOR2" severity error;
wait;

-- XOR
S <= "110"; 
A <= '0';
B <= '1';
carryIn <= '0';
assert(F ='1' and carryOut = '0') report "Fail XOR1" severity error;
wait;
S <= "110"; 
A <= '1';
B <= '0';
carryIn <= '0';
assert(F ='1' and carryOut = '0') report "Fail XOR2" severity error;
wait;


-- XNOR
S <= "111"; 
A <= '0';
B <= '1';
carryIn <= '0';
assert(F ='0' and carryOut = '0') report "Fail XNOR1" severity error;
wait;
S <= "111"; 
A <= '1';
B <= '0';
carryIn <= '0';
assert(F ='0' and carryOut = '0') report "Fail XNOR2" severity error;
wait;

-- Full Adder
S <= "000"; 
A <= '0';
B <= '0';
carryIn <= '1';
assert(F ='1' and carryOut = '0') report "Fail FullAdder1" severity error;
wait;
S <= "000"; 
A <= '1';
B <= '1';
carryIn <= '1';
assert(F ='1' and carryOut = '1') report "Fail FullAdder2" severity error;
wait;

-- Full Subtractor
S <= "000"; 
A <= '0';
B <= '1';
carryIn <= '1';
assert(F ='0' and carryOut = '1') report "Fail Subtractor1" severity error;
wait;
S <= "000"; 
A <= '1';
B <= '1';
carryIn <= '1';
assert(F ='1' and carryOut = '1') report "Fail Subtractor2" severity error;
wait;

assert false report "Test done." severity note;
wait;
end process; 
end tb;

Edit

library IEEE;
use IEEE.std_logic_1164.all;

entity testbench is             
end testbench;

architecture tb of testbench is
component ALU is
port(A,B :    in bit;                                                        
    S :       in bit_vector(2 downto 0);     
    F:        out bit;                        
    carryIn:  in bit;   
    carryOut: out bit);
end component;

signal A, B, F, carryIn, carryOut: bit;
signal   S : bit_vector(2 downto 0);

begin
DUT: ALU port map (A => A, B => B, F => F, carryIn => carryIn, carryOut => carryOut, S => S);
process
begin

-- AND
S <= "010"; 
A <= '0';
B <= '0';
wait for 20 ns;
assert(F ='0') report "Fail AND1" severity error;

wait for 20 ns;
S <= "010"; 
A <= '0';
B <= '1';
assert(F ='0') report "Fail AND2" severity error;
wait for 20 ns;

-- OR
S <= "011"; 
A <= '0';
B <= '0';
wait for 20 ns;
assert(F ='0') report "Fail OR1" severity error;

wait for 20 ns;

S <= "011"; 
A <= '0';
B <= '1';
wait for 20 ns;
assert(F ='1') report "Fail OR2" severity error;
wait for 20 ns;

-- NAND
S <= "100"; 
A <= '0';
B <= '0';
wait for 20 ns;
assert(F ='1') report "Fail NAND1" severity error;

wait for 20 ns;
S <= "100"; 
A <= '1';
B <= '1';
wait for 20 ns;
assert(F ='0') report "Fail NAND2" severity error;
wait for 20 ns;

-- NOR
S <= "101"; 
A <= '0';
B <= '0';
wait for 20 ns;
assert(F ='1') report "Fail NOR1" severity error;

wait for 20 ns;
S <= "101"; 
A <= '1';
B <= '0';
wait for 20 ns;
assert(F ='0') report "Fail NOR2" severity error;
wait for 20 ns;

-- XOR
S <= "110"; 
A <= '0';
B <= '1';
wait for 20 ns;
assert(F ='1') report "Fail XOR1" severity error;

wait for 20 ns;
S <= "110"; 
A <= '1';
B <= '0';
wait for 20 ns;
assert(F ='1') report "Fail XOR2" severity error;
wait for 20 ns;


-- XNOR
S <= "111"; 
A <= '0';
B <= '1';
wait for 20 ns;
assert(F ='0') report "Fail XNOR1" severity error;
wait for 20 ns;
S <= "111"; 
A <= '1';
B <= '0';
wait for 20 ns;
assert(F ='0') report "Fail XNOR2" severity error;
wait for 20 ns;

-- Full Adder
S <= "000"; 
A <= '0';
B <= '0';
carryIn <= '1';
wait for 20 ns;
assert(F ='1') report "Fail FullAdder1" severity error;
wait for 20 ns;
S <= "000"; 
A <= '1';
B <= '1';
carryIn <= '1';
wait for 20 ns;
assert(F ='1') report "Fail FullAdder2" severity error;
wait for 20 ns;

-- Full Subtractor
S <= "000"; 
A <= '0';
B <= '1';
carryIn <= '1';
wait for 20 ns;
assert(F ='0') report "Full Subtractor 1" severity error;
wait for 20 ns;
S <= "000"; 
A <= '1';
B <= '1';
carryIn <= '1';
wait for 20 ns;
assert(F ='1') report "Full Subtractor 2" severity error;

wait for 20 ns;
assert false report "Test done." severity note;

wait;

end process; 
end tb;

In the current program, I have got the following execution errors:

# EXECUTION:: ERROR  : Fail OR2
# EXECUTION:: Time: 120 ns,  Iteration: 0,  Instance: /testbench,  
Process: line__21.
# EXECUTION:: ERROR  : Fail NAND2
# EXECUTION:: Time: 200 ns,  Iteration: 0,  Instance: /testbench,  
Process: line__21.
# EXECUTION:: ERROR  : Fail NOR2
# EXECUTION:: Time: 280 ns,  Iteration: 0,  Instance: /testbench,  
Process: line__21.
# EXECUTION:: ERROR  : Fail Subtrator1
# EXECUTION:: Time: 560 ns,  Iteration: 0,  Instance: /testbench,  
Process: line__21.
# EXECUTION:: NOTE   : Test done.
# EXECUTION:: Time: 620 ns,  Iteration: 0,  Instance: /testbench,  
Process: line__21.
# KERNEL: Simulation has finished. There are no more test vectors to 
simulate.
# VSIM: Simulation has finished.

It seems to be somethin related to the main program, but I am not sure why it shows these errors because the logic in the testbench seems correct.

I also changed the following part

when "000" => F <= A XOR B XOR carryIn;       -- Full Adder
              carryOut <= (A AND B) OR (carryIn 
AND A) OR (carryIn AND B);  
                                   
when "001" => F <=  (A XOR B) XOR carryIn;    -- Full Subtractor
              carryOut <= ((NOT A) AND (B OR 
carryIn)) OR (B AND carryIn);  

and then get the carryIn in the testbench.


Solution

  • First, you have issues with your process in architecture behavior of ALU:

    • The process shall be sensitive to all inputs, not just S:

      process(S, A, B, carryIn)
      

      This way, if S does not change but the input data change, the outputs are re-computed instead of keeping their previous value.

    • The carryOut output shall always be assigned a value, not just when it is an arithmetic operation. Else, when you will try to synthesize this you will have latches inferred to store its value.

    • For the additions and subtractions you model only the case where carryIn = '1'. What about the other case?

    Example of solution to all problems:

    ```vhdl
    process(S, A, B, carryIn)
    begin
      carryOut <= '0';
      case (S) is
        when "000"  => -- Full Adder
          F <= A XOR B XOR carryIn;
          carryOut <= (A AND B) OR (carryIn AND A) OR (carryIn AND B);  
        when "001"  => -- Full Subtractor
          F <= A XOR B XOR carryIn;
          carryOut <= ((NOT A) AND (B OR carryIn)) OR (B AND carryIn);  
        when "010"  => F <= A AND B;
        when "011"  => F <= A OR B;
        when "100"  => F <= A NAND B;
        when "101"  => F <= A NOR B;
        when "110"  => F <= A XOR B;
        when others => F <= A XNOR B;
      end case;   
    end process;
    ```
    

    (note the use of when others to guarantee that we do not forget a single case).

    Then you also have issues with your simulation environment. You wrote:

    process
    begin
      -- AND
      S <= "010"; 
      A <= '0';
      B <= '0';
      carryIn <= '0';
      assert(F ='0' and carryOut = '0') report "Fail AND1" severity error;
      wait;
      ...
    

    This will not work for two reasons: you check the effect of your test vector before it had any effect. As no time elapses between the signal assignments and the assertion, the F and carryOut values did not change yet. You check the values they had before the changes of the inputs.

    The second issue is that wait; means wait forever. It definitely suspends your process. The other statements will never be executed and the simulation will stop because in your case there is nothing else to be done.

    To solve these two issues write:

    process
    begin
      -- AND
      S <= "010"; 
      A <= '0';
      B <= '0';
      carryIn <= '0';
      wait for 1 ns;
      assert (F = '0') and (carryOut = '0') report "Fail AND1" severity error;
      S <= "010";
      A <= '0';
      B <= '1';
      carryIn <= '0';
      wait for 1 ns;
      assert (F = '0') and (carryOut = '0') report "Fail AND2" severity error;
      ...
    

    Note that you could simplify a bit by not re-assigning the signals that don't change:

    process
    begin
      -- AND
      S <= "010"; 
      A <= '0';
      B <= '0';
      carryIn <= '0';
      wait for 1 ns;
      assert (F = '0') and (carryOut = '0') report "Fail AND1" severity error;
      B <= '1';
      wait for 1 ns;
      assert (F = '0') and (carryOut = '0') report "Fail AND2" severity error;
      ...
    

    One last note: VHDL is a high level programming language. You could use some of its features to simplify your testbench. For example you could use the ieee.numeric_bit_unsigned package that allows arithmetic operations on vectors and conversions between vectors and integers. Something like (not tested):

    ...
    use ieee.numeric_bit_unsigned.all;
    ...
    process
    begin
      for i in 0 to 7 loop -- loop over the 8 possible values of (A,B,carryIn)
        (A, B, carryIn) <= to_bitvector(i, 3);
        -- AND
        S <= "010";
        wait for 1 ns;
        assert F = ((A and B)) and (carryOut = '0') report "Fail AND" severity error;
        -- OR
        S <= "011";
        wait for 1 ns;
        assert (F = (A or B)) and (carryOut = '0') report "Fail OR" severity error;
        ...
        ...
        -- Full Adder
        S <= "000";
        wait for 1 ns;
        assert carryOut & f = ('0' & A) + ('0' & B) + carryIn
          report "Fail FullAdder" severity error;
        -- full subtractor
        S <= "001";
        wait for 1 ns;
        assert carryOut & F = ('0' & A) - (('0' & B) + carryIn)
          report "Fail FullSubtractor" severity error;
      end loop;
      assert false report "Test done." severity note;
      wait;
    end process;