2.8 select storage array type
This paper introduces some criteria for selecting storage types based on flexibility, memory usage, speed and sorting requirements.
2.8.1 flexibility
- If the index of an array is a continuous nonnegative integer, a fixed width or dynamic array should be used.
- If the fixed width array has been selected when the array is compiled, and the array width is not known until the program runs, select the dynamic array
- As long as the type matches, any length of queue can be passed to the subroutine
- Array indexes are irregular, sparse distributed indexes generated by random values or addresses, and associative arrays should be selected for content-based addressing.
2.8.2 memory usage
- Using dual state type can reduce the storage consumption during simulation
- Associative arrays should be used to model memory on the megabyte scale
2.8.3 speed
- It is also considered to select the array type according to the number of memory accesses per clock cycle
- Fixed width and dynamic arrays are continuous storage space, and the time to access any location is the same
- The read and write speed of the queue is basically the same as that of fixed width or dynamic array. Queues are accessed quickly at the beginning and end, but they need to be moved in the middle, which takes linear time
- Associative array access needs to calculate hash table and tree, and the access speed is the slowest
2.8.4 sorting
- SV can sort any type of one-dimensional array (fixed width, dynamic, associative array and queue), and select the array type according to the frequency of adding elements to the array
- If all elements are added at one time, select fixed width or dynamic array, and you only need to sort the array once. If the array is added one by one, select the queue.
- If the values of the array are discontinuous and different from each other, consider using the associative array and the element value Province as the index. Use the exist() function to check and delete.
2.8.5 select the optimal data structure
The following are suggestions for selecting common data structures:
- Network array package. Features: fixed length, sequential access. For fixed or variable length packets, fixed width and dynamic array are adopted respectively
- An integrator that holds the desired values. Features: the length is unknown before simulation, accessed by value, and the length changes frequently. General selection queue
- Ordered structure. Features: if the data is output in a predictable order, a queue is used. If the output order is uncertain, an associative array is used
- Model large capacity memory with more than one million entries. If you do not need to use all the storage space, you can use associative arrays to achieve sparse storage.
- The command name or opcode in the file. Features: convert string to fixed value. Read the string from the file, and then use the command name as the string index to find the command name or opcode in the associative array.
On the basis of OOP, it also involves an array of handles to objects
2.9 creating new data types using typedef
typedef statements can be used to create new data types. For example, an arithmetic logic unit ALU is required to be configured at compile time to accommodate operands with different bit widths such as 8 bits, 16 bits, 24 bits or 32 bits. In Verilog, you can define a macro for the bit width and type of operands respectively.
// Old Verilog style `define OPSIZE 8 `defeine OPREG reg[' OPSIZE-1:0] 'OPREG op_a,op_b;
In the above case, there is no new type of innovation, but only text replacement. In SV, the following code can be used to create a new type. Convention: all custom user types have the suffix "_t".
// New SV style parameter OPSIZE = 8; typedef reg[OPSIZE-1:0] opreg_t; opreg_t op_a,op_b;
Generally speaking, if the data bit width does not match, the value will be expanded or truncated. SV allows replication between these basic types without warning.
**parameter and typedef statements can be put into a package so that they can be shared by the whole design and test platform.
typedef bit[31:0] unit ; //32-bit two state unsigned number typedef int unsigned unit; // Definition of equivalence // Declare an array of this type typedef int fixed_array5[5]; fixed_array5 f5; initial begin foreach (f5[i]) f5[i]=i; end
2.10 create user-defined structure
One of Verilog's biggest defects is that it has no data structure. In SV, the keyword struct can be used to create, but struct has few functions. It is better to use classes directly on the test platform.
Just as Verilog modules include both data (signals) and code (always/initial code blocks and subroutines), classes contain data and programs for debugging and reuse. However, struct just organizes data together and lacks data manipulation methods.
However, because struct is a collection of data, it can be integrated. If you want to model a complex data type in the design code, such as pixels, you can put it in struct. The structure can be transferred through the module port. But if you want to generate random data with constraints, you should use classes.
2.10.1 create a new type using struct
Function: combine several variables into a structure.
struct {bit[7:0] r,g,b} pixel; // The above statement just creates a pixel variable directly. If you want to share it in the port and program, you must create a new type. typedef stuct {bit[7:0] r,g,b} pixel_s; // Use the suffix "_s" in struct to represent a custom type.
2.10.1 initialize the structure
Assign multiple values to a structure in a declaration or procedure assignment statement.
initial begin typedef struct{ int a; byte b; int d; } my_struct_s; my_struct_s st = '{32'haaaa_aaaad, 8'hbb, 16'hcccc, 32'hdddd_dddd }; $display("str = %x %x %x %x",st.a,st.b,st.c,st.d); // The structure variable calls the data in it end
2.10.3 consortium union
Compared with the structure, the union section saves bytes, but a more complex data structure must be created and maintained.
2.10.4 consolidation structure
typedef struct packed {bit[7:0] r,g,b;} pixel_p_s; pixel_p_s my_pixel;
- If the structure is operated frequently, for example, the entire structure needs to be copied frequently, the efficiency of using the merged structure will be higher.
- However, if the operation is often directed at individual members within the structure rather than the whole, the structure should not be merged.
2.11 type conversion
The variety of data types in SV means that they need to be converted before them.
If the bit distribution of the source variable and the target variable are exactly the same, such as integer and enumeration types, they can be assigned to each other directly.
If the bit distribution is different, such as byte array and word array, you need to rearrange the bit distribution using the stream operator
2.11.1 static conversion
The static conversion operation does not check the converted value.
When converting, specify the target type and put single quotation marks before the expression to be converted.
int i; real r; i =int '(10-0.1); // Conversion is not mandatory r = real(42); // Conversion is not mandatory
2.11.2 dynamic conversion
The dynamic conversion function $cast allows you to check for out of bounds values.
2.1.3 flow operator
The stream operators < < and > > are used to the right of the assignment expression, followed by an expression, structure or array.
The stream operator is used to package the subsequent data into a bitstream. Operator > > turns data from left to right into a stream, while < < turns data from right to left into a stream.
initial begin int h; bit[7:0]b,g[4], j[4] = '{8'ha,8'hb,8'hc,'8hd}; bit[7:0]q,r,s,t; h={>>{j}}; // 0a0b0c0d- pack arrays into integers h={<<{j}}; // b030d050 bit reverse order h={<<byte{j}}; // 0d0c0b0a byte flashback g={<<byte {j}}; // 0d,0c,0b,9a split into arrays b={<<{8'b0011_0101}}; // 1010_ 1100 bit flashback b={<<4{8'b0011_0101}}; // 0101_ 0011 half byte flashback {>>{q,r,s,t}} = j ; // Spread j into four byte variables h={>>{t,s,r,q}}; // Concentrate bytes on hli end
Many connectors {} can be used to do the same, but the flow operator is simpler and easier to read
Example: use the flow operator to convert the queue
initial begin bit[15:0] wg[$]= {16'h1234,16'h5678}; bit[7:0] bg[$]; // Convert a word array to a byte array bq={>>{wg}}; // 12 34 56 78 // Convert byte array to word array bq = {8'h98,8'h76,8'h54,8'h32}; wq={>>{bq}}; // 98 76 54 32 end
**Subscript [256] in array declaration is equivalent to [0:255] instead of [255:0]**
Stream operations can be used to package or split structures (such as ATM cells) into byte arrays.
Example: use the stream operator to convert a structure into a dynamic byte array, and then the byte array is converted into a structure in turn.
initial begin typedef struct{ int a; byte b; shortint c; int d; } my_struct_s; my_struct_s st = '{32'haaaa_aaaa, 8'hbb, 16'hcccc, 32'hdddd_dddd}; byte b[]; // Convert structure to byte array b={>>{st}}; //{aa aa aa aa bb cc cc dd dd dd dd}; // Convert byte array to structure b='{8'h11,8'h22,8'h33,8'h44,8'h55,8'h66,8'h77,8'h88,8'h99,8'haa,8'hbb}; st = {>>{b}}; // st=11223344,55,6677,8899aabb end
2.12 enumeration types
Use a text macro before enumerating types. Macros are too scoped and visible to the debugger.
- Enumeration creates a type that is limited to a collection of specific names, such as opcodes in instructions or state names in state machines.
- Another way to define constants is to use parameters. However, parameters need to define each value separately, and enumeration types can automatically assign different values to each name in the list.
The simplest enumeration type declaration: a list of constant names and one or more variables
enum {RED,BLUE,GREEN} color; // Enumeration type // Create a data type representing 0,1,2 typedef enum{INIT,DECODE,IDLE} fsmstate_e; fsmstate_e pstate,nstate; // Declare custom type variables initial begin case(paste) IDLE: nstate =INIT; // Data assignment INIT: nstate = DECODE; default: nstate =IDLE; endcase $display("Next state is %s", nstate.name(); // Displays the symbol name of the status ) end
**The suffix "_e" indicates the enumeration type
2.12.1 defining enumeration values
The enumeration value defaults to an integer incremented from 0.
typedef enum{INIT,DECODE=2,IDLE} fsmtype_e;
2.12.2 subroutines of enumeration type
SV traverses functions of enumeration types:
- first() ;last(); next(); next(N); prev(); prev(N);
Use the do... while loop to iterate through all values.
// Traverse all enumeration members typedef enum{RED, BLUE, GREEN} color_e; color_e color; color =color.first(); do begin $display("color =%0d/%s", color,color.name); color =color.next(); end while(color!=color.first());
2.12.3 conversion of enumeration type
The default type of enumeration type is dual state int. SV does not allow non display type conversion.
typedef enum{RED, BLUE,GREEN} COLOR_E; COLOR_E color,c2; int c; initial begin color =BLUE; c= color; c++; if(! $cast(color,c)) $display("Cast failed for c=%0d",c); $display("Color is %0d /%s",color,color.name); c++ c2=COLOR_E'(c); / $display("c2 is %0d /%s",c2,c2.name); end
2.13 constants – const
2.14 string
The string type in SV can be used to store variable length strings.
Example: string method
string s; initial begin s="IEEE"; $display(s.getc(0)); $display(s.tolower()); s.putc(s.len()-1,"-"); s={s,"P1800"}; $display(s.substr(2,5)); // Create a temporary string, my_log($psprintf("%s %5d",s,42)); end task my_log(string message); // Print the information into the log $display("@%0t: %s",$time,message); endtask
2.15 expression bit width
2.16 summary
SV provides many new data types and structures, which makes it possible to write test platforms on OOP at a higher level of abstraction.
- Queues are ideal for creating scoreboards, adding or deleting data frequently
- Dynamic arrays allow you to specify the array width when the program is running
- Associative arrays are used for sparse storage
- Enumeration types create a list of constants, making the code easy to read and write.