Chapter start Previous page Next page
A concurrent statement [VHDL LRM9] is one of the following statements:
concurrent_statement ::= block_statement | process_statement | [ label : ] [ postponed ] procedure_call ; | [ label : ] [ postponed ] assertion ; | [ label : ] [ postponed ] conditional_signal_assignment | [ label : ] [ postponed ] selected_signal_assignment | component_instantiation_statement | generate_statement
The following sections describe each of these statements in turn.
A block statement has the following format [VHDL LRM9.1]:
block_statement ::=
block_label: block [(guard_expression)] [is]
[generic (generic_interface_list);
[generic map (generic_association_list);]]
[port (port_interface_list);
[port map (port_association_list);]]
{block_declarative_item}
begin
{concurrent_statement}
end block [block_label] ;
Blocks may have their own ports and generics and may be used to split an architecture into several hierarchical parts (blocks can also be nested). As a very general rule, for the same reason that it is better to split a computer program into separate small modules, it is usually better to split a large architecture into smaller separate entity-architecture pairs rather than several nested blocks.
A block does have a unique
feature: It is possible to specify a guard expression for a block. This
creates a special signal, GUARD , that you can use within the
block to control execution [VHDL LRM9.5].
It also allows you to model three-state buses by declaring guarded signals
(signal kinds register and bus).
When you make an assignment
statement to a signal, you define a driver for that signal. If you make
assignments to guarded signals in a block, the driver for that signal is
turned off, or disconnected, when the GUARD signal is FALSE
. The use of guarded signals and guarded blocks can become quite complicated,
and not all synthesis tools support these VHDL features.
The following example shows
two drivers, A and B , on a three-state bus TSTATE
, enabled by signals OEA and OEB . The drivers
are enabled by declaring a guard expression after the block declaration
and using the keyword guarded in the assignment statements.
A disconnect statement [VHDL LRM5.3]
models the driver delay from driving the bus to the high-impedance state
(time to "float").
library ieee; use ieee.std_logic_1164.all;
entity bus_drivers is end;
architecture Structure_1 of bus_drivers is
signal TSTATE: STD_LOGIC bus; signal A, B, OEA, OEB : STD_LOGIC:= '0';
begin
process begin OEA <= '1' after 100 ns, '0' after 200 ns;
OEB <= '1' after 300 ns; wait; end process;
B1 : block (OEA = '1')
disconnect all : STD_LOGIC after 5 ns; -- Only needed for float time.
begin TSTATE <= guarded not A after 3 ns; end block;
B2 : block (OEB = '1')
disconnect all : STD_LOGIC after 5 ns; -- Float time = 5 ns.
begin TSTATE <= guarded not B after 3 ns; end block;
end;
1 2 3 4 5 6 7
Time(fs) + Cycle tstate a b oea oeb b1.GUARD b2.GUARD
---------------------- ------ ---- ---- ---- ---- -------- --------
0+ 0: 'U' '0' '0' '0' '0' FALSE FALSE
0+ 1: * 'Z' '0' '0' '0' '0' FALSE FALSE
100000000+ 0: 'Z' '0' '0' *'1' '0' * TRUE FALSE
103000000+ 0: * '1' '0' '0' '1' '0' TRUE FALSE
200000000+ 0: '1' '0' '0' *'0' '0' * FALSE FALSE
200000000+ 1: * 'Z' '0' '0' '0' '0' FALSE FALSE
300000000+ 0: 'Z' '0' '0' '0' *'1' FALSE * TRUE
303000000+ 0: * '1' '0' '0' '0' '1' FALSE TRUE
Notice the creation of implicit guard signals b1.GUARD and b2.GUARD for each guarded block. There is another, equivalent, method that uses the high-impedance value explicitly as in the following example:
architecture Structure_2 of bus_drivers is signal TSTATE : STD_LOGIC; signal A, B, OEA, OEB : STD_LOGIC := '0'; begin process begin OEA <= '1' after 100 ns, '0' after 200 ns; OEB <= '1' after 300 ns; wait; end process; process(OEA, OEB, A, B) begin if (OEA = '1') then TSTATE <= not A after 3 ns; elsif (OEB = '1') then TSTATE <= not B after 3 ns; else TSTATE <= 'Z' after 5 ns; end if; end process; end;
This last method is more widely
used than the first, and what is more important, more widely accepted by
synthesis tools. Most synthesis tools are capable of recognizing the value
'Z' on the RHS of an assignment statement as a cue to synthesize
a three-state driver. It is up to you to make sure that multiple drivers
are never enabled simultaneously to cause contention.
A process statement has the following format [VHDL LRM9.2]:
process_statement ::=
[process_label:]
[postponed] process [(signal_name {, signal_name})]
[is] {subprogram_declaration | subprogram_body
| type_declaration | subtype_declaration
| constant_declaration | variable_declaration
| file_declaration | alias_declaration
| attribute_declaration | attribute_specification
| use_clause
| group_declaration | group_template_declaration}
begin
{sequential_statement}
end [postponed] process [process_label];
The following process models a 2:1 MUX (combinational logic):
entity Mux_1 is port (i0, i1, sel : in BIT := '0'; y : out BIT); end; architecture Behave of Mux_1 is begin process (i0, i1, sel) begin -- i0, i1, sel = sensitivity set case sel is when '0' => y <= i0; when '1' => y <= i1; end case; end process; end;
This process executes whenever an event occurs on any of the signals in the process sensitivity set (i0, i1, sel). The execution of a process occurs during a simulation cycle--a delta cycle. Assignment statements to signals may trigger further delta cycles. Time advances when all transactions for the current time step are complete and all signals updated.
The following code models a two-input AND gate (combinational logic):
entity And_1 is port (a, b : in BIT := '0'; y : out BIT); end; architecture Behave of And_1 is begin process (a, b) begin y <= a and b; end process; end;
The next example models a
D flip-flop (sequential logic). The process statement is executed
whenever there is an event on clk . The if statement
updates the output q with the input d on the rising
edge of the signal clk . If the if statement condition
is false (as it is on the falling edge of clk ), then the assignment
statement q <= d will not be executed, and q
will keep its previous value. The process thus requires the value of q
to be stored between successive process executions, and this implies sequential
logic.
entity FF_1 is port (clk, d: in BIT := '0'; q : out BIT); end; architecture Behave of FF_1 is begin process (clk) begin if clk'EVENT and clk = '1' then q <= d; end if; end process; end;
The behavior of the next example
is identical to the previous model. Notice that the wait statement
is at the end of the equivalent process with the signals in the sensitivity
set (in this case just one signal, clk ) included in the sensitivity
list (that follows the keyword on ).
entity FF_2 is port (clk, d: in BIT := '0'; q : out BIT); end; architecture Behave of FF_2 is begin process begin -- The equivalent process has a wait at the end: if clk'event and clk = '1' then q <= d; end if; wait on clk; end process; end;
If we use a wait
statement in a process statement, then we may not use a process
sensitivity set (the reverse is true: If we do not have a sensitivity set
for a process, we must include a wait statement or the process
will execute endlessly):
entity FF_3 is port (clk, d: in BIT := '0'; q : out BIT); end; architecture Behave of FF_3 is begin process begin -- No sensitivity set with a wait statement. wait until clk = '1'; q <= d; end process; end;
If you include ports (interface
signals) in the sensitivity set of a process statement, they
must be ports that can be read (they must be of mode in , inout
, or buffer , see Section 10.7).
A concurrent procedure
call appears outside a process statement [VHDL
LRM9.3]. The concurrent procedure call is a shorthand way of writing
an equivalent process statement that contains a procedure call
(Section 10.10.4):
package And_Pkg is procedure V_And(a,b:BIT; signal c:out BIT); end; package body And_Pkg is procedure V_And(a,b:BIT; signal c:out BIT) is begin c <= a and b; end; end And_Pkg; use work.And_Pkg.all; entity Proc_Call_2 is end; architecture Behave of Proc_Call_2 is signal A, B, Y : BIT := '0'; begin V_And (A, B, Y); -- Concurrent procedure call. process begin wait; end process; -- Extra process to stop. end;
There are two forms
of concurrent signal assignment statement. A selected signal assignment
statement is equivalent to a case statement inside a process
statement [VHDL LRM9.5.2]:
selected_signal_assignment ::=
with expression select
name|aggregate <= [guarded]
[transport|[reject time_expression] inertial]
waveform when choice {| choice}
{, waveform when choice {| choice} } ;
The following design unit,
Selected_1, uses a selected signal assignment. The equivalent unit, Selected_2,
uses a case statement inside a process statement.
entity Selected_1 is end; architecture Behave of Selected_1 is signal y,i1,i2 : INTEGER; signal sel : INTEGER range 0 to 1; begin with sel select y <= i1 when 0, i2 when 1; end; entity Selected_2 is end; architecture Behave of Selected_2 is signal i1,i2,y : INTEGER; signal sel : INTEGER range 0 to 1; begin process begin case sel is when 0 => y <= i1; when 1 => y <= i2; end case; wait on i1, i2; end process; end;
The other form of concurrent
signal assignment is a conditional signal assignment statement that, in
its most general form, is equivalent to an if statement inside
a process statement [VHDL
LRM9.5.1]:
conditional_signal_assignment ::=
name|aggregate <= [guarded]
[transport|[reject time_expression] inertial]
{waveform when boolean_expression else}
waveform [when boolean_expression];
Notice that in VHDL-93 the
else clause is optional. Here is an example of a conditional
signal assignment, followed by a model using the equivalent process with
an if statement:
entity Conditional_1 is end; architecture Behave of Conditional_1 is signal y,i,j : INTEGER; signal clk : BIT; begin y <= i when clk = '1' else j; -- conditional signal assignment end; entity Conditional_2 is end; architecture Behave of Conditional_2 is signal y,i : INTEGER; signal clk : BIT; begin process begin if clk = '1' then y <= i; else y <= y ; end if; wait on clk; end process; end;
A concurrent signal assignment statement can look just like a sequential signal assignment statement, as in the following example:
entity Assign_1 is end; architecture Behave of Assign_1 is signal Target, Source : INTEGER; begin Target <= Source after 1 ns; -- looks like signal assignment end;
However, outside a process
statement, this statement is a concurrent signal assignment and has its
own equivalent process statement. Here is the equivalent process
for the example:
entity Assign_2 is end; architecture Behave of Assign_2 is signal Target, Source : INTEGER; begin process begin Target <= Source after 1 ns; wait on Source; end process; end;
Every process is executed once
during initialization. In the previous example, an initial value will be
scheduled to be assigned to Target even though there is no
event on Source . If, for some reason, you do not want this
to happen, you need to rewrite the concurrent assignment statement as a
process statement with a wait statement before
the assignment statement:
entity Assign_3 is end; architecture Behave of Assign_3 is signal Target, Source : INTEGER; begin process begin wait on Source; Target <= Source after 1 ns; end process; end;
A concurrent assertion
statement is equivalent to a passive process statement (without
a sensitivity list) that contains an assertion statement followed
by a wait statement [VHDL
LRM9.4].
concurrent_assertion_statement ::= [ label : ] [ postponed ] assertion ;
If the assertion condition
contains a signal, then the equivalent process statement will
include a final wait statement with a sensitivity clause. A
concurrent assertion statement with a condition that is static expression
is equivalent to a process statement that ends in a wait
statement that has no sensitivity clause. The equivalent process will execute
once, at the beginning of simulation, and then wait indefinitely.
A component instantiation statement in VHDL is similar to placement of a component in a schematic--an instantiated component is somewhere between a copy of the component and a reference to the component. Here is the definition [VHDL LRM9.6]:
component_instantiation_statement ::= instantiation_label: [component] component_name |entity entity_name [(architecture_identifier)] |configuration configuration_name [generic map (generic_association_list)] [port map (port_association_list)] ;
We examined component instantiation using a component_name in Section 10.5. If we instantiate a component in this way we must declare the component (see BNF [10.9]). To bind a component to an entity-architecture pair we can use a configuration, as illustrated in Figure 10.1, or we can use the default binding as described in Section 10.7. In VHDL-93 we have another alternative--we can directly instantiate an entity or configuration. For example:
entity And_2 is port (i1, i2 : in BIT; y : out BIT); end; architecture Behave of And_2 is begin y <= i1 and i2; end; entity Xor_2 is port (i1, i2 : in BIT; y : out BIT); end; architecture Behave of Xor_2 is begin y <= i1 xor i2; end; entity Half_Adder_2 is port (a,b : BIT := '0'; sum, cry : out BIT); end; architecture Netlist_2 of Half_Adder_2 is use work.all; -- need this to see the entities Xor_2 and And_2 begin X1 : entity Xor_2(Behave) port map (a, b, sum); -- VHDL-93 only A1 : entity And_2(Behave) port map (a, b, cry); -- VHDL-93 only end;
A generate statement [VHDL LRM9.7] simplifies repetitive code:
generate_statement ::=
generate_label: for generate_parameter_specification
|if boolean_expression
generate [{block_declarative_item} begin]
{concurrent_statement}
end generate [generate_label] ;
Here is an example (notice the labels are required):
entity Full_Adder is port (X, Y, Cin : BIT; Cout, Sum: out BIT); end; architecture Behave of Full_Adder is begin Sum <= X xor Y xor Cin; Cout <= (X and Y) or (X and Cin) or (Y and Cin); end; entity Adder_1 is port (A, B : in BIT_VECTOR (7 downto 0) := (others => '0'); Cin : in BIT := '0'; Sum : out BIT_VECTOR (7 downto 0); Cout : out BIT); end; architecture Structure of Adder_1 is use work.all; component Full_Adder port (X, Y, Cin: BIT; Cout, Sum: out BIT); end component; signal C : BIT_VECTOR(7 downto 0); begin AllBits : for i in 7 downto 0 generate LowBit : if i = 0 generate FA : Full_Adder port map (A(0), B(0), Cin, C(0), Sum(0)); end generate; OtherBits : if i /= 0 generate FA : Full_Adder port map (A(i), B(i), C(i-1), C(i), Sum(i)); end generate; end generate; Cout <= C(7); end;
The instance names within
a generate loop include the generate parameter.
For example for i=6 , FA'INSTANCE_NAME is
:adder_1(structure):allbits(6):otherbits:fa: