Sei sulla pagina 1di 32

Threads

 The power of parallel threads


 How to Create threads
 How to Control the threads
 Communication between threads
Creating Threads
 Fork/join blocks provide the primary mechanism for creating
concurrent processes.

 fork / join | join_none | join_any create threads

 SystemVerilog introduces two new ways to create threads –


with the fork...join_none and fork...join_any statements.

 join_<option> will decide when code after the fork-join


should execute
task display1 ();
$display ("From task 1\n");
endtask

task display2 ();


$display ("From task 2\n");
endtask
Output:
task display3 ();
$display ("From task 3\n");
endtask Started
From task3
program test; From task1
Initial begin
$display(" Started \n"); From task2
fork Ended
display1();
display2();
display3();
join
$display(" Ended \n"); Concurrent threads can execute in any order.
end
endprogram
task display1 ();
$display ("From task 1\n");
endtask

task display2 ();


$display ("From task 2\n");
endtask
Output:
task display3 ();
$display ("From task 3\n");
endtask Started
From task2
program test; Ended
Initial begin
$display(" Started \n"); From task1
fork From task3
display1();
display2();
display3();
join_any
$display(" Ended \n");
end
endprogram
task display1 ();
$display ("From task 1\n");
endtask

task display2 ();


$display ("From task 2\n");
endtask
Output:
task display3 ();
$display ("From task 3\n");
endtask Started
Ended
program test; From task2
Initial begin
$display(" Started \n"); From task1
fork From task3
display1();
display2();
display3();
join_none
$display(" Ended \n");
end
endprogram
Controlling threads
 wait fork
 wait (<expression>)
 wait_order (<statements>)
 disable blk_name
 disable fork
 events
Wait fork
task display1 ();
#2;
$display ("From task 1\n");
endtask Without wait fork:
task display2 (); Started
$display ("From task 2\n");
endtask From task2
Ended
task display3 ();
$display ("From task 3\n"); From task3
endtask
program test;
initial begin With wait fork:
$display (“ Started \n”); Started
fork
display1(); From task2
display2();
display3(); Ended
join_any From task3
$display (“Ended \n”);
wait fork From task1
end
endprogram
wait fork
 The wait fork statement blocks process execution flow until
all immediate child subprocesses (processes created by the
current process, excluding their descendants) have
completed their execution.
 The wait fork statement allows a program block to wait for
the completion of all its concurrent threads before exiting.
 when all the initial blocks in the program are done, the simulator
exits.
 Use the wait fork statement to wait for all child threads
Wait fork
task do_test;
fork
s1();
s2();
join_any
fork
s3();
s4();
The wait fork statement shall block the
join_none execution flow of the task do_test until all four
spawned processes complete before returning
wait fork; to its caller
endtask
Disable label
 Ex2: Disabling named block

initial begin : init_blk


$display(“Start of simulation \n”);

begin : block_name
...
if (a == 0)
disable block_name;
b=1; $display(“value of b =%0d in block_name\n”,b);
end // end of named block

// continue with code following named block


$display(“ after disable label \n”);
end
fork
begin : T1
@ev1;
#100 task1();
end
begin :T2
@reset ;
disable T1;
end
join
Disable fork
initial begin
$display(“Start of simulation \n”);

fork
$display( “ Thread 1 \n”);
$display( “ Thread 2 \n”);
$display( “ Thread 3 \n”);
join_any

disable fork;
$display(“End of simulation \n”);
end
Disable fork
task get_first( output int adr );

fork
wait_device( 1, adr );
wait_device( 7, adr );
wait_device( 13, adr );
join_any

disable fork; After fork/join_any the get_first task shall


resume execution and proceed to kill the
outstanding wait_device processes
endtask
Disable fork statement
 The disable fork statement terminates all active
descendants (subprocesses) of the calling process.

 disable fork shall end only the processes that were


spawned by the calling thread.

 The disable fork statement terminates all descendants of


the calling process as well as the descendants of the
process’s descendants.

 In other words, if any of the child processes have


descendants of their own, the disable fork statement shall
terminate them as well
Level-Sensitive Timing Control (wait)
 Procedural statement can also be delayed until a condition becomes true.

 Wait for a certain condition to be true before a statement or a block of


statements is executed.

forever begin
wait (count_enable) ;
#20 count = count + 1;
end

 In the above example, the value of count_enable is monitored


continuously.
 If count_enable is 0, the statement is not entered.
 If it is logical 1, the statement count = count + 1 is executed after 20 time
units.
 If count_enable stays at 1, count will be incremented every 20 time units.

 The wait statement is used to model level-sensitive timing control.


Event sequencing: wait_order()
 The wait_order construct suspends the calling process until
all of the specified events are triggered in the given order
(left to right).
event a,b,c;
bit success;
wait_order( a, b, c )
success = 1;
else
success = 0;
 Suspends the current process until events a, b, and c trigger
in the order a –> b –> c.
 If the events trigger out of order then else part is executed
Event
 Verilog RACE: There is always the possibility of a race condition in Verilog
where one thread blocks on an event at the same time another triggers it.
=> If the triggering thread executes before the blocking thread, the trigger is
missed
event e1, e2;
initial begin
$display("@%0t: 1: before trigger", $time);
OutPut:
- > e1;
@e2; @0: 1: before trigger
$display("@%0t: 1: after trigger", $time); @0: 2: before trigger
end @0: 1: after trigger
initial begin
$display("@%0t: 2: before trigger", $time);
-> e2;
@e1;
$display("@%0t: 2: after trigger", $time);
end
Persistent triggered property
 SystemVerilog introduces the triggered() method that lets you check
