Structure expressions

A structure expression (packed or unpacked) can be built from member expressions using braces and commas, with the members in declaration order. Replicate operators can be used to set the values for the exact number of members. Each member expression shall be evaluated in the context of an assignment to the type of the corresponding member in the structure. It can also be built with the names of the members.

module mod1;
typedef struct {
int x;
int y;
} st;
st s1;
int k = 1;
initial begin
#1 s1 = {1, 2+k}; // by position
#1 $display( s1.x, s1.y);
#1 s1 = {x:2, y:3+k); // by name
#1 $display( s1);
#1 $finish;
end
endmodule

It can sometimes be useful to set structure members to a value without having to keep track of how many members there are, or what the names are. This can be done with the default keyword:

initial s1 = {default:2}; // sets x and y to 2

The {member:value} or {data_type: default_value} syntax can also be used:

ab abkey[1:0] = {{a:1, b:1.0}, {int:2, shortreal:2.0}};

Note that the default keyword applies to members in nested structures or elements in unpacked arrays in structures. It descends the nesting to a built-in type or a packed array of them.

struct {
int A;
struct {
int B, C;
} BC1, BC2;
}
ABC = {A:1, BC1:{B:2, C:3}, BC2:{B:4,C:5}};
DEF = {default:10};

To deal with the problem of members of different types, a type can be used as the key. This overrides the
default for members of that type:

typedef struct {
logic [7:0] a;
bit b;
bit signed [31:0] c;
string s;
} sa;
sa s2;
initial s2 = {int:1, default:0, string:""}; // set all to 0 except the array of bits to 1 and string to ""

Similarly, an individual member can be set to override the general default and the type default:

initial #10 s1 = {default:’1, s : ""}; // set all to 1 except s to ""

SystemVerilog determines the context of the braces when used in the context of an assignment. If used in the context of an assignment to an unpacked structure, the braces represent an unpacked structure literal or expression. Outside the context of an assignment to an aggregate type, an explicit cast must be used with the braces to distinguish it from a concatenation. When the braces include a label, type, or default key, the braces shall not be interpreted as a concatenation for both packed and unpacked structure types.

The matching rules are as follows:

  • A member:value: specifies an explicit value for a named member of the structure. The named member must be at the top level of the structure—a member with the same name in some level of substructure shall not be set. The value must be castable to the member type and is evaluated in the context of an assignment to the named member, otherwise, an error is generated.
  • The type:value specifies an explicit value for a field in the structure that is equivalent to the type and has not been set by a field name key above. If the same type key is mentioned more than once, the last value is used. The value is evaluated in the context of an assignment to the matching type.
  • The default:value applies to members that are not matched by either member name or type key and are not either structures or unpacked arrays. The value is evaluated in the context of each assignment to a member by default and must be castable to the member type, otherwise an error is generated. For unmatched structure members, the type and default specifiers are applied recursively according to the rules in this section to each member of the substructure. For unmatched unpacked array members, the type and default keys are applied to the array according to the rules for unpacked arrays. Every member must be covered by one of these rules. If the type key, default key, or replication operator is used on an expression with side effects, the number of times that expression evaluates is undefined.

<< Previous | Next >>

Concatenation

Braces ( { } ) are used to show concatenation. The concatenation is treated as a packed vector of bits. It can be used on the left-hand side of an assignment or in an expression.

logic log1, log2, log3;
{log1, log2, log3} = 3’b111;
{log1, log2, log3} = {1’b1, 1’b1, 1’b1}; // same effect as 3’b111

Software tools can generate a warning if the concatenation width on one side of an assignment is different than the expression on the other side. The following examples can give a warning of size mismatch:

bit [1:0] packet = {32’b1,32’b1}; // right hand side is 64 bits
int i = {1’b1, 1’b1}; //right hand side is 2 bits

SystemVerilog enhances the concatenation operation to allow concatenation of variables of type string. In general, if any of the operands is of type string, the concatenation is treated as a string and all other arguments are implicitly converted to the string type. String concatenation is not allowed on the left-hand side of an assignment, only as an expression.

string hello = "hello";
string s;
s = { hello, " ", "world" };
$display( "%s\n", s ); // displays 'hello world'
s = { s, " and goodbye" };
$display( "%s\n", s ); // displays 'hello world and goodbye'

The replication operator (also called a multiple concatenation) form of braces can also be used with variables of type string. In the case of string replication, a non-constant multiplier is allowed.

int n = 3;
string s = {n { "hello " }};
$display( "%s\n", s ); // displays 'hello hello hello '

Unlike bit concatenation, the result of a string concatenation or replication is not truncated. Instead, the destination variable (of type string) is resized to accommodate the resulting string.

Unpacked array expressions

