Ana səhifə Blog Arithmetic-Logic Unit(ALU) Implementation With Vhdl

Arithmetic-Logic Unit(ALU) Implementation With Vhdl

Arithmetic-Logic Unit(ALU) Implementation With Vhdl

There's not any limit of the things we can do with a computer. We can create 3d graphics, we can extract statistical data from billions of records, we can control external electronic devices such as a remote controlled toy car, we can manage clusters of computers in an organized way etc. But when we look at it deep enough, we can say all this stuff is just composition of few, very simple operations. According to inputs A and B we can say;

  • A+B
  • A-B
  • A+1
  • A-1
  • A and B
  • A or B
  • A
  • not A

Of course all data that stored at A and B, are in binary form. We can see that first four operations are arithmetic operations and the rest is logical operations. For arithmetic operations, a simple adder would be enough since we can rewrite all four in the form X + Y. But how are we supposed to represent negative numbers? Actually, just using behavioural implementation would be enough to obtain necessary result. So we could program our chip just like programming at C. However, I wanted to do something which is more complicated and have logic gates actually involved in.

Before starting, I suggest you to look at here. My ALU desing is very similar to one at page 15 of this document. I flipped logic and arithmetic operation selections because I wanted to work on it, not just copying. Operations are like this via selection bits :

  • A+B => 000
  • A-B => 001
  • A+1 => 010
  • A-1=> 011
  • A and B => 100
  • A or B => 101
  • A => 110
  • not A => 111

Since we know what our ALU will do, we'll need some extra elements to achieve this. But before that I'll try to explain every operation one by one so maybe I can clarify this a little bit more.

At the beginning, we got an 8 bit adder, an 8 bit shifter, two 8 bit data inputs(I1 and I2), and a 3 bit selection input(Sel). Let's begin with A+B. It's easy since we got an 8 bit adder. We just need to send our inputs to adder circuit and get sum. But how about A-B, A+1 or A-1? Luckily, we can use our adder for these operations too. We can rewrite A-B as A+(-B) and same principle apply to other operations and all arithmetic operations will be handled with 8bit adder. There'll be a distinct block which gets correct input for adder according to selection. This specific block is called Arithmetic Extender(AE). AE will return

  • B for A+B with Sel 000
  • B' for A-B with Sel 001
  • 0 for A+1 with Sel 010
  • 1 for A-1 with Sel 011
  • 0 for all with Sel 1XX

Looks confusing? Why do we send 1s to adder for A-1 or B' for A-B? Let's assume we got 00001111 as Input 1(A) and 00000011 for Input 2(B). If we want to reverse B to -B we need to take 2s complement of it. 2s complement has two steps; invert all bits and add 1. By getting invert of B at AE, we completed the first part of it. But we still need to add 1 to it. At this point we use Carry input of adder. If we invert B and send it to adder with Input1 A and a carry 1, we'll get A-B as a result. This also makes sense for sending 0 and carry 1 for A+1 and  sending all 1s for A-1 since binary representation of (-1) in 8 bits is 11111111. These calculations reveals that we also need a block for carry input which will return 1 for Sel 010 and 001 which will be called Carry Extender. There'll be also a 0 part for Sel 1XX, where Carry also will be 0. That means Input will pass through adder unchanged where arithmetic operations were not selected. Addition to these, we need to handle logical operations which is relatively easier since we just need put values to truth tables and by using K-map simplification, circuits can be obtained easily. Here's the Selection bits for logical operations:

  • A and B with Sel 100
  • A or B with Sel 101
  • A with Sel 110
  • A' with Sel 111,
  • A with Sel 0XX

Here are the vhdl codes for Arithmetic Extender AE, Logical Extender LE and Carry Extender CE.
CE:
library ieee;
use ieee.std_logic_1164.all;
entity CARRY_EXTENDER is port(
Sel : in std_logic_vector(2 downto 0);
O : out std_logic);
end CARRY_EXTENDER;
architecture IMP of CARRY_EXTENDER is
begin
O <= ((not Sel(2)) and Sel(1) and (not Sel(0))) or
((not Sel(2)) and (not Sel(1)) and Sel(0));
end IMP;

AE 1 bit:
library ieee;
use ieee.std_logic_1164.all;
entity ARITHMETIC_EXTENDER is port(
Sel : in std_logic_vector(2 downto 0);
I : in std_logic;
O : out std_logic);
end ARITHMETIC_EXTENDER;
architecture IMP of ARITHMETIC_EXTENDER is
begin
O <= (( (not I) and (not Sel(2) and Sel(0)) ) or
( (not Sel(2)) and Sel(1) and Sel(0) ) or
( I and (not Sel(2)) and (not Sel(1)) and (not Sel(0)) ));
end IMP;

