32 Bit ALU in VHDL Carry Out

PeterPanter picture PeterPanter · Jul 31, 2013 · Viewed 18.2k times · Source

I'm supposed to write a simple 32 Bit Alu in VHDL. Everything is working fine, except for two things. The ALU is supposed to have an carry out and overflow flag and I cant't figure out how to implement that.

First a general question. The circuit diagram shows that for subtraction the ALU inverts the subtrahend and adds "1" to create the negative equivalent of the input value in 2s-complement. Does this mean that I should work with unsigned values for the input? Or should I stick to std_logic_vector?

Since the carry out bit is the bit which does not "fit" into the result word, I tried to zero extend the Summands, create a temporary 33 bit Sum signal and then simply divide the result into Carry Out and actual sum. Unfortunately all I get when simulating is "UU...U" as output for the sum.( I did it as described here: https://en.wikibooks.org/wiki/VHDL_for_FPGA_Design/4-Bit_ALU)

And for the Overflow flag: Since the description of the ALU is behavioral I don't have access to any carries, which means I can't determine if an overflow occured by simply xoring the last two carries(assuming that the values are in 2s-complement, but I'm not quite sure in this point as my first question shows...). Is there another way to identify the overflow? Like simply turning the "An overflow occurs when..." rules one finds on the internet into if statements?

This is my code so far. This version is giving me "UUU...U" for the output when adding/ subtracting.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity ALU is
Port ( Clk : in STD_LOGIC;
          A : in  std_logic_vector (31 downto 0);
       B : in  std_logic_vector(31 downto 0);
       Y : out  std_logic_vector(31 downto 0);
       OP : in  std_logic_vector(2 downto 0);
          Nul   : out boolean;
       Cout : out  STD_LOGIC);
end ALU;

architecture Behavioral of ALU is

signal Smd0, Smd1, Sum : std_logic_vector (31 downto 0);
signal temp : std_logic_vector (32 downto 0);
signal Cry : STD_LOGIC;
signal snul : boolean;


begin

Smd0 <= A;
Smd1 <= B;
Y <= Sum;
Cout <= Cry;
nul <= snul;
process(Clk) begin

if (rising_edge(Clk)) then

    if ( Sum = "00000000000000000000000000000000") then -------Zero flag
        snul <= true;
    else
        snul <= false;
    end if;

    case OP is
        when "000" =>
            Sum <= Smd0 and Smd1;
        when "001" =>
            Sum <= Smd0 xor Smd1;
        when "010" =>
            temp <= std_logic_vector((unsigned("0" & Smd0) + unsigned(Smd1)));
            Sum <= temp(31 downto 0);
            Cry <= temp(32);
        when "100" =>
            Sum <= Smd0 and not Smd1;
        when "101" =>
            Sum <= Smd0 xor not Smd1;
        when "110" =>
            Sum <= std_logic_vector((unsigned(Smd0) - unsigned(Smd1)));
        when "111" =>
            if (A < B) then
                Sum <= "00000000000000000000000000000001";
            else 
                Sum <= "00000000000000000000000000000000";
            end if;
        when others =>
            NULL;
    end case;

end if;
end process;
end Behavioral;

Any comments on the code would be greatly appreciated since I'm completely new to VHDL(We talked about it half a lecture...) and this is what I figured out by googling and playing around.

That's the given circuit diagram:

CDiag

//Edit:

Another thing. My Zero flag does not work properly after "000". Any idea why it's output is good except for the first case?

Answer

OllieB picture OllieB · Jul 31, 2013

In answer to your first question: Yes, use unsigned from library IEEE.std_numeric. It's ideal for this sort of operation.

Secondly, overflow can be detected by comparing the output with the input. For instance, in two's compliment, if you perform +ve plus +ve and overflow, the result will have the msb set so the result is -ve.

To summarise for addition and subtraction

Addition     | (+ve) - (+ve) | (+ve) - (-ve) | (-ve) - (+ve) | (-ve) + (-ve)|
-----------------------------------------------------------------------------
Result (+ve) |       -       |        -      |        -      |    overflow  | 
-----------------------------------------------------------------------------
Result (-ve) |    overflow   |        -      |        -      |       -      | 
-----------------------------------------------------------------------------

Subtraction  | (+ve) - (+ve) | (+ve) - (-ve) | (-ve) - (+ve) | (-ve) - (-ve)|
-----------------------------------------------------------------------------
Result (+ve) |       -       |        -      |    overflow   |      -        |
----------------------------------------------------------------------------- 
Result (-ve) |       -       |    overflow   |       -       |      -        |
-----------------------------------------------------------------------------

Similar rules can be worked out for multiplication and division, but are slightly more involved.

EDIT

Below is a suggested way to go about this (you do realise vhdl is (mostly) case insensitive I hope? You seem to like using the shift key). From you're question I've no idea which flag you want to be the overflow flag, so I haven't put one in.

library ieee;
use ieee.std_logic_164.all;
use ieee.numeric_std.all;

entity alu is
port ( 
    signal clk   : in  std_logic;
    signal a     : in  std_logic_vector(31 downto 0);
    signal b     : in  std_logic_vector(31 downto 0);
    signal y     : in  std_logic_vector(31 downto 0);
    signal op    : in  std_logic_vector(3  downto 0);
    signal nul   : out boolean;
    signal cout  : out std_logic
)
end entity;

architecture behavioral of alu is
   type op_type is (op_and, op_a_and_nb, op_a_xor_nb, op_compare, 
                    op_xor, op_add, op_sub, op_nop);
   signal enum_op : op_type;

   signal a_minus_b : std_logic_vector(32 downto 0);
   signal a_plus_b  : std_logic_vector(32 downto 0);
   signal reg       : std_logic_vector(32 downto 0);

begin

   a_minus_b <= std_logic_vector(signed(a(a'high) & a) - signed(b(b'high) & b));
   a_plus_b  <= std_logic_vector(signed(a(a'high) & a) + signed(b(b'high) & b));

   process(op)
   begin
      case op is
      when "000" => enum_op <= op_and;
      when "001" => enum_op <= op_xor;
      when "010" => enum_op <= op_add;
      when "100" => enum_op <= op_a_and_nb;
      when "101" => enum_op <= op_a_xor_nb;
      when "110" => enum_op <= op_sub;
      when "111" => enum_op <= op_compare;
      when others => enum_op <= op_nop;
      end case;
   end process;

   process(clk)
   begin
      if rising_edge(clk) then
         case enum_op is
         when op_add       => reg <= a_plus_b;
         when op_sub       => reg <= a_minus_b;
         when op_and       => reg <= '0' & (a and b);
         when op_xor       => reg <= '0' & (a xor b);
         when op_a_and_nb  => reg <= '0' & (a and not b);
         when op_a_xor_nb  => reg <= '0' & (a xor not b);
         when op_compare   => 
            reg(32) <= '0';
            reg(31 downto 1) <= (others => '0'); 
            reg(0)  <= a_minus_b(32);
         when op_nop       =>
            reg(32) <= '0';
      end if;
   end process;

   y <= reg(31 downto 0);
   count <= reg(32);
   nul <= unsigned(reg) = '0';

end architecture;