Braces are also used for expressions to assign to unpacked arrays. Unlike in C, the expressions must match element for element, and the braces must match the array dimensions. Each expression item shall be evaluated in the context of an assignment to the type of the corresponding element in the array. This means that the following examples do not give size warnings, unlike the similar assignments above:

bit abc [1:0] = {1,1}; // no size warning as bit can be set to 1
int xyz [1:0] = {1'b1, 1'b1}; // no size warning as int can be set to 1'b1

The syntax of multiple concatenations can be used for unpacked array expressions as well. Each replication represents a single dimension.

bit [1:0] unpackedbits = {2 {y}} ; // same as {y, y}
int n[1:2][1:3] = {2{{3{y}}}}; // same as {{y,y,y},{y,y,y}}

SystemVerilog determines the context of the braces when used in the context of an assignment. If used in the context of an assignment to an unpacked array, the braces represent an unpacked array literal or expression.

Outside the context of an assignment on the right-hand side, an explicit cast must be used with the braces to distinguish it from a concatenation. An aggregate expression cannot be used as the target of an assignment. The following is considered illegal:

logic [2:0] a [1:0];
logic [2:0] b, c;
always {b,c} = a; // illegal assignment the braces are not determined to be an unpacked array expression

It can sometimes be useful to set array elements to a value without having to keep track of how many members there are. This can be done with the default keyword:

initial unpackedint = {default:2}; // sets elements to 2

For more arrays of structures, it is useful to specify one or more matching type keys, as illustrated under structure expressions, below.

struct {int a; time b;} a_key[1:0];
a_key = {{a:1, b:2ns}, {int:5, time:$time}};

When the braces include a type, or default key, the braces shall not be interpreted as a concatenation for both packed and unpacked array types.

The rules for unpacked array matching are as follows:

  • An index:value, specifies an explicit value for a keyed element index. The value is evaluated in the context of an assignment to the indexed element and shall be castable to its type. It shall be an error to specify the same index more than once in a single array expression.
  • For type:value, if the element or sub-array type of the unpacked array is equivalent to this type, then each element or sub-array shall be set to the value. The value must be castable to the array element or sub-array type. Otherwise, if the unpacked array is multidimensional, then there is a recursive descent into each subarray of the array using the rules in this section and the type and default keys. Otherwise, if the unpacked array is an array of structures, there is a recursive descent into each element of the array using the rules for structure expressions and the type and default keys. If more than one type matches the same element, the last value shall be used.
  • For default:value, this key specifies the default value to use for each element of an unpacked array that has not been covered by the earlier rules in this section. The value is evaluated in the context of each assignment to an element covered by the default and must be castable to the array element type. Every element shall be covered by one of these rules. If the type key, default key, or replication operator is used on an expression with side effects, the number of times that expression evaluates is undefined.

<< Previous | Next >>

Operators and Expressions

Assignment operators

SystemVerilog assignment operator includes the C assignment operators and special bitwise assignment operators: +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, <<<=, and >>>=. An assignment operator is semantically equivalent to a blocking assignment, with the exception that any left-hand side index expression is only evaluated once. For example:

a[i]+=2; // same as a[i] = a[i] +2;

In SystemVerilog, an expression can include a blocking assignment, provided it does not have a timing control, Such an assignment must be enclosed in parentheses to avoid common mistakes such as using a=b for a==b, or a|=b for a!=b.

The semantics of such an assignment expression are those of a function that evaluates the right-hand side, casts the right-hand side to the left-hand data type, stacks it, updates the left-hand side, and returns the stacked value. The type returned is the type of the left-hand side data type. If the left-hand side is a concatenation, the type returned shall be an unsigned integral value whose bit length is the sum of the length of its operands.

SystemVerilog includes increment and decrement assignment operators ++i, –i, i++, and i–. These do not need parentheses when used in expressions. These increment and decrement assignment operators behave as blocking assignments.

The ordering of assignment operations relative to any other operation within an expression is undefined. An implementation can warn whenever a variable is both written and read or written within an integral expression or in other contexts where an implementation cannot guarantee the order of evaluation. In the following example:

i = 10;
j = i++ + (i = i - 1);

After execution, the value of j can be 18, 19, or 20 depending upon the relative ordering of the increment and the assignment statements.

Operations on logic and bit types

When a binary operator has one operand of type bit and another of type logic, the result is of type logic. If one operand is of type int and the other of type integer, the result is of type integer.

The operators != and == return an X if either operand contains an X or a Z, as in Verilog-2001. This is converted to a 0 if the result is converted to a type bit.

The unary reduction operators (&, ~&, |, ~|, ^, ~^) can be applied to any integer expression (including packed arrays). The operators shall return a single value of type logic if the packed type is four-valued, and of type bit if the packed type is two-valued.

int i;
bit b = &i;
integer j;
logic c = &j;

Wild equality and wild inequality

SystemVerilog wild-card comparison operators, as described below.

