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:
//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?
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;