When you make a breadboarded prototype with discrete integrated circuits Section 1.6, each integrated circuit is a self-contained and reusable unit; using VHDL vocabulary, each IC has its own entity and architecture. The ICs are placed on the breadboard and connected together with wires. There is an analogous feature in VHDL for developing reusable modules and connecting them together. This is called structural VHDL.
We’ll illustrate the idea with a simple, almost trivial, example. We have previously seen that a full adder implements the addition operation for single bits. The VHDL model for a full adder looks like this:
library ieee;
use ieee.std_logic_1164.all;
entity full_addder is
port( a, b, cin: in std_logic;
sum, cout: out std_logic );
end full_adder;
architecture gate_level of full_adder is
begin
sum <= a xor b xor cin;
cout <= (a and b) or (a and cin) or (b and cin);
end gate_level;
Listing15.3.1.VHDL model for a full adder.
A multi-bit, say four-bit, adder is constructed by chaining together, or cascading, full-adder modules. You may recognize the syntax from testbenches (e.g., Listing 7.2.1).
library ieee;
use ieee.std_logic_1164.all;
entity adder4 is
port( X, Y: in std_logic_vector(3 downto 0);
Cin : in std_logic;
Z: out std_logic_vector(3 downto 0);
Cout: out std_logic);
end adder4;
architecture structural of adder4 is
component full_adder -- 1
port( a, b, cin: in std_logic;
sum, cout: out std_logic );
end component;
signal c0, c1, c2: std_logic; -- 2
begin
bit0: full_adder port map( -- 3
a => X(0),
b => Y(0),
cin => Cin, -- 4
sum => Z(0),
cout => c0 ); -- 5
bit1: full_adder port map( -- 6
a => X(1), b => Y(1), cin => c0, -- 5
sum => Z(1), cout => c1 );
bit2: full_adder port map(
a => X(2), b => Y(2), cin => c1,
sum => Z(2), cout => c2 );
bit3: full_adder port map(
a => X(3), b => Y(3), cin => c2,
sum => Z(3), cout => Cout ); -- 4
end structural;
Listing15.3.2.Structural VHDL model of a four-bit adder. Comments are keyed to explanations in the text.
The top-level entity of the four-bit adder is what one would expect: two four-bit inputs X and Y, a four-bit output Z, and single-bit carry-in and carry-out ports. The structural VHDL features of the architecture are marked by the comments:
The full adder, previously described by its own entity and architecture, is declared as a component within the four-bit adder’s architecture. This is identical to the way a unit under test is declared in a testbench. The component declaration matches the full adder’s entity block.
Signals c0, c1, c2 will wire the carry chain between the full adders.
The four-bit adder is constructed from four instances of the full_adder component. This is like pulling four full-adder ICs out of a parts drawer. It is always a good idea to give each instance a descriptive label (here, bit0, etc) as an aid to the reader. The port map of the instance specifies how the component is wired up. Here, a => X(0) says that the a port of the full adder is wired to bit 0 of the top-level X port.
Very often, a component port and a mapped signal will have the same name. The cin (carry in) port of full adder bit0 is wired to the Cin port of the top-level entity, and the cout (carry out) port of full adder bit3 is wired to the Cout port of the top-level entity. To avoid errors, remember the syntax: component port => top-level port, or signal.
Wiring between components is always done with declared signals. You cannot directly map port-to-port between components. The desired connection of bit0’s carry-out port to bit1’s carry-in port is accomplished by mapping port cout of bit0 and port cin of bit1 to the same signal, c0.
If it does not compromise clarity, multiple port mappings can be put on the same line to save space, as shown for three of the full adder instances.
Using the numeric_std library, a four-bit adder can be modeled much more simply, and implemented more efficiently, on an FPGA. This is why I said the example is “almost trivial”. Where structural VHDL really shines is in a modular design as diagrammed in Figure 15.1.1. There, each of the interfaces, and the core processor, may be separately designed and validated as components, and then instantiated and wired together in a top-level structural model. The modules are also available for reuse in a subsequent design. This is a very good way to plan and execute a large project.
Checkpoint15.3.3.
Write a behavioral architecture, using the numeric_std library, for the four-bit adder in Listing 15.3.2. You can do it with a single combinational process.
Hint.
When you add two four-bit numbers, with or without a carry-in, you must allow five bits for the result. The Cout bit is bit 4 of this result.