=?=             a =?= b            a equals b,            X, and Z values act as wild cards
!?=             a !?= b             a not equal b,         X, and Z values act as wild cards

The wild equality operator (=?=) and inequality operator (!?=) treat X and Z values in a given bit position as a wildcard. A wildcard bit matches any bit value (0, 1, Z, or X) in the value of the expression being compared against it.

These operators compare operands bit for bit and return a 1-bit self-determined result. If the operands to the wild-card equality/inequality are of unequal bit length, the operands are extended in the same manner as for the case equality/inequality operators. If the relation is true, the operator yields a 1. If the relation is false, it yields a 0.

The three types of equality (and inequality) operators in SystemVerilog behave differently when their operands contain unknown values (X or Z). The == and != operators result in X if any of their operands contains an X or Z. The === and !== check the 4-state explicitly, therefore, X and Z values shall either match or mismatch, never resulting in X. The =?= and !?= operators treat X or Z as wild cards that match any value, thus, they too never result in X.

Real operators

Operands of type shortreal have the same operation restrictions as Verilog real operands. The unary operators ++ and — can have operands of type real and shortreal (the increment or decrement is by 1.0). The assignment operators +=, -=, *=, /= can also have operands of type real and shortreal.

If any operand, except before the ? in the ternary operator, is real, the result is real. Otherwise, if any operand, except before the ? in the ternary operator, is shortreal, the result is shortreal.

Size

The number of bits of an expression is determined by the operands and the context, following the same rules as Verilog. In SystemVerilog, casting can be used to set the size context of an intermediate value.

With Verilog, tools can issue a warning when the left and right-hand sides of an assignment are different sizes. Using the SystemVerilog size casting, these warnings can be prevented.

Sign

The rules for determining the signedness of SystemVerilog expression types shall be the same as those for Verilog. A shortreal converted to an integer by type coercion shall be signed.

<< Previous | Next >>

Type Compatibility

SystemVerilog constructs and operations require a certain level of type compatibility for their operands to be legal. There are four levels of type compatibility: Equivalent, Assignment Compatible, Cast Compatible, and Non-Equivalent.

Equivalent Types

Two data types shall be defined as equivalent data types using the following inductive definition. If the two data types are not defined as equivalent using the following definition, then they shall be defined to be non-equivalent.

  • Any built-in type is equivalent to every other occurrence of itself, in every scope.
  • A simple typedef or type parameter override that renames a built-in or user-defined type is equivalent to that built-in or user-defined type within the scope of the type identifier.
typedef bit node; // ’bit’ and ’node’ are equivalent types
typedef type1 type2; // ’type1’ and ’type2’ are equivalent types
  • An anonymous enum, struct, or union type is equivalent to itself among variables declared within the same declaration statement and no other types.
struct {int A; int B;} AB1, AB2; // AB1, AB2 have equivalent types
struct {int A; int B;} AB3; // AB3 is not type equivalent to AB1
  • A typedef for an enum, unpacked struct, or unpacked union, or a class is equivalent to itself and variables are declared using that type within the scope of the type identifier.
typedef struct {int A; int B;} AB_t;
AB_t AB1; AB_t AB2; // AB1 and AB2 have equivalent types
typedef struct {int A; int B;} otherAB_t;
otherAB_t AB3; // AB3 is not type equivalent to AB1 or AB2
  • Packed arrays, packed structures, and built-in integral types are equivalent if they contain the same number of total bits, are either all 2-state or all 4-state, and are either all signed or all unsigned. Note that if any bit of a packed structure or union is 4-state, the entire structure or union is considered 4-state.
typedef bit signed [7:0] BYTE; // equivalent to the byte type
typedef struct packed signed {bit[3:0] a, b;} uint8; // equivalent to the byte type
  • Unpacked array types are equivalent by having equivalent element types and identical shapes. Shape is defined as the number of dimensions and the number of elements in each dimension, not the actual range of the dimension.
bit [9:0] A[0:5];
bit [1:10] B[6];
typedef bit [10:1] uint10;
uint10 C[6:1]; // A, B and C have equivalent types
typedef int anint[0:0]; // anint is not type equivalent to int
  • Explicitly adding signed or unsigned modifiers to a type that does not change its default signing, does not create a non-equivalent type. Otherwise, the signing must match to have equivalence.
typedef bit unsigned ubit; // type equivalent to bit
  • A typedef for an enum, unpacked struct, or unpacked union, or a class type declared in a package is always equivalent to itself, regardless of the scope where the type is imported.

The scope of a type identifier includes the hierarchical instance scope. This means that each instance with user-defined types declared inside the instance creates a unique type. To have type equivalence among multiple instances of the same module, interface, or program, a type must be declared at higher level in the compilation unit scope than the declaration of the module, interface or program, or imported from a package.
The following example is assumed to be within one compilation unit, although the package declaration need not be in the same unit:

