[go: up one dir, main page]

0% found this document useful (0 votes)
10 views84 pages

SV Session 3

This document provides an introduction to SystemVerilog, covering key concepts such as program control, tasks and functions, and their enhancements over Verilog. It discusses various operators, loops, and the new features in tasks and functions, including argument passing mechanisms and recursion support. The document also highlights the challenges of backward compatibility with Verilog and the implications for variable lifetime and scope.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views84 pages

SV Session 3

This document provides an introduction to SystemVerilog, covering key concepts such as program control, tasks and functions, and their enhancements over Verilog. It discusses various operators, loops, and the new features in tasks and functions, including argument passing mechanisms and recursion support. The document also highlights the challenges of backward compatibility with Verilog and the implications for variable lifetime and scope.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 84

SESSION 3

Introduction To SystemVerilog
Prepared by Ahmed Eissa
eissa.s.ahmed@gmail.com
CONTENTS

• Program Control
• Tasks and Functions
• Hierarchy
• Processes
• Interprocess communication
PROGRAM CONTROL
PROGRAM CONTROL: OPERATORS

• The SystemVerilog operators are a combination of Verilog and C


operators
• Traditionally Verilog didn’t increment and decrement operators.
• SystemVerilog added C assignment operators, such as +=, and
the C increment and decrement operators, ++ and --
PROGRAM CONTROL: OPERATORS

• assignment_operator ::=
– = | += | -= | *= | /= | %= | &= | |= | ^= | <<= | >>= | <<<= | >>>=
• conditional_expression ::=
– cond_predicate ? { attribute_instance } expression : expression
• unary_operator ::=
– + | - | ! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~
• binary_operator ::=
– + | - | * | / | % | == | != | === | !== | =?= | !?= | && | || | **
– | < | <= | > | >= | & | | | ^ | ^~ | ~^ | >> | << | >>> | <<<
• inc_or_dec_operator ::= ++ | --
PROGRAM CONTROL: LOOPS

• Verilog provides for,


while, repeat and forever
loops.
• SystemVerilog enhances
the Verilog for loop, and
adds a do...while loop and
a foreach loop.
PROGRAM CONTROL: LOOPS

• SystemVerilog enhanced Verilog loop by:


– The ability to declare the for loop control variable within the for loop
▪ This creates a local variable within the loop.
▪ allows the initial declaration or assignment statement to be one or more
comma-separated statements
initial begin for ( int count = 0; count < 3; count++ )
for (int i = 0; i <= 255; value = value +((a[count]) * (count+1));
i++)
... for ( int j=0,i=7;j<8;j++,i--)
end $display("\tValue j=%0d Value of i=%0d",j,i);
...
end for ( int count = 0, done = 0, int j = 0; j * count < 125;
j++ )
PROGRAM CONTROL: LOOPS

• SystemVerilog added the foreach construct which specifies


iteration over the elements of an array
– can be applied to fixed, dynamic or associative arrays
– the loop variable is given in brackets
– can be used to iterate through multi dimensional arrays too
▪ The number of loop variables must match the number of dimensions of the
array variable
– Loop variables are automatic, read-only, and their scope is local to the
string
loop words [2] = { "hello", "world" };
int prod [1:8] [1:3];
foreach( words [ j ] )
$display( j , words[j] ); // print each index and value
foreach( prod[ k, m ] )
prod[k][m] = k * m; // initialize
PROGRAM CONTROL: LOOPS

• SystemVerilog adds the C


jump statements break,
continue and return.
– break // out of loop as C
– continue // skip to end
of loop as C
TASKS AND FUNCTIONS
TASKS AND FUNCTIONS

• Verilog-2001 has static and automatic tasks and functions. Static


tasks and functions share the same storage space for all calls to
the tasks or function within a module instance. Automatic tasks
and function allocate unique, stacked storage for each instance.
• SystemVerilog is 100% Verilog-compatible
– The curse of backward compatibility
TASKS AND FUNCTIONS

• SystemVerilog also adds:


