# Self-Learning: SystemVerilog Learning from: https://www.youtube.com/@openlogic925 ***Date: 2024/1/22*** ## Signals Modelling - We can create signals using `logic` just like how we can do it with `reg` and `wire`. ``` verilog= module example (); logic x, y, z; assign x = y; // Continuous assignment always @* begin // Sequential statement y = z; end initial z = y; // Procedural statement endmodule ``` ### Multiple Driven - But `logic` can't be mutiple driven, only `wire` can do that. Example#0 - Multiple Driver ``` verilog= module example (); // Multiple drivers for w0 wire w0, w1, w2; assign w0 = w1; assign w0 = w2; // illegal multiple drivers for l0 logic l0, l1, l2; assign l0 = l1; assign l0 = l2; endmodule ``` ### Race Condition - It act like a television with two remotes, where we press both remotes at the same time, one of them will go first. Example#1 - Race Condition ``` verilog= module example (); logic l0; initial l0 = 1'b0; initial l0 = 1'b1; endmodule ``` Example#2 - Race Condition ``` verilog= module example (); logic l0; always @(posedge clk) begin l0 = 1'b0; end always @(posedge clk) begin l- = 1'b1; end endmodule ``` ### Summary - `logic` is a new data type introduced in SystemVerilog. - It can be used with procedural statements, sequential statements and continuous assignments. - It cannot be used with multiple drivers; only `wire` can support that. --- ## Numerical Variables ### Data Type - High impedance **Z**: no voltage, "no value" - Unknown **X**: voltage level unknown, 0 or 1. | TYPE | WIDTH | STATE | SIGNED | | -------- | -------- | -------- | -------- | | reg | 1 | 4 states: 0, 1, Z, X | signed | | wire | 1 | 4 states: 0, 1, Z, X | 1 bit | | logic | 1 | 4 states: 0, 1, Z, X | 1 bit | | integer | 32 | 4 states: 0, 1, Z, X | 1 bit | | bit | 1 | 2 states: 0, 1 | 1 bit | | byte | 8 | 2 states: 0, 1 | signed | | shortint | 16 | 2 states: 0, 1 | signed | | int | 32 | 2 states: 0, 1 | signed | | longint | 64 | 2 states: 0, 1 | signed | Example#1 - compare the value ``` verilog= module example (); byte byte0; // 2 state variable, initialize as 8'b00000000 logic [7:0] l8; // 4 state variable, initialize as 8'bxxxxxxxx initial begin // only compare 2 state variable, if (byte0 == l8) $display("equal"); // return `True` since `logic` hasn't been initialized (8'bxxxx_xxxx) // Compare 4 state variable if (byte0 === l8) $display("equal"); // Return `False` since l8 = 8'bxxxx_xxxx // We can use system function to check the unknown if ($isunknown(l8)) $display("l8 is unknown"); // => return `True` l8 = 8'b0000_0000; if ($isunknown(l8)) $display("l8 is unknown"); // => return `False` end endmodule ``` Example#2 - signed data type ``` verilog= module example (); byte byte0; initial begin // Since `byte` is a signed data type range from -128 to 127, // the loop will never end (0-127, and then -128 => never count to 200). for (byte0 = 0; byte0 < 200; byte0++) begin $display("byte0 = %d", byte0); end $display("Count finished"); end endmodule ``` Correction#2 ``` verilog= module example (); // we must add `unsigned` here, // s.t. it range from 0 to 255. byte unsigned byte0; initial begin for (byte0 = 0; byte0 < 200; byte0++) begin $display("byte0 = %d", byte0); end $display("Count finished"); end endmodule ``` - Some efficiency way: ``` verilog= module example (); typedef byte unsigned ubyte; // Then, byte unsigned ubyte0; // equals to ubyte ubyte0; endmodule ``` ### Summary - Numerical variables: - Width - 2 states / 4 states - Signed ## Enumeration ### Some Examples: ``` verilog= enum {RED, GREEN, BLUE} colour; // int colour; enum bit[1:0] {R, G, B} colour; // bit[1:0] colour; enum {R=1, G, B} colour; // R=1, G=2, B=3; enum {R, G=2, B} colour; // R=0, G=2, B=3; ``` ### Issue Issue#1 - If we want to define `colour0` and `colour1` with same type as follow: ``` verilog= module example (); int i0; int i1; enum {RED, GREEN} colour0; // illegal enum {RED, GREEN} colour1; // we cannot redefine values endmodule ``` - Solution: ``` verilog= module example (); int i0; int i1; typedef enum {RED, GREEN} eColour; eColour colour0; eColour colour1; endmodule ``` Issue#2 - Even the two multiple enum variables have the same value, we cannot mix and match them. - From the example below: Enum variable is scoped. RED != DAY even if they are both 0 ``` verilog= module example (); enum {RED, GREEN} colour; enum {DAY, NIGHT} mode; initial begin colour = RED; colour = DAY; // illegal!!!!!!! end endmodule ``` ### Another example ``` verilog= module example (); enum {RED, GREEN} colour; initial begin colour = RED; colour = colour.first(); // Get the first value: RED colour = colour.next(); // Get the next colour: GREEN colour = colour.next(); // Get the RED again. colour = colour.last(); // Get the last colour: GREEN end endmodule ``` ### Summary - Enum is a number data type which provides name to its value. - It has predefined built-in function. ## Stucture - Structure was not available in verilog originally, it is introduced in SystemVerilog ``` verilog= module example (); // Recommand using typdef: typedef struct { bit b; byte byt; int i; } aStruct; aStruct s0, s1; initial begin s0.b = 1'b0; // Accessing elements using fot operator s0.byt = 8'd7; s0.i = 32'habcd; // We can assign a structure value to another structure value s1 = s0; end endmodule ``` ### Summary - `struct` is an aggregate data type which can contains multiple variables of different types. ## Fixed Size Array ### Loop example Example#1 ``` verilog= module example (); bit b[1:0]; initial begin for (int i = 0; i < $size(b); i++) begin b[i] = i; end end endmodule ``` Example#2 ``` verilog= module example (); bit by[1:0][2:0]; initial begin for (int i = 0; i < 2; i++) begin for (int j = 0; j < 3; j++) begin by[i][j] = 3 * i + j; end end end endmodule ``` Let's introduce **foreach** : - The following counter is implicit, and so the loop will simply go through all elements. Example#3 ``` verilog= module example (); bit b[1:0]; initial begin foreach (b[i]) begin b[i] = i; end end endmodule ``` Example#4 ``` verilog= module example (); bit by[1:0][2:0]; initial begin foreach (b[i, j]) begin by[i][j] = 3* i + j; end end endmodule ``` ### Pack / Unpack ``` verilog= module example (); bit b0, b1, b2; bit b[2:0]; // unpacked bit[2:0] b_packed; // packed // Notice that byte can't be packed endmodule ``` ### Summary - Array is an aggregate type. - It provides a more scalable implementation (3 different bits compare to an array of 3 bits). --- ***Date: 2024/1/25*** ## Variable-Sized Array Example#0 - three types of variable sized arrays ``` verilog= module example(); byte dynamicArray[]; byte queue[$]; byte associativeArray[*]; endmodule ``` - Notice that they are only used in testbench, since they are not synthesizable. ### Dynamic Array Example#1 - how to use dynamic array ``` verilog= module example(); int array[]; // here we call a dynamic array initial begin // elements are created by calling a 'new' with a square bracket. // The number in the bracket indicates the size. // In this example, two elements are created. array = new[2]; // Modifiy the element one-by-one array[0] = 12; array[1] = 34; // we can call a 'new' again with different depth array = new[5]; // in general, the old ones would be lost. // Following is the way we create new elements and copy the old ones over array = new[5](array); array.delete(); // clear all elements end endmodule ``` ### Queue Example#2 - how to use queue ``` verilog= module example(); int array[$]; //queue initial begin // Following function allows us to push element into the queue array.push_back(12); // be put at index 0 array.push_back(34); // be put at index 1 // index: 1 0 // *----*----* 34 1 <---- push back // element: | 34 | 12 | 12 0 // *----*----* // Following function goes in an opposite direction array.push_front(56); // index: 2 1 0 34 2 // *----*----*----* 12 1 // element: | 34 | 12 | 56 | 56 0 <---- push front // *----*----*----* array.pop_front(); // index: 1 0 x 34 1 // *----*----*----* 12 0 // element: | 34 | 12 | xx | xx x <---- pop front // *----*----*----* array.pop_back(); // index: x 0 xx x <---- pop back // *----*----* 12 0 // element: | xx | 12 | // *----*----* array.insert(1, 90); // insert(a, b) insert an element(b) at the index(a) array.delete(1); // delete an element at the provided index end endmodule ``` ### Associative array Example#3 - how to use associative array ``` verilog= module example(); int array[*]; // calling a associative array initial begin array[10] = 12; // create an element at index 10 array[100] = 34; // create another one at index 100 // Notice that there are only 2 elements in the array, // even the maximum index is at 100 array.delete(100); // delete any element by calling delete function and by providing the index end endmodule ``` ### Summary | Dynamic Array | Queue | Associative Array | | -------- | -------- | -------- | | Create items out of nothing | A container for storing items | A hash / map for key value pair | | array[] | array[$] | array[*] | | Use new[] | "push" & "pop" | "key" and "value" | | delete() - all items | delete() - one / all items | delete() - one / all items | | Dynamically create items if need be; delete items to free up memory when not needed | Mainly to manage sequence of items, e.g. comparing stimulus and response one by one. | Modulling a real memory, without having to create every possible memory. | - Variable sized array is an aggregate type in testbench. - It provides a scalable implementation, just like fixed size array. - It may achieve memory optimization compare to fixed size array. ## Functions / Tasks ### Automatic Syntax Example#0 - without automatic syntax - `a` is "hardware" signal; it exists from the beginning till the end ``` verilog= module example(); function void increment(); int a; a = a + 1; endfunction initial begin increment(); // a = 1 increment(); // a = 2 increment(); // a = 3 end endmodule ``` Example#1 - automatic syntax - `a` is "software" variable; it's created when the function is called and destroyed when the function call finishes. ``` verilog= module example(); function automatic void increment(); int a; a = a + 1; endfunction initial begin increment(); // a = 1 increment(); // a = 1 increment(); // a = 1 end endmodule ``` ### Difference Between Function / Task Example#2 - Function - Function has return value capability. ``` verilog= module example(); // function int result; function int calculate(int a, b); return (a + b); endfunction initial begin result = calculate(1, 2); result = calculate(3, 4); result = calculate(5, 6); end endmodule ``` Example#3 - Task - Task has timing control capability. ``` verilog= module example(); // task int result; task calculate(int a, b); #1; return (a + b); endtask initial begin calculate(1, 2); // task doesn't support return value calculate(3, 4); calculate(5, 6); end endmodule ``` ### Summary - Function has return value. - Function cannot contain delay / timing control. - Task has no return value. - Task can contain delay / timing control. --- ***Date: 2024/1/25*** ## Function / Task Argument - Argument by default has input direction - Argument can be of input, output or inout direction; these arguments are passed by value. - Argument can be passed by reference using `ref` keyword. ## Threads: Fork-Join - Thread is a process that runs in parallel. Example#0 - A and B runs in parallel and C runs after A and B finish ``` verilog= module example(); initial begin fork #10ns $display("Doing A"); #20ns $display("Doing B"); join $display("Doing C"); end endmodule ``` Example#1 - fork join + begin end ``` verilog= module example(); initial begin fork #10ns $display("Doing A"); begin #20ns $display("Doing B0"); #10ns $display("Doing B1"); end // A and B0 will run in parallel, and B1 runs after B0 finish. join $display("Doing C"); // After above threads are finished, run C. end endmodule ``` Example#2 - fork join_any ``` verilog= module example(); initial begin fork #10ns $display("Doing A"); #20ns $display("Doing B"); join_any #10ns $display("Doing C"); // Because of the join_any construct, any of A, B finish, C will start. end endmodule ``` Example#3 - fork join_none ``` verilog= module example(); initial begin fork #10ns $display("Doing A"); #20ns $display("Doing B"); join_none $display("Doing C"); // fork join_none does not wait for any of the threads // It launches the threads and continues thereafter. // In the example here A and B start at time 0 // Without waiting for A and B to finish, C also starts at time 0 end endmodule ``` ## Event Example#0 - Event using Alias @ ``` verilog= module example(input logic clk); // Signals as 'event' using alias @ initial begin @(clk); // wait for any change in clk @(posedge clk); // wait for a positive edge @(negedge clk); // wait for a negative edge @(posedge clk or negedge clk); @(posedge clk, negedge clk); // @(posedge clk or negedge reset); end endmodule ``` ### Difference between Event and Wait. - With @, `clk` is treated as an event, not an expression ``` verilog= if (clk) <-> if (clk == 1) @(clk) != @(clk == 1) ``` - The following `clk` is treated as an expression, not an event ``` verilog= wait(clk) <-> wait(clk==1) ``` ### Summary - Events are used to synchronize between different threads and concurrent excution. --- ***Date: 2024/1/27*** ## Class: Basic - Class - Encapulation - Dynamic Initialization - Abstraction - Pointer - Inheritance - Polymorphism Example#0 - Encapsulation - Grouping of variables and functions in one 'reasonable' bundle, as a class ``` verilog= class cStatisticsCalculator; int q[$]; function int getMean(); return(q.sum() / q.size()); endfunction endclass // We should create a big class which supports both the array and string. // We can but that just doesn't make sense class cString; string str; function int reverse();...endfunction endclass ``` - Using class ``` verilog=16 module example(); cStaticCalculator sc; // sc is a class variable or handlle // We must initialize a class handle before we can use it initial begin sc = new(); // Handle is null at first (does'nt exist). It needs to be initialized. sc.q.push_back(1); sc.q.push_back(3); $display("%0d", sc.getMean() ); end endmodule ``` Example#1 - Dynamic Initialization - The following example dynamically choose which handle to initialize; either a or b is used; only 1 byte of memory is consumed. ``` verilog= class ca; byte b = 10; endclass class cb; byte b = 20; endclass module example(); input bit sel, output byte result ); ca a; cb b; initial begin if (sel == 1'b0) a = new(); result = a.b; else b = new(); result = b.b; end endmodule ``` ### Summary - Class is used to create new type - Class variable / handle can be initialized dynamically. ## Class: Members Attribute - The following example is the concept of "Abstraction". In a class there will be members which we do not want to expose to users. - Abstraction is the concept where we hide the implementation complexity from users. Example#0 - Abstraction ``` verilog= class cLife; local int lift = 3; // local members are not accessible by caller function void crash(); life = life - 1; if (life == 0) $display("Game over"); endfunction function int getLife(); return life; endfunction endclass ``` - Call the function ``` verilog=13 module example(); cLife cl; initial begin cl = new(); cl.crash(); cl.crash(); $display("%0d", cl.getLife() ); end endmodule ``` Example#1 - FibonacciGenerator ``` verilog= class cFibonacciNumber; local int last1 = 1, last2 = 1, result; function int getNumber(int id); if (id == 0) result = last2; else if (id == 1) result = last1; else for (int i = 2; i < id; i++) begin result = last2 + last1; last2 = last1; last1 = result; end return result; endfunction endclass ``` - Call the function ``` verilog=16 module example(); cFibonacciGenerator cfg; initial begin cfg = new(); $display("%0d", cfg.getNumber(0) ); // 1 $display("%0d", cfg.getNumber(1) ); // 1 $display("%0d", cfg.getNumber(10) ); // 89 $display("%0d", cfg.getNumber(11) ); // 144 end endmodule ``` Exapmle#2 - Static Member - Although there may have multiple callers calling the same class, the static member will be the same variable in every caller. ``` verilog= class cNumber; int i; static int count = 0; function new(); count++; endfunction endclass module example(); // 3 handles // 3 member int i, // but only 1 "static member" int count cNumber cn0; cNumber cn1; cNumber cn2; initial begin cn0 = new(); // cn0.count = 1; cn1 = new(); // cn0.count = cn1.count = 2; cn2 = new(); end endmodule ``` Example#3 - change the value of static member without using class handle ``` verilog= class cNumber; ... static bit debug = 0; ... endclass module example(); ... initial begin cNumber::debug = 1; ... end endmodule ``` Example#4 - Keyword "This" - `this.id` refers to the class member `id` ``` verilog= class cNumber; int id; // class member id function new(int id); this.id = id; endfunction endclass ``` ### Summary - `local`: make a class member inivisible by caller - `static`: make a class member "shared" between all instances of the same class - `this`: refer to class member (to resolve naming conflict in methods variable) ## Class: Pointer Refer to [SystemVerilog in 5 Minutes - 12b Class Pointer](https://youtu.be/20zbe1dtjv4?si=WB0oVZTJGQfI_Mi1) for more examples ### Summary - A class handle is a pointer - The 'actual' variable is known as an object, and a calss handle is pointing to the variable to access it. - Point behavior makes a class handle different from a typical variable (array, function call, and copying). ## Class: Randomization Example#0 - Randomization / Constraints ``` verilog= class cNumber; rand bit b; rand byte by; // We can use 'randc' if don't expext the repeating numbers constraint cst { by > 0; // incorrect to write 0<b<3 by < 3; by == 1; by != 2; } endclass ``` ``` verilog=12 module example(); cNumber cn; initial begin cn = new(); // initialize repeat (6) begin cn.randomize(); // randomize end end endmodule ``` - Refer to [SystemVerilog Tutorial in 5 Minutes - 12c Class Randomization](https://youtu.be/n1qcoE5fTEU?si=-ZDoYrV0LgH_nhIb) for more functions about randomization/constraints. ### Summary - A class can have random variables and constraints - A class has a bulit-in function called randomize which would assign rand variable with a random value. ## Class: Inheritance - Copying `cNumber` and create a extention class `ecNumber` ``` verilog= class ecNumber extends cNumber; ``` ### Summary - A class is highly reusable and its behaviour is overridable by using inheritance (extends). ## Class: Polymorphism Example#0 ``` verilog= class cNumber; rand int i; virtual function void print(); $display("i=%0d", i); endfunction endclass class ecNumberHex extends cNumber; virtual function void print(); $display("i=%0h", i); endfunction endclass ``` - By using `virtual` keyword, a pointer will always look for overriden method. - Without `virtual`, it will execute the function of the pointer class, i.e.: - `cn` will execute `cNumber` print - `cnh` will execute ecNumberHex print ``` verilog=14 module example(); cNumber cn ecNumberHex cnh task run (cNumber arg); arg.randomize(); arg.print(); endtask; initial begin cn = new(); cnh = new(); run(cn); run(cnh); end endmodule ``` ### Summary - A class polymorphism behaviour is a resulting behaviour of a class pointer and class inheritance --- ***Date: 2024/xx/xx***