2

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?

DangerDan
  • 137
  • 1
  • 2
  • 14
  • Your temporary signal is incorrect. If you want to use a temporary like this, you need to use a variable, not a signal. Remember, signal are updated in parallel on the event in the sensitivity list, so you'll not have the behaviour you're expecting using signals – OllieB Jul 31 '13 at 21:21
  • Thanks, it's working now. So, as somebody more experienced than me, would you say this is working proberly? I'll play around with different values later, but I'd like to have a general opinion. – DangerDan Aug 01 '13 at 10:18

2 Answers2

5

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;
OllieB
  • 1,431
  • 9
  • 14
  • That's what I meant with "The rules from the internet", thanks! So to clarify things: When thinking about the numbers being processed by the ALU, I should consider them in twos complement? I'm confused at this point since according to the circuit diagram the ALU does take them in standard binary(because the Alu does the reverse + 1 thing when getting the opcode for substraction. Please correct me if I'm wrong). – DangerDan Aug 01 '13 at 10:01
  • Could you please elaborate on why you did certain things? I don't get the "a(a'high)" part and why you divide this into two processes. Thanks so far! – DangerDan Aug 01 '13 at 11:21
  • a(a'high) is an index using an attribute. In this case, its equivelent of writing a(31). Its used in this case to perform sign extension on your operands a and b, which I've assumed are twos complement. – OllieB Aug 01 '13 at 11:24
  • Its split into two processes because I thought it would be easier to read for others. I had to decode your op codes manually, so by explicitly decoding them to an enumeration, I hope to make the post more easily readable to others. – OllieB Aug 01 '13 at 11:26
  • Ah ok, thanks! And what would be your suggestion for the overflow flag? Is there some more sophisticated trick or should I stick to comparing the summands and the sum? And another question: So thinking in twos complement is the right thing to do in this case? Or is this dependent on what I "feed" the inputs with? Because according to the circuit diagram, when substraction is requested, the ALU negates B and adds 1 to get the negative value. But this only works when the original value is standard unsigned binary, or am I wrong? – DangerDan Aug 01 '13 at 12:24
  • 1
    @OllieB: You forgot the signal assignment when setting `enum_op`. – Bill Lynch Aug 01 '13 at 13:10
  • OK, obviously I forgot that positive twos complement is identical to unsigned binary positive, sorry -.- – DangerDan Aug 01 '13 at 14:41
  • @PeterPanter: Any chance of some voting ? – OllieB Aug 01 '13 at 15:34
  • Just did that ;) While playing around with different values in the testbench I realized that I don't quite get how the cout is working - could you explain why you did it this way, please? – DangerDan Aug 01 '13 at 16:22
  • Count : I've taken to be carry out. When numbers are added, for instance "11" + "01", the carrying of a bit is the carry = "100". This is the same process as decimal 9 + 1 = 10. – OllieB Aug 01 '13 at 16:44
  • THe implementation I've given assumes 32 bit signed numbers as inputs. It may be that you're application requires 32 bit unsigned numbers. I'm sure you adjust accordingly. If not, please break your questions down and repost, because this is becoming distinctly more than one question. – OllieB Aug 01 '13 at 16:45
  • Ok, I understand that. I get that the excess bit should contain the carry out. But I've adjusted the ALU to 8 bit (less typing...) and tried "11011001" + "01011100" (-39 + 92). Althogh the result in the simulation is fine, the carry bit is not set, which is wrong. That's what I don't understand. – DangerDan Aug 01 '13 at 19:39
1

This answer might be helpful: https://stackoverflow.com/a/15499109/47453. Note the explanation about the difference between signal and variable.

Basically, you should change your process around a little bit.

process (Clk)
    variable temp : std_logic_vector (32 downto 0);
begin
        ...
        case OP is
            when "010" =>
                temp := std_logic_vector((unsigned("0" & Smd0) + unsigned(Smd1)));
                sum <= temp(31 downto 0);
                cry <= temp(32);
        ...
Community
  • 1
  • 1
Bill Lynch
  • 80,138
  • 16
  • 128
  • 173