– More capabilities for declaring task and function ports
– Function output and inout ports
– Void functions
– Multiple statements in a task or function without requiring a begin...end or
fork...join block
– Returning from a task or function before reaching the end of the task or
function
– Passing arguments by reference instead of by value
– Passing argument values by name instead of by position
– Default argument values
– Recursion is supported
– Importing and exporting functions through the Direct Programming Interface
(DPI)
TASKS AND FUNCTIONS: NEW
FEATURES
• Each formal argument has one of the task mytask1 (output int x, input logic y = 5);
following directions: ...
– input // copy value in at beginning endtask
– output // copy value out at end task mytask2;
– inout // copy in at beginning and out at output x;
end
input y;
– ref // pass reference
int x;
• Default direction is input if no direction logic y;
has been specified. Once a direction is ...
given, subsequent formals default to
endtask
the same direction.
• Default type is logic
• Arguments can have default values too
• Arguments can be passed by name
also
TASKS AND FUNCTIONS: TASKS NEW
FEATURES
function int fun( int j = 1, string s = task read(int j = 0, int k, int data = 1 );
"no" ); ...
... endtask;
endfunction read( , 5 ); // is equivalent to read( 0, 5, 1 );
read( 2, 5 ); // is equivalent to read( 2, 5,
fun( .j(2), .s("yes") ); // fun( 2, "yes" ); 1 );
read( , 5, ); // is equivalent to read( 0, 5,
fun( .s("yes") ); // fun( 1, "yes" ); 1 );
fun( , "yes" ); // fun( 1, "yes" ); read( , 5, 7 ); // is equivalent to read( 0, 5, 7
fun( .j(2) ); // fun( 2, "no" ); );
fun( .s("yes"), .j(2) ); // fun( 2 , "yes" ); read( 1, 5, 2 ); // is equivalent to read( 1, 5,
2 );
fun( .s(), .j() ); // fun( 1 , "no" );
read( ); // error; k has no default value
fun( 2 ); // fun( 2, "no" );
fun( ); // fun( 1, "no" );
TASKS AND FUNCTIONS: NEW
FEATURES
• Pass by value is the default function int crc( byte packet
mechanism for passing [1000:1] );
arguments to subroutines, it is for( int j= 1; j <= 1000; j++ ) begin
also the only one provided by
crc ^= packet[j];
Verilog-2001
– This argument passing mechanism end
works by copying each argument endfunction
into the subroutine area
– If the subroutine is automatic, then
the subroutine retains a local copy
...
of the arguments in its stack. If the byte packet1[1000:1];
arguments are changed within the int k = crc( packet1 );
subroutine, the changes are not
visible outside the subroutine. ...
Challenge:
What if you want to pass a big memory, say 1000 bytes, to a task or a
function? Easy, declare a
new input to
that
function/task
with the 1000 as
size
Challenge:
What if you want to pass a big memory, say 1000 bytes, to a task or a function?

Solution:
Pass by reference
TASKS AND FUNCTIONS: NEW
FEATURES
• Arguments passed by reference are function int crc( ref byte packet
not copied into the subroutine area, [1000:1] );
rather, a reference to the original
argument is passed to the subroutine. for( int j= 1; j <= 1000; j++ ) begin
crc ^= packet[j];
• The subroutine can then access the
argument data via the reference. end
• No casting shall be permitted for endfunction
arguments passed by reference? // the following lines didn't change
– Why? ...
• The caller and the function share the byte packet1[1000:1];
same representation of the argument int k = crc( packet1 );
– Any changes made to the argument
either within the caller or the ...
subroutine shall be visible to each
other.
Challenge:
What if I didn’t want the function to change an argument passed by reference

Solution:
const
TASKS AND FUNCTIONS: NEW
FEATURES
• To protect arguments passed task show ( const ref byte [] data );
by reference from being for ( int j = 0; j < data.size ; j++ )
modified by a subroutine, the $display( data[j] ); // data can be
const qualifier can be read but not written
• used together with ref to endtask
indicate that the argument,
although passed by reference,
is a read-only variable.
TASKS AND FUNCTIONS: NEW
FEATURES
• What is the direction of a, b, u task mytask3(a, b, output logic [15:0]
and v? u, v);
– a and b default to inputs ...
– u and v are both outputs endtask

• Be extra carful when using ref


in a multi argument
task/function
• Recommendation: Always
explicitly declare direction
TASKS AND FUNCTIONS: NEW
FEATURES
• In Verilog, a task exits when task do_something(int value);
the endtask is reached.
• With SystemVerilog, the return if (value > 256) begin
statement can be used to exit
the task before the endtask return;
keyword. end

....

endtask
TASKS AND FUNCTIONS: NEW
FEATURES
• In Verilog, functions must • SystemVerilog allows functions
return values. The return value to be declared as type void,
is specified by assigning a which do not have a return
value to the name of the value.
function.
function [15:0] myfunc1 (input [7:0] x,y); function void myprint (int
myfunc1 = x * y - 1; //return value is
assigned to function name a);
endfunction ...
endfunction
TASKS AND FUNCTIONS: NEW
FEATURES
• For non-void functions, a value function [15:0] myfunc2 (input [7:0]
can be returned by assigning x,y);
the function name to a value, return x * y - 1; //return value is
as in Verilog, or by using return specified using return statement
with a value. The return endfunction
statement shall override any
value assigned to the function
name.
• When the return statement is
used, non-void functions must
specify an expression with the
return.
THE CURSE OF BACKWARD
COMBABILITY
TASKS AND FUNCTIONS