package p1;
typedef struct {int A;} t_1;
endpackage
typedef struct {int A;} t_2;

module sub();
import p1:t_1;
parameter type t_3 = int;
parameter type t_4 = int;
typedef struct {int A;} t_5;
t_1 v1; t_2 v2; t_3 v3; t_4 v4; t_5 v5;
endmodule

module top();
typedef struct {int A;} t_6;
sub #(.t_3(t_6)) s1 ();
sub #(.t_3(t_6)) s2 ();

initial begin
s1.v1 = s2.v1; // legal - both types from package p1
s1.v2 = s2.v2; // legal - both types from $unit
s1.v3 = s2.v3; // legal - both types from top
s1.v4 = s2.v4; // legal - both types are int
s1.v5 = s2.v5; // illegal - types from s1 and s2
end

endmodule

Assignment Compatible

All equivalent types and all non-equivalent types that have implicit casting rules defined between them are assignment-compatible types. For example, all integral types are assignment-compatible. Conversion between assignment-compatible types can involve loss of data by truncation or rounding.
Compatibility can be in one direction only. For example, an enum can be converted to an integral type without a cast, but not the other way around.

Cast Compatible

All assignment-compatible types, plus all non-equivalent types that have defined explicit casting rules are cast-compatible types. For example, an integral type requires a cast to be assigned to an enum.

Type Incompatible

These are all the remaining non-equivalent types that have no defined implicit or explicit casting rules. Class handles and chandles are type incompatible with all other types.

<< Previous | Next >>

Signal Aliasing

The Verilog assign statement is a unidirectional assignment and can incorporate a delay and strength change.

To model a bidirectional short-circuit connection it is necessary to use the alias statement. The members of an alias list are signals whose bits share the same physical nets. The example below implements a byte-order swapping between bus A and bus B.

module byte_swap (inout wire [31:0] A, inout wire [31:0] B);
   alias {A[7:0],A[15:8],A[23:16],A[31:24]} = B;
endmodule

This example strips out the least and most significant bytes from a four-byte bus:

module byte_rip (inout wire [31:0] W, inout wire [7:0] LSB, MSB);
   alias W[7:0] = LSB;
   alias W[31:24] = MSB;
endmodule

The bit overlay rules are the same as those for a packed union with the same member types: each member shall be the same size, and connectivity is independent of the simulation host. The nets connected with an alias statement must be type compatible, that is, they have to be of the same net type. For example, it is illegal to connect a wand net to a wor net with an alias statement. This is a stricter rule than applied to nets joining at ports because the scope of an alias is limited and such connections are more likely to be a design error. Variables and hierarchical references cannot be used in alias statements. Any violation of these rules shall be considered a fatal error.

The same nets can appear in multiple alias statements. The effects are cumulative. The following two examples are equivalent. In either case, low12[11:4] and high12[7:0] share the same wires.

module overlap(inout wire [15:0] bus16, inout wire [11:0] low12, high12);
alias bus16[11:0] = low12;
alias bus16[15:4] = high12;
endmodule
module overlap(inout wire [15:0] bus16, inout wire [11:0] low12, high12);
alias bus16 = {high12, low12[3:0]};
alias high12[7:0] = low12[11:4];
endmodule

To avoid errors in the specification, it is not allowed to specify an alias from an individual signal to itself or to specify a given alias more than once. The following version of the code above would be illegal since the top four and bottom four bits are the same in both statements:

alias bus16 = {high12[11:8], low12};
alias bus16 = {high12, low12[3:0]};

This alternative is also illegal because the bits of bus16 are being aliased to itself:

alias bus16 = {high12, bus16[3:0]} = {bus16[15:12], low12};

Alias statements can appear anywhere in module instance statements, If an identifier that has not been declared as a data type appears in an alias statement, then an implicit net is assumed, following the same rules as implicit nets for a module instance. The following example uses an alias along with the automatic name binding to connect pins on cells from different libraries to create a standard macro:

module lib1_dff(Reset, Clk, Data, Q, Q_Bar);
...
endmodule

module lib2_dff(reset, clock, data, a, qbar);
...
endmodule

module lib3_dff(RST, CLK, D, Q, Q_);
...
endmodule

macromodule my_dff(rst, clk, d, q, q_bar); // wrapper cell
input rst, clk, d;
output q, q_bar;
alias rst = Reset = reset = RST;
alias clk = Clk = clock = CLK;
alias d = data = D;
alias q = Q;
alias Q_ = q_bar = Q_Bar = qbar;
‘LIB_DFF my_dff (.*); // LIB_DFF is any of lib1_dff, lib2_dff or lib3_dff
endmodule

<< Previous | Next >>

Data Declarations

