Chap 2 Data Types Notes
Chap 2 Data Types Notes
1
Yet another language I have to learn. Why? What are the benefits.
1. Both in RTL and test-benches. We are being asked to do more with less. Can’t
expect to have huge teams of people. Need to improve methodology. Let the
tools do the work. I’ll point out which constructs should be synthesizable. An
easy rule of thumb is anything dynamic (i.e. not known at compile time) is not
synthesizable.
2. Create stimulus with randomization and use coverage data to steer the stimulus
into interesting areas of the design.
3. To evaluate the quality of the verification. Coverage is required for automatic
generation and direction of stimulus and to know your random stimulus is
preforming the testing specified in the test plan.
4. Think and code at a higher level. Better productivity.
5. To increase or even just maintain productivity
2
1) Verilog-2005 and SystemVerilog merged in the 2009 SystemVerilog standard. There is
no more just verilog.
2) Verilog RTL synthesis standard describes a standard syntax and semantics for Verilog®
HDL-based RTL synthesis. It defines the subset of IEEE Std 1364-2001 (Verilog HDL) that
is suitable for RTL synthesis and defines the semantics of that subset for the synthesis
domain.
3) No synthesis spec for SystemVerilog yet. Considering it took 10 years for a synthesis
spec for verilog-1995 it will be a while.
3
1) What good is a language if the tool doesn’t support it. Nothing more frustrating
than trying to use an example in a book or a training and it doesn’t work.
2) Need to really take a hard look at your tool chain before you start putting
SystemVerilog in your RTL. Don’t forget the equivalency checker.
4
Use of logic instead of reg eliminates the confusion that a variable declared as type
reg implies a register will be synthesized. Synthesizable.
5
1) Look at some new types SystemVerilog introduces.
2) Why use 2-state types
1) Because the simulator only has to keep track of 0’s and 1’s. Not 0, 1, X,
or Z like 4-state types like wires and regs
2) Unless you are using SystemC, C/C++ variables are 2-state
3) bit is an unsigned user defined width type, byte is a signed 8 bit type, shortint is
signed 16 bit type, int is signed 32 bit type, longint is signed 64 bit type
4) Lets look at a few examples:
1) my_reg is a 16-bit value
2) my_reg2 is a 16-bit value but my_reg and my_reg2 are not the same.
my_reg is unsigned, my_reg2 is signed.
5) Can create an 8-bit unsigned variable by declaring it as “byte unsigned
unsigned_byte; Useful when we talk about creating user defined types.
6) Only useful for testbenches. Do not use to connect the output of your DUT with
these or X and Z’s will be resolved to a 0. Not synthesizable.
6
1) Any bit in a 4-state variable that is X or Z will resolve to 0 in 2-state.
2) $isunknown will return a 1 if any bit in the variable is x or z. Returns 0 otherwise.
7
See Chap_2_Data_Types/exercise1 for code.
my_byte ranges from -128 to 127
final value of:
my_int is 32’h0000_0F00 because the X’s and Z’s in 4-state
my_integer resolve to 0 when assigned to 2-state my_int.
my_bit is 32768 because my_bit is not signed
my_short_int1 is -32768 because my_short_int1 is signed
my_short_int2 is 32767 because we have wrapped from max_neg to
max_pos
8
Enhancements to Verilog-2001 style array declarations
This makes parameterization of array’s less error prone. For the size declarations of
a 2-D array (i.e. a memory):
parameter MEM_WIDTH = 8
parameter MEM_DEPTH = 128;
don’t try logic [4] my_logicmem [64]; since the elements of my_logicmem are only 4-bits
wide.
9
1) Not the grave mark used for compiler directives like `default_nettype none
2) Remember that the first value in the list is assigned to array[0], 2nd to array[1],
etc. so ascend[0] = 0, ascend[1]=1, ascend[2] = 2, ascend[3] = 3
3) If $display after int ascend[4] = ‘{0,1,2,3}; will get ‘{0,1,2,3}
4) %p will print an assignment pattern equivalent to the data objects value. The data
object can be an array, structure, class, etc.
10
See Chap_2_Data_Types/exercise2 for code.
my_mem = {A5, A5, 0} because reading from an out of bounds 4-valued type
returns an X but it is resolved to a 0 when assigned to 2-value logic.
my_logic = x reading from an out of bounds 4-valued type returns an X
my_logicmem= {0,1,2,0} because reading from an out of bounds 2-value type
returns a 0
my_mem does not change since the write to my_mem[3] is out of range.
my_logic = 1 since my_logicmem[1] = 1
my_logic = 4’h5 since my_mem[1] = 8’hA5 but my_logic is only 4-bits wide
my_logic = x since my_logicmem[4] returns an X since the index is out of bounds.
my_logicmem[x] = x.
11
1) The size of src is 5, not 32
2) For both for and foreach the index variable is local.
3) For a multi-dimensional array with packed and unpacked dimensions what is returned by
$size? It is the first unpacked array dimension. For example, for
logic [3:0][4:0] mixed_array [0:5] [0:6] [0:7];
$size(mixed_array) = 5.
12
1) Loop through every element goes from [i=0] [j=0 to 2]to [i=1] [j=0 to 2] and will
print out 0, 1, 2, 3, 4, 5
2) Loop through the first dimension will go from i=0, j=0 to i=1, j=0 and print out 0 and 3
3) Loop through the 2nd dimension will go from i=1, j=0 to i=1, j=1, to i=1, j=2 and print out
3, 4, 5
13
1) Comparision is limited to equality and inequality only
2) First compare will print src == dst
3) After copy and assignment of src[0] = 5 dst = {0,1,2,3,4} and src = {5,1,2,3,4}
4) Second compare will print src == dst since only elements 1:4 are compared.
14
1) $displayb is a system function where the format specifier is binary.
2) The double comma causes a space to be inserted.
15
See Chap_2_Data_Types/exercise3 for code
`default_nettype none
module test;
bit [11:0] my_array [4];
initial begin
my_array = '{12'h012, 12'h345, 12'h678, 12'h9AB};
$display("--------------");
end
endmodule // test
16
1) Lets take a step back and go over unpacked arrays before going over packed
arrays.
2) SystemVerilog simulators differentiate between a packed and unpacked array.
3) To trigger on up_array will need to specify the byte, for example always
@(up_array[1])
17
1) bytes refers to the 4-byte word
2) bytes[3] refers to the 4th byte of bytes
3) bytes[3][7] refers to the most significant bit of the most
significant byte. In this case 1’b1 since
bytes[3]=8’hCA=8’b11001010
4) compiling bit [7:0][3] bytes; will report error Packed
dimension must specify a range.
5) $displayb(bytes[31]); prints out the 32nd byte of bytes,
which is obviously out of range
6) Synthesizable
18
bit [2:0] [3:0] [7:0] barray; can be thought of as contiguous.
19
1) barray[0][3] refers to barray[0] byte 3, i.e. 8 bits
2) barray[0][1][6] refers to barray[0] byte 1 bit 6.
20
1) [3:0][7:0] is the packed dimensions
2) [0:7] [0:7] [0:7]; is the unpacked dimensions
21
See Chap_2_Data_Types/exercise4 and Chap_2_Data_Types/exercise5 for code
22
1. int dyn[], d2[]; creates 2 dynamic arrays with a base type of int called
dyn and d2.
2. d2=new[20](dyn); allocates 20 new elements, copies the
existing 5 elements to the beginning of the array, and
deallocates the old 5 element array.
3. dyn=new[100]; allocates 100 new and deallocates the old 5
element array. Old values are lost.
4. Assignment to an unallocated location in a dynamic array is ignored, no compile error
result.
5. New is not the only way to allocate new space, i.e. grow the dynamic array, any
assignment that adds to the number of elements will grow the dynamic array as
required. For example, dyn = {dyn, 5}; will add element 5 to the array. The size is now 6.
6. How do I shrink the dynamic array since delete() completely destroys the array? Any
assignment that will reduce the number of elements will shrink the dynamic array as
required. For example, dyn = dyn[1:3]; will reduce the size to 3.
7. If I declare a dynamic array with dyn = new[5] and then execute dyn = new[4](dyn);
what happens? The first 4 elements are copied to the new array and the size shrinks to
4.
8. Not being able to view dynamic arrays makes sense. How
would you show in a viewer the array is created, changing
size, and deleted during run-time?
9. Not synthesizable since dynamic.
23
1) If you saved these transactions in a fixed size array you would have to make it as
big as it will ever be, wasting space.
2) Makes the array easy to maintain because all you have to do is add elements
and the array automatically adjusts.
3) mask.size; and mask.delete; is the object oriented style of function call.
4) Assign to a dynamic array just like a normal array, using the [index]
24
1) Next we’ll look at a way to manage data called a queue.
2) Maybe mention that q.insert(1,v) inserts before value #1
2) Queues will dynamically re-size as you add and delete items from it. Very useful
in testbenches to queue up transactions and then release them.
3) string coworkers[$] declares a queue of strings
4) reg [7:0] address[$] declares a queue of 8-bit addresses
5) coworkers.insert(1, “Holger”) inserts the string “Holger” in position 1 (between
“Willy” and “Rob”)
6) Can also push_front and pop_back and return the size. Not synthesizable since
they are dynamic. Since dynamic cannot view in Questa’s waveform viewer.
25
See Chap_2_Data_Types/exercise6 for code
26
1) An associative array is an array where the index into the array can be any
variable, not just 0,1,2,3, It could be a string, an int, a user defined type, etc.
2) An associative array is declared with a data type in square brackets, such as [int].
This is the index type.
3) This example will set assoc[1] = 1, assoc[2] = 2, assoc[3] = 4, ..., assoc[7] = 128
4) indx = 0 after idx has been left shifted 8 times.
5) foreach will loop through each element of the associative array.
27
1) Recall that an index into an associative array can be any type. Lets use a string
to index into the array.
2) Use the string name of the register as the index. We can do the same for any
characteristic of our array we want to keep track of. Reset values, permissions, etc.
3) Remember that the string is the index
28
1. num returns the # of entries in the associative array.
2. exists returns a 1 if an element referenced by the specified index exists.
The array is ordered by the index. If int is the index the order is from smallest index
to largest index. If string is the index the order is
alphabetical. This can get confusing for user defined types. The order for address is
ADC_REG(0), DAC_REG(1), LCD_REG(4), LED_REG(2), and I2C_REG(3)
3. first assigns to the given index variable the value of the smallest index in the
associative array and returns a 1 if the array is non empty
4. last assigns to the given index variable the value of the smallest index in the
associative array and returns a 1 if the array is non empty
5. next assigns to the given index variable the value of the index that is greater than
the given index. If there is a next a 1 is returned. prev does the same except
finds the previous entry
6. delete removes the entry at the specified index. If no index is specified the
associative array is deleted
29
See Chap_2_Data_Types/exercise7 for code
`default_nettype none
module test;
initial begin
end
endmodule // test
30
1) We’ll go over each of these in more detail.
2) The ordering array methods cannot be used on associative arrays which makes sense
since there is no notion of order in an associative array.
31
1) Sum just keeps adding and letting the result overflow. If one has an odd # of 1’s
one.sum = 1, if one has an even # of 1’s one.sum = 0.
2) Assigning the sum to a signal of sufficient width doesn’t help either. We’ll see how to
solve this in a few slides.
3) The XOR operator is particularly useful for parity calculations over a number of words.
For example, I designed a RS485 interface that calculated an 8-bit longitudinal party
over 3 words and then sent the parity to the interface. It would of been easy in my
testench to load up an array with the 3 words and calculate the parity in 1 line.
32
1) Cannot randomly pick an element from an associative array in one line.
2) This example randomly selects the n’th element by looping through the array.
3) When assigning to an array can specify the index and the element referenced by that
index using the : expression. For example 0:1 assigns aa[0] = 1 and 30:64 assigns
aa[30]=64.
4) Get a random value from 6 (aa.size – 1) to 0
5) Loop until the counter equals the random value stored in element.
6) Set indx to the current index and break from the loop.
7) Display the element that was selected randomly.
33
1) Note that the %p is the format specifier for a queue. Interestingly the radix is always
decimal (for Mentor) and there no way to change it. VCS prints out arrays using the %p
format specifier in hex radix.
34
1) When using and array locator method with a “with” clause a queue is returned.
2) Note that item is now a reserved word.
35
1) Recall the issue we had a few slides back computing the sum of single bits? This
example shows how to solve this problem
2) Cast item to an int before doing the sum
36
1) Unlike other methods which return a queue
2) The LRM says that reverse can work on packed array. I tried this in QuestaSim and it
doesn’t work.
3) Shuffle does not take a seed. Everytime you re-run modelsim you’ll get the same random
order. But if you shuffle an array twice in 1 run you will get a different order.
4) I also tried sorting this array using the with clause ex: with (item > 0); and the results are
garbled.
5) Note that $display("f.reverse = %p", f.reverse); will result in # ** Error: test.sv(30):
Function with return type of void cannot be used in/as an expression. since the return type
of f.reverse is not an array but a void.
37
See Chap_2_Data_Types/exercise8 for code
`default_nettype none
module test;
// d) Sort all values in the queue and print out the resulting queue
byte_queue.sort;
$display("byte_queue.sort = %p", byte_queue); // # byte_queue.sort = '{-1, 2, 127}
note that $display("byte_queue.sort = %p", byte_queue.sort); will result in ** Error: test.sv(23): Function with return type of void
cannot be used in/as an expression.
38
Based on flexibility notes:
1) Fixed size needs to be known at compile time.
Based on memory usage notes:
1) For example a byte array wastes ¾ of memory if unpacked.
2) Queues are slightly less memory efficient because of maintaining pointers.
3) If your array grows and shrinks often use a queue since repeatedly calling new for a
dynamic array is very expensive.
39
Based on speed notes:
1) For a queue the pointers all have to be rearranged if element is pushed in middle. Fast if
pushed on ends.
2) Because for an associative array the value could be anywhere. Hash tables and trees are
used to improve search time.
Choosing the best data structure notes:
1) Network packets: Say you want to create a bunch of network packets to test or you want
to store a bunch of received network packets. What would be the best structure? Network
packets are typically fixed size. You’ll be adding them to the tail. Use a fixed size or
dynamic array.
2) Scoreboard of expected values: A scoreboard is used to hold expected values. It is filled
as transactions are created. The size is not known until run time, it’s accessed by value, and
is constantly changing size. Use a queue or possibly an associative array if there are more
than a few hundred elements and your testbench is inserting/deleting in the middle.
3) Modeling > 1M entry memories: Associative array if you don’t need every location. Use
2-state values packed into 32-bits.
4) Translating opcode names to opcode values: In this scenario you are translating an
opcode string to a fixed value. For example, translating an ADD to it’s hex equivalent. Use
an associative array with the string opcode as an index.
40
1) Remember that byte is signed 2-state type
2) Usage:
Declare a variable my_data of type ubyte_t
Declare a variable my_bus of type global_bus_t
Should be synthesizable
Convention is to append _t on type definitions
41
1) Lets look at a new construct called Structures. Similar to VHDL record
2) Why: Create a structure of transactions to test a memory like in HW2. In your
structure would be an address, data to write, write/read flags, and read data. As
you create new transactions put them in a queue.
3) This example initializes the elements in the declaration and in sequential code.
8’hA5 is assigned to data_in. 4’hC is assigned to address.
4) Can view stuctures in questasim’s waveform viewer. Should be synthesizable
since not dynamic. Limitations: Can’t put wire’s in the struct. Initialization must
be done explicitly. Can’t put executable code like tasks or functions in structures.
No direction information. Can be instantiated with direction information. Useful
in RTL if entire struct is same direction. An even better way is to create an
interface which we’ll talk about in the class-based portion of the class. Overall,
struct’s are not used very much because a class has all the capability with none
of the limitations.
42
See Chap_2_Data_Types/exercise9 for code
`default_nettype none
module test;
typedef struct {
bit7_t header;
bit7_t cmd;
bit7_t data;
bit7_t crc;
} packet;
packet my_packet;
initial begin
my_packet.header = 7'h5A;
end
endmodule // test
43
1) `include requires that every variable in the included files need a unique name.
For a large verification team coming up with unique names can be difficult.
2) Packages can include tasks, classes, enumerated types, etc.
44
Recall that package ABC defined parameters abc_data_width and timeout, type
abc_data_t, and string message.
module test;
import ABC::*; // Search ABC for symbols from package ABC
abc_data_t data; // User defined type defined in package ABC.
string message = "Test timed out"; // Override message definition in ABC
initial begin
#(timeout); // Parameter defined in ABC
$display("Timeout - %s", message);
$finish;
end
endmodule
45
1) Static cast does not check the bounds of the cast. We’ll see how a cast can be
out of bounds later.
2) Rounding occurs if there is a fractional part. If the statement was i=int'(10.0-0.6); i
would equal 9
3) Can type convert between user defined types as well.
4) Remember that a byte is only 8-bits wide. 256 is 9’h100 so what can fit in the
8bits is all 0’s. This is not an out of bounds cast.
46
1) h is packed because it is defined to be of type int.
2) Is j packed or unpacked? The array of bits is packed. the array of the 8-bits is
unpacked.
3) To convert j to h could use h={j[0], j[1], j[2], j[3]}; but this gets unwieldy.
4) h={ << byte {j}}; Reverses the stream at a byte granularity instead of a bit
granularity. The granularity specifier can be a user defined type. Useful for converting big
endian to little endian or vice versa.
47
1) i is packed because int’s are packed
2) After {<<{b1,b2,b3,b4}}=k; $display("b1= %h", b1); // # b1= F7 because EF from left to
right is 1110_1111 so from right to left is 1111_0111 = F7
48
Solution: See Chap_2_Data_Types/exercise10 for solution.
`default_nettype none
module test;
// Create a user defined type, nibble, of 4 bits
typedef bit [3:0] nibble_t;
49
1) Lets say you have a register file of 3 registers, CFG, ADC_REG, and CTRL you
want to test.
2) Each field in the enumeration is associated with an integer. You can define the
association if you want. By default first value in list = 0. 2nd =1, etc. I would
recommend sticking with the default. By default the base type of an enumeration is
an integer which initializes to 0. Remember this factoid! Naming convention is to
put a _e suffix on enumerations.
3) Not visible in a simulation waveform window
4) This is strictly not an enumerated type, it is an enumeration. No types are
created, just constants. I use this often for testing a configuration register design.
The config regs typically change rapidly and are a big job to maintain. Enumerate
both the configuration registers as well as what each bit in each configuration
register means. Enumerations are placed outside of the module declaration like a
‘define
First we are going to look at using enumerated types in a testbench. Then we’ll look
at using them in synthesizable RTL.
50
1) Enumerated types expands on enumerations
2) Allows you to declare variables whose type is the enumerated type.
3) Naming convention is to put a _t suffix on enumerated types
4) Value of register is visible in waveform Unlike enumerations
5) Assigning my_reg to a value not in the enumerated type is a compiler error
51
All these methods are built-in to the language.
1) typedef enum {ST0, ST1, ST2} state_e; // Declare an enumerated type called state_t
2) state_e state; // Declare a variable state of type state_t. If we assume that the
present value of state is ST0 this is what these methods will return.
3) I’ve used the .next and .prev to walk through a list of registers and write/read to
all of them. .next and .prev wrap
4) next with no arguments is the same as next(1). Similar with prev.
5) Depending on the format specifier state.last will print the name or the value.
52
1) How to loop through every member of an enumerated type? Not that easy
2) Might be tempted to use a for loop. Problem is that the loop ends when color is
equal to the last color, GREEN.
3) If I use color <= color.last instead of color != color.last the loop never finishes
becasue next never provides a color greater than the last color, GREEN.
53
1) Start with a basic testbench. The config reg module has one 2-bit input called
address. Instantiate by name, not position.
2) typedef enum {CFG, ADC_REG, CTRL} reg_e;// Add an enumerated type definition
called reg_e
3) reg_e my_reg; // Declare a variable address of type reg_e
4) my_reg = CFG; // Initialize the address to CFG. Not really required since address
will initialize to 0
5) my_reg = my_reg.next; // Increment through the registers
54
1) Lets look at how this will simulate
2) Note that address starts at CFG since the default base type for enumerated
types is int and the default for an int is 0. Also note that the enumerated type will
wrap. Will get a # ** Warning: (vsim-3015) test.sv(27): [PCDPC] - Port size (2 or
2) does not match connection size (32) for port 'address'. If you want to avoid
this connect address to my_reg[1:0]. Default type for enumerated type is int.
55
See Chap_2_Data_Types/exercise11 for solution.
Note that opcode.next; will not traverse the opcodes. It will just give the next of the present opcode.
`default_nettype none
module test;
initial begin
// Loop through all the values of variable opcode every 10ns
// Solution 1 but note that an int instead of an enumerated
// loop variable is used.
for (int i=0;i<opcode.last+1;i++) begin
$display("opcode = %s", opcode);
#10ns;
opcode = opcode.next(1);
end
end
endmodule // test
56
1) Look at the usage of enumerated types in RTL
2) Declare an enumerated type state_e.
3) Declare 2 variables current_state and next_state of type state_e
4) Why would we want to do this?
5) Error prone to add states and lots of typing. Need to do the same with
next_state.
6) Next we’re going to take a good look at using enumerations and enumerated
types in RTL. We’ll start with using enumerations in RTL and then add
enumerated types.
57
1) First begin with enumerations in RTL
2) Start with basic outline
3) Add enumeration state_e
4) Can use name of enumeration instead of the encoded 2-bit value. A little better
than localparam because the encoding is done for you.
58
1) How does this simulate: Key observation here is next_state when reset = 0 is 01.
Why? Because the always block to determine next_state was entered. What event
occurred to enter the always block? Current state was reset so it went from X, the
initialized vale for 4-state logic, to 0.
59
1) Now lets expand on enumerations and declare an enumerate type
2) Mostly same RTL as before
3) Declare an enumerated type called state_e
4) Declare current_state and next_state to be of type_e
5) Lets see how it simulates
60
1) What is happening here? next_state is ST0. It should be ST1. Lets look at a
close up.
2) Each vertical line is 1ns
3) Remember that the default base type of an enumerated type is integer so
current_state and next_state are initialized to 0 which is ST0. Since we reset
current_state to 0 there is never an event on current_state to cause us to enter the
next_state always block.
61
1) Lets specify the base-type to be a 4-state type so it should initialize to X.
2) At initialization reg [1:0] will equal 2’bxx so at reset should see an event on
current_state.
62
In this example Not_A is element 2 so when Not_A is assigned to i, i is assigned the number
2.
63
1) Note that since the cast failed for i=4 opcode did not change.
2) opcode = opcode_t'(i); is a legal assignment but there is no type checking done. This is
a static cast. It should have failed since there is no opcode at index = 4.
3) Suggest always using $cast and checking the value returned or 1 (success) or 0 (failure).
64
1) Another new type is the string type and it’s associated operators
2) s = {"ON ", s.substr(s.len()-13, s.len()-1)}; uses concatenation and substr to
replace a string
3) s={s, “ 2008”}; concatenates 2008 to the end of ON Semiconductor
65