|
Chapter7 Threads and Interprocess Communication
1 Most blocks in your testbench environment are modeled with a transactor and run in their own thread. The Systemverilog scheduler is the traffic cop that chooses which thread runs next. You can use the techniques in this chapter to control the threads and thus your testbench.
2 You cannot put an always block in a program. However, you can easily get around this by using a forever loop in an initial block.
3 Verilog's fork/join all statements inside the fork...join have to finish before the rest of the block can continue. fork...join_none :A fork...join_none block schedules each statement in the block, but execution continues in the parent thread.and fork...join_any : If one finished ,then you can execute the following blocks. statements.Your testbench communicates, synchronizes, and controls these threads
with existing constructs such as events, @ event control, the wait and
disable statements, plus new language elements such as semaphores and
mailboxes.
4 Always use automatic variables to hold values in concurrent threads.
5 program automatic test(busif.TB bus);
// Code for interface not shown
task wait_for_tr(Transaction tr);
fork
begin
wait (bus.cb.addr != tr.addr);
$display("@%0d: Addr match %d", $time, tr.addr);
end
join_none
endtask
Transaction tr;
initial
repeat (10)
begin
// Create a random transaction
tr = new;
if (!tr.randomize) $finish;
// Send it into the DUT
transmit(tr); // Task not shown
// Wait for reply from DUT
wait_for_tr(tr);
end
endprogram
keep in mind that if you extend the wait_for_tr at here, then tr becomes a local variable, then it will have problems. so you need to invoke it in the routines
6 SystemVerilog schedules the threads inside a fork...join_none but they are not executed until after the original code blocks,
initial begin
for (int j=0; j<3; j++)
fork
$write(j); // Bug – gets final value of index
join_none
#0 $display("\n");
end is executed as:
j Statement
0 for (j=0; ...
0 Spawn $write(j) [thread 0]
1 j++
1 Spawn $write(j) [thread 1]
2 j++
2 Spawn $write(j) [thread 2]
3 j++
3 join_none
3 #0
3 $write(j) [thread 0]
3 $write(j) [thread 1]
3 $write(j) [thread 2]
3 $display(“\n”)
7 You should use automatic variables inside a fork...join statement to
save a copy of a variable as shown below:
initial begin
for (int j=0; j<3; j++)
fork
automatic int k = j; // Make copy of index
$write(k); // Print copy
join_none
#0 $display;
end
8 parameter TIME_OUT = 1000;
task wait_for_tr(Transaction tr);
fork
begin
// Wait for response, or some maximum delay
fork : timeout_block
wait (bus.cb.addr != tr.addr);
#TIME_OUT $display("@%0d: Error: timeout", $time);
join_any
disable timeout_block;//to kill threads in the timeout_block,all the threads in this block are killed
$display("@%0d: Addr match %d", $time, tr.addr);
end
join_none
endtask
9 SystemVerilog introduces the disable fork statement so you can stop all child threads that have been spawned from the current thread. Watch out, as you might unintentionally stop too many threads, such as those created from routine calls. You should always surround the target code with a fork...join to limit the scope of a disable fork statement.
10 initial begin
wait_for_tr(tr0); // Spawn thread 0
// Create a thread to limit scope of disable
fork
begin
wait_for_tr(tr1); // Spawn thread 2
fork // Spawn thread 3
wait_for_tr(tr2); // Spawn thread 4
join
// Stop threads 1 & 2, but leave 0 alone
#(TIME_OUT/2) disable fork;//disables threads in its level and its child.
end
join
end
The code calls wait_for_tr that starts thread 0. Next a fork...join
creates thread 1. Inside this thread, one is spawned by the wait_for_tr task
and one by the innermost fork...join, which spawns thread 4 by calling the
task. After a delay, a disable fork stops the child threads. Only threads 2,
3, and 4 are below thread 1, so they are the only ones stopped. Thread 0 is
outside the fork...join block that has the disable, so it is unaffected.
initial begin
wait_for_tr(tr0); // Spawn thread 0
begin : threads_1_2
wait_for_tr(tr1); // Spawn thread 1
wait_for_tr(tr2); // Spawn thread 2
end
// Stop threads 1 & 2, but leave 0 alone
#(TIME_OUT/2) disable threads_1_2;
join
end
11 task run_threads;
... // Create some transactions
wait_for_tr(tr1); // Spawn first thread
wait_for_tr(tr2); // Spawn second thread
wait_for_tr(tr3); // Spawn third thread
... // Do some other work
// Now wait for the above threads to complete
wait fork;
endtask
12 In Verilog a thread waits for an event with the @ operator.(edge sensitive,)
First, an event is now a handle to a synchronization object that can be
passed around to routines. This feature allows you to share events across
objects without having to make the events global.
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.
SystemVerilog introduces the triggered function that lets you check
whether an event has been triggered, including during the current time-slot.
13 to be continued