02 SystemVerilogLecture1
02 SystemVerilogLecture1
For example:
design1.sv, tbdesign1.sv
To compile SystemVerilog code with VCS, use the sverilog flag when compiling.
For example:
vcs sverilog design1.sv tbdesign1.sv ./simv
elements of the language and give some design examples before moving on to the more complex features that are available. 4-state data types: (0, 1, x or z values)
reg, logic, integer, wire, wor, wand (logic is identical to reg)
assigned to all bit positions of a vector with a new and simple syntax:
my_bus my_bus my_bus my_bus = = = = '0; '1; 'x; 'z; // // // // fills fills fills fills with with with with all all all all 0 1 x z
the signals going in and out of a module directly in the module statement itself:
module simple (output logic a, b, input logic c, d); ... endmodule
Design a ripple-carry adder module for adding two 10-bit signed numbers by first sign-extending each operand to 11 bits, adding the sign-extended values and then returning the 10 most significant bits (MSBs) of the sum as the output of the module. (Note: This is called "Step 1a" of a larger example which will be considered later.) Note that you must correctly scale the power-of-two weights of these output bits when interpreting the numerical value that is produced at the output. Also, note that the truncation may introduce a small error compared to the full-precision result.
p[9:0]
q[9:0]
r[10:1]
half_adder full_adder full_adder full_adder full_adder full_adder full_adder full_adder full_adder full_adder full_adder endmodule
10
Construct a testbench to perform exhaustive testing of this module to prove that your design code is correct. Note that the check value calculated in the testbench should correctly model the truncation and scaling process used in the design.
module tbstep1; // testbench for adder of Step 1a. // exhaustive checking of all 1024*1024 possible cases logic signed [9:0] x, y; logic signed [9:0] s; integer sval; integer check; integer i, j; integer num_correct; integer num_wrong; // // // // // // // 10-bit signed inputs 10-bit sum output of the adder scaled value of the sum value used to check correctness loop variables counter to keep track of num. correct counter to keep track of num. wrong
11
// exhaustive simulation of all possible cases initial begin // initialize the counter variables num_correct = 0; num_wrong = 0; // loop through all possible cases and record the results for (i = 0; i < 1024; i = i + 1) begin x = i; for (j = 0; j < 1024; j = j + 1) begin y = j; check = x + y; // adjust check value for truncation if (((check % 2) == 1) || ((check % 2) == -1)) check = check - 1; // compute and check the sum #10 sval = s*2; if (sval == check) num_correct = num_correct + 1; else num_wrong = num_wrong + 1; // following line is commented out, but is useful for debugging // $display($time, " %d + %d = %d (%d)", x, y, sval, check); end end // print the final counter values $display("num_correct = %d, num_wrong = %d", num_correct, num_wrong); end endmodule
12
Testbench Results
The results from compiling and running the design and testbench are:
num_correct = 1048576, num_wrong = 0
If the debug statement is not commented out, then a portion of the output looks as follows (notice the truncation):
102000 102010 102020 102030 102040 102050 102060 102070 102080 102090 9 9 9 9 9 9 9 9 9 9 + + + + + + + + + + -41 -40 -39 -38 -37 -36 -35 -34 -33 -32 = = = = = = = = = = -32 -32 -30 -30 -28 -28 -26 -26 -24 -24 ( ( ( ( ( ( ( ( ( ( -32) -32) -30) -30) -28) -28) -26) -26) -24) -24)
13
Assume that the carry into bit position i is Ci. Then, the sum at bit position i, Si, and carry into the next higher bit position, Ci+1, are given by:
S i = Psum i C i , C i +1 = G i + Pi C i
A CLA adder uses the fact that these functions can be computed at all bit positions in parallel, and this information can be used to speed up the carry (and hence the sum) computations.
C1 = G 0 + P0 C 0 C 2 = G 1 + P1C1 = G 1 + P1 ( G 0 + P0 C 0 ) = G 1 + P1G 0 + P1P0 C 0 C 3 = G 2 + P2 C 2 = G 2 + P2 (G 1 + P1G 0 + P1P0 C 0 ) = G 2 + P2 G 1 + P2 P1G 0 + P2 P1 P0 C 0 C 4 = G 3 + P3C 3 = G 3 + P3 ( G 2 + P2 G 1 + P2 P1G 0 + P2 P1P0 C 0 ) = G 3 + P3G 2 + P3 P2 G 1 + P3 P2 P1G 0 + P3 P2 P1P0 C 0
14
C 4 = G 3:0 + P3:0 C 0
Similarly, we can define group generate and propagate functions for other, nonoverlapping 4-bit groups:
15
C8 = G 7:4 + P7:4 C 4 = G 7:4 + P7:4 (G 3:0 + P3:0 C 0 ) = G 7:4 + P7:4 G 3:0 + P7:4 P3:0 C 0 C12 = G 11:8 + P11:8 C 8 = G 11:8 + P11:8 (G 7:4 + P7:4 G 3:0 + P7:4 P3:0 C 0 ) = G 11:8 + P11:8 G 7:4 + P11:8 P7:4 G 3:0 + P11:8 P7:4 P3:0 C 0 C16 = G 15:12 + P15:12 C12 = G 15:12 + P15:12 (G 11:8 + P11:8 G 7:4 + P11:8 P7:4 G 3:0 + P11:8 P7:4 P3:0 C 0 ) = G 15:12 + P15:12 G 11:8 + P15:12 P11:8 G 7:4 + P15:12 P11:8 P7:4 G 3:0 + P15:12 P11:8 P7:4 P3:0 C 0
This can be extended to another level of hierarchy (i.e., blocks) by forming block generate and propagate functions, which allows us to get block carries. For example:
G 15:0 = G 15:12 + P15:12 G 11:8 + P15:12 P11:8 G 7:4 + P15:12 P11:8 P7:4 G 3:0 P15:0 = P15:12 P11:8 P7:4 P3:0 C 16 = G 15:0 + P15:0 C 0
16
Pi: j = (Pi Pi 1 L Pm )(Pm 1Pm2 L Pj ) = Pi:m Pm1: j Gi: j = (Gi + Pi Gi 1 + Pi Pi 1Gi 2 +L+ Pi Pi 1 L Pm+1Gm ) + (Pi Pi 1 L Pm )Gm 1: j = Gi:m + Pi:mGm 1: j
Define an operator o which implements the recursive relations for Pi:j and Gi:j, i.e.:
Simon Knowles, A Family of Adders, 15th IEEE Symposium on Computer Arithmetic, pp. 277-284, 2001.
18
19
20
// second level of operator o opo opo08(p76, g76, p54, g54, p74, g74); opo opo09(p65, g65, p43, g43, p63, g63); opo opo10(p54, g54, p32, g32, p52, g52); opo opo11(p43, g43, p21, g21, p41, g41); opo opo12(p32, g32, p10, g10, p30, g30); opo opo13(p21, g21, p[0], g[0], p20, g20); // third level opo opo14(p74, opo opo15(p63, opo opo16(p52, opo opo17(p41, of operator o g74, p30, g30, p70, g70); g63, p20, g20, p60, g60); g52, p10, g10, p50, g50); g41, p[0], g[0], p40, g40);
21
// form the sum signals xor(s[0], psum[0], c0); xor(s[1], psum[1], c1); xor(s[2], psum[2], c2); xor(s[3], psum[3], c3); xor(s[4], psum[4], c4); xor(s[5], psum[5], c5); xor(s[6], psum[6], c6); xor(s[7], psum[7], c7); endmodule
22
// instantiate the 8-bit Kogge-Stone adder kogge_stone ks1(a, b, c0, s, c8); // simulation of 50 random addition operations initial repeat (50) begin // get new operand values and compute a check value a = $random; b = $random; c0 = $random; check = a + b + c0; // compute and display the sum every 10 time units #10 $display($time, " %d + %d + %d = %d (%d)", a, b, c0, {c8, s}, check); end 23
13 + 118 + 1 = 132 (132) 237 + 140 + 1 = 378 (378) 198 + 197 + 0 = 395 (395) 229 + 119 + 0 = 348 (348) 143 + 242 + 0 = 385 (385) 232 + 197 + 0 = 429 (429) 189 + ... 45 + 1 = 235 (235)
24
25
Knowles Architecture
Knowles has shown that the KS and LF designs are limiting cases of a set of minimumdepth designs. There are 3 other possible 8-bit designs, such as:
26
27
pre-increment
Example: n=++k; First, k is incremented. Then, the new value of k is assigned to n. Example: n=k--; The value of k is assigned to n and then k is decremented. Example: n=--k; First, k is decremented. Then, the new value of k is assigned to n.
28
post-decrement
pre-decrement
-= operator:
y -= x;
*= operator:
y *= x;
/= operator:
y /= x;
< 16; i = i + 1)
SystemVerilog allows declaration of a local loop variable: for (int i = 0; i < 16; i++) ...
30
32
Structures cont.
Use the "dot notation" to access a particular element of a structure. For example:
my_structure.a = 1'b1;
We can also combine struct and typedef to create new data types:
typedef struct { logic a, b, c; int d, e, f; bit [3:0] g, h; } my_new_type;
33
Initialization of Structures
Initial values can be specified within curly
braces by listing them in the order that they are declared within the structure:
typedef struct { logic a, b, c; int d, e, f; bit [3:0] g, h; } my_new_type; my_new_type var1 = {1'b0, 1'b1, 1'b0, 5, 6, 7, 4'hA, 4'hB};
34
through the ports of a module, do the following: Place the typedef struct statement outside of the module. Then, you can pass variables of that type into and out of the module. Similarly, you can pass arguments to functions and tasks which are structures.
35
Packed Structures
You can declare a struct to be packed. This means that all of the elements in the struct are stored as contiguous bits which are stored as a vector:
First element in the struct is the left-most element of the vector. Last element in the struct is the right-most element of the vector. The number of bits in the vector is equal to the sum of the number of bits needed to hold each individual element of the struct.
37
Unions
Similar to unions in C. A union represents a single unit of storage, but having more than one representation. Each representation may be of a different data type. In a typical application of a union, sometimes the data is needed in one representation, while at other times it is needed in a different representation. Packed unions are synthesizable, but unpacked unions are not synthesizable.
38
39
-3
Note that the value -3 is represented as a 4-bit signed binary number by: 1101 When this 4-bit pattern is interpreted as an unsigned number, its value is 8+4+1=13.
41
Arrays
Multi-dimensional arrays are allowed. For example, a 3-dimensional array of integers of size 16x8x4 can be declared as: int my_array[0:15][0:7][0:3]; The same array can also be declared as follows: int my_array[16][8][4]; To assign a value to a specific array element, just specify each of its index values. For example: my_array[4][3][2] = 55;
42
int arr[10]; initial begin for (int i = 0; i < $size(arr); i++) arr[i] = 3*i; end
45
Arrays of Structures
We can form an array in which each element is a structure: // create the structure typedef struct { int a, b, c; logic [15:0] d, e, f; } collection;
// this contains 3 10-element arrays typedef struct { int a[10], b[10], c[10]; logic [15:0] d, e, f; } new_collection;
49
list of the allowed values, followed by the name of the variable. For example:
enum {Shanghai, Beijing, Nanjing} city;
to represent the different enumerated names are 32-bit 2-state values, which corresponds to the int data type.
The first name listed gets the value 0. The second name listed gets the value 1, etc.
51
However, the rules are complex and the designer can easily make a mistake. Also, it is difficult for other people reading the code to determine what the designer intended.
52
Thus, the block will be evaluated whenever any input signal to the block changes. This corresponds to the way in which a combinational logic block operates. The block is also evaluated once at simulation time 0 so that the combinational logic block is properly initialized.
53
always_comb Example
A 2-to-1 MUX with data inputs in0 and
in1, select signal select and output mux_out can be modeled as follows (the inferred sensitivity list includes in0, in1 and select): always_comb if (select) mux_out = in1; else mux_out = in0;
54
Thus, the block will be evaluated whenever any input signal to the block changes. This corresponds to the way in which a latched logic block operates. The block is also evaluated once at simulation time 0 so that the latched logic block is properly initialized.
56
always_latch Example
A D-type latch which is transparent when the clock is high can be expressed as:
Functions
begin ... end is not required in functions containing multiple statements. Those statements will be executed sequentially. The blocking assignment = must be used in each of the statements. As in Verilog, you can assign the value to be returned to the name of the function. Alternatively, you can use a return statement to return a value.
58
References
IEEE Standard for SystemVerilog Unified Hardware Design, Specification, and Verification Language, IEEE, IEEE Std 1800TM-2005, 22 November 2005. S. Sutherland, S. Davidmann and P. Flake, SystemVerilog for Design, Kluwer Academic Publishers, 2004. C. Spear, SystemVerilog for Verification, Springer, 2006. SystemVerilog Tutorials, Doulos Ltd., http://www.doulos.com/knowhow/sysverilog/tutorial/ SystemVerilog Tutorial, electroSofts.com, http://electrosofts.com/systemverilog/
61