There are several forms of data in SystemVerilog: literals, parameters, constants, variables, nets, and attributes
Constants are literals, genvars parameters, localparams, and specparams.
Variables must be written by procedural statements, and nets must be written by continuous assignments or ports.
SystemVerilog extends the functionality of variables by allowing them to either be written by procedural statements or driven by a single continuous assignment, similar to a wire. Since the keyword reg no longer describes the user’s intent in many cases, the keyword logic is added as a more accurate description that is equivalent to reg.

SystemVerilog requires data to be declared before it is used, apart from implicit nets. The rules for implicit nets are the same as in Verilog-2001.

A variable can be static (storage allocated on instantiation and never de-allocated) or automatic (stack storage allocated on entry to a scope (such as a task, function or block) and de-allocated on exit). SystemVerilog follows Verilog with respect of the static default storage class, with automatic tasks and functions, but allows static to override a default of automatic for a particular variable in such tasks and functions.

Constants

Constants are named data variables that never change. There are three kinds of constants, declared with the keywords localparam, specparam, and const.

localparam byte colon1 = ":" ;
specparam int delay = 10 ; // specparams are used for specify blocks
const logic flag = 1 ;

A parameter or local parameter can only be set to an expression of literals, parameters or local parameters, genvars, enumerated names, or a constant function of these. Hierarchical names are not allowed.
A specparam can also be set to an expression containing one or more specparams.

A static constant declared with the const keyword can only be set to an expression of literals, parameters, local parameters, genvars, enumerated names, a constant function of these, or other constants. The parameters, local parameters or constant functions can have hierarchical names because constants declared with the const keyword are calculated after elaboration. An automatic constant declared with the const keyword can be set to any expression that would be legal without the const keyword.

const logic option = a.b.c ;

A constant expression contains literals and other named constants.
An instance of a class (an object handle) can also be declared with the const keyword.

const class_name object = new(5,3);

This means that the object acts like a variable that cannot be written. The arguments to the new method must be constant expressions. The members of the object can be written (except for those members that are declared const).

SystemVerilog enhancements to parameter and localparam constant declarations. SystemVerilog does not change specparam constants declarations. A const form of constant differs from a localparam constant in that the localparam must be set during elaboration, whereas a const can be set during simulation, such as in an automatic task.

Variables

A variable declaration consists of a data type followed by one or more instances.

shortint s1, s2[0:9];

A variable can be declared with an initializer, for example:

int i = 0;

In Verilog-2001, an initialization value specified as part of the declaration is executed as if the assignment were made from an initial block after the simulation has started. Therefore, the initialization can cause an event on that variable at simulation time zero.

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 does 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 can 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, 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.

The default values for SystemVerilog variables are:

4 state integral:   ’x
2 state integral:   ’0
real, shortreal:     0.0
Enumeration:      First value in the enumeration
string:        “” (empty string)
event:         New event
class:       Null

Scope and lifetime

Any data declared outside a module, interface, task, or function, is global in scope (can be used anywhere after its declaration) and has a static lifetime (exists for the whole elaboration and simulation time).

SystemVerilog data declared inside a module or interface but outside a task, process or function is local in scope and static lifetime (exists for the lifetime of the module or interface).

Data declared in an automatic task, function, or block has the lifetime of the call or activation and a local scope.

Data declared in a static task, function, or block defaults to a static lifetime and a local scope.

Verilog-2001 allows tasks and functions to be declared as automatic, making all storage within the task or function automatic. SystemVerilog allows specific data within a static task or function to be explicitly declared as automatic. Data declared as automatic has the lifetime of the call or block, and is initialized on each entry to the call or block. The lifetime of a forkjoin, forkjoin_any, or forkjoin_none block shall encompass the execution of all processes spawned by the block. The lifetime of a scope enclosing any forkjoin block includes the lifetime of the forkjoin block.

SystemVerilog also allows data to be explicitly declared as static. Data declared to be static in an automatic task, function or block has a static lifetime and a scope local to the block.

module msl;
int st0; // static
initial begin
int st1; //static
static int st2; //static
automatic int auto1; //Automatic
end
task automatic t1();
int auto2; //Automatic
static int st3; //static
automatic int auto3; //Automatic
endtask
endmodule

SystemVerilog adds an optional qualifier to specify the default lifetime of all variables declared in a task, function, or block defined within a module, interface, or program. The lifetime qualifier is automatic or static. The default lifetime is static.

program automatic test ;
int i; // not within a procedural block - static
task foo( int a ); // arguments and variables in foo are automatic
$display("%0d",a);
endtask
endmodule

Class methods and declared for loop variables are by default automatic, regardless of the lifetime attribute of the scope in which they are declared.
Note that automatic or dynamic variables cannot be written with nonblocking or continuous assignments.
Automatic variables and dynamic constructs—object handles, dynamic arrays, associative arrays, strings, and event variables—shall be limited to the procedural context.