whether an event has been riggered, including during the current
time-slot.

event e1, e2; ====OutPut========


initial begin @0: 1: before trigger
$display("@%0t: 1: before trigger", $time); @0: 2: before trigger
-> e1; @0: 1: after trigger
wait (e2.triggered()); @0: 2: after trigger
$display("@%0t: 1: after trigger", $time);
end
initial begin
$display("@%0t: 2: before trigger", $time);
-> e2;
wait (e1.triggered());
$display("@%0t: 2: after trigger", $time);
end
event.triggered()
 The triggered event property helps eliminate a
common race condition that occurs when both the
trigger and the wait happen at the same time
Mailbox
mailbox mbx;
int j; Generator thread
initial begin
mbx = new(1); // Mailbox size = 1

fork Output:
for (int i=1; i<4; i++) begin Producer: before put(1)
$display("Producer: before put(%0d)", i); Producer: after put(1)
mbx.put(i); Producer: before put(2)
$display("Producer: after put(%0d)", i); Consumer: after get(1)
end Producer: after put(2)
Producer: before put(3)
repeat(3) begin Consumer: after get(2)
mbx.get(j); Producer: after put(3)
$display("Consumer: after get(%0d)", j); Consumer: after get(3)
end
join
end
Driver thread
How to pass information between two threads
 Mailbox : 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.

Mailbox is a built-in class that provides the following methods:

 Create a mailbox: new(int size=0)

 The put() method stores a message in the mailbox in strict FIFO order.
If the mailbox was created with a bounded queue, the process shall be suspended
until there is enough room in the queue.

 try_put() : Try to place a message in a mailbox without blocking.


If the mailbox is not full, then the specified message is placed in the mailbox, and
the function returns a positive integer.
If the mailbox is full, the method returns 0 without placing the message.
 get() : The get() method retrieves one message from the mailbox, that is,
removes one message from the mailbox queue.
If the mailbox is empty, then the current process blocks until a message is
placed in the mailbox.
If the type of the message variable is not equivalent to the type of the
message in the mailbox, a run-time error is generated.

 try_get() method attempts to retrieves a message from a mailbox without


blocking.
If the message is available then it retrieves the message (removes message)
If the message is not available then it return 0 without blocking.
 peek() : The peek() method copies a message from a mailbox
without removing the message from the queue.
If the mailbox is empty, then the current process blocks until a
message is placed in the mailbox

 try_peek() : The try_peek() method tries to copy one message


from the mailbox without removing the message from the
mailbox queue.
If the mailbox is empty, then the method returns 0 without
copying the message.

 num() : The num() method returns the number of messages


currently in the mailbox
class Driver;
Class environemnt; Transaction tr;
mailbox mbx = new(); virtual interface mem_intf vif;
Generator gen = new(mbx); task run;
Driver drv = new(mbx); forever begin
task run () mbx.get(tr);
fork @(vif.cb);
gen.run(); vif.cb.addr <= tr.addr;
drv.run(); vif.cb.wdata <= tr.data;
join …
endtask end
enclass endtask

class Generator;
Transaction tr;
task run;
repeat (10) begin
tr = new();
assert(tr.randomize());
mbx.put(tr);
end
endtask
endclass
Parameterized mailbox
 Normal mailbox:
mailbox mbx;
mbx=new();

 Paramerized Mailbox:

typedef mailbox #(Transaction) mbox;


mbox mbx = new(1);

Difference:
All type mismatches are caught by the compiler and not at run time
Communication Between Threads
 SystemVerilog also provides a powerful and easy-to-use set of synchronization
mechanism that can be created and reclaimed dynamically.
 This set comprises of a semaphore built-in class, which can be used for synchronization
and mutual exclusion to shared resources.

semaphore sem;
sem=new(1); //creates semaphore with 1 key
sem.get(1);
sem.put(1);

 get() : If the specified number of keys is available, the method returns and execution
continues. If the specified number of keys is not available, the process blocks until the
keys become available.
 When the semaphore.put() function is called, the specified number of keys is returned
to the semaphore
 The semaphore try_get() method is used to procure a specified number of keys from a
semaphore, but without blocking.
Allocates with one task device1_drive();
key sem.get(1);
semaphore sem; @(bus.cb);
initial begin bus.cb.addr <=tr.addr;
sem=new(1); bus.cb.data <= tr.data;
fork …….
device1_drive(); sem.put(1);
device2_drive();
join endtask
end
task device2_drive();
sem.get(1); Wait for bus to be
@(bus.cb); available
When completed , bus.cb.addr <=tr.addr;
return the keys back bus.cb.data <= tr.data;
…….
sem.put(1);

endtask
Semaphores
 Conceptually, a semaphore is a bucket.
 When a semaphore is allocated, a bucket that contains a fixed
number of keys is created.
 Processes using semaphores must first procure a key from the
bucket before they can continue to execute.
 If a specific process requires a key, only a fixed number of
occurrences of that process can be in progress simultaneously,
All others must wait until a sufficient number of keys is
returned to the bucket.
 Semaphores are typically used for mutual exclusion, access
control to shared resources, and basic synchronization.
What is the output ?
program test;
initial begin
for (int j=0; j<3; j++) Output:
fork
3
$write(j);
3
join_none
3
#0
$display("\n");
end
endprogram
Make copy of index
program test;
initial begin
for (int j=0; j<3; j++) Output:
fork
0
automatic int k = j;
1
$write(k);
join_none 2
#0
$display("\n");
end
endprogram

Potrebbero piacerti anche