conditional_expression ::=
expression1 expression ? { attribute_instance } expression2 expression :
expression3
expression
=> ->
7.10.1 Built-in namespace package
SystemVerilog
provides a built-in namespace package that contains system
types (e.g., classes), variables, tasks, and
functions. The builtin
namespace resides at the top of the hierarchy. Users may not insert additional declarations into
the built-in namespace package. The built-in package
is implicitly wildcard imported into the compilation-unit scope of every
compilation unit (see Section 18.3). The
Thus, declarations in the built-in namespace package are
directly available in any scope, (like system tasks and functions,) and can be unless
they are redefined by user code. in any other
scope. However, unlike system tasks and functions, the tasks and functions in
the built-in namespace may not be redefined by PLI functions.
built_in_data_type ::= [ std :: ] data_type_identifier // Not in Annex A
built_in_function_call ::= [std :: ] built_in_identifier
[ ( list_of_arguments
) ]
The package name std
followed by the scope resolution operator :: with no identifier on the left can be used to unambiguously
access names in the built-in namespace package. For example:
std::sys_task(); //
unambiguously call the system provided sys_task
Unlike
system tasks and functions, tasks and functions in the built-in namespace package need not be prefixed with a $ to avoid collisions with user-defined identifiers. This mechanism allows
functional extensions to the language in a backward compatible manner, without
the addition of new keywords or polluting
local name spaces.
Aggregate expressions can be copied in an assignment, through a
port, or as an argument to a task or function. Aggregate expressions can also
be compared with equality or inequality operators. To be copied or compared,
the type of an aggregate expression must be equivalent. See section 5.8.1
Equivalent Types.
Unpacked structures structure types are equivalent by the
hierarchical name of its their type alone. This means
in order to have two equivalent unpacked structures in two different scopes,
the type must be defined in one of the following ways:
— Defined in a higher-level
scope common to both expressions.
— Passed through type
parameter.
— Imported by hierarchical
reference through an interface port.
Unpacked arrays types are
equivalent by having equivalent element types and identical shape. Shape is
defined as the number of dimensions and the number of elements in each
dimension, not the actual range of the dimension.
There are various kinds of arithmetic that can be
useful: saturating, arbitrary size floating point, carry save etc. It is convenient to use the normal arithmetic
operators for readability, rather than relying on function calls.
overload_declaration ::= // from Annex A.2.8
bind overload_operator function data_type function_identifier (overload_proto_formals)
overload_operator ::= + | ++ | - | -- | *
| ** | / | % | == | != | < | <=
| > | >= | =
overload_proto_formals ::= data_type {, data_type} ;
Syntax 7-2—operator overloading syntax (excerpt from Annex A)
The overload declaration allows the arithmetic operators
to be applied to data types that are normally illegal for them, such as
unpacked structures. It does not change
the meaning of the operators for those types where it is legal to apply them. This means that such code does not change
behaviour when operator overloading is used.
The overload declaration links an operator to a
function prototype. The arguments are
matched, and the type of the result is then checked. Multiple functions may have the same arguments
and different return types. If no
expected type exists because the operator is in a self-determined context, then
a cast must be used to select the correct function. Similarly if more than one expected type is
possible, due to nested operators, and could match more than one function, a cast
must be used to select the correct function.
An expected result type exists in any of the following
contexts:
·
Right hand side of an assignment or
assignment expression
·
Actual input argument of a task or function
call
·
Input port connection of a module, interface
or program
·
Actual parameter to a module, interface,
program or class
·
Relational operator with unambiguous
comparison
·
Inside a cast
Two functions cannot have the same arguments and
different return types. For example,
suppose there is a structure type float:
typedef struct {
bit sign;
bit [3:0] exponent;
bit [10:0] mantissa;
} float;
The + operator can be applied to this structure by
invoking a function as indicated in the overloading declarations below:
bind + function float faddif(int, float);
bind + function float faddfi(float, int);
bind + function float faddrf(real,
float);
bind + function float faddrf(shortreal, float);
bind + function float faddfr(float, real);
bind + function float faddfr(float, shortreal);
bind + function float faddff(float, float);
bind + function float fcopyf(float); // unary
+
bind + function float fcopyi(int); // unary +
bind + function float fcopyr(real); //
unary +
bind + function float fcopyr(shortreal); // unary +
float A, B, C, D;
assign A = B + C; //equivalent
to A = faddff(B, C);
assign D = A + 1.0; //equivalent to A = faddfr(A, 1.0);
The overloading declaration links the +
operator to each function prototype according to the equivalent argument types
in the overloaded expression, which normally must match exactly. The exception is if the actual argument is an
integer type and there is only one prototype with a corresponding integer
argument, the actual is implicitly cast to the type in the prototype.
Note that the function prototype does not need to
match the actual function declaration exactly.
If it does not, then the normal implicit casting rules apply when
calling the function. For example the fcopyi function can be defined with an int
argument:
function float fcopyi (int i);
float o;
o.sign = i[31];
o.exponent = 0;
o.mantissa = 0;
…
return o;
endfunction
Overloading the assignment operator also serves to
overload implicit assignments or casting.
Here these are using the same functions as the unary +.
bind = function float fcopyi(int); //
cast int to float
bind = function float fcopyr(real); // cast real to float
bind = function float fcopyr(shortreal); //
cast shortreal to float
The operators that can be overloaded are the
arithmetic operators, the relational operators and assignment. Note that the assignment operator from a
float to a float cannot be overloaded here because it is already legal. Similarly, equality and inequality between
floats cannot be overloaded.
No format can be assumed for 0 or 1, so the user
cannot rely on subtraction to give equality, or on addition to give
increment. Similarly no format can be
assumed for positive or negative, so comparison must be explicitly coded.
An assignment operator such as += is automatically
built from both the + and = operators successively, where the = has its normal
meaning. For example
float A, B;
bind + function float faddff(float,
float);
always @(posedge clock) A += B; //
equivalent to A = A + B
The scope and
visibility of the overload declaration follows the same search rules as a data
declaration. The overload declaration
must be defined before use in a scope which is visible. The function bound by the overload declaration
uses the same scope search rules as a function enable from the scope where the
operator is invoked.
7.17 Streaming
operators (pack / unpack)
The
bit-stream casting described in Section 3.16 is most useful when the conversion
operation can be easily expressed using only a type cast, and the specific
ordering of the bit-stream is not important. Sometimes, however, a stream that
matches a particular machine organization is required. The streaming operators
perform packing of bit-stream types (see Section 3.16) into a sequence of bits
in a user-specified order. When used in the left-hand-side, the streaming
operators perform the reverse operation, unpack a
stream of bits into one or more variables. If the data being packed contains any 4-state types, the result of a pack
operation is a 4-state stream; otherwise,
the result of a pack is a 2-state stream. Unpacking a 4-state stream
into a 2-state type is done by a cast to a 2-state variable, and vice-versa.
The
syntax of the bit-stream concatenation is:
streaming_expression ::= { stream_operator
[ slice_size ] stream_concatenation
} From
Annex A.8.1
stream_operator ::= >> | <<
slice_size ::= type_identifier
| constant_expression
stream_concatenation ::= { stream_expression
{ , stream_expression
} }
stream_expression ::=
expression
| array_identifier [ with [ array_range_expression
] ]
array_range_expression ::=
expression
| expression : expression
| expression +: expression
| expression -: expression
primary ::= From
Annex A.8.4
…
|
streaming_expression
Syntax 7-3—streaming
concatenation syntax (excerpt from Annex A)
The
stream-operator determines the order in which data is streamed: >> causes data to be streamed in
left-to-right order, while << causes data to be streamed in right-to-left
order. If a slice-size is specified then the data to be streamed is first
broken up into slices with the specified number of bits, and then the slices
are streamed in the specified order. If a slice-size is not specified, the
default is 1 (or bit). If, as a
result of slicing, the last slice is less than the slice width then no padding
is added.
For
example:
int j = { “A”, “B”, “C”, “D” };
{ >> {j}} // generates stream “A” “B”
“C” “D”
{ << byte {j}} // generates stream “D” “C” “B” “A”
(little endian)
{ << 16 {j}} //
generates stream “C” “D” “A” “B”
{ << { 8’b0011_0101 }} // generates stream ‘b1010_1100 (bit
reverse)
{ << 4 { 6’b11_0101 }} // generates stream ‘b0101_11
{ >> 4 { 6’b11_0101 }} // generates stream ‘b1101_01 (same)
{ << 2 {<<{ 4’b1101 }}} // generates stream ‘b1110
The
streaming operators operate directly on integral types and streams. When
applied to unpacked aggregate types, such as unpacked arrays, unpacked
structures, or classes, they recursively traverse the data in depth-first order
until reaching an integral type. A multi-dimensional packed array is thus
treated as a single integral type, whereas an unpacked array of packed items
causes each packed item to be streamed individually. The streaming operators
can only process bit-stream types; any other types shall generate an error.
The
result of the pack operation can be assigned directly to any bit-stream type
variable. If the left-hand side represents a fixed-size variable and the stream
is larger than the variable, an error will be generated. If the variable is
larger than the stream, the stream is left-justified and zero-filled on the
right. If the left-hand side represents a dynamic-size variable, such as a
queue or dynamic array, the variable is resized to accommodate the entire
stream. If after resizing, the variable is larger than the stream, the stream
is left-justified and zero- filled on the right. The stream is not an integral
value; to participate in an expression, a cast is required.
The
unpack operation accepts any bit-stream type on the right-hand side, including
a stream. The right-hand side data being unpacked is allowed to have more bits
than are consumed by the unpack operation. However, if more bits are needed than
are provided by the right-hand side expression, an error is generated.
For
example:
int a, b, c;
bit [96:1] y = {>>{ a, b, c }}; // OK: pack a, b, c
int j = {>>{ a, b, c }}; // error: j is 32 bits < 96
bits
bit [99:0] b = {>>{ a, b, c }}; // OK: b is padded with 4 bits
{>>{ a, b, c }} = 23’b1; //
error: too few bits in stream
{>>{ a, b, c }} = 96’b1; //
OK: unpack a = 0, b = 0, c = 1
{>>{ a, b, c }}
= 100’b1; // OK: unpack as
above (4 bits unread)
7.17.1 Streaming dynamically-sized
data
If the unpack
operation includes unbounded dynamically-sized types, the process is greedy (as
in a cast): the first dynamically-sized item is resized to accept all the
available data (excluding subsequent fixed-sized items) in the stream; any
remaining dynamically-sized items are left empty. This mechanism is sufficient
to unpack a packet-sized stream that contains only one dynamically-sized data
item. However, when the stream contains
multiple variable-sized data packets, or each data packet contains more than
one variable-sized data item, or the size of the data to be unpacked is stored
in the middle of the stream, this mechanism can become cumbersome and
error-prone. To overcome these problems, the unpack operation allows a with expression to explicitly specify the
extent of a variable-sized field within the unpack operation.
The
syntax of the with
expression is:
stream_expression ::=
expression
| array_identifier [ with [ array_range_expression
] ]
array_range_expression ::=
expression
| expression : expression
| expression +: expression
| expression -: expression
Syntax 7-4—with expression
syntax (excerpt from Annex A)
The
array range expression within the with construct must be of integral type and evaluate to
values that lie within the bounds of a fixed-size array, or to positive values
for dynamic arrays or queues. The array identifier may be any one-dimensional
unpacked array (including a queue). The expression within the
with is evaluated immediately
before its corresponding array is streamed (i.e., packed or unpacked). Thus,
the expression may refer to data that is unpacked by the same operator but
before the array. If the expression refers to variables that are unpacked after
the corresponding array (to the right of the array) then the expression is
evaluated using the previous values of the variables.
When
used within the context of an unpack operation and the array-identifier
designates a variable-sized array, the array shall be resized to accommodate
the range expression. If the array-identifier designates a fixed-sized array
and the range expression evaluates to a range outside the extent of the array,
only the range that lies within the array is unpacked and an error is
generated. If the range expression evaluates to a range smaller than the extent
of the array (fixed or variable sized), only the specified items are unpacked
into the designated array locations; the remainder of the array is unmodified.
When
used within the context of a pack (on the right-hand side), it behaves the same
as an array slice: The specified number of array items are
packed into the stream. If the range expression evaluates to a range smaller
than the extent of the array, only the specified array items are streamed. If
the range expression evaluates to a range greater than the extent of the array
size, the entire array is streamed and the remaining items are generated using
the default value (as described in Table 5-1) for the given array.
For example, the code
below uses streaming operators to model a packet transfer over a byte stream
that uses little-endian encoding:
byte stream[$]; // byte stream
class Packet
rand int
header;
rand int
len;
rand byte payload[];
int crc;
constraint G { len > 1; payload.size
== len ; }
function void post_randomize; crc
= payload.sum;
endfunction
endclass
...
send: begin // Create
random packer and transmit
byte q[$];
Packet p = new;
void’(p.randomize());
q = {<< byte{p.header, p.len, p.payload, p.crc}}; // pack
stream = {stream, q}; // append to
stream
end
...
receive: begin // Receive packet,
unpcak, and remove
byte q[$];
Packet p = new;
{<< byte{
p.header, p.len, p.payload with [0
+: p.len], p.crc }} =
stream;
stream = stream[ $bits(p) / 8 : $
]; // remove packet
end
In the example above, the pack operation could
have been written as either:
q = {<<byte{p.header, p.len, p.payload with [0
+: p.len], p.crc}};
or
q = {<<byte{p.header, p.len, p.payload with [0
: p.len-1], p.crc}};
or
q = {<<byte{p}};
The result in this case would be the same
since p.len is the size of p.payload as specified by the constraint.
SystemVerilog supports integer singular
value sets and set membership operators.
The syntax to define a set expression
for the set membership operator is:
inside_expression ::=
expression inside range_list_or_array { open_range_list } // from Annex A.8.3
range_list_or_array ::=
variable_identifier
| { value_range { , value_range }
}
value_range ::=
expression
| [ expression : expression
]
Syntax 7-2—inside expression syntax (excerpt from Annex A)
The expression on
the left-hand side of the inside operator
is any integral
SystemVerilog singular expression.
The set-membership open_range_list on
the right-hand side of the inside operator range_list_or_array is a
comma-separated list of integral
expressions and
or ranges. If an expression in the list is an
aggregate array, its elements are traversed by descending into the array until
reaching a singular value. The members of the set are scanned until a match is
found and the operation returns 1’b1. Values can be repeated, so values and value ranges can overlap.
The order of evaluation of the expressions and ranges is non-deterministic. Value
ranges are specified in ascending order with a low and high bound, enclosed by
square braces [ ], and separated by a colon ( : ), as in [low_bound:high_bound]. Ranges include all of the integer elements
between the bounds. If the bound to the left of the colon is greater than the
bound to the right the range is empty and contains no values.
The inside operator
evaluates to true if the expression is contained in the set; otherwise it
evaluates to false.
For example:
if ( a inside { [16:23], [32:47] } ) ...
if (a inside {b, c}) ...
Set values and
ranges can be any integral expression. Values can be repeated, so values and
value ranges can overlap.
int a, b, c;
if ( a inside {b, c} ) ...
int array [$] = {3,4,5};
if ( ex inside {1, 2, array} ) … // same as {
1, 2, 3, 4, 5}
The inside operator uses the equality (==) operator on non-integral expressions to
perform the comparison. If no match is found, the inside operator returns 1’b0. Integral expressions also use the equality
operator, except that a z inside a value in the set is treated as a don’t care
and that bit position shall not be considered. Note that unlike comparisons
performed by the casez statement, z values in the expression on
the left-hand side are not treated as a don’t-care; the don’t-care is
unidirectional.
logic [2:0] val;
while ( val inside
{3’b1?1} ) … // matches 3’b101, 3’b111,
3’b1x1, 3’b1z1
If no match
is found, but some of the comparisons result in x, the inside operator shall return 1’bx. The return value is effectively the or reduction
of all the comparisons in the set with the expression on the left-hand side.
wire r;
assign r=3’bz11 inside {3’b1?1, 3’b011}; // r = 1’bx
A range can
be specified with a low and high bound enclosed by square braces [ ], and separated by a colon ( :
), as in [low_bound:high_bound]. A
bound specified by $ shall represent the lowest or highest value
for the type of the expression on the left-hand side. A match is found if the
expression on the left-hand side is inclusively within the range. When specifying
a range, the expressions must be of a singular type for with the relation
operators (<=, >=) are defined. If the bound to the left of
the colon is greater than the bound to the right, the range is empty and
contains no values.
For example:
bit ba = a inside {
[
string I;
if (I inside {[“a rock”:”hard
place”]}) … // I between “a rock”
and a “hard place”