<< Previous | Next >>

Nets, Regs, and logic

Verilog-2001 states that a net can be written by one or more continuous assignments, primitive outputs, or through module ports. The resultant value of multiple drivers is determined by the resolution function of the net type. A net cannot be procedurally assigned. If a net on one side of a port is driven by a variable on the other side, a continuous assignment is implied. A force statement can override the value of a net. When released, it returns to the resolved value.

Verilog-2001 also states that one or more procedural statements can be written to variables, including procedural continuous assignments. The last write determines the value. A variable cannot be continuously assigned. The force statement overrides the procedural assign statement, which in turn overrides the normal assignments. A variable cannot be written through a port; it must go through an implicit continuous assignment to a net.

In SystemVerilog, all variables can now be written either by one continuous assignment or by one or more procedural statements, including procedural continuous assignments. It shall be an error to have multiple continuous assignments or a mixture of procedural and continuous assignments writing to any term in the expansion of a written longest static prefix of a logic variable. All data types can be written through a port.

SystemVerilog variables can be packed or unpacked aggregates of other types. Multiple assignments made to independent elements of a variable are examined individually. An assignment where the left-hand side contains a slice is treated as a single assignment to the entire slice. It shall be an error to have a packed structure or array-type written with a mixture of procedural and continuous assignments. Thus, an unpacked structure or array can have one element assigned procedurally, and another element assigned continuously. And, each element of a packed structure or array can have a single continuous assignment. For example, assume the following structure declaration:

struct {
bit [7:0] A;
bit [7:0] B;
byte C;
} abc;

The following statements are legal assignments to struct abc:

assign abc.C = sel ? 8’hBE : 8’hEF;
not (abc.A[0],abc.B[0]),
(abc.A[1],abc.B[1]),
(abc.A[2],abc.B[2]),
(abc.A[3],abc.B[3]);
always @(posedge clk) abc.B <= abc.B + 1;

The following additional statements are illegal assignments to struct abc:

// Multiple continuous assignments to abc.C
assign abc.C = sel ? 8’hDE : 8’hED;
// Mixing continuous and procedural assignments to abc.A
always @(posedge clk) abc.A[7:4] <= !abc.B[7:4];

For the preceding rule, a declared variable initialization or a procedural continuous assignment is considered a procedural assignment. A force statement is neither a continuous nor a procedural assignment. A release statement shall not change the variable until there is another procedural assignment or shall schedule a re-evaluation of the continuous assignment driving it. A single force or release statement shall not be applied to a whole or part of a variable that is being assigned by a mixture of continuous and procedural assignments.

A continuous assignment is implied when a variable is connected to an input port declaration. This makes assignments to a variable declared as an input port illegal. A continuous assignment is implied when a variable is connected to the output port of an instance. This makes procedural or continuous assignments to a variable connected to the output port of an instance illegal.

SystemVerilog variables cannot be connected to either side of an inout port. SystemVerilog introduces the concept of shared variables across ports with the ref port type.

The compiler can issue a warning if a continuous assignment could drive strengths other than St0, St1, StX, or HiZ to a variable. In any case, SystemVerilog applies automatic type conversion to the assignment, and the strength is lost.

An assignment as part of the logic declaration is a variable initialization, not a continuous assignment. For example:

wire w = vara & varb; // continuous assignment
logic v = consta & constb; // initial procedural assignment
logic vw; // no initial assignment
assign vw = vara & varb; // continuous assignment to a logic
real circ;
assign circ = 2.0 * PI * R; // continuous assignment to a real

<< Previous | Next >>

Array methods

Array Locator Methods

Locator methods iterate over the array elements, which are then used to evaluate the expression specified by the with clause. The iterator argument optionally specifies the name of the variable used by the with expression to designate the element of the array at each iteration. If it is not specified, the name item is used by default. The scope for the iterator name is the with expression.

The following locator methods are supported (the with clause is mandatory) :

  • find(): returns all the elements satisfying the given expression
  • find_index(): returns the indexes of all the elements satisfying the given expression
  • find_first(): returns the first element satisfying the given expression
  • find_first_index(): returns the index of the first element satisfying the given expression
  • find_last(): returns the last element satisfying the given expression
  • find_last_index(): returns the index of the last element satisfying the given expression

For the following locator methods, the with clause (and its expression) can be omitted if the relational operators (<, >, ==) are defined for the element type of the given array. If a with clause is specified, the relational operators (<, >, ==) must be defined for the type of the expression.

  • min(): returns the element with the minimum value or whose expression evaluates to a minimum
  • max(): returns the element with the maximum value or whose expression evaluates to a maximum
  • unique(): returns all elements with unique values or whose expression is unique
  • unique_index(): returns the indexes of all elements with unique values or whose expression is unique