AE 8 bits:
library ieee;
use ieee.std_logic_1164.all;
entity ARITHMETIC_EXTENDER8 is port(
Sel : in std_logic_vector(2 downto 0);
I : in std_logic_vector(7 downto 0);
O : out std_logic_vector(7 downto 0));
end ARITHMETIC_EXTENDER8;
architecture IMP of ARITHMETIC_EXTENDER8 is
component ARITHMETIC_EXTENDER is port(
Sel : in std_logic_vector(2 downto 0);
I : in std_logic;
O : out std_logic);
end component;
begin
U1 : ARITHMETIC_EXTENDER port map(Sel, I(0), O(0));
U2 : ARITHMETIC_EXTENDER port map(Sel, I(1), O(1));
U3 : ARITHMETIC_EXTENDER port map(Sel, I(2), O(2));
U4 : ARITHMETIC_EXTENDER port map(Sel, I(3), O(3));
U5 : ARITHMETIC_EXTENDER port map(Sel, I(4), O(4));
U6 : ARITHMETIC_EXTENDER port map(Sel, I(5), O(5));
U7 : ARITHMETIC_EXTENDER port map(Sel, I(6), O(6));
U8 : ARITHMETIC_EXTENDER port map(Sel, I(7), O(7));
end IMP;

LE 1 bit:
library ieee;
use ieee.std_logic_1164.all;
entity LOGICAL_EXTENDER is port(
I1 : in std_logic;
I2 : in std_logic;
Sel : in std_logic_vector(2 downto 0);
O : out std_logic);
end LOGICAL_EXTENDER;
architecture IMP of LOGICAL_EXTENDER is
begin
O <= ((I1 and (not Sel(2))) or
((not I1) and Sel(2) and Sel(1) and Sel(0)) or
(I1 and Sel(2) and Sel(1) and (not Sel(0))) or
(I2 and Sel(2) and (not Sel(1)) and Sel(0)) or
(I1 and Sel(2) and (not Sel(1)) and Sel(0)) or
(I1 and I2 and Sel(2) and (not Sel(1))));
end IMP;

LE 8 bits:
library ieee;
use ieee.std_logic_1164.all;
entity LOGICAL_EXTENDER8 is port(
I1 : in std_logic_vector(7 downto 0);
I2 : in std_logic_vector(7 downto 0);
Sel : in std_logic_vector(2 downto 0);
O : out std_logic_vector(7 downto 0));
end LOGICAL_EXTENDER8;
architecture IMP of LOGICAL_EXTENDER8 is
component LOGICAL_EXTENDER is port(
I1 : in std_logic;
I2 : in std_logic;
Sel : in std_logic_vector(2 downto 0);
O : out std_logic);
end component;
begin
U1 : LOGICAL_EXTENDER port map(I1(0),I2(0),Sel, O(0));
U2 : LOGICAL_EXTENDER port map(I1(1),I2(1),Sel, O(1));
U3 : LOGICAL_EXTENDER port map(I1(2),I2(2),Sel, O(2));
U4 : LOGICAL_EXTENDER port map(I1(3),I2(3),Sel, O(3));
U5 : LOGICAL_EXTENDER port map(I1(4),I2(4),Sel, O(4));
U6 : LOGICAL_EXTENDER port map(I1(5),I2(5),Sel, O(5));
U7 : LOGICAL_EXTENDER port map(I1(6),I2(6),Sel, O(6));
U8 : LOGICAL_EXTENDER port map(I1(7),I2(7),Sel, O(7));
end IMP;

Since we got all the components we need, including adder which is explained before here now we can construct our ALU with these. It's important to remember we should place these components with an order. Adder should be the last one that will operate since all input changes should be already finished. Here's the vhdl code for ALU:

library ieee;
use ieee.std_logic_1164.all;
entity AL_UNIT is port(
Sel : in std_logic_vector(2 downto 0);
I1 : in std_logic_vector(7 downto 0);
I2 : in std_logic_vector(7 downto 0);
O : out std_logic_vector(7 downto 0));
end AL_UNIT;
architecture IMP of AL_UNIT is
component LOGICAL_EXTENDER8 is port(
I1 : in std_logic_vector(7 downto 0);
I2 : in std_logic_vector(7 downto 0);
Sel : in std_logic_vector(2 downto 0);
O : out std_logic_vector(7 downto 0));
end component;
component ARITHMETIC_EXTENDER8 is port(
Sel : in std_logic_vector(2 downto 0);
I : in std_logic_vector(7 downto 0);
O : out std_logic_vector(7 downto 0));
end component;
component ADDER8 is port(
I1 : in std_logic_vector(7 downto 0);
I2 : in std_logic_vector(7 downto 0);
CarryIn : in std_logic;
O : out std_logic_vector(7 downto 0);
unsignedOverflow, signedOverflow : out std_logic);
end component;
component CARRY_EXTENDER is port(
Sel : in std_logic_vector(2 downto 0);
O : out std_logic);
end component;
signal carry, unsignedOverflow, signedOverflow : std_logic;
signal AEO, LEO : std_logic_vector(7 downto 0); -- input which we'll get from AE as second input according to operation
begin
U1 : CARRY_EXTENDER port map(Sel, carry);
U2 : LOGICAL_EXTENDER8 port map(I1, I2, Sel, LEO);
U3 : ARITHMETIC_EXTENDER8 port map(Sel, I2, AEO);
U4 : ADDER8 port map(LEO, AEO, carry, O, unsignedOverflow, signedOverflow);
end IMP;

And here's the simulation results:

alu

 

We can add more components such as shifter to the end if necessary.