• what will the following function module test;


print?
# count=1
task myfunc();
int count;
# count=2
count++;
# count=3
$display("count=%0d",count);
# count=4
endtask

initial begin
myfunc();
myfunc();
myfunc();
myfunc();
end
endmodule : test
TASKS AND FUNCTIONS:STATIC
LIFETIME
• Static: its memory never de-allocated until simulation ends.
• Automatic: it is stack storage of variable (for multiple entries to a
task, function or block, it will have stack storage) and its memory
will be de-allocated once execution of that method or block is over.
THE CURSE OF BACKWARD
COMPATIBILITY
• When Verilog was created in the 1980s, it was tightly tied to describing
hardware. As a result, all objects in the language were statically allocated.
• In particular, routine arguments and local variables were stored in a fixed
location, rather than pushing them on a stack like other programming languages.
• As SystemVerilog must be 100% compatible with Verilog
– Any data declared outside a module, interface, task, or function, is global in scope (can
be used anywhere after its declaration) and has a static lifetime (exists for the whole
elaboration and simulation time).
– SystemVerilog data declared inside a module or interface but outside a task, process
or function is local in scope and static in lifetime (exists for the lifetime of the module
or interface). This is roughly equivalent to C static data declared outside a function,
which is local to a file.
– Data declared in an automatic task, function or block has the lifetime of the call or
activation and a local scope. This is roughly equivalent to a C automatic variable.
– Data declared in a static task, function or block defaults to a static lifetime and a local
scope
THE CURSE OF BACKWARD
COMPATIBILITY
• SystemVerilog allows specific data within a static task or function
to be explicitly declared as automatic. Data declared as automatic
has the lifetime of the call or block, and is initialized on each entry
to the call or block.
• SystemVerilog also allows data to be explicitly declared as static.
Data declared to be static in an automatic task, function or block
has a static lifetime and a scope local to the block. This is like C
static data declared within a function.
TASKS AND FUNCTIONS: STATIC
LIFETIME
module msl;
int st0; // static
initial begin
int st1; //static
static int st2; //static
automatic int auto1; //automatic

end
task automatic t1();
int auto2; //automatic
static int st3; //static
automatic int auto3; //automatic

endtask
endmodule
TASKS AND FUNCTIONS: AUTOMATIC
LIFETIME
• what will the following function module test;
print?
# count=1
task automatic myfunc();
# count=1 int count;
# count=1 count++;
# count=1 $display("count=%0d",count);
endtask

initial begin
myfunc();
myfunc();
myfunc();
myfunc();
end
endmodule : test
Challenge:
What if wanted to make all functions/tasks inside a certain module automatic?
For example, a testbench would normally have all its method automatic

Solution:
module static
TASKS AND FUNCTIONS: AUTOMATIC
LIFETIME
• SystemVerilog adds an optional qualifier to specify the default
lifetime of all variables declared in task, function or block defined
within a module, interface or program. The lifetime qualifier is
automatic or static. The default lifetime is static.
TASKS AND FUNCTIONS: AUTOMATIC
LIFETIME
• what will the following function module automatic test;
print?
# count=1
task myfunc();
# count=1 int count;
# count=1 count++;
# count=1 $display("count=%0d",count);
endtask

initial begin
myfunc();
myfunc();
myfunc();
myfunc();
end
endmodule : test
TASKS AND FUNCTIONS: RECURSION

• Why wasn’t recursion supported in Verilog?


• Verilog started out with having only static lifetimes of functions or
tasks, meaning that there was no call stack for arguments or
variables local to the routines.
• . This meant you could not have recursive or re-entrant routines,
unlike most other modern programming languages.
TASKS AND FUNCTIONS: RECURSION

module tryfact; # 0 factorial=1


// define the function # 1 factorial=1
function integer factorial (input [31:0] operand); # 2 factorial=1
begin
# 3 factorial=1
if (operand >= 2)
factorial = factorial (operand - 1) * operand; # 4 factorial=1
else # 5 factorial=1
factorial = 1; # 6 factorial=1
end # 7 factorial=1
endfunction: factorial
// test the function • As the function is static by
integer result;
initial begin
default , there is no call stack
for (int n = 0; n <= 7; n++) begin
result = factorial(n);
$display("%0d factorial=%0d", n, result);
end
end
endmodule: tryfact
TASKS AND FUNCTIONS: RECURSION

