Make the changes shown in red:
In SystemVerilog, setting the initial value of a static variable as part of the variable declaration (including static class members) shall occur before any initial or always blocks are started, and so they do not generate an event. If an event is needed, an initial block should be used to assign the initial values.
Initial values in SystemVerilog are not
constrained to simple constants; they may include run-time expressions,
including dynamic memory allocation. For example, a static class handle or a mailbox can be created and initialized by
calling its new method (see section 11.4), or static variables can be
initialized to random values by calling the $urandom
system task. This
requires a special pre-initial pass at run-time.
Make the changes shown in red:
Note that automatic or dynamic variables cannot be used to trigger an event expression or be written with a nonblocking assignment. Automatic variables and dynamic constructs—objects handles, dynamic arrays, associative arrays, strings, and event variables — are limited to the procedural context.
Replace the entire Section 15 with the following in red:
Program
Block
15.1
Introduction (informative)
The module is the basic building block
in Verilog. Modules can contain hierarchies of other modules, wires, task and
function declarations, and procedural statements within always and initial blocks. This construct works extremely
well for the description of hardware. However, for the testbench, the emphasis
is not in the hardware-level details such as wires, structural hierarchy, and
interconnects, but in modeling the complete environment in which a design is
verified. A lot of effort is spent getting the environment properly initialized
and synchronized, avoiding races between the design and the testbench,
automating the generation of input stimuli, and reusing existing models and
other infrastructure.
The program block serves three basic purposes:
1)
It
provides an entry point to the execution of testbenches.
2)
It
creates a scope that encapsulates program-wide data.
3)
It
provides a syntactical context that specifies execution in the Reactive region.
The program construct serves as a clear separator
between design and testbench, and, more importantly, it specifies specialized
execution semantics in the Reactive
region for all elements declared within the program. Together with clocking
domains, the program construct provides for race-free interaction between the
design and the testbench, and enables cycle and transaction level abstractions.
The
abstraction and modeling constructs of SystemVerilog simplify the creation and
maintenance of testbenches. The ability to instantiate and individually connect
each program instance enables their use as generalized models.
15.2
The program construct
A typical
program contains type and data declarations, subroutines, connections to the
design, and one or more procedural code streams. The connection between design
and test-bench uses the same interconnect mechanism as used by SystemVerilog to
specify port connections, including interfaces. The syntax for the program
block is:
program_declaration ::= //
From Annex A ??
{ attribute_instance } program
[ lifetime ] program_identifier [ parameter_port_list ]
[ list_of_ports ] ; [ timeunits_declaration ] { program_item }
endprogram
[ : program_identifier]
lifetime::= static | automatic
program_item ::=
{ attribute_instance } module_or_generate_item_declaration
| { attribute_instance } specparam_declaration
| { attribute_instance } local_parameter_declaration
| { attribute_instance } parameter_declaration
| { attribute_instance } clocking_declaration
| { attribute_instance } continuous_assign
| { attribute_instance }class_declaration
| { attribute_instance } initial_construct
Syntax 15-1—Program
declaration syntax (excerpt from Annex A)
For example:
program test (input clk, input [16:1] addr, inout [7:0]
data);
initial ...
endprogram
or
program test ( interface device_ifc );
initial ...
endprogram
A more complete example is included in
sections 13.7 and 13.8.
Although the program construct is new to SystemVerilog, its
inclusion is a natural extension. The program construct can be considered a
leaf module with special execution semantics. Once declared, a program block
can be instantiated in the required hierarchical location (typically at the top
level) and its ports can be connected in the same manner as any other module.
Program blocks can be nested within
modules or interfaces. This allows multiple cooperating programs to share
variables local to the scope. Nested programs with no ports or top-level
programs that are not explicitly instantiated are implicitly instantiated once.
Implicitly instantiated programs have the same instance and declaration name.
For example:
module test(...)
int shared; //
variable shared by programs p1 and p1
program p1;
...
endprogram
program p2;
...
endprogram // p1
and p2 are implicitly instantiated once in module test
endmodule
A program block can contain one or more
initial blocks.
It may not contain always blocks, UPD’s, modules,
interfaces, or other programs.
Type and data declarations within the
program are local to the program scope and have static lifetime. Program
variables can only be assigned using blocking assignments. Non-program
variables can only be assigned using nonblocking assignments. Using nonblocking
assignments with program variables or blocking assignments with design
(non-program) variables shall be an error.
15.3
Multiple programs
It is allowed to have any arbitrary
number of program definitions or instances. The programs can be fully
independent (without inter-program communication), or cooperative. The degree of communication can be controlled
by choosing to share data using nested blocks or hierarchical references
(including $root), or making the data private by declaring it inside the
corresponding program block.
15.4
Eliminating testbench races
There are two major sources of
non-determinism in Verilog. The first one is that active events are processed
in an arbitrary order. The second one is
that statements without time-control constructs in behavioral blocks do not
execute as one event. However, from the testbench perspective, these effects
are all unimportant details. The primary task of a testbench is to generate
valid input stimulus for the design under test, and to verify that the device
operates correctly. Furthermore, testbenches that use cycle abstractions are
only concerned with the stable or steady state of the system for both checking
the current outputs and for computing stimuli for the next cycle. Formal tools
also work in this fashion.
To avoid the races inherent in the
Verilog event scheduler, program statements are scheduled to execute in the Reactive region, after all clocks in the
design have triggered and the design has settled to its steady state. In
addition, design signals driven from within the program must be assigned using
nonblocking assignments. Thus, even signals driven with no delay are propagated
into the design as one event. With this behavior, correct cycle semantics can
be modeled without races; thereby making program-based testbenches compatible
with clocked assertions and formal tools.
Since the program executes in the Reactive region,
the clocking domain construct is very useful to automatically sample the
steady-state values of previous time steps or clock cycles. Programs that read
design values exclusively through clocking domains with non-zero input skews
are insensitive to read-write races. It is
important to note that simply sampling input signals (or setting non-zero skews
on clock domain inputs) does not eliminate the potential for races. Proper
input sampling only addresses a single clocking domain. With multiple clocks,
the arbitrary order in which overlapping or simultaneous clocks are processed
is still a potential source for races. The program construct addresses this
issue by scheduling its execution in the Reactive
region, after all design events have been processed, including clocks driven by
non-blocking assignments.
15.4.1 Zero-skew clocking domain races
When a clocking domain sets both input
and output skews to #0 (see section 13.3) then its inputs are sampled at the
same time as its outputs are driven. This type of zero-delay processing is a
common source of non-determinism that may result in races. Nonetheless, even in
this case, the program minimizes races by means of two mechanisms. First, by
constraining program statements to execute in the Reactive region, after all zero-delay transitions have propagated
through the design and the system has reached a quasi steady state. Second, by requiring design variables or nets to be modified only
via nonblocking assignments. These two mechanisms reduce the likelihood
of a race; nonetheless, a race is still possible when skews are set to zero.
15.5
Blocking tasks in cycle/event mode
Calling program tasks or functions from
within design modules is illegal and shall result in an error. This is because
the design must not be aware of the testbench. Programs are allowed to call
tasks or functions in other programs or within design modules. Functions within
design modules can be called from a program, and require no special handling.
However, blocking tasks within design modules that are called from a program do
require explicit synchronization upon return from the task. That is, when
blocking tasks return to the program code, the program block execution is
automatically postponed until the Reactive
region. The copy out of the parameters happens when the task returns.
Calling blocking tasks in design modules from within programs
requires careful consideration. Expressions evaluated by the task before
blocking on the first timing control will use the values after they have been updated by nonblocking assignments. In
contrast, if the task is called from a module at the start of the time step
(before nonblocking assignments are processed) then those same expressions will
use the values before they have been
updated by nonblocking assignments.
module ...
task T;
S1: a = b;
// May execute before or after the Observe
region
#5;
S2: b <= 1'b1; //
Always executes before the Observe
region
endtask
endmodule
If task T above is called from within a module then the statement S1 may execute immediately when the Active region is processed, before variable b is updated by a nonblocking assignment. If the same task is
called from within a program then
the statement S1 will execute when the Reactive region is processed, after variable b may have been updated by nonblocking assignments. Statement S2 always executes immediately after the delay expires; it does
not wait for the reactive region even though it was originally called from the
program block.
15.6
Program control tasks
In addition to the normal simulation
control tasks ($stop and $finish), a program can use the $exit control task.
15.6.1 $exit()
Each program can be finished by calling
the $exit system
task. When all programs exit, the simulation finishes.
The syntax for the $exit system task is:
task $exit();
When all initial blocks in a program finish (i.e., they execute their last
statement), the program implicitly calls $exit. Calling $exit causes all processes spawned by the current
program to be terminated.