SV Session 3
SV 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
• assignment_operator ::=
– = | += | -= | *= | /= | %= | &= | |= | ^= | <<= | >>= | <<<= | >>>=
• conditional_expression ::=
– cond_predicate ? { attribute_instance } expression : expression
• unary_operator ::=
– + | - | ! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~
• binary_operator ::=
– + | - | * | / | % | == | != | === | !== | =?= | !?= | && | || | **
– | < | <= | > | >= | & | | | ^ | ^~ | ~^ | >> | << | >>> | <<<
• inc_or_dec_operator ::= ++ | --
PROGRAM CONTROL: LOOPS
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
....
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
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
• 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
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
always_latch begin
if(ck)
q <= d;
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
//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