Examples:

string SA[10], qs[$];
int IA[*], qi[$];
// Find all items greater than 5
qi = IA.find( x ) with ( x > 5 );
// Find indexes of all items equal to 3
qi = IA.find_index with ( item == 3 );
// Find the first item equal to Bob
qs = SA.find_first with ( item == "Bob" );
// Find the last item equal to Henry
qs = SA.find_last( y ) with ( y == "Henry" );
// Find the index of the last item greater than Z
qi = SA.find_last_index( s ) with ( s > "Z" );
// Find the smallest item
qi = IA.min;
// Find the string with a largest numerical value
qs = SA.max with ( item.atoi );
// Find all unique strings elements
qs = SA.unique;
// Find all unique strings in lower-case
qs = SA.unique( s ) with ( s.tolower );

Array ordering methods

Array ordering methods can reorder the elements of one-dimensional arrays or queues.
The general prototype for the ordering methods is:

function void ordering_method ( array_type iterator = item )

The following ordering methods are supported:

  • reverse(): reverses all the elements of the array (packed or unpacked). Specifying a with clause shall be a compiler error.
  • sort(): sorts the unpacked array in ascending order, optionally using the expression in the with clause.

The with clause (and its expression) is optional when the relational operators are defined for the array element type.

  • rsort(): sorts the unpacked array in descending order, optionally using the expression in the with clause.

The with clause (and its expression) is optional when the relational operators are defined for the array element type.

  • shuffle(): randomizes the order of the elements in the array. Specifying a with clause shall be a compiler error.

Examples:

string s[] = { "hello", "good", "morning" };
s.reverse; // s becomes { "morning", "good", "hello" };
logic [4:1] a = 4’bXZ01;
a.reverse; // a becomes 4’b10ZX
int q[$] = { 4, 5, 3, 1 };
q.sort; // q becomes { 1, 3, 4, 5 }
struct { byte red, green, blue } c [512];
c.sort with ( item.red ); // sort c using the red field only
c.sort( x ) with ( x.blue << 8 + x.green ); // sort by blue then green

Array reduction methods

Array reduction methods can be applied to any unpacked array to reduce the array to a single value. The expression within the optional with clause can be used to specify the item to use in the reduction.

The prototype for these methods is:

function expression_or_array_type reduction_method (array_type iterator = item)

The method returns a single value of the same type as the array element type or, if specified, the type of the expression in the with clause. The with clause can be omitted if the corresponding arithmetic or boolean reduction operation is defined for the array element type. If a with clause is specified, the corresponding arithmetic or boolean reduction operation must be defined for the type of the expression.

The following reduction methods are supported:

  • sum(): returns the sum of all the array elements, or if a with clause is specified, returns the sum of the values yielded by evaluating the expression for each array element.
  • product(): returns the product of all the array elements, or if a with clause is specified, returns the product of the values yielded by evaluating the expression for each array element.
  • and(): returns the bit-wise AND ( & ) of all the array elements, or if a with clause is specified, returns the bit-wise AND of the values yielded by evaluating the expression for each array element
  • or(): returns the bit-wise OR ( | ) of all the array elements, or if a with clause is specified, returns the bitwise OR of the values yielded by evaluating the expression for each array element
  • xor(): returns the logical XOR ( ^ ) of all the array elements, or if a with clause is specified, returns the XOR of the values yielded by evaluating the expression for each array element.

Examples:

byte b[] = { 1, 2, 3, 4 };
int y;
y = b.sum ; // y becomes 10 => 1 + 2 + 3 + 4
y = b.product ; // y becomes 24 => 1 * 2 * 3 * 4
y = b.xor with ( item + 4 ); // y becomes 12 => 5 ^ 6 ^ 7 ^ 8

Iterator index querying

The expressions used by array manipulation methods sometimes need the actual array indexes at each iteration, not just the array element. The index method of an iterator returns the index value of the specified dimension.

The prototype of the index method is:

function int_or_index_type index ( int dimension = 1 )

The slowest variation is dimension 1. Successively faster varying dimensions have sequentially higher dimension numbers. If the dimension is not specified, the first dimension is used by default. The return type of the index method is an int for all array iterator items except associative arrays, which returns an index of the same type as the associative index type.

For example:

int arr[]
int mem[9:0][9:0], mem2[9:0][9:0];
int q[$];
//Finds all items equal to their position (index)
q = arr.find with ( item == item.index );
//Finds all items in mem that are greater than the corresponding item in mem2
q = mem.find( x ) with ( x > mem2[x.index(1)][x.index(2)] );

<< Previous | Next >>

Puzzles

