Structures and unions

Structure and union declarations follow the C syntax but without the optional structure tags before the ‘{‘.

struct { bit [7:0] opcode; bit [23:0] addr; } REG; // anonymous structure defines variable REG

REG.opcode = 1; // set field opcode in REG.

Some additional examples of declaring structure and unions are:

typedef struct {
bit [7:0] opcode;
bit [23:0] addr;
} REG ; // named structure type
REG REG1; // define variable

typedef union { int i; shortreal f; } num; // named union type
num n;
n.f = 0.0; // set n in floating point format

typedef struct {
bit isfloat;
union { int i; shortreal f; } n; // anonymous type
} tagged_st; // named structure
tagged_st a[9:0]; // array of structures

A structure can be assigned as a whole and passed to or from a function or task as a whole. A packed structure consists of bit fields, which are packed together in memory without gaps. This means that they are easily converted to and from bit vectors. An unpacked structure has an implementation-dependent packing, normally matching the C compiler.

Like a packed array, a packed structure can be used as a whole with arithmetic and logical operators. The first member specified is the most significant and subsequent members follow in decreasing significance. The structures are declared using the packed keyword, which can be followed by the signed or unsigned keywords, according to the desired arithmetic behavior. The default is unsigned:

struct packed signed {
int a;
shortint b;
byte c;
bit [7:0] d;
} pack1; // signed, 2-state

struct packed unsigned {
time a;
integer b;
logic [31:0] c;
} pack2; // unsigned, 4-state

If any data type within a packed structure is 4-state, the whole structure is treated as 4-state. Any 2-state members are converted as if cast. One or more bits of a packed structure can be selected as if it were a packed array, assuming an [n-1:0] numbering.

Non-integer data types, such as real and shortreal, are not allowed in packed structures or unions nor are in unpacked arrays.
A packed structure can be used with a typedef.

typedef struct packed { // default unsigned
bit [3:0] GFC;
bit [7:0] VPI;
bit [11:0] VCI;
bit CLP;
bit [3:0] PT ;
bit [7:0] HEC;
bit [47:0] [7:0] Payload;
bit [2:0] filler;
} s_atmcell;

A packed union shall contain members that must be packed structures or packed arrays or integer data types all of the same size (in contrast to an unpacked union, where the members can be of different sizes). This ensures that you can read back a union member that was written as another member. A packed union can also be used as a whole with arithmetic and logical operators, and its behavior is determined by the signed or unsigned keyword, the latter being the default. If a packed union contains a 2-state member and a 4-state member, the entire union is 4-state. There is an implicit conversion from 4-state to 2-state when reading and from 2-state to 4-state when writing the 2-state member.

For example, a union can be accessible with different access widths:

typedef union packed { // default unsigned
s_atmcell acell;
bit [423:0] bit_slice;
bit [52:0][7:0] byte_slice;
} u_atmcell;
u_atmcell u1;
byte b; bit [3:0] nib;
b = u1.bit_slice[415:408]; // same as b = u1.byte_slice[51];
nib = u1.bit_slice [423:420]; // same as nib = u1.acell.GFC;

Note that writing one member and reading another is independent of the byte ordering of the machine, unlike a normal union of normal structures, which are C-compatible and have members in ascending address order.

The signing of unpacked structures is not allowed. The following declaration would be considered illegal:

typedef struct signed {
int f1 ;
logic f2 ;
} IllegalSignedUnpackedStructType; // illegal declaration

<< Previous | Next >>

Enumerations

An enumerated type declares a set of integral named constants. Enumerated data types provide the capability to abstractly declare strongly typed variables without either a data type or data value(s) and later add the required data type and value(s) for designs that require more definition. Enumerated data types also can be easily referenced or displayed using the enumerated names as opposed to the enumerated values.

In the absence of a data type declaration, the default data type shall be int. Any other data type used with enumerated types shall require an explicit data type declaration.

An enumerated type defines a set of named values. In the following example, color1 and color2 are defined to be variables of the anonymous (unnamed) enumerated int type that includes the three members: red, yellow and green.

enum {red, yellow, green} color1, color2; // anonymous int type

An enumerated name with x or z assignments assigned to an enum with no explicit data type or an explicit 2-state declaration shall be a syntax error.

// Syntax error: XX=2’bx in below example

enum {IDLE, XX=’x, S1=2’b01, S2=2’b10} state, next;

An enum declaration of a 4-state type, such as integer, that includes one or more names with x or z assignments shall be permitted.

// Correct: IDLE=0, XX=’x, S1=1, S2=2

enum integer {IDLE, XX=’x, S1=’b01, S2=’b10} state, next;

An unassigned enumerated name that follows an enum name with x or z assignments shall be a syntax error.

// Syntax error: IDLE=2’b00, XX=2’bx, S1=??, S2=??

enum integer {IDLE, XX=’x, S1, S2} state, next;

The values can be cast to integer types, and increment from an initial value of 0. This can be overridden.

enum {bronze=3, silver, gold} medal; // silver=4, gold=5

The values can be set for some of the names and not set for other names. The optional value of an enum named constant is an elaboration time constant expression and can include references to parameters, local parameters, genvars, other enum named constants, and constant functions of these. Hierarchical names and const variables are not allowed. A name without a value is automatically assigned an increment of the value of the previous name.

enum {a=3, b=7, c} alphabet; // c is automatically assigned the increment-value of 8

If an automatically incremented value is assigned elsewhere in the same enumeration, this shall be a syntax error.

enum {a=0, b=7, c, d=8} alphabet; // Syntax error: c and d are both assigned 8

If the first name is not assigned a value, it is given the initial value of 0.

enum {a, b=7, c} alphabet; // a=0, b=7, c=8

Any enumeration encoding value that is outside the representable range of the enum shall be an error. If any of the enum members are defined with a different-sized constant, this shall be a syntax error.

enum bit [3:0] {bronze='h3, silver, gold='h5} medal; // Correct declaration – bronze and gold are unsized

enum bit [3:0] {bronze=4'h3, silver, gold=4'h5} medal; // Correct declaration – bronze and gold sizes are redundant

enum bit [3:0] {bronze=5'h13, silver, gold=3'h5} medal; // Error in the bronze and gold member declarations

Defining new data types as enumerated types

A type name can be given so that the same type can be used in many places.

typedef enum {NO, YES} boolean;

boolean myvar; // named type

Enumerated types in numerical expressions

Elements of enumerated type variables can be used in numerical expressions. The value used in the expression is the numerical value associated with the enumerated value. For example:

typedef enum { red, green, blue, yellow, white, black } Colors;

Colors col;

integer a, b;

a = blue * 3;

col = yellow;

b = col + green;

From the previous declaration, blue has the numerical value 2. This example assigns a value of 6 (2*3), and it assigns b a value of 4 (3+1). An enum variable or identifier used as part of an expression is automatically cast to the base type of the enum declaration (either explicitly or using int as the default). An assignment to an enum variable from an expression other than the same type shall require a cast. Casting to an enum type shall cause a conversion of the expression to its base type without checking the validity of the value.

typedef enum {Red, Green, Blue} Colors;

typedef enum {Mo,Tu,We,Th,Fr,Sa,Su} Week;

Colors C;

Week W;

int I;

C = Colors’(C+1);               // C is converted to an integer, then added to one, then converted back to a Colors type

C = C + 1; C++; C+=2; C = I; // Illegal because they would all be assignments of expressions without a cast

C = Colors’(Su);                // Legal; puts an out of range value into C

I = C + W;                       // Legal; C and W are automatically cast to int

SystemVerilog includes a set of specialized methods to enable iterating over the values of enumerated types.

first() :

The prototype for the first() method is:

function enum first();

The first() method returns the value of the first member of the enumeration.

last() :

The prototype for the last() method is:

function enum last();

The last() method returns the value of the last member of the enumeration.

next() :

The prototype for the next() method is:

function enum next( int unsigned N = 1 );

The next() method returns the Nth next enumeration value (default is the next one) starting from the current value of the given variable. A wrap to the start of the enumeration occurs when the end of the enumeration is reached. If the given value is not a member of the enumeration, the next() method returns the first member.

prev() :

The prototype for the prev() method is:

function enum prev( int unsigned N = 1 );

The prev() method returns the Nth previous enumeration value (default is the previous one) starting from the current value of the given variable. A wrap to the end of the enumeration occurs when the start of the enumeration is reached. If the given value is not a member of the enumeration, the prev() method returns the last member.

num() :

The prototype for the num() method is:

function int num();

The num() method returns the number of elements in the given enumeration.

name() :

The prototype for the name() method is:

function string name();

The name() method returns the string representation of the given enumeration value. If the given value is not a member of the enumeration, the name() method returns the empty string.

<< Previous | Next >>

User-defined types

type_declaration ::=

typedef data_type type_identifier variable_dimension ;

| typedef interface_instance_identifier . type_identifier type_identifer ;

| typedef [ enum | struct | union | class ] type_identifier ;

The user can define a new type using typedef.

typedef int Myint;

This can then be instantiated as:

Myint a, b;

A type can be used before it is defined, provided it is first identified as a type by an empty typedef:

typedef Mytype;
Mytype M = 1;
typedef int Mytype;

Note that this does not apply to enumeration values, which must be defined before they are used.

User-defined type identifiers have the same scoping rules as data identifiers, except that hierarchical reference to type identifiers shall not be allowed. References to type identifiers defined within an interface through ports are allowed provided they are locally re-defined before being used.

interface intf_i;
typedef int data_t;
endinterface

module sub(intf_i p)
typedef p.data_t my_data_t;
my_data_t data;  // type of ’data’ will be int when connected to interface above
endmodule

User-defined type names must be used for complex data types in casting (see Section 3.14, below), which only allows simple type names, and as type parameter values when unpacked array types are used. Sometimes a user-defined type needs to be declared before the contents of the type has been defined. This is of use with user-defined types derived from enum, struct, union, and class.

For examples :

typedef enum type_declaration_identifier;
typedef struct type_declaration_identifier;
typedef union type_declaration_identifier;
typedef class type_declaration_identifier;
typedef type_declaration_identifier;

Note that, while this is useful for coupled definitions of classes, it cannot be used for coupled definitions of structures, since structures are statically declared and there is no support for pointers to structures.

The last form shows that the type of the user-defined type does not have to be defined in the forward declaration.

A typedef inside a generate shall not define the actual type of a forward definition that exists outside the
scope of the forward definition.

<< Previous | Next >>

Event data types

The event data type is an enhancement over Verilog named events. SystemVerilog events provide a handle to a synchronization object. Like Verilog, event variables can be explicitly triggered and waited for. Furthermore, SystemVerilog events have a persistent triggered state that lasts for the duration of the entire time step. In addition, an event variable can be assigned another event variable or the special value null. When assigned another event variable, both event variables refer to the same synchronization object. When assigned null, the association between the synchronization object and the event variable is broken. Events can be passed as arguments to tasks.

The syntax to declare an event is:

event variable_name [= initial_value];

Where variable_name is a valid identifier and the optional initial_value can be another event variable or the special value null.

If an initial value is not specified then the variable is initialized to a new synchronization object.

If the event is assigned null, the event becomes nonblocking, as if it were permanently triggered.

Examples:

event E1;               // declare a new event called E1

event E2 = E1;  // declare E2 as alias to E1

event empty = null    // event variable with no synchronization object

<< Previous | Next >>

String data type

SystemVerilog includes a string data type, which is a variable size, dynamically allocated array of bytes.
SystemVerilog also includes a number of special methods to work with strings.

Verilog supports string literals, but only at the lexical level. In Verilog, string literals behave like packed arrays of a width that is a multiple of 8 bits. A string literal assigned to a packed array of an integral variable of a different size is either truncated to the size of the variable or padded with zeroes to the left as necessary.

In SystemVerilog string literals behave the same as in Verilog However, SystemVerilog also supports the string data type to which a string literal can be assigned. When using the string data type instead of an integral variable, strings can be of arbitrary length and no truncation occurs. Literal strings are implicitly converted to the string type when assigned to a string type or used in an expression involving string type operands.

Variables of type string can be indexed from 0 to N-1 (the last element of the array), and they can take on the special value “”, which is the empty string.

The syntax to declare a string is:

string variable_name [= initial_value];

where variable_name is a valid identifier and the optional initial_value can be a string literal or the value “”

for an empty string.

Example :
string myName = "John";

If an initial value is not specified in the declaration, the variable is initialized to “”, the empty string.

SystemVerilog provides a set of operators that can be used to manipulate combinations of string variables and string literals.

A string literal can be assigned to a string or an integral type. If their size differs the literal is right justified
and either truncated on the left or zero-filled on the left, as necessary.

Example :
byte c = "A"; // assign to c "A"
bit [10:0] a = "\x41"; // assigns to a ‘b000_0100_0001
bit [1:4][7:0] h = "hello" ; // assigns to h "ello"

A string, string literal, or packed array can be assigned to a string variable. The string variable shall grow or shrink to accommodate the packed array. If the size (in bits) of the packed array is not a multiple of 8, then the packed array is zero-filled on the left.

Example :
string s1 = "hello"; // sets s1 to "hello"
bit [11:0] b = 12’ha41;
string s2 = b; // sets s2 to ’h0a41

As a second example:

reg [15:0] r;
integer i = 1;
string b = "";
string a = {"Hi", b};

r = a;           // OK
b = r;           // OK (implicit cast, implementations can issue a warning)
b = "Hi";       // OK
b = {5{"Hi"}}; // OK
a = {i{"Hi"}}; // OK (non-constant replication)
r = {i{"Hi"}}; // invalid (non-constant replication)
a = {i{b}};     // OK
a = {a,b};      // OK
a = {"Hi",b};  // OK
r = {"H",""};  // yields “H\0” “” is converted to 8’b0
b = {"H",""};  // yields “H” “” is the empty string
a[0] = "h";    // OK same as a[0] = “hi” )

len()

function int len()

  • str.len() returns the length of the string, i.e., the number of characters in the string (excluding any terminating character).
  • If str is “”, then str.len() returns 0.

putc()

task putc(int i, string s)
task putc(int i, byte c)

  •  str.putc(i, c) replaces the ith character in str with the given integral value.
  •  str.putc(i, s) replaces the ith character in str with the first character in s.
  •  s can be any expression that can be assigned to a string.
  •  putc does not change the size of str: If i < 0 or i >= str.len(), then str is unchanged.

getc()

function int getc(int i)

  •  str.getc(i) returns the ASCII code of the ith character in str.
  •  If i < 0 or i >= str.len(), then str.getc(i) returns 0.

toupper()

function string toupper()

  • str.toupper() returns a string with characters in str converted to uppercase.
  • str is unchanged.

tolower()

function string tolower()

  • str.tolower() returns a string with characters in str converted to lowercase.
  • str is unchanged.

compare()

function int compare(string s)

  • str.compare(s) compares str and s and embedded null bytes are included.

icompare()

function int icompare(string s)

  • str.icompare(s) compares str and s, the comparison is case insensitive and embedded null bytes are included.

substr()

function string substr(int i, int j)

  • str.substr(i, j) returns a new string that is a substring formed by characters in position i through j of str.
  • If i < 0, j < i, or j >= str.len(), substr() returns ” ” (the empty string).

<< Previous | Next >>

System Verilog data types

2-state (two-value) and 4-state (four-value) data types

Types that can have unknown and high-impedance values are called 4-state types. These are logic, reg, integer, and time. The other types do not have unknown values and are called 2-state types, for example, bit and int.
The difference between int and integer is that int is 2-state logic and integer is 4-state logic. 4-state values have additional bits that encode the X and Z states. 2-state data types can simulate faster, take less memory, and are preferred in some design styles.

Signed and unsigned data types

Integer types use integer arithmetic and can be signed or unsigned. This affects the meaning of certain operators such as ‘<’, etc.

int unsigned ui;
int signed si;

The data types byte, shortint, int, integer, and longint default to signed. The data types bit, reg and logic default to unsigned, as do arrays of these types.
Note that the signed keyword is part of Verilog-2001. The unsigned keyword is a reserved keyword in Verilog- 2001, but is not utilized.

Real and shortreal data types

The real data type is from Verilog-2001 and is the same as a C double. The shortreal data type is a SystemVerilog data type and is the same as a C float.

Void data type

The void data type represents non-existent data. This type can be specified as the return type of functions, indicating no return value. This type can also be used for members of tagged unions

Integer data types

SystemVerilog offers several integer data types, representing a hybrid of both Verilog and C data types:

shortint :   2-state SystemVerilog data type, 16 bit signed integer
int :         2-state SystemVerilog data type, 32 bit signed integer
longint :    2-state SystemVerilog data type, 64 bit signed integer
byte  :       2-state SystemVerilog data type, 8 bit signed integer or ASCII character
bit  :        2-state SystemVerilog data type, user-defined vector size
logic  :      4-state SystemVerilog data type, user-defined vector size
reg  :        4-state Verilog-2001 data type, user-defined vector size
integer  :   4-state Verilog-2001 data type, 32 bit signed integer
time  :       4-state Verilog-2001 data type, 64-bit unsigned integer

<< Previous | Next >>

Parameters

Parameters do not belong to either the variable or the net group. Parameters are not variables; they are constants. There are two types of parameters: module parameters and specify parameters. It is illegal to redeclare a name already declared by a net, parameter, or variable declaration. 

Both types of parameters accept a range specification. By default, parameters and specify params shall be as wide as necessary to contain the value of the constant, except when a range specification is present.

For example: 
parameter msb = 7; // defines msb as a constant value 7 
parameter e = 25, f = 9; // defines two constant numbers 
parameter r = 5.7; // declares r as a real parameter 
parameter byte_size = 8, byte_mask = byte_size - 1; 
parameter average_delay = (r + f) / 2; 
parameter signed [3:0] mux_selector = 0;

Parameters represent constants; hence, it is illegal to modify their value at run time. However, module parameters can be modified at compilation time to have values that are different from those specified in the declaration assignment. This allows customization of module instances. A parameter can be modified with the defparam statement or in the module instance statement. Typical uses of parameters are to specify delays and width of variables.

local parameters are identical to parameters except that they cannot directly be modified by defparam statements.

<< Previous | Next >>

Memory

A one-dimensional array with elements of type reg is also called a memory. These memories can be used to model read-only memories (ROMs), random access memories (RAMs), and reg files. Each reg in the array is known as an element or word and is addressed by a single array index.

Array declarations examples:

Array declarations examples:
reg [7:0] mema[0:255]; // declares a memory mema of 256 8-bit // registers. The indices are 0 to 255 
integer inta[1:64]; // an array of 64 integer values

Assignment to above array elements-
mema = 0; // Illegal syntax- Attempt to write to entire array 
arrayb[1] = 0; // Illegal Syntax - Attempt to write to elements // [1][0]..[1][255] 
arrayb[1][12:31] = 0; // Illegal Syntax - Attempt to write to // elements [1][12]..[1][31] 
mema[1] = 0; // Assigns 0 to the second element of mema 
arrayb[1][0] = 0; // Assigns 0 to the bit referenced by indices // [1][0] 
inta[4] = 33559; // Assign decimal number to integer in array 
chng_hist[t_index] = $time; // Assign current simulation time to element addressed by integer index

<< Previous | Next >>

UVM Introduction

The Universal Verification Methodology (UVM) is a standardized methodology for verifying integrated circuit designs. UVM is derived mainly from the OVM (Open Verification Methodology) which was, to a large part, based on the eRM (e Reuse Methodology) for the e verification language developed by Verisity Design in 2001. The UVM class library brings much automation to the SystemVerilog language such as sequences and data automation features (packing, copy, compare) etc., and unlike the previous methodologies developed independently by the simulator vendors, is an Accellera standard with support from multiple vendors: Aldec, Cadence, Mentor Graphics, Synopsys, Xilinx Simulator(XSIM).

Verilog Data Types

Mainly Data types are used to represent the data storage and transmission elements in digital hardware.
Lets understand all fundamentals about Data types-

Value set:
It have four basic values-

0 – represents a logic zero, or a false condition 
1 – represents a logic one, or a true condition 
x – represents an unknown logic value 
z – represents a high-impedance state 
Here – X i.e unknown value might be either 0 or 1.

Nets and variables: 
There are two main groups of data types: the variable data types and the net data types. These two groups differ in the way that they are assigned and hold values. 

Net types: Net can represent physical connections between structural entities, such as gates. A net shall not store a value (except for the trireg net). 
If no driver is connected to a net, its value shall be high-impedance (z) unless the net is a trireg, in which case it shall hold the previously driven value.

There are several distinct types of nets-
Wire and tri nets:
The wire and tri nets connect elements. The net types wire and tri shall be identical in their syntax and functions; two names are provided so that the name of a net can indicate the purpose of the net in that model. A wire net can be used for nets that are driven by a single gate or continuous assignment. The tri net type can be used where multiple drivers drive a net.

A variable: shall store a value from one assignment to the next. The default initialization value for reg, time, and integer data types shall be the unknown value, x.

Vectors: A net or reg declaration without a range specification shall be considered 1 bit wide and is known as a scalar. Multibit net and reg data types shall be declared by specifying a range, which is known as a vector.
Vector nets and regs shall obey laws of arithmetic modulo-2 to the power n (2n), where n is the number of bits in the vector. Vector nets and regs shall be treated as unsigned quantities, unless the net or reg is
declared to be signed or is connected to a port that is declared to be signed.

For example:
wand w;    // a scalar net of type "wand"
tri [15:0] busa;   // a three-state 16-bit bus
trireg (small) storeit;  // a charge storage node of strength small
reg a;  //scalar reg
reg[3:0] v;   // a 4-bit vector reg made up of (from most to least significant)v[3], v[2], v[1], and v[0]
reg signed [3:0] signed_reg;   // a 4-bit vector in range -8 to 7
reg [-1:4] b;  // a 6-bit vector reg
wire w1, w2;  // declares two wires
reg [4:0] x, y, z;  // declares three 5-bit regs

Regs:
Assignments to a reg are made by procedural assignments. Because the reg holds a value between assignments, it can be used to model hardware registers. Edge-sensitive (i.e., flip-flops) and level sensitive (i.e., reset-set and transparent latches) storage elements can be modeled.

For example: 
reg a; // a scalar reg 
reg[1:0] v; // a 2-bit vector reg made up of v[1], and v[0] 
wire w1, w2; // declares two wires 

Integers, reals, times, and realtimes:
An integer is a general-purpose variable used for manipulating quantities that are not regarded as hardware registers.
A time variable is used for storing and manipulating simulation time quantities in situations where timing checks are required and for diagnostics and debugging purposes. This data type is typically used in conjunction with the $time system function. 

For example: 
integer a; // integer value 
time last_chng; // time value 
real float ; // a variable to store a real value 
realtime rtime ; // a variable to store time as a real value

Arrays:
Arrays can be used to group elements of the declared element type into multidimensional objects. Arrays shall be declared by specifying the element address range(s) after the declared identifier.

Memory:
A one-dimensional array with elements of type reg is also called a memory. These memories can be used to model read-only memories (ROMs), random access memories (RAMs), and reg files. Each reg in the array is known as an element or word and is addressed by a single array index.

Array declarations examples:

reg [7:0] mema[0:255]; // declares a memory mema of 256 8-bit // registers. The indices are 0 to 255 
integer inta[1:64]; // an array of 64 integer values

Assignment to above array elements-

mema = 0; // Illegal syntax- Attempt to write to entire array 
arrayb[1] = 0; // Illegal Syntax - Attempt to write to elements // [1][0]..[1][255] 
arrayb[1][12:31] = 0; // Illegal Syntax - Attempt to write to // elements [1][12]..[1][31] 
mema[1] = 0; // Assigns 0 to the second element of mema 
arrayb[1][0] = 0; // Assigns 0 to the bit referenced by indices // [1][0] 
inta[4] = 33559; // Assign decimal number to integer in array 
chng_hist[t_index] = $time; // Assign current simulation time to element addressed by integer index

Parameters:
Parameters do not belong to either the variable or the net group. Parameters are not variables; they are constants. There are two types of parameters: module parameters and specify parameters. It is illegal to redeclare a name already declared by a net, parameter, or variable declaration. 

Both types of parameters accept a range specification. By default, parameters and specify params shall be as wide as necessary to contain the value of the constant, except when a range specification is present.

For example: 
parameter msb = 7; // defines msb as a constant value 7 
parameter e = 25, f = 9; // defines two constant numbers 
parameter r = 5.7; // declares r as a real parameter 
parameter byte_size = 8, byte_mask = byte_size - 1; 
parameter average_delay = (r + f) / 2; 
parameter signed [3:0] mux_selector = 0;

Parameters represent constants; hence, it is illegal to modify their value at run time. However, module parameters can be modified at compilation time to have values that are different from those specified in the declaration assignment. This allows customization of module instances. A parameter can be modified with the defparam statement or in the module instance statement. Typical uses of parameters are to specify delays and width of variables.

<< Previous | Next >>