module tryfact; # 0 factorial=1


// define the function # 1 factorial=1
function automatic integer factorial (input [31:0] # 2 factorial=2
operand);
# 3 factorial=6
begin
if (operand >= 2) # 4 factorial=24
factorial = factorial (operand - 1) * operand; # 5 factorial=120
else # 6 factorial=720
factorial = 1; # 7 factorial=5040
end
endfunction: factorial • In SystemVeilog, full recursion
// test the function
integer result;
is supported
initial begin
for (int n = 0; n <= 7; n++) begin
result = factorial(n);
$display("%0d factorial=%0d", n, result);
end
end
HIERARCHY
HIERARCHY: VERILOG

• Verilog has a simple organization. All data, functions and tasks are in modules except for
system tasks and functions, which are global, and can be defined in the PLI.
• In Verilog, only net, reg, integer and time data types can be passed through module
ports
• SystemVerilog adds many enhancements for representing design hierarchy:
– Packages containing declarations such as data, types, classes, tasks and functions
– Separate compilation support
– A compilation-unit scope visible only within a compilation unit
– Nested module declarations, to aid in representing self-contained models and libraries
– Relaxed rules on port declarations
– Simplified named port connections, using .name
– Implicit port connections, using .*
– Time unit and time precision specifications bound to modules
– A concept of interfaces to bundle connections between modules
– the ability to pass any data type through module ports, including nets, and all variable types
including reals, arrays, and structures.
HIERARCHY: PACKAGES

• SystemVerilog packages provide an additional mechanism for


sharing parameters, data, type, task, function, sequence, and
property declarations amongst multiple SystemVerilog modules,
interfaces and programs.
• Types, variables, tasks, functions, sequences, and properties may
be declared within a package. Such declarations may be
referenced within modules, interfaces, programs
• Packages can be imported into other packages
HIERARCHY: PACKAGES

package ComplexPkg; • Packages must exist in order


typedef struct {
for the items they define to be
float i, r;
} Complex;
recognized by the scopes in
function Complex add(Complex a, b); which they are imported.
add.r = a.r + b.r;
• One way to use declarations
add.i = a.i + b.i;
endfunction
made in a package is to
function Complex mul(Complex a, b); reference them using the
mul.r = (a.r * b.r) - (a.i * b.i); scope resolution operator “::”.
mul.i = (a.r * b.i) + (a.i * b.r); ComplexPkg::Complex cout =
endfunction ComplexPkg::mul(a, b);
endpackage : ComplexPkg
ComplexPkg::Complex
cout =
ComplexPkg::mul(a, b);
HIERARCHY: PACKAGES

• An alternate method for utilizing import ComplexPkg::Complex;


package declarations is via the import ComplexPkg::add;
import statement.
import ComplexPkg::*;
• A wildcard import allows all
identifiers declared within a
package to be imported
provided the identifier is not
otherwise defined in the
importing scope:
HIERARCHY: MODULES AND PORTS

• With SystemVerilog, a port can typedef struct {


be a declaration of a net, an bit isfloat;
interface, an event, or a
variable of any type, including int i;
an array, a structure or a shortreal f;
union.
} my_st; // named structure
module mh1 (input int in1, input
shortreal in2, my_st out);
...
endmodule
HIERARCHY: MODULES AND PORTS

• SV also added implicit and


explicit module port
connection by name
HIERARCHY: MODULES AND PORTS

module alu ( using positional port connection


output reg [7:0] alu_out, module alu_accum1 (
output reg zero,
input [7:0] ain, bin, output [15:0] dataout,
input [2:0] opcode);
input [7:0] ain, bin,
// RTL code for the alu module
endmodule input [2:0] opcode,

module accum ( input clk, rst_n);


output reg [7:0] dataout,
input [7:0] datain,
wire [7:0] alu_out;
input clk, rst_n); alu alu (alu_out, , ain, bin, opcode);
// RTL code for the accumulator module
endmodule accum accum (dataout[7:0], alu_out, clk, rst_n);

module xtend ( xtend xtend (dataout[15:8], alu_out[7], clk, rst_n);


output reg [7:0] dout,
endmodule
input din,
input clk, rst_n);
// RTL code for the sign-extension module
endmodule
HIERARCHY: MODULES AND PORTS

