DIGITAL LOGIC DESIGN
VHDL Coding for FPGAs
Unit 4
STRUCTURAL DESCRIPTION
Hierarchical design: port-map, for-generate, if-
generate.
Examples: Adder, multiplier, ALU, Look-up Table.
Introduction to Parametric Coding.
Daniel Llamocca
STRUCTURAL DESCRIPTION
It is the generalization of the Concurrent Description. The circuits
are described via interconnection of its subcircuits. This subcircuits
can be described in concurrent code and/or sequential code.
Example: Multiplexor 2-to-1.
a b
0
s
f
b f
1
a
s
This case is trivial, since the interconnection is realised via logic
operators, but nevertheless it is an example of structural
description.
Daniel Llamocca
STRUCTURAL DESCRIPTION
Example: 4-to-16 decoder
w0 y0 y0
w0
w1 y1 y1
We can describe this w1
y2 y2
decoder in an structured E y3 y3
way based on 2-to-4 w0 y0 y4
decoders. w1
y1 y5
w2 y0 y2
w0 y6
However, we can also w3 w1
y1 E
y3 y7
describe the 4-to-16 E E
y2
y0 y8
decoder using the with- y3 w0
y1 y9
select statement. w1
y2 y10
E
y3 y11
w0 y0 y12
y1 y13
w1
y2 y14
E
y3 y15
Daniel Llamocca
STRUCTURAL DESCRIPTION
Example: DLX Processor
S1
ALU
CONTROLLER
In this type of systems, it is best to
S2
describe each component first,
then assemble them to make the
A
large system. REGISTER C
FILE
B
X1
We do not need to see such large
TEMP
system to realise the importance of
the Structural Description. IAR
X2
PC
IR MAR
MDR
Daniel Llamocca
STRUCTURAL DESCRIPTION
Many systems can be described entirely in one single block: we
can use the behavioral description, and/or concurrent statements
(with-select, when-else).
However, it is advisable not to abuse of this technique since it
makes: i) the code less readable, ii) the circuit verification process
more cumbersome, and iii) circuits improvements less evident.
The structural description allows for a hierarchical design: we can
‘see’ the entire circuit as the pieces it is made of, then identify
critical points and/or propose improvements on each piece.
It is always convenient to have basic building blocks from which we
can build more complex circuits. This also allows building block (or
sub-system) to be re-used in a different circuit.
Daniel Llamocca
STRUCTURAL DESCRIPTION
Example: 4-bit add/sub for numbers in 2’s complement
x0 y0
The circuit can be described in one single block.
However, it is best to describe the Full Adder as a c i+1
FA
ci
block in a separate file (full_add.vhd), then use
as many full adders to build the 4-bit adder. s0
my_addsub.vhd
The place where we use and connect as many full
adders as desired, and possibly add extra circuitry full_add.vhd
is called the ‘top file’ (my_addsub.vhd). This
creates a hierarchy of files in the VHDL project:
x3 y3 x2 y2 x1 y1 x0 y0
add/sub
add =0
sub = 1
c out c4 c3 c2 c1 c 0 c in
FA FA FA FA
overflow
s3 s2 s1 s0
Daniel Llamocca
4-bit 2’s complement Adder
Full Adder: VHDL Description (fulladd.vhd):
library ieee; x y
use ieee.std_logic_1164.all;
c out c in
entity fulladd is FA
port ( cin, x, y: in std_logic;
s, cout: out std_logic); s
end fulladd;
architecture struct of fulladd is
begin
s <= x xor y xor cin;
cout <= (x and y) or (x and cin) or (y and cin);
end struct;
Daniel Llamocca
4-bit 2’s complement Adder
Top file (my_addsub.vhd): We need 4 full adders block and extra
logic circuitry. library ieee;
use ieee.std_logic_1164.all;
entity my_addsub is
port ( addsub: in std_logic;
x,y: in std_logic_vector(3 downto 0);
s: out std_logic_vector(3 downto 0);
cout, overflow: out std_logic);
In order to use theend my_addsub;
file ‘fulladd.vhd’
into the top file, we architecture struct of my_addsub is
component fulladd
need to declare it in port ( cin, x, y: in std_logic; We copy what
the top file: s, cout: out std_logic); is in the entity of
full_add.vhd
end component;
signal c: std_logic_vector(4 downto 0);
signal yt: std_logic_vector(3 downto 0);
begin -- continued on next page
Daniel Llamocca
4-bit 2’s complement Adder
Here, we:
Insert the required extra circuitry (xor gates and I/O
connections).
Instantiate the full adders and interconnect them (using the
port map statement)
-- continuation from previous page
c(0) <= addsub; cout <= c(4);
overflow <= c(4) xor c(3);
yt(0) <= y(0) xor addsub; yt(1) <= y(1) xor addsub;
yt(2) <= y(2) xor addsub; yt(3) <= y(3) xor addsub;
f0: fulladd port map(cin=>c(0),x=>x(0),y=>yt(0),s=>s(0),cout=>c(1));
f1: fulladd port map(cin=>c(1),x=>x(1),y=>yt(1),s=>s(1),cout=>c(2));
f2: fulladd port map(cin=>c(2),x=>x(2),y=>yt(2),s=>s(2),cout=>c(3));
f3: fulladd port map(cin=>c(3),x=>x(3),y=>yt(3),s=>s(3),cout=>c(4));
end struct;
Daniel Llamocca
4-bit 2’s complement Adder
Use of ‘port map’ statement:
port map (signal in full adder => signal in top file, ...)
Instantiating and connecting the first full adder:
f0: fulladd port map(cin=>c(0),x=>x(0),y=>yt(0),s=>s(0),cout=>c(1));
x3 y3 x2 y2 x1 y1 x0 y0
add/sub
yt3 yt2 yt1 yt0
x y x y x y x y
c4 c3 c2 c1 c0
c out cout c in cout c in cout c in cout c in
overf low s s s s
s3 s2 s1 s0
Daniel Llamocca
4-bit 2’s complement Adder
Use of ‘port map’ statement:
port map (signal in full adder => signal in top file, ...)
Instantiating and connecting the second full adder:
f1: fulladd port map(cin=>c(1),x=>x(1),y=>yt(1),s=>s(1),cout=>c(2));
x3 y3 x2 y2 x1 y1 x0 y0
add/sub
yt3 yt2 yt1 yt0
x y x y x y x y
c4 c3 c2 c1 c0
c out cout c in cout c in cout c in cout c in
overf low s s s s
s3 s2 s1 s0
Daniel Llamocca
4-bit 2’s complement Adder
Use of ‘port map’ statement:
port map (signal in full adder => signal in top file, ...)
Instantiating and connecting the third full adder:
f2: fulladd port map(cin=>c(2),x=>x(2),y=>yt(2),s=>s(2),cout=>c(3));
x3 y3 x2 y2 x1 y1 x0 y0
add/sub
yt3 yt2 yt1 yt0
x y x y x y x y
c4 c3 c2 c1 c0
c out cout c in cout c in cout c in cout c in
overf low s s s s
s3 s2 s1 s0
Daniel Llamocca
4-bit 2’s complement Adder
Use of ‘port map’ statement:
port map (signal in full adder => signal in top file, ...)
Instantiating and connecting the fourth full adder:
f3: fulladd port map(cin=>c(3),x=>x(3),y=>yt(3),s=>s(3),cout=>c(4));
x3 y3 x2 y2 x1 y1 x0 y0
add/sub
yt3 yt2 yt1 yt0
x y x y x y x y
c4 c3 c2 c1 c0
c out cout c in cout c in cout c in cout c in
overf low s s s s
s3 s2 s1 s0
Daniel Llamocca
FOR-GENERATE Statement
In the 4-bit adder example, if we wanted to use say 8 bits, we would
need to instantiate 8 full adders and write 8 port map statements.
Instantiating components can be a repetitive task, thus the for-
generate statement is of great help here:
yt(0) <= y(0) xor addsub; yt(1) <= y(1) xor addsub;
yt(2) <= y(2) xor addsub; yt(3) <= y(3) xor addsub;
f0: fulladd port map(cin=>c(0),x=>x(0),y=>yt(0),s=>s(0),cout=>c(1));
f1: fulladd port map(cin=>c(1),x=>x(1),y=>yt(1),s=>s(1),cout=>c(2));
f2: fulladd port map(cin=>c(2),x=>x(2),y=>yt(2),s=>s(2),cout=>c(3));
f3: fulladd port map(cin=>c(3),x=>x(3),y=>yt(3),s=>s(3),cout=>c(4));
-- continuation from previous page
c(0) <= addsub; cout <= c(4);
overflow <= c(4) xor c(3);
gi: for i in 0 to 3 generate
yt(i) <= y(i) xor addsub;
fi: fulladd port map(cin=>c(i),x=>x(i),y=>yt(i),s=>s(i),cout=>c(i+1));
end generate;
end struct;
Daniel Llamocca
INTRODUCTION TO PARAMETRIC CODING
N-bit adder/ library ieee;
subtractor. We can use ieee.std_logic_1164.all;
choose the value of N entity my_addsub is
in the entity. generic (N: INTEGER:= 4);
port( addsub : in std_logic;
x, y : in std_logic_vector (N-1 downto 0);
s : out std_logic_vector (N-1 downto 0);
The architecture code overflow : out std_logic;
cout : out std_logic);
is tweaked so as to end my_addsub;
make it parametric.
architecture structure of my_addsub is
component fulladd
port( cin, x, y : in std_logic;
s, cout : out std_logic);
end component;
Example: Parametric N-bit signal c: std_logic_vector (N downto 0);
adder/subtractor in 2’s signal yx: std_logic_vector (N-1 downto 0);
begin
complement:
c(0) <= addsub; cout <= c(N);
my_addsub.zip: overflow <= c(N) xor c(N-1);
gi: for i in 0 to N-1 generate
my_addsub.vhd, yx(i) <= y(i) xor addsub;
fulladd.vhd fi: fulladd port map (cin=>c(i),x=>x(i),y=>yx(i),
tb_my_addsub.vhd, s=>s(i),cout=>c(i+1));
my_addsub.ucf end generate;
end structure;
Daniel Llamocca
INTRODUCTION TO PARAMETRIC CODING
Example: N-bit library ieee;
use ieee.std_logic_1164.all;
adder/subtractor. use ieee.std_logic_arith.all; -- for conv_std_logic_vector
entity tb_my_addsub is
generic (N: INTEGER:= 4); -- must match the HW description
end tb_my_addsub;
Testbench: Use of
‘for loop’ inside the
architecture structure of my_addsub is
component my_addsub -- Do not do 'generic map' in testbench
stimulus process to port( addsub
x, y
: in std_logic;
: in std_logic_vector (N-1 downto 0);
generate all possible s : out std_logic_vector (N-1 downto 0);
overflow,cout : out std_logic);
input combinations. end component;
-- Inputs
signal addsub: std_logic:='0';
signal x,y: std_logic_vector (N-1 downto 0):= others => '0');
Parametric Testbench: -- Outputs
signal overflow, cout: std_logic;
It depends on N, signal s: std_logic_vector (N-1 downto 0);
which must match the begin
uut: my_addsub port map (addsub, x, y, s, overflow, cout);
hardware description st: process
begin
wait for 100 ns;
y<="0000"; x<="0000"; wait for 10 ns; addsub <= '0'; -- Pick '1' to test subtraction
y<="0000"; x<="0001"; wait for 10 ns; gi: for i in 0 to 2**N-1 loop
... y <= conv_std_logic_vector(i,N); wait for 10 ns;
y<="0000"; x<="1111"; wait for 10 ns;
y<="0001"; x<="0000"; wait for 10 ns; gj: for j in 0 to 2**N-1 loop
y<="0001"; x<="0001"; wait for 10 ns; x <= conv_std_logic_vector(j,N); wait for 10 ns;
... end loop;
y<="0001"; x<="1111"; wait for 10 ns; end loop;
...
wait;
end process;
Daniel Llamocca end;
Example: 4-bit array multiplier
Interesting example of b(3) b(2) b(1) b(0)
structural description. By a(0) x
cin
y
connecting full adders
a0b3 a0b2 a0b1 a0b0
m03 m02 m01 m00 FULL
and AND gates, we build s03 s02 s01 s00
ADDER
the array multiplier. c02 c01 c00
cout
s
We parameterize the
a(1)
a1b3 a1b2 a1b1 a1b0
circuit with ‘if-generate’, m13 m12 m11 m10 p(0)
‘for-generate’, and the s13
c12
s12
c11
s11
c10
s10
use of arrays in VHDL. a(2)
a2b3 a2b2 a2b1 a2b0
Testbench: we use m23 m22 m21 m20 p(1)
conv_std_logic_vector to s23
c22
s22
c21
s21
c20
s20
specify input values
a(3)
a3b3 a3b2 a3b1 a3b0
m33 m32 m31 m30 p(2)
Parametric N-bit array multiplier: s33 s32 s31 s30
c32 c31 c30
my_mult.zip:
my_mult.vhd.vhd, m43 m42 m41 m40
fulladd.vhd p(3)
tb_my_mult.vhd,
my_mult.ucf
p(7) p(6) p(5) p(4)
Daniel Llamocca
Example: Arithmetic Logic Unit
This circuit executes two types of
a N
operations: logic (or bit-wise) and ARITHMETIC
arithmetic. b
N UNIT
The arithmetic unit and the logic unit 0
are described in different VHDL files. N y
1
The arithmetic unit relies heavily on
the parametric adder/subtractor unit. LOGIC UNIT
The VHDL code is parameterized so as sel(3)
to allow for two N-bit operands.
4
The ‘sel’ inputs selects the operationselto Operation
sel
Function Unit
be carried on (as per the table). 0 0 0 0 y <= a Transfer 'a'
0 0 0 1 y <= a + 1 Increment 'a'
0 0 1 0 y <= a - 1 Decrement 'a'
my_alu.zip: 0 0 1 1 y <= b Transfer 'b'
Arithmetic
0 1 0 0 y <= b + 1 Increment 'b'
my_alu.vhd, 0 1 0 1 y <= b - 1 Decrement 'b'
my_alu_arith.vhd, 0 1 1 0 y <= a + b Add 'a' and 'b'
0 1 1 1 y <= a - b Subtract 'b' from 'a'
my_alu_logic.vhd, 1 0 0 0 y <= NOT a Complement 'a'
1 0 0 1 y <= NOT b Complement 'b'
my_addsub.vhd, 1 0 1 0 y <= a AND b AND
fulladd.vhd 1 0 1 1 y <= a OR b OR
Logic
1 1 0 0 y <= a NAND b NAND
tb_my_alu.vhd 1 1 0 1 y <= a NOR b NOR
1 1 1 0 y <= a XOR b XOR
Daniel Llamocca 1 1 1 1 y <= a XNOR b XNOR
Example: 6-to-6 LUT
The LUT contents (64 bytes ) are specified as a set of 6
parameters. These 6 parameters are ‘generics’ in the VHDL code.
Note that if the parameters are modified, we will get a different
circuit that needs to be re-synthesized. In other words, the LUT
contents are NOT inputs to the circuit.
ILUT
6 4 LSBs LI(3..0)
6 bits
4 4 4 4 6 6 LUT
6 to 1
b5 ... b1 b0
b5
2 MSBs
64 words of 6 bits
6
LUT4
LUT4
LUT4
LUT4
LUT
6 to 1
6
column 5
column 1
column 0
b4
6
...
6
...
LI(4)
MUX MUX
6 LUT
LI(5) 6 to 1
LUT5-to-1 b0
MUX
LUT 6-to-6 LUT 6-to-6
OLUT(i) LUT6-to-1
my6to6LUT.zip: my6to6LUT.vhd, my6to1LUT.vhd, my5to1LUT.vhd,
my4to1LUT.vhd, tb_my6to6LUT.vhd, my6to6LUT.ucf
Daniel Llamocca