An ALU or an Arithmetic Logic Unit is the part of a microprocessor that performs the arithmetic and logical operations.
We’ll start off coding an ALU using VHDL in a series of progressions. This post is important because here, we breakdown every single detail of the coding process. You can expand in on this to code virtually any ALU.
In this post, we will be coding a simple 1-bit ALU that handles three essential functions: Addition, subtraction, and multiplication. We are going to achieve this by using a half -adder, a half-subtractor, and a multiplier. For the sake of progression, we will code only the arithmetic functionality in this post. In the upcoming posts, we will code the logical functionality. And by the end of this VHDL course, we will code a full-fledged ALU.
Contents
Arithmetic Logic Circuit (ALU) and its structure
We will be writing the VHDL code for the ALU using structural modeling architecture. We will also write the testbench for our ALU HDL code, generate the simulation waveforms, and the RTL schematic to verify our design. To re-iterate for the sake of clarity, we will be coding a 1-bit ALU.
Take a look at the structure above. We have:
- Two 4:1 multiplexers. Giving a total of two final outputs. These two share two select lines SEL1 and SEL2. The first multiplexer has one free input that we attach to a GND component. The second multiplexer has two free inputs that, too, are attached to the GND component. We will use these two slots to expand the functionality of the ALU later on.
- A half adder with two inputs and two outputs. Sum and Carry. The sum output is given to the first multiplexer. The carry output is given to the second multiplexer.
- A half subtractor with two inputs and two outputs. Difference and Borrow. The difference output is given to the first multiplexer. The borrow, to the second multiplexer.
- A multiplier with its output given to the first multiplexer.
Truth table for the ALU
Here are the possible values for SEL1 and SEL2 and the operations that will be performed based on them. This is for your understanding. Since we aren’t using the structural modeling style, we won’t be needing the truth table/the behavior of the multiplexers to define the working of the circuit.
SEL 2 | SEL 1 | Output of MUX 1 | Output of MUX 2 |
0 | 0 | SUM | CARRY |
0 | 1 | DIFFERENCE | BORROW |
1 | 0 | PRODUCT | GND |
1 | 1 | GND | GND |
Now that we have understood the working of the structure of the circuit let’s finally get down to coding it.
Explanation of the VHDL code for a 1-bit ALU using the structural method. How does the code work?
As we have seen in the post on structural VHDL for full-adder, we have to code in the individual components of the main circuit before we can code the main circuit using structural modeling. Here, the individual components include the half adder, the half subtractor, the multiplier, and the multiplexer. Additionally, recall that we can use any modeling style to code the individual components. So let’s code them first. You can see that each component’s code is the entire code for that component. By that, we mean that even the header files are included for each individual component.
VHDL code for the Half-adder (Component U1)
We code in the component U1 using basic dataflow style of architecture modeling. The logic equations of the half adder and its circuit can be found here.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity HA is Port ( HAA, HAB : in STD_LOGIC; SUM, CARRY : out STD_LOGIC); end HA; architecture dataflow of HA is begin SUM <= HAA XOR HAB; CARRY <= HAA AND HAB; end dataflow;
VHDL code for the half subtractor (Component U2)
The VHDL code for the component U2 is also written using the dataflow modeling style. The half subtractors logic equation and working are explained here.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity HS is Port ( HSA, HSB : in STD_LOGIC; DIFFERENCE, BORROW : out STD_LOGIC); end HS; architecture dataflow of HS is begin DIFFERENCE <= HSA XOR HSB; BORROW <= (not HSA) AND HSB; end dataflow;
VHDL code for the multiplier (component U3)
The VHDL code for component U3 is written using the dataflow style too. The multiplier’s concept and logic are explained here.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity multiplier is Port ( MA, MB : in STD_LOGIC; PRODUCT : out STD_LOGIC); end multiplier; architecture dataflow of multiplier is begin PRODUCT <= MA AND MB; end dataflow;
VHDL code for the multiplexer (Components U4 and U5)
The fourth and fifth components are multiplexers. We have previously seen the mux VHDL for dataflow and mux VHDL for behavioral. Here, we will use the dataflow style of modeling to write their VHDL code. The logic circuit and logic equations for multiplexers can be found here.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity MUX is Port ( A1,A2,A3,A4 : in STD_LOGIC; S : in STD_LOGIC_VECTOR (1 downto 0); X : out STD_LOGIC); end mux; architecture dataflow of MUX is begin with S select X <= A1 when "00", A2 when "01", A3 when "10", A4 when others; end dataflow;
VHDL code for the GND component (U0)
The GND component (U0) is assigned to the STD_LOGIC ‘U,’ which stands for Uninitialized. Here, it means that we are yet to initialize the signal.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity ground is Port ( N : inout STD_LOGIC); end ground; architecture dataflow of ground is begin N <= 'U'; end dataflow;
Now that all the components are initialized and defined, we can start with the structural modeling of the 1-bit ALU. We start off by declaring the entity
. Here we mention all the inputs and outputs of the overall ALU circuit. This means the two inputs, the two select lines, and the two outputs.
entity ALU is Port ( A,B,SEL1,SEL2 : in STD_LOGIC; ALU1,ALU2 : out STD_LOGIC); end ALU;
Next up is the architecture statement.
architecture Structural of ALU is
In structural modeling, between the architecture
statement and the begin
statement, there are a couple of things we need to do.
So recall the individual components that we declared above. Those need to be CONNECTED to the main program. How do we connect the individual components to the main program in the structural modeling architecture in VHDL?
We do that by using a block known as Component Declaration. This is extremely similar in syntax to an entity declaration. The only difference is that we replace the term entity
with component
. Watch.
component HA is Port ( HAA, HAB : in STD_LOGIC; SUM, CARRY : out STD_LOGIC); end component; component HS is Port ( HSA, HSB : in STD_LOGIC; DIFFERENCE, BORROW : out STD_LOGIC); end component; component multiplier is Port ( MA, MB : in STD_LOGIC; PRODUCT : out STD_LOGIC); end component; component MUX is Port ( A1,A2,A3,A4 : in STD_LOGIC; S : in STD_LOGIC_VECTOR (1 downto 0); X : out STD_LOGIC); end component; component ground is Port ( N : inout STD_LOGIC); end component;
Last but not least, we need to declare all the signals that are involved in this operation. The two inputs, the outputs of the arithmetic digital circuits that are fed as inputs to the multiplexers, and the final outputs.
signal S0,S1,S2,S3,S4,S5: STD_LOGIC;
and now we can
begin
The actual part of coding in structural is very straightforward. All you need to do is list each component (U0 to U5) out. And then use PORT MAP
to assign the components inputs to the signals that we declared. That’s it. You’re good to go. Remember to end the architecture. You should know by now what that is we hope! (Hint: It’s got the word end in it).
U0: ground PORT MAP(N=>S5); U1: HA PORT MAP(HAA=>A,HAB=>B,SUM=>S0,CARRY=>S3); U2: HS PORT MAP(HSA=>A,HSB=>B,DIFFERENCE=>S1,BORROW=>S4); U3: multiplier PORT MAP(MA=>A,MB=>B,PRODUCT=>S2); U4: MUX PORT MAP(A1=>S0,A2=>S1,A3=>S2,A4=>S5,X=>ALU1,S(0)=>SEL1,S(1)=>SEL2); U5: MUX PORT MAP(A1=>S3,A2=>S4,A3=>S5,A4=>S5,X=>ALU2,S(0)=>SEL1,S(1)=>SEL2);
Full VHDL code for an Arithmetic Logic Unit (ALU) using the structural modeling method
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity HA is Port ( HAA, HAB : in STD_LOGIC; SUM, CARRY : out STD_LOGIC); end HA; architecture dataflow of HA is begin SUM <= HAA XOR HAB; CARRY <= HAA AND HAB; end dataflow; library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity HS is Port ( HSA, HSB : in STD_LOGIC; DIFFERENCE, BORROW : out STD_LOGIC); end HS; architecture dataflow of HS is begin DIFFERENCE <= HSA XOR HSB; BORROW <= (not HSA) AND HSB; end dataflow; library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity multiplier is Port ( MA, MB : in STD_LOGIC; PRODUCT : out STD_LOGIC); end multiplier; architecture dataflow of multiplier is begin PRODUCT <= MA AND MB; end dataflow; library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity MUX is Port ( A1,A2,A3,A4 : in STD_LOGIC; S : in STD_LOGIC_VECTOR (1 downto 0); X : out STD_LOGIC); end mux; architecture dataflow of MUX is begin with S select X <= A1 when "00", A2 when "01", A3 when "10", A4 when others; end dataflow; library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity ground is Port ( N : inout STD_LOGIC); end ground; architecture dataflow of ground is begin N <= 'U'; end dataflow;
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity ALU is Port ( A,B,SEL1,SEL2 : in STD_LOGIC; ALU1,ALU2 : out STD_LOGIC); end ALU; architecture Structural of ALU is component HA is Port ( HAA, HAB : in STD_LOGIC; SUM, CARRY : out STD_LOGIC); end component; component HS is Port ( HSA, HSB : in STD_LOGIC; DIFFERENCE, BORROW : out STD_LOGIC); end component; component multiplier is Port ( MA, MB : in STD_LOGIC; PRODUCT : out STD_LOGIC); end component; component MUX is Port ( A1,A2,A3,A4 : in STD_LOGIC; S : in STD_LOGIC_VECTOR (1 downto 0); X : out STD_LOGIC); end component; component ground is Port ( N : inout STD_LOGIC); end component; signal S0,S1,S2,S3,S4,S5: STD_LOGIC; begin U0: ground PORT MAP(N=>S5); U1: HA PORT MAP(HAA=>A,HAB=>B,SUM=>S0,CARRY=>S3); U2: HS PORT MAP(HSA=>A,HSB=>B,DIFFERENCE=>S1,BORROW=>S4); U3: multiplier PORT MAP(MA=>A,MB=>B,PRODUCT=>S2); U4: MUX PORT MAP(A1=>S0,A2=>S1,A3=>S2,A4=>S5,X=>ALU1,S(0)=>SEL1,S(1)=>SEL2); U5: MUX PORT MAP(A1=>S3,A2=>S4,A3=>S5,A4=>S5,X=>ALU2,S(0)=>SEL1,S(1)=>SEL2); end Structural;
Testbench
We will write the testbench with a process statement. You can check out all the different types and variations of a testbench in VHDL here.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity ALU1bit_tb is end entity; architecture tb of ALU1bit_tb is component ALU is Port ( A,B,SEL1,SEL2 : in STD_LOGIC; ALU1,ALU2 : out STD_LOGIC); end component; signal A, B, SEL1, SEL2, ALU1, ALU2 : STD_LOGIC; begin uut: ALU port map( A => A, B => B, SEL1 => SEL1, SEL2 => SEL2, ALU1 => ALU1, ALU2 => ALU2); stim: process begin A <= '0'; B <= '0'; SEL1 <= '0'; SEL2 <= '0'; wait for 20 ns; SEL1 <= '0'; SEL2 <= '1'; wait for 20 ns; SEL1 <= '1'; SEL2 <= '0'; wait for 20 ns; SEL1 <= '1'; SEL2 <= '1'; wait for 20 ns; A <= '0'; B <= '1'; SEL1 <= '0'; SEL2 <= '0'; wait for 20 ns; SEL1 <= '0'; SEL2 <= '1'; wait for 20 ns; SEL1 <= '1'; SEL2 <= '0'; wait for 20 ns; SEL1 <= '1'; SEL2 <= '1'; wait for 20 ns; A <= '1'; B <= '1'; SEL1 <= '0'; SEL2 <= '0'; wait for 20 ns; SEL1 <= '0'; SEL2 <= '1'; wait for 20 ns; SEL1 <= '1'; SEL2 <= '0'; wait for 20 ns; SEL1 <= '1'; SEL2 <= '1'; wait for 20 ns; wait; end process; end tb;
RTL Schematic
Simulation Waveform
Edit: Post updated with the testbench, RTL Schematic, and Simulation Waveform by Deepak Joshi.