module alu ( Instantiation using implicit .name port


output reg [7:0] alu_out, connections
module alu_accum3 (
output reg zero, output [15:0] dataout,
input [7:0] ain, bin, input [7:0] ain, bin,
input [2:0] opcode); input [2:0] opcode,
// RTL code for the alu module input clk, rst_n);
endmodule wire [7:0] alu_out;
alu alu (.alu_out, .zero(), .ain, .bin, .opcode);
module accum ( accum accum
output reg [7:0] dataout, (.dataout(dataout[7:0]), .datain(alu_out), .clk, .rst_n);
input [7:0] datain, xtend xtend (.dout(dataout[15:8]), .din(alu_out[7]), .clk, .rst_n);
input clk, rst_n); endmodule
// RTL code for the accumulator module
endmodule

module xtend (
output reg [7:0] dout,
input din,
input clk, rst_n);
// RTL code for the sign-extension module
endmodule
HIERARCHY: MODULES AND PORTS

module alu ( Instantiation using implicit .* port


output reg [7:0] alu_out, connections
module alu_accum4 (
output reg zero,
input [7:0] ain, bin, output [15:0] dataout,
input [2:0] opcode);
input [7:0] ain, bin,
// RTL code for the alu module
endmodule input [2:0] opcode,

module accum ( input clk, rst_n);


output reg [7:0] dataout,
input [7:0] datain,
wire [7:0] alu_out;
input clk, rst_n); alu alu (.*, .zero());
// RTL code for the accumulator module
endmodule accum accum (.*, .dataout(dataout[7:0]), .datain(alu_out));

module xtend ( xtend xtend (.*, .dout(dataout[15:8]), .din(alu_out[7]));


output reg [7:0] dout,
endmodule
input din,
input clk, rst_n);
// RTL code for the sign-extension module
endmodule
PROCESSES
PROCESSES: PROCEDURAL
STATEMENTS
• Procedural statements are introduced by the following:
– initial // enable this statement at the beginning of simulation and
execute it only once
– final // do this statement once at the end of simulation
– always, always_comb, always_latch, always_ff // loop forever
– task // do these statements whenever the task is called
– function // do these statements whenever the function is called and
return a value
PROCESSES: PROCEDURAL
STATEMENTS
• The final block:
– like an initial block, except that it occurs at the end of simulation time
and
– Defines a procedural block of statements
– Executes without delays in a single simulation cycle
– Typically used to display statistical information about the simulation.
final begin
$display("Number of cycles executed
%d",$time/period);
$display("Final PC = %h",PC);
end
PROCESSES: PROCEDURAL
STATEMENTS
• always: represents a general purpose always procedure, which can
be used to represent repetitive behavior such as clock oscillators.
– The general purpose always procedure, because of its looping nature,
is only useful when used in conjunction with some form of timing
control
• always_comb: used to model combinational circuits
– inferred sensitivity list that includes the expressions
– automatically triggered once at time zero
PROCESSES: PROCEDURAL
STATEMENTS
• always_latch: similar to always_comb except that software tools
should perform additional checks and warn if the behavior in an
always_latch construct does not represent latched logic
– Used to model
• always_ff: procedure imposes the restriction that it contains one
and only one event control (only one “@”) and no blocking timing
controls
– Used to model flip flops
PROCESSES: PROCEDURAL
STATEMENTS
always_comb
a = b & c;

always_latch begin
if(ck)
q <= d;

always_ff @(posedge clk)


a <= b;
PROCESSES: FORK JOIN

• The fork...join construct enables the creation of concurrent


processes from each of its parallel statements
PROCESSES: FORK JOIN

• SystemVerilog provides three choices for


specifying when the parent (forking)
process resumes execution
– join: The parent process blocks until all the
processes spawned by this fork complete. .
– join_any: The parent process blocks until any
one of the processes spawned by this fork
complete.
– join_none: The parent process continues to
execute concurrently with all the processes
spawned by the
– fork. The spawned processes do not start
executing until the parent thread executes a
blocking statement
• When defining a fork...join block,
encapsulating the entire fork within a
begin...end block causes the entire block to
execute as a single process, with each
statement executing sequentially.
PROCESSES: FORK JOIN
program main ;
initial
begin BEFORE fork time = 10
#(10); time = 15 # 5
$display(" BEFORE fork time = %d ",$time ); time = 20 # 10
fork
begin time = 30 # 20
# (20); time = 30 Outside the main fork
$display("time = %d # 20 ",$time );
end
begin
#(10);
$display("time = %d # 10 ",$time );
end
begin
#(5);
$display("time = %d # 5 ",$time );
end
join
$display(" time = %d Outside the main fork ",$time );
#(40);
end
endprogram
PROCESSES: FORK JOIN
program main;
initial begin
BEFORE fork time = 10
#(10);
$display(" BEFORE fork time = %d ",$time );
time = 15 # 5
fork time = 15 Outside the main fork
begin time = 20 # 10
# (20); time = 30 # 20
$display("time = %d # 20 ",$time );
end
begin
#(10);
$display("time = %d # 10 ",$time );
end
begin
#(5);
$display("time = %d # 5 ",$time );
end
join_any
$display(" time = %d Outside the main fork ",$time );
#(40);
end
endprogram
PROCESSES: FORK JOIN
program main ;
initial
begin BEFORE fork time = 10
#10; time = 10 Outside the main fork
$display(" BEFORE fork time = %d ",$time ); time = 15 # 5
fork
begin
time = 20 # 10
# (20); time = 30 # 20
$display("time = %d # 20 ",$time );
end
begin
#(10);
$display("time = %d # 10 ",$time );
end
begin
#(5);
$display("time = %d # 5 ",$time );
end
join_none
$display(" time = %d Outside the main fork ",$time );
#(40);
end
endprogram
PROCESSES: DYNAMIC PROCESSES

• SystemVerilog provides task do_test;


constructs that allow one fork
process to terminate or wait exec1();
for the completion of other
exec2();
processes. The wait fork
construct waits for the join_any
completion of processes. The fork
disable fork construct stops exec3();
the execution of processes.
exec4();
• Specifying wait fork causes the join_none
calling process to block until
wait fork; // block until exec1 ... exec4
all its sub-processes have complete
completed
endtask
PROCESSES: DYNAMIC PROCESSES

• The disable fork statement task get_first( output int adr );


terminates all active fork
descendants (sub-processes)
of the calling process. wait_device( 1, adr );
• The disable fork statement wait_device( 7, adr );
terminates all descendants of wait_device( 13, adr );
the calling process, as well as
the descendants of the join_any
process’ descendants, that is, disable fork;
if any of the child processes
have descendants of their own, endtask
the disable fork statement
shall terminate them as well.
PROCESSES: FORK JOIN
initial begin
fork
begin
$display($time,"\tProcess-1 of fork-1 Started");
#5;
0 Process-1 of fork-1 Started
$display($time,"\tProcess-1 of fork-1 Finished"); 0 Process-2 of fork-1 Started
end
begin 5 Process-1 of fork-1 Finished
$display($time,"\tProcess-2 of fork-1 Started"); 5 After disable-fork
#20;
$display($time,"\tProcess-2 of fork-1 Finished");
end
join_any

fork
begin
$display($time,"\tProcess-1 of fork-2 Started");
#5;
$display($time,"\tProcess-1 of fork-2 Finished");
end
//Process-2
begin
$display($time,"\tProcess-2 of fork-2 Started");
#20;
$display($time,"\tProcess-2 of fork-2 Finished");
end
join_none
disable fork;
$display($time,"\tAfter disable-fork");
end
Challenge:
What if I wanted to kill certain processes?
say the all processes inside second fork in the previous example

Solution:
use disable <process label>?
PROCESSES: DYNAMIC PROCESSES
task dis_thread(int t);
• Verilog supports the disable fork:fork_la
construct, which terminate a
process when applied to the named repeat(t)begin
block being executed by the $display("%0d,still alive %t",
process. t, $time);
• The disable fork statement differs #500;
from disable in that disable fork end
considers the dynamic parent-child
relationship of the processes,
whereas disable uses the static, begin
syntactical information of the #1000;
disabled block. $display("timeout!!");
• disable label will kill the process end
comprising the same label join_any
– This is very dangerous disable fork_la;
endtask
PROCESSES: DYNAMIC PROCESSES

module test;
task automatic dis_thread(int t); # 1,still alive 0
fork:fork_la
repeat(t)begin
$display("%0d,still alive %t", t, $time);
# 3,still alive 0
#500;
end

begin
#1000;
$display("timeout!!");
end
join_any
disable fork_la;
• As can be seen, when
endtask dis_thread (1) is killed,
initial begin dis_thread (3) is also killed,
fork
dis_thread(1);
because both processes have
dis_thread(3); the same task label.
join
end
endmodule : test
Challenge:
What if I wanted to kill certain processes?
say the all processes inside second fork in the previous example

Solution:
use disable <process label>?
Challenge:
What if I wanted to kill certain processes?
say the all processes inside second fork in the previous example

Solution:
use fork guard
PROCESSES: DYNAMIC PROCESSES
module test;
task dis_thread(int t);
fork : fork_guard
begin # 1,still alive 0
fork
repeat(t)begin
$display("%0d,still alive %t", t, $time); # 3,still alive 0
#500;
end # 3,still alive 500
begin
#1000; # timeout!!
$display("timeout!!");
end
join_any • You can rather easily protect against this by
disable fork; causing your fork...join_any, and its later
end
join
disable-fork, to run in a new child process of
endtask its own.

initial begin
• This limits the effect of your disable-fork so
fork that it can affect only the newly launched
dis_thread(1); processes that you care about, and is
dis_thread(3); guaranteed to have no other unwanted
join effects.
end
endmodule : test
INTERPROCESS
SYNCHRONIZATION AND
COMMUNICATION
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION: MAILBOX
• Dynamic processes and OOP coding styles require more
sophistication than Verilog provides.
• A mailbox is a communication mechanism that allows messages to
be exchanged between processes. Data can be sent to a mailbox
by one process and retrieved by another.
• Conceptually, mailboxes behave like real mailboxes. When a letter
is delivered and put into the mailbox, one can retrieve the letter
(and any data stored within)
• A mailbox is actually a class what is instantiated using the new
function
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION: MAILBOX
• Mailbox is a built-in class that provides the following methods:
– Create a mailbox: new()
– Place a message in a mailbox: put()
– Try to place a message in a mailbox without blocking: try_put()
– Retrieve a message from a mailbox: get() or peek()
– Try to retrieve a message from a mailbox without blocking: try_get() or
try_peek()
– Retrieve the number of messages in the mailbox: num()
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION: MAILBOX
• function new(int bound = 0); • task put( singular message);
– The put() method places a message in a mailbox.
– Mailboxes are created with the – The message is any singular expression, including
new() method. object handles.
– The put() method stores a message in the mailbox
– The prototype for mailbox new() is: in strict FIFO order. If the mailbox was created with
– The new() function returns the a bounded queue the process shall be suspended
until there is enough room in the queue
mailbox handle, or null if the
mailboxes cannot be created. • function int try_put( singular message);
– The try_put() method attempts to place a message
– bound is the size of the mailbox. If in a mailbox.
the bound argument is zero then – The message is any singular expression, including
object handles.
the mailbox is unbounded (the
– The try_put() method stores a message in the
default). mailbox in strict FIFO order. This method is
meaningful only
• function int num(); – for bounded mailboxes. If the mailbox is not full
then the specified message is placed in the mailbox
– The num() method returns the and the
number of messages currently in – function returns 1. If the mailbox is full, the method
the mailbox. returns 0.
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION: MAILBOX
• task get( ref singular message ); • task peek( ref singular message );
– The message can be any singular expression, and it must – The message can be any singular expression, and it must be a
be a valid left-hand side expression. valid left-hand side expression.
– The peek() method copies one message from the mailbox
– The get() method retrieves one message from the without removing the message from the mailbox
mailbox, that is, removes one message from the mailbox
– queue. If the mailbox is empty then the current process blocks
queue. If the mailbox is empty then the current process
until a message is placed in the mailbox.
blocks until a message is placed in the mailbox.
– If there is a type mismatch between the message variable and
– If there is a type mismatch between the message variable the message in the mailbox, a runtime error is generated.
and the message in the mailbox, a runtime error is – Note that calling peek() can cause one message to unblock
generated. more than one process. As long as a message
– The mailbox waiting queue is FIFO. This does not – remains in the mailbox queue, any process blocked in either a
guarantee the order in which processes arrive at the peek() or get() operation shall become
queue, only that their arrival order shall be preserved by – unblocked.
the mailbox.
• function int try_peek( ref singular message );
• function int try_get( ref singular message ); – The message can be any singular expression, and it must be a
valid left-hand side expression.
– The message can be any singular expression, and it must – The try_peek() method tries to copy one message from the
be a valid left-hand side expression. mailbox without removing the message from the
– The try_get() method tries to retrieve one message from – mailbox queue. If the mailbox is empty, then the method
the mailbox. If the mailbox is empty, then the returns 0. If there is a type mismatch between the
– method returns 0. – message variable and the message in the mailbox, the
method returns –1. If a message is available and the message
– If there is a type mismatch between the message variable type matches, the type of the message variable, the message
and the message in the mailbox, the method returns –1. is copied and the method returns 1.
– If a message is available and the message type matches
the type of the message variable, the message is retrieved
and the method returns 1.
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION: MAILBOX
• Mailboxes can be classified as:
– Unbounded mailboxes
▪ No restrictions placed on size of mailbox.
▪ put() will never block.
▪ Ex: mailbox m = new ();
– Bounded mailboxes
▪ Number of entries is determined when the mailbox is created.
▪ Bound value should be positive.
▪ put() will be blocked if the mailbox is full.
▪ Ex: mailbox m = new (5); // mailbox of depth = 5
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION: MAILBOX
• Mailboxes can be classified as:
– Generic Mailbox (type-less mailbox)
▪ The default mailbox is type-less. that is, a single mailbox can send and
receive data of any type.
▪ Very powerful but may cause lots of issues
– Parameterized mailbox (mailbox with particular type)
▪ Parameterized mailbox is used to transfer a data of particular type
mailbox mailbox_name; // generic mailbox
mailbox#(int) mailbox_name; // parameterized mailbox
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION: MAILBOX
module mailbox_ex;
mailbox checker_data = new();
task input_monitor();
bit [7:0] data = 0;
for(integer i = 0; i < 4; i ++) begin
# [3] Putting data : 24 into mailbox
#(3); # [3] Got data : 24 from mailbox
data = $random();
$display("[%0d] Putting data : %x into mailbox", $time,data); # [6] Putting data : 81 into mailbox
checker_data.put(data); # [6] Got data : 81 from mailbox
end
endtask # [9] Putting data : 09 into mailbox
task my_checker(); # [9] Got data : 09 from mailbox
integer i = 0; # [12] Putting data : 63 into
// This can be any valid data type
bit [7:0] data = 0; mailbox
while (1) begin
checker_data.get(data);
# [12] Got data : 63 from mailbox
$display("[%0d] Got data : %x from mailbox", $time,data);
end
endtask
initial begin
fork
input_monitor();
my_checker();
join_any
#1000;
end
endmodule : mailbox_ex
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION: SEMAPHORE
• A semaphore allows you to
control access to a resource.
• When a semaphore is
allocated, a bucket that Driver 2
contains a fixed number of keys DUT
is created.
– Processes using semaphores Driver 1
must first procure a key from the
bucket before they can continue
to execute.
semaphore smTx;
• Semaphore is a built-in class
that provides several functions
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION: SEMAPHORE

• new( int keyCount = 0)


– Create a new semaphore with keyCount keys
– keyCount is the initial number of keys
– keyCount may increase beyond its initial value
• function void put( int keyCount = 1)
– keyCount is the number of keys returned to the semaphore .
• task get ( int keyCount = 1 )
– KeyCount is the number of keys to obtain form the semaphore
• function int try_get(int keyCount =1)
– KeyCount is the number of keys to obtain form the semaphore
– Returns 0 if the keyCount keys are not available
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION: SEMAPHORE
module semaphore_ex;
semaphore sema; //declaring semaphore
sema 0 Current Simulation Time
initial begin 30 Current Simulation Time
sema=new(1); //creating sema with '1' key
fork
display(); //process-1
display(); //process-2
join
end

//display method
task automatic display();
sema.get(); //getting '1' key from sema
$display($time,"\tCurrent Simulation
Time");
#30;
sema.put(); //putting '1' key to sema
endtask
endmodule
INTERPROCESS SYNCHRONIZATION
AND COMMUNICATION
• Semaphores cannot be used for data transfer between two concurrent
processes ,however it helps in synchronizing them. As an example if two parallel
processes lets say two different drivers are driving a same set of signals, say a reset
signal ,then to avoid contention it becomes necessary to use
semaphores.

Driver 2
DUT
Driver 1
FINAL REMARKS
COMMON INTERVIEW QUESTION

• Difference between static and dynamic variables?


• How automatic functions work?
• Disable fork issue proper usage?
• What is the difference between disable fork and Verilog’s disable?
• Difference between always, always_comb, always_latch, always_ff
• What is the usage of mailboxes and semaphores?
ASSIGNMENTS

• Reading assignment: Study chapter three from SystemVerilog for


Verification book (38 page)
• Read the following articles:
– https://hardikmodh.wordpress.com/2015/11/05/disabling-threads-in-
systemverilog-be-careful-while-using-it/
– https://verificationacademy.com/forums/systemverilog/what-exact-differ
ence-between-static-tasks/functions-and-automatic-tasks/functions-plea
se-explain-clear-example#reply-44935
– each is 3-10 minutes read
• Solve lab 1
REFERENCES

• Main textbook: SystemVerilog for Verification by Chris Spear


• Writing Testbenches using System Verilog
• Systemverilog Language Reference Manual
THANK YOU

You might also like