statement_item ::=
blocking_assignment ;
| nonblocking_assignment ;
| procedural_continuous_assignments ;
| case_statement
| conditional_statement
| inc_or_dec_expression ;
| function_call ;
| function_call_statement
| disable_statement
| event_trigger
| loop_statement
| jump_statement
| par_block
| procedural_timing_control_statement
| seq_block
| system_task_enable task_enable_statement
| task_enable
| wait_statement
| procedural_assertion_item
| clocking_drive
| void ’ ( function_call
)
| randsequence
| scope_randomize
| randcase_statement
| expect ( property_spec ) [ action_block ]
| expect ( property_instance ) [ action_block
]
| expect_property_statement
blocking_assignment ::=
variable_lvalue =
delay_or_event_control expression
| hierarchical_variable_identifier = new [ constant_expression ] [ ( variable_identifier
) ]
| hierarchical_dynamic_array_variable_identifier
[ ] = dynamic_array_new
| class_variable_identifier = new [ ( list_of_arguments ) ] class_new
| class_variable_identifier . randomize [ ( ) ] with constraint_block
;
| operator_assignment
conditional_statement ::= //
from Annex A.6.6
if ( expression cond_predicate
) statement_or_null [ else statement_or_null ]
| unique_priority_if_statement
unique_priority_if_statement ::=
[ unique_priority
] if (expression cond_predicate) statement_or_null
{ else if (expression cond_predicate) statement_or_null }
[ else statement_or_null ]
unique_priority ::= unique
| priority
cond_predicate
::=
expression_or_cond pattern { &&
expression_or_cond_pattern }
expression_or_cond_pattern ::=
expression | cond_pattern
cond_pattern ::= expression matches
pattern
case_statement ::= //
from Annex A.6.7
[ unique_priority ] case ( expression ) case_item { case_item } endcase
| [ unique_priority
] casez ( expression ) case_item { case_item } endcase
| [ unique_priority
] casex ( expression ) case_item { case_item } endcase
[ unique_priority ] case_keyword ( expression ) case_item { case_item } endcase
| [ unique_priority
] case_keyword ( expression ) matches
case_pattern_item { case_pattern_item } endcase
case_keyword ::= case | casez
| casex
case_item ::=
expression { , expression } : statement_or_null
| default
[ : ] statement_or_null
case_pattern_item ::=
pattern [ && expression ] : statement_or_null
| default
[ : ] statement_or_null
8.4.1 Pattern matching
Pattern matching provides a visual and succinct notation to compare
a value against structures, tagged unions and constants, and to access their
members. SystemVerilog adds pattern matching capability to case
and if statements, and to conditional expressions. Before describing
pattern matching in those contexts, we first describe the general concepts.
A pattern is a nesting of tagged union and structure expressions with
identifiers, constant expressions, and the wildcard pattern “.*” at the leaves. For tagged union patterns,
the identifier following the tagged keyword is a union member name. For void
members the nested member pattern is omitted.
pattern ::= //
from Annex A.6.7.1
variable_identifier
| .*
| . constant_expression
| tagged
member_identifier [ pattern ]
| { pattern , ... , pattern }
| { member_identifier : pattern , ... , member_identifier
: pattern }
Syntax 8-4—Pattern syntax (excerpt from Annex A)
A pattern always occurs in a context of known type because it is
matched against an expression of known type. Recursively, its nested patterns
also have known type. A constant expression pattern must be of integral type.
Thus a pattern can always be statically type-checked.
Each pattern introduces a new scope; the extent of this scope is
described separately below for case statements, if statements and conditional
expressions. Each pattern identifier is implicitly declared as a new variable
within the pattern's scope, with the unique type that it must have based on its
position in the pattern. Pattern identifiers must be unique in the pattern,
i.e., the same identifier cannot be used in more than one position in a single
pattern.
In pattern-matching, we always match the value V of an expression
against a pattern. Note that static type-checking ensures that V and the
pattern have the same type. The result of a pattern match is:
—
A 1-bit
determined value: 0 (false, or fail) or 1 (true, or succeed). The result cannot
be x or z even if the value and pattern contain such bits.
—
If the
match succeeds, the pattern identifiers are bound to the corresponding members
from V, using ordinary procedural assignment.
Each pattern is matched using the following simple recursive rule:
— An identifier pattern always succeeds
(matches any value), and the identifier is bound to that value (using ordinary
procedural assignment).
— The wildcard pattern “.*” always
succeeds.
— A constant expression pattern succeeds if V
is equal to its value.
— A tagged union pattern succeeds if the
value has the same tag and, recursively, if the nested pattern matches the
member value of the tagged union.
— A structure pattern succeeds if,
recursively, each of the nested member patterns matches the corresponding
member values in V. In structure patterns with named members, the textual order
of members does not matter, and members may be omitted. Omitted members are
ignored.
Conceptually, if the value V is seen as a flattened vector of bits,
the pattern specifies which bits to match, with what values they should be
matched and, if the match is successful, which bits to extract and bind to the
pattern identifiers. Matching can be insensitive to x and z values, as
described in the individual constructs below.
8.4.1.1 Pattern matching in case statements
In a pattern-matching case statement, the expression in parentheses
is followed by the keyword matches, and the statement contains a series of “case_pattern_items”. The left-hand side of a case item,
before the “:”, consists of a pattern and, optionally, the
operator && followed by a boolean “filter” expression. The right-hand side of a
case item is a statement. Each pattern introduces a new scope, in which its
pattern identifiers are implicitly declared; this scope extends to the optional
filter expression and the statement in the right-hand side of the same case
item. Thus different case items can reuse pattern identifiers.
All the patterns are completely statically type-checked. The
expression being tested in the pattern-matching case statement must have a
known type, which is the same as the type of the pattern in each case item.
The expression in parentheses in a pattern-matching case statement
shall be evaluated exactly once. Its value V shall be matched against the
left-hand sides of the case items, one at a time, in the exact order given,
ignoring the default case item if any. During this linear search, if a case
item is selected, its statement is executed and the linear search is
terminated. If no case item is selected, and a default case item is given, then
its statement is executed. If no case item is selected and no default case item
is given, no statement is executed.
For a case item to be selected, the value V must match the pattern
(and the pattern identifiers are assigned the corresponding member values in V ) and then the boolean filter
expression must evaluate to true (a determined value other than 0).
Example:
typedef union tagged {
void Invalid;
int Valid;
} VInt;
...
VInt v;
...
case (v) matches
tagged Invalid : $display ("v is Invalid");
tagged Valid n : $display ("v is Valid with
value %d", n);
endcase
In the case statement, if v currently has the Invalid tag, the
first pattern is matched. Otherwise, it must have the Valid tag, and the second pattern is matched.
The identifier n is bound to the value of the Valid member, and this value is displayed. It is
impossible to access the integer member value (n) when the tag is Invalid.
Example:
typedef union tagged {
struct {
bit [4:0] reg1, reg2, regd;
} Add;
union tagged
{
bit [9:0] JmpU;
struct {
bit [1:0] cc,
bit [9:0] addr;
} JmpC;
} Jmp;
} Instr;
...
Instr instr;
...
case (instr) matches
tagged Add {r1,r2,rd}
&& (rd != 0): rf[rd] = rf[r1]
+ rf[r2];
tagged Jmp j : case (j) matches
tagged JmpU a : pc = pc +
a;
tagged JmpC {c,a}: if (rf[c]) pc = a;
endcase
endcase
If instr holds an Add instruction, the first pattern is matched, and the
identifiers r1, r2 and rd are bound to
the three register fields in the nested structure value. The right-hand side
statement executes the instruction on the register file rf.
It is impossible to access these register fields if the tag is Jmp. If instr holds a Jmp instruction, the second pattern is
matched, and the identifier j is bound
to the nested tagged union value. The inner case statement, in turn, matches
this value against JmpU and JmpC patterns, and so on.
Example (same as previous example, but using wildcard and constant
patterns to eliminate the rd = 0 case; in some processors, register 0 is a special “discard”
register):
case (instr) matches
tagged Add {.*,.*, . 0}: ; // no op
tagged Add {r1,r2, rd} : rf[rd]
= rf[r1] + rf[r2];
tagged Jmp j : case (j) matches
tagged JmpU a : pc =
pc + a;
tagged JmpC {c,a}: if (rf[c]) pc = a;
endcase
endcase
Example (same as previous example, but note that the first inner
case statement involves only structures and constants but no tagged unions):
case (instr) matches
tagged Add s: case
(s) matches
{.*,.*, . 0}: ; // no
op
{r1,r2, rd}: rf[rd] = rf[r1] + rf[r2];
endcase
tagged Jmp j: case (j) matches
tagged JmpU a : pc = pc +
a;
tagged JmpC {c,a}: if (rf[c]) pc = a;
endcase
endcase
Example (same as previous example, but using nested tagged union
patterns):
case (instr)
matches
tagged Add {r1,r2,rd}
&& (rd != 0) : rf[rd]
= rf[r1] + rf[r2];
tagged Jmp (tagged JmpU
a) : pc = pc + a;
tagged Jmp (tagged JmpC {c,a}) : if (rf[c]) pc = a;
endcase
Example (same as previous example, with named structure
components):
case (instr) matches
tagged Add {reg2:r2,regd:rd,reg1:r1}
&& (rd != 0): rf[rd] = rf[r1]
+ rf[r2];
tagged Jmp (tagged JmpU
a) : pc = pc + a;
tagged Jmp (tagged JmpC {addr:a,cc:c}) : if (rf[c])
pc = a;
endcase
As usual, the casez and casex keywords can be used instead of case, with the same semantics. In other words, during
pattern matching, wherever two bits are compared (whether they are tag bits or
members), the casez
form ignores z bits,
and the casex
form ignores both z
and x bits.
The priority and unique qualifiers play their usual
role. priority
implies that some case item must be selected. unique also implies that exactly
one case item will be selected, so that they may be evaluated in parallel.
8.4.1.2 Pattern matching in if statements
The
predicate in an if statement can be a series of clauses
separated with the && operator. Each clause is either an expression
(used as a boolean filter),
or has the form expression matches pattern. The clauses represent a sequential conjunction from left to
right, i.e., if any clause fails the remaining clauses are not evaluated, and
all of them must succeed for the predicate to be true. Boolean expression
clauses are evaluated as usual. Each pattern introduces a new scope, in which
its pattern identifiers are implicitly declared; this scope extends to the
remaining clauses in the predicate and to the corresponding “true” arm of the if statement.
In
each e matches p clause, e and p must have the same known statically
known type. The value of e is matched
against the pattern p as described
above.
Even
though the pattern matching clauses always return a defined 1-bit result, the
overall result may be ambiguous because of the boolean filter expressions in the predicate. The standard
semantics of if statements hold, i.e., the first statement is executed if and
only if the result is a determined value other than 0.
Example:
if
(e matches (tagged Jmp (tagged JmpC {cc:c,addr:a})))
... // c and a can be used here
else
...
Example
(same as previous example, illustrating a sequence of two pattern-matches with
identifiers bound in the first pattern used in the second pattern).
if
(e matches (tagged Jmp j),
j matches (tagged JmpC {cc:c,addr:a}))
... // c and a can be used here
else
...
Example
(same as first example, but adding a boolean
expression to the sequence of clauses). The idea expressed is: “if e is a
conditional jump instruction and the condition register is not equal to zero
...”.
if
(e matches (tagged Jmp (tagged JmpC {cc:c,addr:a}))
&& (rf[c] != 0))
... // c and a can be used here
else
...
The priority and unique qualifiers play their usual role for if statements even if they use pattern matching.
8.4.1.3 Pattern matching in conditional expressions
A conditional expression (e1 ? e2 : e3) can
also use pattern matching, i.e., the predicate e1 can be a sequence of expressions and “expression matches pattern” clauses separated with
the && operator, just like the predicate of an if statement. The
clauses represent a sequential conjunction from left to right, i.e., if any
clause fails the remaining clauses are not evaluated, and all of them must
succeed for the predicate to be true. Boolean expression clauses are evaluated
as usual. Each pattern introduces a new scope, in which its pattern identifiers
are implicitly declared; this scope extends to the remaining clauses in the
predicate and to the consequent expression e2.
As described in the previous section, e1 may evaluate to true, false or an ambiguous value. The semantics
of the overall conditional expression are described in Section 7.18, based on
these three possible outcomes for e1.
If the expression denotes a
clocking- domain block input
or inout (see Section 15), the event control operator uses the
synchronous values, that is, the values sampled by the clocking event. The
expression can also denote a clocking-domain
block name (with no edge qualifier) to be
triggered by the clocking event.
A variable used
with the event control can be any one of the integral data types (see Section
3.3.1) or string. The variable can be either a simple variable or a ref argument
(variable passed by reference); it can be a member of an array,
associative-array, or object (class instance) of the aforementioned types. Objects (handles) and aggregate types are
not allowed.
Event control
variables can include object data members, in which case the object handle is
evaluated only once when the event control statement is executed. Likewise, an
object data member in an event control shall block until that particular data
member changes value, not when the handle to the object is modified. For
example:
Event
expressions must return singular values. Aggregate types
may be used in an expression provided the expression reduces to a singular
value. The object members or aggregate elements may be any type as long as the
result of the expression is a singular value.
If the event expression is a reference to a simple object handle or chandle variable, an event is created when a write to that
variable is not equal to its previous value.
Non-virtual methods of an
object and built-in methods or system functions for an aggregate type are
allowed in event control expressions as long as the type of the return value is
singular and the method is defined as a function, not a task.
Changing the value of object data members, aggregate elements, or the size of a
dynamically sized array referenced by a method or function shall cause the
event expression to be re-evaluated. An implementation may cause the event
expression to be re-evaluated when changing the value or size even if the
members are not referenced by the method or function.
real AOR[]; // dynamic
array of reals
byte stream[$]; //
queue of bytes
initial wait(AOR.size() > 0) ....; // waits for array to be
allocated.
initial
wait($bits(stream) > 60)...; // waits for total number of bits in stream
greater than 60
Packer Packet p = new; // Packet 1
Packet q = new; // Packet 2
initial fork
@(p.status); // Wait for status in Packet 1 to change
@ q; //
Wait for a change to handle q
# 10 q = p; // triggers @q.
p = q; // Has
no effect on the wait in Process 1
// @(p.status) now waits for status
in Packet 2 to change, if not already different from Packet 1
join_none join
// @(p.status) continues to wait for status of Packet 1 to
change