Section 7.2

LRM-100

Change in Syntax 7-1 (change in red):

conditional_expression ::=

expression1 expression ? { attribute_instance } expression2 expression : expression3 expression

Section 7.9

LRM-71

Change in Table 7-2 (change in red):

=> ->

Section 7.10.1

LRM-95

Changes (change in red):

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.

Section 7.15

LRM-111

Changes (change in red):

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.

LRM-75 LRM-111

Changes (change in red):

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.

Section 7.16 (New)

LRM-77

Add new section following:

7.16 Operator Overloading

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.

Section 7.17 (New)

LRM-109

Add new section following the new Section 7.16:

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.

Section 7.17

LRM-105

Changes (change in red):

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 { [16:23], [32:47] };

string I;

if (I inside {[“a rock”:”hard place”]}) …       // I between “a rock” and a “hard place”