In VHDL, we widely use structural modeling for large designs. It allows us to write reusable code. We define a smaller entity in a separate file and can use it in a larger entity as a component. We use signals to interconnect components and eventually create large systems using small sub-systems.
For example, imagine you have created an adder, a subtractor, a multiplier, and a divider circuit before. Now, you want to write code for an ALU (Arithmetic & Logic Unit). Then you don’t have to write it entirely from scratch. You can use those pre-written entities as components in your ALU code. To use them, you just need to map its inputs and outputs according to your design requirements.
Contents
Component declaration
In structural modeling, the first thing to do is to instantiate components using component declaration. In component declaration, we define the name, Inputs/outputs, and type of Inputs/outputs of the component.
The component name we declare here may or may not exist in the library. If it is not present in the library, then you can either create a component with that name, or you can explicitly bound any other existing entity to it using a configuration. Otherwise, the VHDL compiler can’t simulate the model. Let’s look at an example of a component declaration.
component AND1 port(A,B : in BIT; Y : out BIT); end component; component half_adder port(A,B : in BIT; sum, carry : out BIT); end componenet;
We declare components in the declaration part of the architecture body. We can also define a component in a package. Then we can use it in any architecture body by using the library and use clause. After defining it inside a package, it will not require an explicit declaration.
Package Declaration
Let’s convert the above example into a package declaration.
package components is component AND1 port(A,B : in BIT; Y : out BIT); end component; component half_adder port(A,B : in BIT; sum, carry : out BIT); end componenet; end compoenents;
Now, assume that we compile this package into a library Our_Lib. (Here’s an in-depth tutorial on creating libraries in VHDL). Now we can write the architecture body as:
library Our_Lib; use Our_Lib.components.all; architecture structural of example is signal ex1,ex1: BIT; --No more component declaration is needed begin -- component instantiation here end;
The use of packages actually makes reusable code as designers can share the packages with other designers. Or one can use a package in multiple designs without writing it again and again.
Component instantiation
Component instantiation is nothing but just the syntax for using the component in the circuit. Its syntax is
component label: component-name port map ( association-list) ;
- We can use any legal identifier as a label for the component, and it has to be unique in the architecture.
- The component label is also known as the name of the instance.
- The name of the component will remain the same as in its declaration.
- The association list contains the signals and ports of the entity.
- We use signals for the interconnection of components inside the circuit, and ports serve as a terminal point or input /outputs.
Note:
- Signals in the entity are called actuals.
- An actual can only be an object of the signal class; VHDL does not allow variable and constants here.
- Ports of a component in the association list are called locals.
- If the port is not connected anywhere, then you must use the
open
keyword to indicate it.
Association techniques
We have two ways to do the association of locals with actuals or signals with ports:
- Positional association.
- Named association.
Positional association
In positional association, we map the signal or port to the port at the respective positions in the component declaration. Sounds complicated? Let’s understand it with an example.
For the following component declaration:
component half_adder port(A,B : in BIT; sum, carry : out BIT); end componenet;
Component instantiation statement using positional association:
H1: half_adder port map(S1, S2, P(1), P(2));
In the above statement, H1 is the label for the current instantiation of component half_adder. And it will map S1 to A, S2 to B, P(1) to sum and, P(2) to carry. As association occurs based on the position of the actual in the list, that’s why we call it positional association.
Named association
In named association, every local is attached to an actual explicitly using the syntax
local => actual;
Here position and sequence do not matter as we defined each of them explicitly.
For the following component declaration:
component half_adder port(A,B : in BIT; sum, carry : out BIT); end componenet;
Component instantiation statement using named association:
H1: half_adder port map( S1 => A, S2 => B, P(1) => sum, P(2) => carry);
Rules for association while instantiating a component
There are some rules for instantiation, and it applies to both named and positional associations. Those rules are:
- The type of local and the actual must be the same for a valid association.
- If the local is in readable mode, so must the actual.
- Signals that are defined locally, we can read/ write to them so those can be associated with local of any mode.
- If an actual is of mode in, it can’t be associated with a local of mode out/ inout.
- If an actual is of mode out, it can’t be associated with a local of mode in/ inout.
- But if an actual is of mode inout, it can be associated with a local of mode in, out or input.
Internal signal declaration
As we know, in structural modeling, we use signals interconnect components. We initialize these signals inside the architecture body, so we call them the internal signals.
Let’s see an example to understand all the topics mentioned above. We’ll design a decade counter using JK FF.
entity DECADE_COUNTER is port (INPUT: in BIT; Z: buffer BIT_VECTOR(0 to 3)); end DECADE_COUNTER; architecture STRUCTURAL of DECADE_COUNTER is component JK_FF port (J, K, CLK: in BIT; Q, QBAR: buffer BIT); end component; component AND_GATE port (A, B: in BIT; C: out BIT); end component; signal S1, S2: BIT; signal S_HIGH: BIT := '1'; begin A1: AND_GATE port map (Z(2), Z(1), S1); JK1: JK_FF port map (S_HIGH, S_HIGH, INPUT, Z(0), open); JK2: JK_FF port map (S2, S_HIGH, Z(0), Z(1), open); JK3: JK_FF port map (S_HIGH, S_HIGH, Z(1), Z(2), open); JK4: JK_FF port map (S1, S_HIGH, Z(0), Z(3), S2); end STRUCTURAL;
Above code models a decade counter. In the code, you can see we’ve used every syntax that is explained in this article so far.
Mixed Modeling Style
Now that we have studied dataflow modeling, behavioral modeling, and structural modeling styles in VHDL, you should know that you can mix all of these styles up when you are defining a circuit. Use the most optimum method you can think of and let your compiler handle the rest.
This sums up the structural modeling in VHDL. The comment box below awaits your queries if you have any. Next up in this VHDL course, we will study the different operators available in VHDL.