# 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***