Section 5.4

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.

 

Section 5.5

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.

 

Section 15

Replace the entire Section 15 with the following in red:

 

Section 15

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 life­time. 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 con­trol 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.