Que 16: There are 100 water bottles. One of the bottles contains poisoned water. If a rat drinks poisoned water he will die after one hour of it. You have 1 hour to find out which bottle has the poison. How many minimum rats are needed to figure out which bottle contains poison?
Ans 16:
We can use digital logic to find out the poisoned bottle. Also, the rat will die in 1 hour and we have only 1 hour to figure it out we can give that water to rat only once. If we use the digital logic 100 numbers can be represented in 8 bits. If we convert each number into a digital number and give the water to those rats only if that position contains 1. Suppose the poison is in the 10th (00001010)bottle we will give the water to the 2nd and 4th rats. After 1 hour, we check which rats have died(represent logic 1) and which have not died(represent logic 0) and we can form back the number and we can figure it out. So, we need 8 rats to figure out the poisoned water bottle.


Que 17: You have a box with 10 pairs of blue socks, 10 pairs of black socks, and 10 pairs of gray socks of the same size. You can pick one sock at a time. How many times do you have to pick the socks from the box to ensure that you have at least one pair of socks with the same color?
Ans 17:
You have a total of 30 pairs of socks. If you pick 30 socks there might be a chance that all the socks are of the same leg. So, if we pick 1 more sock It will ensure that we will have a pair of socks. So we need to pick 31 socks from the box.


Que 18: You have a rectangular sheet with a circular hole anywhere in the sheet, you know the center of the sheet and the center of the hole, How will you divide the sheet into two parts such that their areas are equal?
Ans 18:
If we draw a straight line from the center of the rectangle in any direction it will divide the rectangle into 2 equal halves and the same in the circle. So we can draw a line that passes through the center of both rectangle and circle. This line will divide the sheet into two parts with equal area.


Que 19: You have a balance scale, You need to measure integer weights from 1kg. to 40kgs. only. How many minimum numbers of weights do you need if you can put weights on one side of the scale?
Ans 19:
We have weights that add up to 40 kg. we have 2 choices either we put it on balance or won’t put it. Hence, we can use binary weights which can count from 1 to 40. So the weights will be 20, 21, 22, 23, 24, 25 which is 1, 2, 4, 8, 16, 32. A total of 6 weights are needed.


Que 20: In the previous question, what is the minimum number of weights needed if you can put your weights on both sides of the scale.
Ans 20:
In this question, we can put the weights on either side of the balance scale or don’t put them on the scale. So, you will have 3 options +1, 0, or -1. We can use base 3 combinations of the weights 30, 31, 32, 33 which is 1, 3, 9, 27. A total of 4 weights are needed.

<< Previous | Next >>

Puzzles

Que 11: Calculate the number of squares in an 8×8 chessboard.
Ans 11: To calculate the number of squares in an 8×8 chessboard, we will calculate first with smaller numbers like 2×2 and 3×3.
In 2×2 there will be 4+1 = 5 squares.
In 3×3 there will be 9+4+1 = 14 squares.
In 4×4 there will be 16+9+4+1 = 30 squares.
Following the pattern we can calculate for 8×8 chessboard will be 64+49+36+25+16+9+4+1 = 204


Que 12: you have a 3 liter and a 5 liters buckets (Assume unlimited water source available). How will you measure 4 liters of water.
Ans 12:
We can follow the below sequence to get 4 liters of water. We can make the combination of 5 + 5 – 3 – 3 = 4 ltr. Refer to Table 6 for the sequence of activity.


Que 13: Find the angle between the minute and hour hands of the clock at 4:20 PM.
Ans 13:
First calculate the position of the minute hand w.r.t. 12 o’clock at 20 min. = (20*360o/60) = 120o. Then we can calculate the position of hour hand w.r.t 12 o’clock at 4:20 PM, we can write it as 420/60 = 41/3 = 13/3. To convert into the angle (13/3×360o / 12 ) = 130o. The Angle formed by the hour and minute hand will be 10o.


Que 14: You have two sand timers, which can show 4 minutes and 7 minutes respectively. Use both the sand timers(at one time or one after another or any other combination) and measure a time of 9 minutes.
Ans 14: Start the 7-minute sand timer and the 4-minute sand timer.
Once the 4-minute sand timer ends turn it upside down instantly.
Once the 7-minute sand timer ends turn it upside down instantly.
After the 4-minute sand timer ends turn the 7-minute sand timer upside down(it has now a minute of sand in it) So effectively 8 + 1 = 9.


Que 15: You have 9 balls, 8 of which have the same weight. The remaining one is defective and heavier than the rest. You can use a balance scale to compare weights to find which is the defective ball. How many measurements do you need so that you will be surely able to do it?
Ans 15:
We can split the balls into 3 groups with 3 balls in each group. Then we can compare any 2 groups if they are equal then defective ball is in the 3rd group or it will be in the heavier group. Then we will get 3 balls in which 1 is heavier, again we can compare 2 balls if they are equal 3rd ball is defective else heavier ball. So, in total, we need to measure 2 times to find out heavier balls.

<< Previous | Next >>