|
10.13 Concurrent Statements
Chapter start Previous
page Next page
10.13 Concurrent Statements
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.
10.13.1 Block Statement
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.
10.13.2 Process Statement
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).
10.13.3 Concurrent Procedure Call
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;
10.13.4 Concurrent Signal Assignment
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;
10.13.5 Concurrent Assertion Statement
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.
10.13.6 Component Instantiation
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;
10.13.7 Generate Statement
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:
Chapter start Previous page Next page
|