SystemVerilog for Design Edition 2 Chapter 5
Posted sasasatori
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SystemVerilog for Design Edition 2 Chapter 5相关的知识,希望对你有一定的参考价值。
SystemVerilog for Design Edition 2 Chapter 5
SystemVerilog adds several enhancements to Verilog for representing large amounts of data. The Verilog array construct is extended both in how data can be represented and for operations on arrays. Structure and union types have been added to Verilog as a means to represent collections of variables.
This section presents:
• Structures
• Unions
• Operations on structures and unions
• Unpacked arrays
• Packed arrays
• Operations on arrays
• Array foreach loop
• Special system functions for working with arrays
• The $bits “sizeof” system function
5.1 Structures
Design data often has logical groups of signals, such as all the control signals for a bus protocol, or all the signals used within a state controller. The Verilog language does not have a convenient mechanism for collecting common signals into a group. Instead, designers must use ad-hoc grouping methods such as naming conventions where each signal in a group starts or ends with a common set of characters.
structures are defined using a C-like syntax
SystemVerilog adds C-like structures to Verilog. A structure is a convenient way of grouping several pieces of related information together. A structure is declared using the struct keyword. Structure members can be any variable type, including user-defined types, and any constant type. An example structure declaration is:
struct
int a, b; // 32-bit variables
opcode_t opcode; // user-defined type
logic [23:0] address; // 24-bit variable
bit error; // 1-bit 2-state var.
Instruction_Word;
the C “tag” is not allowed
The structure declaration syntax in SystemVerilog is very similar to the C language. The one difference is that C allows for an optional “tag” after the struct keyword and before the opening brace. SystemVerilog does not allow a tag.
structures are a collection of variables and/or constants
A structure is a collection of variables and/or constants under a single name. The entire collection can be referenced, using the name of the structure. Each member within the structure also has a name, which is used to select it from the structure. A structure member is referenced the same as in C.
<structure_name>.<variable_name>
For example, to assign a value to the opcode member of the preceding structure, the reference is:
Instruction_Word.address = 32’hF000001E;
structures are different than arrays
A structure differs from an array, in that an array is a collection of elements that are all the same type and size, whereas a structure is a collection of variables and/or constants that can be different types and sizes. Another difference is that the elements of an array are referenced by an index into the array, whereas the members of a structure are referenced by a member name.
5.1.1 Structure declarations
Variables or nets can be defined as a structure
structures can be variables or nets
A structure is a collection of variables, which can be accessed separately or as a whole. A structure as a whole can be declared as a variable using the var keyword. A structure can also be defined as a net, using any of the Verilog net types, such as wire or tri. When defined as a net type, all members of the structure must be 4-state types.
var struct // structure variable
logic [31:0] a, b;
logic [ 7:0] opcode;
logic [23:0] address;
Instruction_Word_var;
wire struct // structure net
logic [31:0] a, b;
logic [ 7:0] opcode;
logic [23:0] address;
Instruction_Word_net;
Declaring a structure as a var or net type is optional. If not specified, then the structure as a whole is considered to be a variable.
struct // structure variable
logic [31:0] a, b;
logic [ 7:0] opcode;
logic [23:0] address;
Instruction_Word_var;
Note that, though a structure as a whole can be declared as a net type, net types cannot be used within structures. Nets can be grouped together under a single name using SystemVerilog interfaces, which are discussed in Chapter 10.
Typed and anonymous structures
structures can be user-defined types
User-defined types can be created from structures, using the typedef keyword, as discussed in section 4.1 on page 75. Declaring a structure as a user-defined type does not allocate any storage. Before values can be stored in the members of a structure that is defined as a user-defined type, a variable of that user-defined type must be declared.
typedef struct // structure definition
logic [31:0] a, b;
logic [ 7:0] opcode;
logic [23:0] address;
instruction_word_t;
instruction_word_t IW; // structure allocation
When a structure is declared without using typedef, it is referred to as an anonymous structure.
struct
logic [31:0] a, b;
logic [ 7:0] opcode;
logic [23:0] address;
instruction;
Local and shared structure definitions
structure definitions can be shared using packages or $unit
A typed structure can be defined within a module or interface, allowing its use throughout that design block. If a typed structure definition needs to be used in more than one design block, or as a port of a module or interface, then the structure definition should be placed in a package, and imported into design blocks or the $unit
compilation-unit space. Typed structures can also be defined directly in the $unit
compilation-unit space. Definitions in packages and in $unit
are discussed in section 2.1 on page 8 and section 2.2 on page 14 in Chapter 2.
5.1.2 Assigning values to structures
Initializing structures
structures can be initialized using a list of values
The members of a structure can be initialized at the time the structure is instantiated, using a set of values enclosed between the tokens ’ and . The number of values between the braces must exactly match the number of members in the structure.
typedef struct
logic [31:0] a, b;
logic [ 7:0] opcode;
logic [23:0] address;
instruction_word_t;
instruction_word_t IW = ’100, 3, 8’hFF, 0;
A similar syntax is used for defining structure constants or structure parameters.
NOTE: The SystemVerilog value list syntax is not the same as C.
SystemVerilog uses the tokens ’ to enclose a value list, whereas C uses . Early versions of the SystemVerilog draft standard used simple braces to delimit value lists, like C. The final version of the IEEE SystemVerilog changed the delimiter to ’ to distinguish the list of values from Verilog’s concatenation operator.
Assigning to structure members
three ways to assign to structures
A value can be assigned to any member of a structure by referencing the name of the member.
typedef struct
logic [31:0] a, b;
logic [ 7:0] opcode;
logic [23:0] address;
instr_t;
instr_t IW;
always @(posedge clock, negedge resetN)
if (!resetN) begin
IW.a = 100; // reference structure member
IW.b = 5;
IW.opcode = 8’hFF;
IW.address = 0;
end
else begin
...
end
Assigning structure expressions to structures
a structure expression is enclosed within ’ ...
A complete structure can be assigned a structure expression. A structure expression is formed using a comma-separated list of values enclosed between the tokens ’ and , just as when initializing a structure. The braces must contain a value for each member of the structure.
always @(posedge clock, negedge resetN)
if (!resetN) IW = ’100, 5, 8’hFF, 0;
else begin
...
end
a structure expression can be listed by order or by member name
The values in the structure expression can be listed in the order in which they are defined in the structure. Alternatively, the structure expression can specify the names of the structure members to which values are being assigned, where the member name and the value are separated by a colon. When member names are specified, the expression list can be in any order.
IW = ’address:0, opcode:8’hFF, a:100, b:5;
It is illegal to mix listing by name and listing by order in the same structure expression.
IW = ’address:0, 8’hFF, 100, 5; // ERROR
Default values in structure expressions
some or all members of a structure can be assigned a default value
A structure expression can specify a value for multiple members of a structure by specifying a default value. The default value can be specified for all members of a structure, using the default keyword.
IW = ’default:0; // set all members of IW to 0
The default value can also be specified just for members of a specific type within the structure, using the keyword for the type. The default keyword or type keyword is separated from the value by a colon.
typedef struct
real r0, r1;
int i0, i1;
logic [ 7:0] opcode;
logic [23:0] address;
instruction_word_t;
instruction_word_t IW;
always @(posedge clock, negedge resetN)
if (!resetN)
IW = ’ real:1.0, default:0 ;
// assign all real members a default of 1.0
// and all other members a default of 0
else begin
...
end
The default value assigned to structure members must be compatible with the type of the member. Compatible values are ones that can be cast to the member’s type.
default value precedence
There is a precedence in how structure members are assigned values. The default keyword has the lowest precedence, and will be overridden by any type-specific defaults. Type-specific default values will be overridden by any explicitly named member values. The following structure expression will assign r0 a value of 1.0, r1 a value of 3.1415, and all other members of the structure a value of 0.
typedef struct
real r0, r1;
int i0, i1;
logic [ 7:0] opcode;
logic [23:0] address;
instruction_word_t;
instruction_word_t IW;
IW = ’ real:1.0, default:0, r1:3.1415 ;
5.1.3 Packed and unpacked structures
unpacked structures can have padding
By default, a structure is unpacked. This means the members of the structure are treated as independent variables or constants that are grouped together under a common name. SystemVerilog does not specify how software tools should store the members of an unpacked structure. The layout of the storage can vary from one software tool to another.
packed structures are stored without padding
A structure can be explicitly declared as a packed structure, using the packed keyword. A packed structure stores all members of the structure as contiguous bits, in a specified order. A packed structure is stored as a vector, with the first member of the structure being the left-most field of the vector. The right-most bit of the last member in the structure is the least-significant bit of the vector, and is numbered as bit 0. This is illustrated in Figure 5-1.
struct packed
logic valid;
logic [ 7:0] tag;
logic [31:0] data;
data_word;
Figure 5-1: Packed structures are stored as a vector
The members of a packed structure can be referenced by either the name of the member or by using a part select of the vector represented by the structure. The following two assignments will both assign to the tag member of the data_word structure:
data_word.tag = 8’hf0;
data_word[39:32] = 8’hf0; // same bits as tag
NOTE: Packed structures can only contain integral values.
packed structures must contain packed variables
All members of a packed structure must be integral values. An integral value is a value that can be represented as a vector, such as byte, int and vectors created using bit or logic types. A structure cannot be packed if any of the members of the structure cannot be represented as a vector. This means a packed structure cannot contain real or shortreal variables, unpacked structures, unpacked unions, or unpacked arrays.
Operations on packed structures
packed structures are seen as vectors
Because a packed structure is stored as a vector, operations on the complete structure are treated as vector operations. Therefore, math operations, logical operations, and any other operation that can be performed on vectors can also be performed on packed structures.
typedef struct packed
logic valid;
logic [ 7:0] tag;
logic [31:0] data;
data_word_t;
data_word_t packet_in, packet_out;
always @(posedge clock)
packet_out <= packet_in << 2;
Note that when a packed structure is assigned a list of values between the tokens ’ and , as discussed in section 5.1.2 on page 98, values in the list are assigned to members of the structure. The packed structure is treated the same as an unpacked structure in this circumstance, rather than as a vector. The values within the ’ braces are separate values for each structure member, and not a concatenation of values.
packet_in = ’1, ’1, 1024;
The preceding line assigns 1 to valid, FF (hex) to tag, and 1024 (decimal) to data.
Signed packed structures
a packed structures used as a vector can be signed or unsigned
Packed structures can be declared with the signed or unsigned keywords. These modifiers affect how the entire structure is perceived when used as a vector in mathematical or relational operations. They do not affect how members of the structure are perceived. Each member of the structure is considered signed or unsigned, based on the type declaration of that member. A partselect of a packed structure is always unsigned, the same as part selects of vectors in Verilog.
typedef struct packed signed
logic valid;
logic [ 7:0] tag;
logic signed [31:0] data;
data_word_t;
data_word_t A, B;
always @(posedge clock)
if ( A < B ) // signed comparison
...
5.1.4 Passing structures through ports
ports can be declared as a structure type
Structures can be passed through module and interface ports. The structure must first be defined as a user-defined type using typedef, which then allows the module or interface port to be declared as the structure type.
package definitions;
typedef enum ADD, SUB, MULT, DIV opcode_t;
typedef struct
logic [31:0] a, b;
opcode_t opcode;
logic [23:0] address;
logic error;
instruction_word_t;
endpackage
module alu
(input definitions::instruction_word_t IW,
input wire clock);
...
endmodule
An alternative style to explicitly naming the package containing the typedef definition as part of the module port would be to import the package into the $unit
compilation-unit declaration space. It is also possible to directly define the user-defined types in the $unit
space. Importing packages and using the $unit
compilation-unit space are discussed in Chapter 2.
When an unpacked structure is passed through a module port, a structure of the exact same type must be connected on each side of the port. Anonymous structures declared in two different modules, even if they have the exact same name, members and member names, are not the same type of structure. Passing unpacked structures through module ports is discussed in more detail in section 9.6.2 on page 252.
5.1.5 Passing structures as arguments to tasks and functions
structures can be passed to tasks and functions
Structures can be passed as arguments to a task or function. To do so, the structure must be defined as a user-defined type using typedef, so that the task or function argument can then be declared as the structure type.
module processor (...);
...
typedef enum ADD, SUB, MULT, DIV opcode_t;
typedef struct // typedef is local
logic [31:0] a, b;
opcode_t opcode;
logic [23:0] address;
logic error;
instruction_word_t;
function alu (input instruction_word_t IW);
...
endfunction
endmodule
When a task or function is called that has an unpacked structure as a formal argument, a structure of the exact same type must be passed to the task or function. An anonymous structure, even if it has the exact same members and member names, is not the same type of structure.
5.1.6 Synthesis guidelines
Both unpacked and packed structures are synthesizable. Synthesis supports passing structures through module ports, and in/out of tasks and functions. Assigning values to structures by member name and as a list of values is supported.
5.2 Unions
a union only stores a single value
SystemVerilog adds C-like unions to Verilog. A union is a single storage element that can have multiple representations. Each representation of the storage can be a different type.
The declaration syntax for a union is similar to a structure, and members of a union are referenced in the same way as structures.
union
int i;
int unsigned u;
data;
...
data.i = -5;
$display("data is %d", data.i);
data.u = -5;
$display("now data is %d", data.u);
unions reduce storage and may improve performance
Although the declaration syntax is similar, a union is very different than a structure. A structure can store several values. It is a collection of variables under a single name. A union can only store one value. A typical application of unions is when a value might be represented as several different types, but only as one type at any specific moment in time.
Typed and anonymous unions
A union can be defined as a type using typedef, in the same way as structures. A union that is defined as a user-defined type is referred to as a typed union. If typedef is not used, the union is referred to as an anonymous union.
typedef union // typed union
int i;
int unsigned u;
data_t;
data_t a, b; // two variables of type data_t
5.2.1 Unpacked unions
An unpacked union can contain any variable type, including real types, unpacked structures and unpacked arrays. Software tools can store values in unpacked unions in an arbitrary manner. There is no requirement that each tool align the storage of the different types used within the union in the same way.
Unpacked unions are not synthesizable. They are an abstract type which are useful for high-level system and transaction level models. As such, it may be useful to store any type in the union including 4-state types, 2-state types, and non-synthesizable types such as real types.
NOTE: Reading from an unpacked union member that is different than the last member written may cause indeterminate results.
If a value is stored using one union member, and read back from a different union member, then the value that is read is not defined, and may yield different results in different software tools.
The following example is not synthesizable, but shows how an unpacked union can store very different value types. The example shows a union that can store a value as either an int type or a real type. Since these types are stored very differently, it is important that a value always be read back from the union in the same type with which it is written. Therefore, the example contains extra logic to track how values were stored in the union. The union is a member of a structure. A second member of the structure is a flag that can be set to indicate that a real value has been stored in the union. When a value is read from the union, the flag can be checked to determine what type the union is storing.
struct
bit is_real;
union
int i;
real r;
value;
data;
//...
always @(posedge write) begin
case (operation_type)
INT_OP: begin
data.value.i <= 5;
data.is_real <= 0;
end
FP_OP: begin
data.value.r <= 3.1415;
data.is_real <= 1;
end
endcase
end
//...
always @(posedge read) begin
if (data.is_real)
real_operand <= data.value.r;
else
int_operand <= data.value.i;
end
5.2.2 Tagged unions
A union can be declared as tagged.
union tagged
int i;
real r;
data;
tagged unions contain an implicit tag member
A tagged union contains an implicit member that stores a tag, which represents the name of the last union member into which a value was stored. When a value is stored in a tagged union using a tagged expression, the implicit tag automatically stores information as to which member the value was written.
values are stored in tagged unions using a tagged expression
A value can be written into a tagged union member using a tagged expression. A tagged expression has the keyword tagged followed by the member name, followed by a value to be stored. A tagged expression is assigned to the name of the union. For example:
data = tagged i 5; // store the value 5 in data.i, and set the implicit tag
Values are read from the tagged union using the union member name.
d_out = data.i; // read value from union
tagged unions check that the union is used in a consistent way
Tagged unions require that software tools monitor the usage of the union, and generate an error message if a value is read from a different union member than the member into which a tagged expression value was last written. For example, if the last tagged expression to write to the union specified member i (an int type), then the following line of code will result in a run-time error:
d_out = data.r; // ERROR: member does not match the union’s implicit tag
Once a value has been assigned to a tagged union using a tagged expression, values can be written to the same union member using the member name. If the member name specified does not match the current union tag, a run-time error will result.
data.i = 7; // write to union member; member name must match the current union tag
It is still the designer’s responsibility to ensure that the design consistently reads values from the union member in which data was last stored. If, however, a design flaw should use the union in an inconsistent way, software tools must inform the designer of the error.
5.2.3 Packed unions
packed union members all have the same size
A union can be declared as packed in the same way as a structure. In a packed union, the number of bits of each union member must be the same. This ensures that a packed union will represent its storage with the same number of bits, regardless of member in which a value is stored. Because of this restrictions, packed unions are syntheiszable.
A packed union can only store integral values, which are values made up of 1 or more contiguous bits. If any member of a packed union is a 4-state type, then the union is 4-state. A packed union cannot contain real or short-real variables, unpacked structures, unpacked unions, or unpacked arrays.
A packed union allows data to be written using one format and read back using a different format. The design model does not need to do any special processing to keep track of how data was stored. This is because the data in a packed union will always be stored using the same number of bits.
The following example defines a packed union in which a value can be represented in two ways: either as a data packet (using a packed structure) or as an array of bytes.
typedef struct packed
logic [15:0] source_address;
logic [15:0] destination_address;
logic [23:0] data;
logic [ 7:0] opcode;
data_packet_t;
union packed
data_packet_t packet; // packed structure
logic [7:0][7:0] bytes; // packed array
dreg;
Figure 5-2: Packed union with two representations of the same storage
Because the union is packed, the information will be stored using the same bit alignment, regardless of which union representation is used. This means a value could be loaded using the array of bytes format (perhaps from a serial input stream of bytes), and then the same value can be read using the data_packet format.
always @(posedge clock, negedge resetN)
if (!resetN) begin
dreg.packet <= ’0; // store as packet type
i <= 0;
end
else if (load_data) begin
dreg.bytes[i] <= byte_in; // store as bytes
i <= i + 1;
end
always @(posedge clock)
if (data_ready)
case (dreg.packet.opcode) // read as packet
...
Packed, tagged unions
A union can be declared as both packed and tagged. In this case, the union members can be different bit sizes, but must still be only integral types (1 or more contiguous bits). Packed, tagged unions only permit reading from the same union member that matches the member of the last tagged expression written into the union.
union tagged packed
logic [15:0] short_word;
logic [31:0] word;
logic [63:0] long_word;
data_word;
5.2.4 Synthesis guidelines
NOTE: Only packed unions are synthesizable
packed unions can be synthesized
A union only stores a single value, regardless of how many type representations are in the union. To realize the storage of a union in hardware, all members of the union must be stored as the same vector size using the same bit alignment. Packed unions represent the storage of a union in this way, and are synthesizable. An unpacked union does not guarantee that each type will be stored in the same way, and is therefore not synthesizable.
Packed, tagged unions are intended to be synthesizable, but at the time this book was written, were not widely supported by synthesis compilers.
5.2.5 An example of using structures and unions
Structures provide a mechanism to group related data together under a common name. Each piece of data can be referenced individually by name, or the entire group can be referenced as a whole. Unions allow one piece of storage to be used in multiple ways.
The following example models a simple Arithmetic Logic Unit that can operate on either signed or unsigned values. The ALU opcode, the two operands, and a flag to indicate if the operation data is signed or unsigned, are passed into the ALU as a single instruction word, represented as a structure. The ALU can operate on either signed values or unsigned values, but not both at the same time. Therefore the signed and unsigned values are modeled as a union of two types. This allows one variable to represent both signed and unsigned values.
Chapter 11 presents another example of using structures and unions to represent complex information in a simple and intuitive form.
Example 5-1: Using structures and unions
package definitions;
typedef enum ADD, SUB, MULT, DIV, SL, SR opcode_t;
typedef enum UNSIGNED, SIGNED operand_type_t;
typedef union packed
logic [31:0] u_data;
logic signed [31:0] s_data;
data_t;
typedef struct packed
opcode_t opc;
operand_type_t op_type;
data_t op_a;
data_t op_b;
instr_t;
endpackage
import definitions::*; // import package into $unit space
module alu
(input instr_t IW,
output data_t alu_out);
always @(IW) begin
if (IW.op_type == SIGNED) begin
case (IW.opc)
ADD : alu_out.s_data = IW.op_a.s_data + IW.op_b.s_data;
SUB : alu_out.s_data = IW.op_a.s_data - IW.op_b.s_data;
MULT: alu_out.s_data = IW.op_a.s_data * IW.op_b.s_data;
DIV : alu_out.s_data = IW.op_a.s_data / IW.op_b.s_data;
SL : alu_out.s_data = IW.op_a.s_data <<< 2;
SR : alu_out.s_data = IW.op_a.s_data >>> 2;
endcase
end
else begin
case (IW.opc)
ADD : alu_out.u_data = IW.op_a.u_data + IW.op_b.u_data;
SUB : alu_out.u_data = IW.op_a.u_data - IW.op_b.u_data;
MULT: alu_out.u_data = IW.op_a.u_data * IW.op_b.u_data;
DIV : alu_out.u_data = IW.op_a.u_data / IW.op_b.u_data;
SL : alu_out.u_data = IW.op_a.u_data << 2;
SR : alu_out.u_data = IW.op_a.u_data >> 2;
endcase
end
end
endmodule
5.3 Arrays
5.3.1 Unpacked arrays
Verilog-1995 arrays
The basic syntax of a Verilog array declaration is:
<data_type> <vector_size> <array_name> <array_dimensions>
For example:
reg [15:0] RAM [0:4095]; // memory array
Verilog-1995 only permitted one-dimensional arrays. A one-dimensional array is often referred to as a memory, since its primary purpose is to model the storage of hardware memory devices such as RAMs and ROMs. Verilog-1995 also limited array declarations to just the variable types reg, integer and time.
Verilog arrays
Verilog-2001 significantly enhanced Verilog-1995 arrays by allowing any variable or net type except the event type to be declared as an array, and by allowing multi-dimensional arrays. Beginning with Verilog-2001, both variable types and net types can be used in arrays.
// a 1-dimensional unpacked array of 1024 1-bit nets
wire n [0:1023];
// a 1-dimensional unpacked array of 256 8-bit variables
reg [7:0] LUT [0:255];
// a 1-dimensional unpacked array of 1024 real variables
real r [0:1023];
// a 3-dimensional unpacked array of 32-bit int variables
integer i [7:0][3:0][7:0];
Verilog restricts array access to one element at a time
Verilog restricts the access to arrays to just one element of the array at a time, or a bit-select or part-select of a single element. Any reading or writing to multiple elements of an array is an error.
integer i [7:0][3:0][7:0];
integer j;
j = i[3][0][1]; // legal: selects 1 element
j = i[3][0]; // illegal: selects 8 elements
unpacked arrays store each element independently
SystemVerilog refers to the Verilog style of array declarations as unpacked arrays. With unpacked arrays, each element of the array may be stored independently from other elements, but grouped under a common array name. Verilog does not define how software tools should store the elements in the array. For example, given an array of 8-bit wide elements, a simulator or other software tool might store each 8-bit element in 32-bit words. Figure 5-3 illustrates how the following declaration might be stored within memory.
wire [7:0] table [3:0];
Figure 5-3: Unpacked arrays can store each element independently
SystemVerilog enhancements to unpacked arrays
SystemVerilog allows unpacked arrays of any type
SystemVerilog extends unpacked array dimensions to include the Verilog event type, and the SystemVerilog types: logic, bit, byte, int, longint, shortreal, and real. Unpacked arrays of user-defined types defined using typedef can also be declared, including types using struct and enum.
bit [63:0] d_array [1:128]; // array of vectors
shortreal cosines [0:89]; // array of floats
typedef enum Mo, Tu, We, Th, Fr, Sa, Su Week;
Week Year [1:52]; // array of Week types
SystemVerilog can reference all or slices of an array
SystemVerilog also adds to Verilog the ability to reference an entire unpacked array, or a slice of multiple elements within an unpacked array. A slice is one or more contiguously numbered elements within one dimension of an array. These enhancements make it possible to copy the contents of an entire array, or a specific dimension of an array into another array.
NOTE: The left-hand and right-hand sides of an unpacked array copy must have identical layouts and types.
copying into multiple elements of an unpacked array
In order to directly copy multiple elements into an unpacked array, the layout and element type of the array or array slice on the lefthand side of the assignment must exactly match the layout and element type of the right-hand side. That is, the element type and size and the number of dimensions copied must be the same.
The following examples are legal. Even though the array dimensions are not numbered the same, the size and layout of each array is the same.
int a1 [7:0][1023:0]; // unpacked array
int a2 [1:8][1:1024]; // unpacked array
a2 = a1; // copy an entire array
a2[3] = a1[0]; // copy a slice of an array
Array copying is discussed in more detail later in this chapter, in section 5.3.7 on page 124.
Simplified unpacked array declarations
C arrays are specified by size
C language arrays always begin with address 0. Therefore, an array declaration in C only requires that the size of the array be specified. For example:
int array [20]; // a C array with addresses from 0 to 19
Hardware addressing does not always begin with address 0. Therefore, Verilog requires that array declarations specify a starting address and an ending address of an array dimension.
int array [64:83]; // a Verilog array with addresses from 64 to 83
SystemVerilog unpacked arrays can also be specified by size
SystemVerilog adds C-like array declarations to Verilog, allowing unpacked arrays to be specified with a dimension size, instead of starting and ending addresses. The array declaration:
logic [31:0] data [1024];
is equivalent to the declaration:
logic [31:0] data [0:1023];
As in C, the unpacked array elements are numbered, starting with address 0 and ending with address size-1.
The simplified C-style array declarations cannot be used with vector declarations (packed arrays). The following example is a syntax error.
logic [32] d; // illegal vector declaration
5.3.2 Packed arrays
The Verilog language allows vectors to be created out of single-bit types, such as reg and wire. The vector range comes before the signal name, whereas an unpacked array range comes after the signal name.
Verilog vectors are onedimensional packed arrays
SystemVerilog refers to vector declarations as packed arrays. A Verilog vector is a one-dimensional packed array.
wire [3:0] select; // 4-bit "packed array"
reg [63:0] data; // 64-bit "packed array"
SystemVerilog allows multidimensional packed arrays
SystemVerilog adds the ability to declare multiple dimensions in a packed array.
logic [3:0][7:0] data; // 2-D packed array
packed arrays have no padding
SystemVerilog defines how the elements of a packed array are stored. The entire array must be stored as contiguous bits, which is the same as a vector. Each dimension of a packed array is a sub field within the vector.
In the packed array declaration above, there is an array of 4 8-bit sub-arrays. Figure 5-4 illustrates how the two-dimensional array above will be stored, regardless of the software compiler, operating system or platform.
Figure 5-4: Packed arrays are stored as contiguous elements
logic [3:0][7:0] data; // 2-D packed array
Packed array types
NOTE: Only bit-wise types can be packed.
Packed arrays must be formed using bit-wise types (logic, bit or reg), other packed arrays, packed structures, and packed unions. Packed arrays can also be formed from any of the Verilog net data types (wire, uwire, wand, tri, triand, trior, tri0, tri1 or trireg).
typedef struct packed
logic [ 7:0] crc;
logic [63:0] data;
data_word;
data_word [7:0] darray; // 1-D packed array of packed structures
Referencing packed arrays
A packed array can be referenced as a whole, as bit-selects, or as part-selects. Multidimensional packed arrays can also be referenced in slices. A slice is one or more contiguous dimensions of an array.
logic [3:0][7:0] data; // 2-D packed array
wire [31:0] out = data; // whole array
wire sign = data[3][7]; // bit-select
wire [3:0] nib = data [0][3:0]; // part-select
byte high_byte;
assign high_byte = data[3]; // 8-bit slice
logic [15:0] word;
assign word = data[1:0]; // 2 slices
Operations on packed arrays
any vector operation can be performed on packed arrays
Because packed arrays are stored as vectors, any legal operation that can be performed on a Verilog vector can also be performed on packed arrays. This includes being able to do bit-selects and partselects from the packed array, concatenation operations, math operations, relational operations, bit-wise operations, and logical operations.
logic [3:0][15:0] a, b, result; // packed arrays
...
result = (a << 1) + b;
packed arrays use Verilog vector rules
There is no semantic difference between a Verilog vector and a SystemVerilog packed array. Packed arrays use the standard Verilog vector rules for operations and assignment statements. When there is a mismatch in vector sizes, a packed array will be truncated on the left or extended to the left, just as with a Verilog vector.
5.3.3 Using packed and unpacked arrays
The ability to declare multi-dimensional arrays as either packed arrays or unpacked arrays gives a great deal of flexibility on how to represent large amounts of complex data. Some general guidelines on when to use each type of array follow.
use unpacked arrays to model memories, and with abstract types
Use unpacked arrays to model:
• Arrays of byte, int, integer, real, unpacked structures, unpacked unions, and other types that are not bit-wise types
• Arrays where typically one element at a time is accessed, such as with RAMs and ROMs
module ROM (...);
byte mem [0:4095]; // unpacked array of bytes
assign data = select? mem[address]: ’z;
...
use packed arrays to create vectors with sub-fields
Use packed arrays to model:
• Vectors made up of 1-bit types (the same as in Verilog)
• Vectors where it is useful to access sub-fields of the vector
logic [39:0][15:0] packet; // 40 16-bit words
packet = input_stream; // assign to all words
data = packet[24]; // select 1 16-bit word
tag = packet[3][7:0]; // select part of 1 word
5.3.4 Initializing arrays at declaration
Packed array initialization
packed arrays are initialized the same as with vectors
Packed arrays can be initialized at declaration using a simple assignment, like vectors in Verilog. The assignment can be a constant value, a concatenation of constant values or a replication of constant values.
logic [3:0][7:0] a = 32’h0; // vector assignment
logic [3:0][7:0] b = 16’hz,16’h0; // concatenate operator
logic [3:0][7:0] c = 162’b01; // replicate operator
In the examples above, the braces represent the Verilog concatenate operator.
Unpacked array initialization
unpacked arrays are initialized with a list of values
Unpacked arrays can be initialized at declaration, using a list of values enclosed between ’ and braces for each array dimension. This syntax is similar to assigning a list of values to an array in C, but with the added apostrophe before the opening brace. Using ’ as the opening delimiter shows that enclosed values are a list of expressions, not the Verilog concatenation of expressions. Note that the C shortcut of omitting the inner braces is not allowed in SystemVerilog. The assignment requires nested sets of braces that exactly match the dimensions of the array.
int d [0:1][0:3] = ’ ’7,3,0,5, ’2,0,1,6 ;
// d[0][0] = 7
// d[0][1] = 3
// d[0][2] = 0
// d[0][3] = 5
// d[1][0] = 2
// d[1][1] = 0
// d[1][2] = 1
// d[1][3] = 6
SystemVerilog provides a shortcut for declaring a list of values. An inner list for one dimension of an array can be repeated any number of times using a Verilog-like replicate factor. The replicate factor is not followed by an apostrophe.
int e [0:1][0:3] = ’ 27,3,0,5 ;
// e[0][0] = 7
// e[0][1] = 3
// e[0][2] = 0
// e[0][3] = 5
// e[1][0] = 7
// e[1][1] = 3
// e[1][2] = 0
// e[1][3] = 5
NOTE: The ’ list and ’n replicated list operators are not the same as the Verilog concatenate and n replicate operators.
the braces are used two ways
When initializing an unpacked array, the ’ braces represent a list of values. This is not the same as a Verilog concatenate operation. As a list of values, each value is assigned to its corresponding element, following the same rules as Verilog assignment statements. This means unsized literal values can be specified in the list, as well as real values.
The Verilog concatenation and replication operators use the braces, without the leading apostrophe. These operators require that literal values have a size specified, in order to create the resultant single vector. Unsized numbers and real values are not allowed in concatenation and replication operators.
Specifying a default value for unpacked arrays
an array can be initialized to a default value
SystemVerilog provides a mechanism to initialize all the elements of an unpacked array, or a slice of an unpacked array, by specifying a default value. The default value is specified within ’ braces using the default keyword, which is separated from the value by a colon. The value assigned to the array must be compatible with the type of the array. A value is compatible if it can be cast to that type.
int a1 [0:7][0:1023] = ’default:8’h55;
An unpacked array can also be an array of structures or other userdefined types (see section 5.3.11 on page 128). These constructs can contain multiple types. To allow initializing different types within an array to different values, the default value can also be specified using the keyword for the type instead of the default keyword. A default assignment to the array will automatically descend into structures or unions to find variables of the specified type. Refer to section 5.1.2 on page 98, for an example of specifying default values based on types.
5.3.5 Assigning values to arrays
Assigning values to unpacked arrays
The Verilog language supports two ways to assign values to unpacked arrays:
• A single element can be assigned a value.
• A bit-select or part select of a single element can be assigned a value (added as part of the Verilog-2001 standard).
SystemVerilog extends Verilog with two additional ways to assign values to unpacked arrays:
• The entire array can be assigned a list of values.
• A slice of the array can be assigned a list of values.
The list of values is specified between ’ braces, the same as with initializing unpacked arrays, as discussed in section 5.3.4 on page 119.
byte a [0:3][0:3];
a[1][0] = 8’h5; // assign to one element
a = ’’0,1,2,3,
’4,5,6,7,
’7,6,5,4,
’3,2,1,0;
// assign a list of values to the full array
a[3] = ’’hF, ’hA, ’hC, ’hE;
// assign list of values to slice of the array
The list of assignments to an unpacked array can also specify a default assignment, using the default keyword. As procedural assignments, specific portions of an array can be set to different default values.
always @(posedge clock, negedge resetN)
if (!resetN) begin
a = ’default:0; // init entire array
a[0] = ’default:4; // init slice of array
end
else begin
//...
end
Assigning values to packed arrays
multi-dimensional packed arrays are vectors with sub-fields
Packed arrays are vectors (that might happen to have sub-fields), and can be assigned values, just as with Verilog vectors. A packed array can be assigned a value:
• To one element of the array
• To the entire array (vector)
• To a part select of the array
• To a slice (multiple contiguous sub-fields) of the array
logic [1:0][1:0][7:0] a; // 3-D packed array
a[1][1][0] = 1’b0; // assign to one bit
a = 32’hF1A3C5E7; // assign to full array
a[1][0][3:0] = 4’hF; // assign to a part select
a[0] = 16’hFACE; // assign to a slice
a = 16’bz, 16’b0; // assign concatenation
5.3.6 Copying arrays
This subsection describes the rules for the four possible combinations of assigning arrays to arrays.
Assigning packed arrays to packed arrays
assigning packed array to packed array is allowed
A packed array can be assigned to another packed array. Since packed arrays are treated as vectors, the arrays can be of different sizes and types. Standard Verilog assignment rules for vectors are used to truncate or extend the arrays if there is a mismatch in array sizes.
bit [1:0][15:0] a; // 32 bit 2-state vector
logic [3:0][ 7:0] b; // 32 bit 4-state vector
logic [15:0] c; // 16 bit 4-state vector
logic [39:0] d; // 40 bit 4-state vector
b = a; // assign 32-bit array to 32-bit array
c = a; // upper 16 bits will be truncated
d = a; // upper 8 bits will be zero filled
Assigning unpacked arrays to unpacked arrays
assigning unpacked array to unpacked array is allowed
Unpacked arrays can be directly assigned to unpacked arrays only if both arrays have exactly the same number of dimensions and element sizes, and are of the same types. The assignment is done by copying each element of one array to its corresponding element in the destination array. The array elements in the two arrays do not need to be numbered the same. It is the layout of the arrays and the types that must match exactly.
logic [31:0] a [2:0][9:0];
logic [0:31] b [1:3][1:10];
a = b; // assign unpacked array to unpacked array
assigning unpacked arrays of different sizes requires casting
If the two unpacked arrays are not identical in layout, the assignment can still be made using a bit-stream cast operation. Bit-stream casting is presented later in this chapter, in section 5.3.7 on page 124.
Assigning unpacked arrays to packed arrays
assigning unpacked arrays to packed arrays requires casting
An unpacked array cannot be directly assigned to a packed array. This is because in the unpacked array, each element is stored independently and therefore cannot be treated as an integral expression (a vector). However unpacked arrays can be assigned to packed arrays using bit-stream casting, as discussed in section 5.3.7 on page 124.
Assigning packed arrays to unpacked arrays
assigning packed arrays to unpacked arrays requires casting
A packed array cannot be directly assigned to an unpacked array. Even if the dimensions of the two arrays are identical, the packed array is treated as a vector, which cannot be directly assigned to an unpacked array, where each array element can be stored independent from other elements. However, the assignment can be made using a bit-stream cast operation.
5.3.7 Copying arrays and structures using bit-stream casting
a bit-stream cast converts arrays to a temporary vector of bits
A bit-stream cast temporarily converts an unpacked array to a stream of bits in vector form. The identity of separate elements within the array is lost—the temporary vector is simply a stream of bits. This temporary vector can then be assigned to another array, which can be either a packed array or an unpacked array. The total number of bits represented by the source and destination arrays must be the same. However, the size of each element in the two arrays can be different.
Bit-stream casting provides a mechanism for:
• assigning an unpacked array to an unpacked array of a different layout
• assigning an unpacked array to a packed array
• assigning a packed array to an unpacked array
• assigning a structure to a packed or unpacked array
• assigning a fixed or dynamically sized array to a dynamically sized array
• assigning a structure to another structure with a different layout.
Bit-stream casting uses the SystemVerilog static cast operator. The casting requires that at least the destination array be represented as a user-defined type, using typedef.
typedef int data_t [3:0][7:0]; // unpacked type
data_t a; // unpacked array
int b [1:0][3:0][3:0]; // unpacked array
a = data_t’(b); // assign unpacked array to unpacked array of a different layout
The cast operation is performed by converting the source array (or structure) into a temporary vector representation (a stream of bits) and then assigning groups of bits to each element of the destination
array. The assignment is made from left to right, such that the leftmost bits of the source bit-stream are assigned to the first element of the destination array, the next left-most bits to the second element, and so forth.
5.3.8 Arrays of arrays
an array can mix packed and unpacked dimensions
It is common to have a combination of unpacked arrays and packed arrays. Indeed, a standard Verilog memory array is actually a mix of array types. The following example declares an unpacked array of 64-bit packed arrays:
logic [63:0] mem [0:4095];
This next example declares an unpacked array of 32-bit elements, where each element is a packed array, divided into 4 8-bit sub fields:
wire [3:0][7:0] data [0:1023];
Indexing arrays of arrays
unpacked dimensions are indexed before packed dimensions
When indexing arrays of arrays, unpacked dimensions are referenced first, from the left-most dimension to the right-most dimension. Packed dimensions (vector fields) are referenced second, from the left-most dimension to the right-most dimension. Figure 5-5 illustrates the order in which dimensions are selected in a mixed packed and unpacked multi-dimensional array.
Figure 5-5: Selection order for mixed packed/unpacked multi-dimensional array
5.3.9 Using user-defined types with arrays
arrays can contain user-defined types
User-defined types can be used as elements of an array. The following example defines a user type for an unsigned integer, and declares an unpacked array of 128 of the unsigned integers.
typedef int unsigned uint;
uint u_array [0:127]; // array of user types
User-defined types can also be defined from an array definition. These user types can then be used in other array definitions, creating a compound array.
typedef logic [3:0] nibble; // packed array
nibble [31:0] big_word; // packed array
The preceding example is equivalent to:
logic [31:0][3:0] big_word;
Another example of a compound array built up from user-defined types is:
typedef logic [3:0] nibble; // packed array
typedef nibble nib_array [0:3]; // unpacked
nib_array compound_array [0:7]; // unpacked
This last example is equivalent to:
logic [3:0] compound_array [0:7][0:3];
5.3.10 Passing arrays through ports and to tasks and functions
In Verilog, a packed array is referred to as a vector, and is limited to a single dimension. Verilog allows packed arrays to be passed through module ports, or to be passed in or out of tasks and functions. Verilog does not allow unpacked arrays to be passed through module ports, tasks or functions.
SystemVerilog allows unpacked arrays as ports and arguments
SystemVerilog extends Verilog by allowing arrays of any type and any number of dimensions to be passed through ports or task/function arguments.
To pass an array through a port, or as an argument to a task or function, the port or task/function formal argument must also be declared as an array. Arrays that are passed through a port follow the same rules and restrictions as arrays that are assigned to other arrays, as discussed in section 5.3.6 on page 123.
module CPU (...);
...
logic [7:0] lookup_table [0:255];
lookup i1 (.LUT(lookup_table));
...
endmodule
module lookup (output logic [7:0] LUT [0:255]);
...
initial load(LUT); //task call
task load (inout logic [7:0] t [0:255]);
...
endtask
endmodule
5.3.11 Arrays of structures and unions
arrays can contain structures or unions
Packed and unpacked arrays can include structures and unions as elements in the array. In a packed array, the structure or union must also be packed.
typedef struct packed // packed structure
logic [31:0] a;
logic [ 7:0] b;
packet_t;
packet_t [23:0] packet_array; // packed array of 24 structures
typedef struct // unpacked structure
int a;
real b;
data_t;
data_t data_array [23:0]; // unpacked array of 24 structures
5.3.12 Arrays in structures and unions
structures and unions can contain arrays
Structures and unions can include packed or unpacked arrays. A packed structure or union can only include packed arrays.
struct packed // packed structure
logic parity;
logic [3:0][ 7:0] data; // 2-D packed array
data_word;
struct // unpacked structure
logic data_ready;
logic [7:0] data [0:3]; // unpacked array
packet_t;
5.3.13 Synthesis guidelines
Arrays and assignments involving arrays are synthesizable. Specifically:
• Arrays declarations — Both unpacked and packed arrays are synthesizable. The arrays can have any number of dimensions.
• Assigning values to arrays — synthesis supports assigning values to individual elements of an array, bit-selects or part-selects of an array element, array slices, or entire arrays. Assigning lists of literal values to arrays is also synthesizable, including literals using the default keyword.
• Copying arrays — Synthesis supports packed arrays directly assigned to packed arrays. Synthesis also supports unpacked arrays directly assigned to unpacked arrays of the same layout. Assigning any type of array to any type of array using bit-stream casting is also synthesizable.
• Arrays in structures and unions — The use of arrays within structures and unions is synthesizable. Unions must be packed, which means arrays within the union must be packed).
• Arrays of structures or unions — Arrays of structures and arrays of unions are synthesizable (unions must be packed). A structure or union must be typed (using typedef) in order to define an array of the structure or union.
• Passing arrays — Arrays passed through module ports, or as arguments to a task or function, is synthesizable.
5.3.14 An example of using arrays
The following example models an instruction register using a packed array of 32 instructions. Each instruction is a compound value, represented as a packed structure. The operands within an instruction can be signed or unsigned, which are represented as a union of two types. The inputs to this instruction register are the separate operands, opcode, and a flag indicating if the operands are signed or unsigned. The model loads these separate pieces of information into the instruction register. The output of the model is the array of 32 instructions.
Example 5-2: Using arrays of structures to model an instruction register
package definitions;
typedef enum ADD, SUB, MULT, DIV, SL, SR opcode_t;
typedef enum UNSIGNED, SIGNED operand_type_t;
typedef union packed
logic [31:0] u_data;
logic signed [31:0] s_data;
data_t;
typedef struct packed
opcode_t opc;
operand_type_t op_type;
data_t op_a;
data_t op_b;
instr_t;
endpackage
import definitions::*; // import package into $unit space
module instruction_register (
output instr_t [0:31] instr_reg, // packed array of structures
input data_t operand_a,
input data_t operand_b,
input operand_type_t op_type,
input opcode_t opcode,
input logic [4:0] write_pointer
);
always @(write_pointer) begin
instr_reg[write_pointer].op_type = op_type;
instr_reg[write_pointer].opc = opcode;
// use op_type to determine the operand type stored in the input operand union
if (op_type == SIGNED) begin
instr_reg[write_pointer].op_a.s_data = operand_a.s_data;
instr_reg[write_pointer].op_b.s_data = operand_b.s_data;
end
else begin
instr_reg[write_pointer].op_a.u_data = operand_a.u_data;
instr_reg[write_pointer].op_b.u_data = operand_b.u_data;
end
end
endmodule
5.4 The foreach array looping construct
foreach loops traverse arrays of any number of dimensions
SystemVerilog adds a foreach loop, which can be used to iterate over the elements of single- and multi-dimensional arrays, without having to specify the size of each array dimension. The argument to a foreach loop is the name of an array followed by a comma-separated list of loop variables enclosed in square brackets. Each loop variable corresponds to one of the dimensions of the array.
int sum [1:8] [1:3];
foreach ( sum[i,j] )
sum[i][j] = i + j; // initialize array
The mapping of loop variables to array indexes is determined by the dimension cardinality, as described in section 5.3.8 on page
foreach loop variables are not declatred
It is not necessary to specify a loop variable for each dimension of an array. A dimension can be skipped by showing a variable position using two commas, without a variable name. Empty loop variables indicate that the loop will not iterate over that dimension of the array. Contiguous empty loop variables at the end of the variable list can be omitted without listing the additional commas.
The following example is a function that generates a check bit for each byte in a 128-bit vector. The vector is represented as a two dimensional packed array of 16 8-bit elements. A foreach loop is specified with just one variable, which represents the first dimension (the [15:0] dimension) of the array.
function [15:0] gen_crc (logic [15:0] [7:0] d);
foreach (gen_crc[i]) gen_crc[i] = ^d[i];
endfunction
Loop variables are automatic, read-only, and local to the loop. The type of each loop variable is implicitly declared to be consistent with the type of array index, which will be int for the types of arrays that have been presented in this book. (SystemVerilog also has associative arrays, which might use a different type for its indices. Associative arrays are not synthesizable).
special system functions for working with arrays
SystemVerilog adds several special system functions for working with arrays. These system functions allow writing verification routines that work with any size array. They may also be useful in abstract models.
$dimensions(array_name)
• Returns the number of dimensions in the array (returns 0 if the object is not an array)
$left(array_name, dimension)
• Returns the most-significant bit (msb) number of the specified dimension. Dimensions begin with the number 1, starting from the left-most unpacked dimension. After the right-most unpacked dimension, the dimension number continues with the left-most packed dimension, and ends with the right-most packed dimension. For the array:
logic [1:2][7:0] word [0:3][4:1];
$left(word,1)
will return 0
$left(word,2)
will return 4
$left(word,3)
will return 1
$left(word,4)
will return 7
$right(array_name, dimension)
• Returns the least-significant bit (lsb) number of the specified dimension. Dimensions are numbered the same as with $left.
$low(array_name, dimension)
• Returns the lowest bit number of the specified dimension, which may be either the msb or the lsb. Dimensions are numbered the same as with $left. For the array:
logic [7:0] word [1:4];
《SystemVerilog验证-测试平台编写指南》学习 - 第2章 数据类型
《SystemVerilog验证-测试平台编写指南》学习 - 第2章 数据类型
??SystemVerilog引进了一些新的数据类型,它们具有如下优点:
(1)双状态数据类型:更好的性能,更低的内存消耗;
(2)队列、动态和关联数组:减少内存消耗,自带搜索和分类功能;
(3)类和结构:支持抽象数据结构;
(4)联合和合并结构:允许对同一数据有多种视图(view);
(5)字符串:支持内建的字符序列;
(6)枚举类型:方便代码编写,增加可读性;
2.1 内建数据类型
??logic类型不能有多个结构性的驱动,在双向总线建模的时候要使用线网类型。
??logic类型只能有一个驱动,否则编译报错,所以logic可以用来查找顶层多驱动的错误。
双状态数据类型
??最简单的双状态数据类型是bit,它是无符号的。另外4种带有符号的双状态数据类型是byte、shortint、int和longint,如下所示:
bit b ; // 双状态,单比特无符号
bit [31:0] b32 ; // 双状态,32比特无符号整数
int unsigned ui ; // 双状态,32比特无符号整数
int i ; // 双状态,32比特有符号整数
byte b8 ; // 双状态,8 比特有符号整数
shortint s ; // 双状态,16比特有符号整数
longint l ; // 双状态,64比特有符号整数
integer i4 ; // 四状态,32比特有符号整数
time t ; // 四状态,64比特无符号整数
real r ; // 双状态,双精度浮点数
记:integer和time是四状态数据类型,integer是32位有符号,time是64位无符号。
注:
??你可能会乐意用byte数据类型代替logic[7:0]的声明,以使得程序更加简洁。但是需要注意的是这些新的数据类型是带符号的,所以byte变量的最大值只有127(取值范围-128 ~ 127)。可以使用byte unsigned,但这其实比使用bit[7:0]还要麻烦。在进行随机化时,带符号变量可能会造成意想不到的结果。
??若把双状态变量连接到待测设计,务必小心,如果待测设计产生了X或者Z,这些值会被转换为双状态值,而测试代码可能永远也察觉不了。这些值被转换为了0还是1并不重要,重要的是要随时检查未知值的传播。使用 $isunknown 操作符,可以在表达式的任意位出现X或者Z时返回1,如下例所示:
if ($isunknown(iport) == 1)
$display("@%0t: 4-state value detected on iport %b", $time, iport);
2.2 定宽数组
2.2.1 声明
??SystemVerilog允许只给出数组宽度的便捷声明方式,和C语言类似。
??可以通过在变量名后面指定维度的方式来创建多维数组。紧凑型声明方式是SystemVerilog特有的。
int lo_hi [0:15] ; // 16个整数[0]...[15]
int c_style [16] ; // 便捷声明,16个整数[0]...[15]
int array2 [0:7][0:3] ; // 完整的声明
int array3 [8][4] ; // 紧凑的声明
array2[7][3] = 1 ; // 设置最后一个元素为1
??若代码中试图从一个越界的地址中读取数据,那么SystemVerilog将返回数组元素的缺省值。比如,四状态logic返回X,双状态int或bit则返回0。这适用于所有数组类型,包括定宽数组、动态数组、关联数组和队列,也同时适用于地址中包含有X和Z的情况。线网在没有驱动时输出是Z。
??对于非压缩数组(非合并数组),很多SystemVerilog仿真器存放数组元素时使用32bit的字符边界,所以byte、shortint和int都是存放在相同长度的一个字中,而longint则存放到两个字中。例如:
bit [7:0] b_unpack [3] ; // 定义3个8位的非压缩数组
注:非压缩数组(非合并数组) 占用更多的内存空间。
2.2.2 常量数组
??使用:一个单引号加大括号来初始化数组。
??可以部分赋值;可以重复次数赋值;可以为那些没有显式赋值的元素指定一个缺省值。如下所示:
int ascend [4] = ‘{0, 1, 2, 3} ; // 对4个元素进行初始化
int descend [5] ;
descend = ‘{4, 3, 2, 1, 0} ; // 为5个元素赋值
descend [0:2] = ‘{5, 6, 7} ; // 为前3个元素赋值
ascend = ‘{4{8}} ; // 4个值全部为8
descend = ‘{9, 8, default:1} ; // {9, 8, 1, 1, 1}
2.2.3 基本的数组操作 -- for和foreach
??操作数组最常见的方式是使用 for 或者 foreach 循环。
??$size函数返回数组的宽度。
??在 foreach 循环中,只需要指定数组名并在后面的方括号中给出索引变量,SystemVerilog便会自动遍历数组中的元素,索引变量将自动声明,并只在循环内有效。
// 在数组操作中使用 for 和 foreach 循环
initial begin
bit [31:0] src [5] ; // 声明5个32位整数
bit [31:0] dst [5] ; // 声明5个32位整数
//bit [31:0] src[5], dst[5] ;
for (int i = 0; i < $size(src); i++)
src[i] = i ;
foreach (dst[j])
dst[j] = src[j] * 2 ; // dst的值是srcd的2倍
end
// 初始化并遍历多维数组
int md[2][3] = ‘{‘{0, 1, 2}, ‘{3, 4, 5}} ; // 定义常量数组
initial begin
$display ("Initial Value:") ;
foreach (md[i, j]) // 正确语法格式
$display ("md[%0d][%0d]=%0d", i, j, md[i][j]) ;
$display ("New Value:") ;
// 对最后3个元素重复赋值5
md = ‘{‘{9, 8, 7}, ‘{3{32‘d5}}} ;
foreach (md[i, j]) // 正确语法格式
$display ("md[%0d][%0d]=%0d", i, j, md[i][j]) ;
end
??打印结果:
Initial Value:
md[0][0]=0
md[0][1]=1
md[0][2]=2
md[1][0]=3
md[1][1]=4
md[1][2]=5
New Value:
md[0][0]=9
md[0][1]=8
md[0][2]=7
md[1][0]=5
md[1][1]=5
md[1][2]=5
注意:foreach 的使用。
??如果不需要遍历数组中的所有维度,可以在 foreach 循环里忽略掉它们。看下面的例子,把一个二维数组打印成一个方形的阵列。它在外层循环遍历一个维度,然后再内层循环遍历第二个维度。
// 打印一个多维数组
initial begin
byte twoD [4][6] ; //
foreach (twoD[i, j])
twoD [i][j] = i * 10 + j ; //赋初值
foreach (twoD[i]) begin
$write ("%2d: ", i) ;
foreach (twoD[, j])
$write ("%3d", twoD[i][j]) ;
$display ;
end
end
??打印结果如下:
0: 0 1 2 3 4 5
1: 10 11 12 13 14 15
2: 20 21 22 23 24 25
3: 30 31 32 33 34 35
??补充:foreach 循环会遍历原始声明中的数组范围。如,
数组 f[5]等同于f[0:4],而 foreach (f[i]) 等同于 for (int i = 0; i <= 4; i++)。
数组 rev[6:2]来说,foreach (rev[i]) 等同于 for (int i=6; i >= 2; i--)。
2.2.4 基本的数组操作 -- 复制和比较
!-->!-->以上是关于SystemVerilog for Design Edition 2 Chapter 5的主要内容,如果未能解决你的问题,请参考以下文章
SystemVerilog for Design Edition 2 Chapter 6