Fall 2008
Dr. Meeta Yadav
Prof. Paul Franzon
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 1
Topics
• Events
• Interprocess Communication
Semaphores
Mailboxes
• Building a testbench with Threads and Interprocess
Communication
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 2
Event
• Events in Verilog
A Verilog event synchronizes threads
A thread waits for an event with the @ operator
The @ operator is edge sensitive, so it always blocks waiting for the event to change
A thread triggers the event with the -> operator
The -> operator unblocks the thread
There is a possibility of race condition in Verilog, if the triggering thread executes before
the blocking thread
event e1,e2;
initial begin
Triggers e1 is triggered
$display (“@%0d: 1: before trigger”, $time);
event e1
-> e1; First block waits on e2
Blocks on @e2;
event e2 $display (“@%0d:1: after trigger”, $time); e2 is triggered
end Second block waits on e1
initial begin
Triggers $display (“@%0d: 2: before trigger”, $time); First block waiting on e2 is unblocked
event e2 -> e2;
Second block waiting on e1 misses
@e1;
Blocks on the trigger and stays blocked
$display (“@%0d:2: after trigger”, $time);
event e1 end
Example: Blocking on an event in Verilog
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 3
Events
• Events in SystemVerilog
SystemVerilog events are used for synchronization
Event is a handle to a synchronization object that can be passed around to
routines
This feature allows events to be shared across objects without having to make
events global
Events are triggered using the -> or ->> operator
Processes can wait for an event to be triggered using the @ and wait()
operator
The triggered state persists throughout the time-step in which the event was
triggered
triggered function lets a verification engineer check whether an event has
been triggered or not
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 4
Events
• Triggering an event
Named events are triggered via the -> operator
Triggering an event unblocks all processes currently waiting on that event
When triggered, named events behave like a one-shot, that is, the trigger state
itself is not observable, only its effect
• Non blocking event trigger
Non blocking events are triggered via the ->> operator
The statement executes without blocking and it creates a nonblocking assign
update event in the time in which the delay control expires or the event-control
occurs
The effect of this update event shall be to trigger the referenced event in the
nonblocking assignment region of the simulation cycle
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 5
Events
• Waiting for an event
@ operator is used to wait for an event
@ operator blocks the calling process until the given event is triggered
For a trigger to unblock a process waiting on an event, the waiting process
must execute the @ statement before the triggering process executes the
trigger operator
If the trigger executes first then the waiting process remains blocked
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 6
Events
• Persistent Trigger: triggered property
SystemVerilog can distinguish event trigger (which is instantaneous) from event
trigger state (which persists through the time step)
The triggered event property evaluates to true if the given event has been
triggered in the current time-step and false otherwise
Triggered property is most useful when used in context of wait construct
wait (hierarchical_event_identifier.triggered)
Triggered property can be used to unblock a process whether the wait executes
before or at the same simulation time as the trigger operation
The triggered event property is useful in avoiding race conditions that occur when
both the trigger and wait happen at the same time
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 7
Events
• Example of Events
event done, blast; Declare 2 events
event done_too=done; done_too is an alias to done
task trigger (event ev); Generic task to trigger an event
-> ev; Two event identifiers done and done_too refer to the same
endtask synchronization object and an event is passed to the generic task
. . . that triggers that event
fork
@ done_too; Wait for done through done_too
#1 trigger(done); Trigger done through task trigger
join
fork
-> blast; Trigger the event blast
wait(blast.triggered); Waits for the event. Even though the two processes are spawned
join at the same time, the second process can wait on the event since
the triggered state holds for the entire duration of time-step
Example: Triggered property
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 8
Events
• Example of Events
event done, blast;
event done_too=done; clk
task trigger (event ev);
-> ev; @done_too
endtask
. . . done
fork
@ done_too;
#1 trigger(done); blast
join
fork
blast.triggered
-> blast;
wait(blast.triggered); state
join wait(blast.
triggered)
Example: Triggered property
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 9
Events
• 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)
wait_order (a,b,c)
a->b->c
If the events are triggered out of order the operation fails
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 10
Events
• Event variables
Merging Variables
Events are a unique data type and can be assigned to one another
When events are assigned to one another the synchronization queue of the source event
is shared by both the source and the destination events
When events are assigned to each another, the two become merged
event a,b,c
a=b;
->a; //also triggers b
->b; //also triggers a
a=c;
->a //also triggers b and c
->b; //also triggers a and c
->c; //also triggers a and b
Example: Merging variables
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 11
Events
• Event variables
When events are merged the assignment only affects the execution of
subsequent event control or wait operations
If a process is waiting for event1 when another event is assigned to event1, the
currently waiting process shall never unblocks. For example:
fork @E2 T1 blocks forever
T1: while(1) @ E2;Thread 1 because E2 is now E1
T2: while(1) @ E1;Thread 2
T3: begin @E1
E2 = E1; Thread 3 T2 unblocks
while(1) -> E2;
end -> E2
E2=E1
join
Example: Effect of merging variables
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 12
Events
• Event variables
To unblock both threads the merger of E1 and E2 should take place outside the fork and join
E2=E1
Parent
E2 = E1;
fork @E2
T1: while(1) @ E2; Thread 1
T2: while(1) @ E1; Thread 2
T3: begin @E1
while(1) -> E2;Thread 3
end
join -> E2
Example: Effect of merging variables
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 13
Events
• Reclaiming Events
When an event variable is assigned a special null value, the association between the
event variable and the underlying synchronization queue is broken
When no event variable is associated with an underlying synchronization queue, the
resources of the queue become available for reuse
Triggering a null event should have no effect
The outcome of waiting on a null event is undefined and implementations can issue a
run-time warning
event E1=null Undefined: might block forever or not at all
@E1
wait (E1.triggered) Undefined
-> E1 No effect
Example: Reclaiming an event using null construct
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 14
Events
• Event Comparison
Event variables can be compared against other values, some of them are as follows:
Equality (==) with another event or null
Inequality (!=) with another event or null
Case equality (===) with another event or null
Case inequality (!==) with another event or null
Test for boolean value that shall be 0 if the event is null and 1 otherwise
event E1, E2;
if ( E1 ) // same as if ( E1 != null )
E1 = E2;
if ( E1 == E2 )
$display( "E1 and E2 are the same event" );
Example: Event comparison
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 15
Interprocess Communication: Semaphores
• Semaphores
Conceptually, a semaphore is a bucket
Semaphores are typically used for mutual exclusion, access control to shared resources,
and for basic synchronization
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
Semaphore is a built-in class that provides the following methods:
Create a semaphore with a specified number of keys: new()
Obtain one or more keys from the bucket: get()
Return one or more keys into the bucket: put()
Try to obtain one or more keys without blocking: try_get()
Semaphores can be used in a testbench when you have a resource, such as a bus, that
may have multiple requestors from inside the testbench but as a part of a physical
design, can only have one driver
semaphore smTx;
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 16
Interprocess Communication: Mailboxes
How do you pass information between two threads?
• Mailboxes
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
From a hardware point of view a mailbox is nothing but a
FIFO with a source and a sink
The source puts the data into the mailbox and the sink 4
3
2
1
source
gets values from the mailbox
Mailboxes can have a maximum size or can be unlimited Full!!!
Mailboxes are created either having a bounded or mailbox
unbounded queue size
A bounded mailbox becomes full when it contains the
bounded number of messages
A process that attempts to place a message in into a full
mailbox shall be suspended until enough room
becomes available in the mailbox queue sink
If a sink tries to retrieve data from a mailbox that is
empty it blocks it until data is put in the mailbox
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 17
Interprocess Communication: Mailboxes
• Mailboxes
Mailbox is an inbuilt class that provides the following methods:
Creae 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()
mailbox mbxRcv;
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 18
Interprocess Communication: Mailboxes
• Mailbox in a testbench
How do you pass information between two threads?
The generator creates many transactions and passes them to a driver
The generator and driver must operate asynchronously
program mailbox_example (bus_if.TB bus,…);
class Generator Class Generator
Transaction tr; Instantiate class Transaction
mailbox mbx; Create unbounded mailbox
function new(mailbox mbx);
this.mbx=mbx;
endfunction
task run;
repeat (10) begin
tr=new; Create 10 transactions
assert(tr.randomize);
Send out the transaction
mbx.put(tr);
end
endtask
endclass cont…
Example: Exchanging objects using a mailbox: the Generator class
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 19
Interprocess Communication: Mailboxes
• Mailbox in a testbench
class Driver
Transaction tr;
mailbox mbx;
function new(mailbox mbx);
this.mbx=mbx;
endfunction
task run;
repeat (10) begin
mbx.get(tr); Fetch next transaction
@(posedge busif.cb.ack)
…
end
endtask
endclass cont…
Example: Example of using a mailbox in a testbench
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 20
Interprocess Communication: Mailboxes
• Mailbox in a testbench
Mailbox containing gen and drv
mailbox mbx;
Generator gen;
Driver drv;
initial begin 4
3
2
1
generator
mbx=new;
gen=new(mbx);
drv=new(mbx);
fork mbx
gen.run(); Spawn the generator
drv.run(); Spawn the driver
join
end
endprogram driver
Example: Example of using a mailbox in a testbench, continued
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 21
Interprocess Communication: Mailboxes
• Bounded Mailboxes
program automatic bounded;
mailbox mbx;
initial begin
mbx = new(1); Size of mailbox is 1
fork
for (int i=1; i<4; i++) begin Producer
$display("@%0d: Producer: putting %0d", $time, i);
mbx.put(i);
$display("@%0d: Producer: put(%0d) done %0d",$time, i);
end
repeat(3) begin
Consumer
int j;
#1ns mbx.get(j);
$display("@%0d: Consumer: got %0d", $time, j);
end
join_any
end
endprogram
Example: Bounded mailbox
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 22
Interprocess Communication: Mailboxes
• Bounded Mailboxes
program automatic bounded;
mailbox mbx;
initial begin
mbx = new(1);
fork
for (int i=1; i<4; i++) begin Producer
$display("@%0d: Producer: putting %0d", $time, i);
mbx.put(i);
$display("@%0d: Producer: put(%0d) done %0d",$time, i);
end
repeat(3) begin Consumer Producer runs ahead
int j;
#1ns mbx.get(j);
of consumer!!!
$display("@%0d: Consumer: got %0d", $time, j);
end
join_any
end
endprogram
Example: Bounded mailbox
@0 Producer: putting 1 @1 Consumer: got(1)
@0 Producer: put(1) done @2 Consumer: got(2)
@0 Producer: putting 2
@1 Producer: 3 2 done
put(2) 1
@1 Producer: putting 3
Mailbox
@2 Producer: put(3) done
Producer Consumer
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 23
Interprocess Communication: Mailboxes
• Unsynchronized threads communicating with the testbench
Construct mailbox mailbox mbx;
program automatic unsynchronized;
class Producer; Instantiate Producer Producer p;
Instantiate Consumer Consumer c;
task run;
for(int i=1; i<4; i++) begin
intial begin
$display(“Producer: before
mbx=new;p=new;c=new;
put(%0d)”,i);
Put integer in mailbox fork
mbx.put(i);
Run producer and p.run;
end
consumer in parallel c.run;
endtask
join
endclass
end
class Consumer;
endprogram
task run;
int i;
repeat(3) begin Example: Producer-consumer
mbx.get(i); Get integer from mailbox without synchronization, continued
$display(“Consumer: after
get(%0d)”,i);
end
endtask
endclass
cont…
Example: Producer-consumer without synchronization
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 24
Interprocess Communication: Mailboxes
• Unsynchronized threads communicating with the testbench
program automatic unsynchronized;
class Producer;
task run; Producer
for(int i=1; i<4; i++) begin
$display(“Producer: before
Producer: before put(1)
put(%0d)”,i);
mbx.put(i);
3
end Producer: 2
before put(2)
endtask
endclass Producer: 1
before put(3)
class Consumer; There is no synchronization
task run;
int i; so the producer puts all
repeat(3) begin three integers into the
mbx.get(i);
Mailbox
$display(“Consumer: after
mailbox before the
get(%0d)”,i); consumer can get the first
end one. This is because the
endtask
endclass thread continues running
mailbox mbx; until there is a blocking
Producer p;
Consumer c;
statement and the Producer
intial begin has none
mbx=new;p=new;c=new; Consumer: after get(1)
fork
p.run; Consumer: after get(2)
c.run;
join Consumer: after get(3)
end
endprogram Consumer
Example: Producer-consumer without synchronization
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 25
Interprocess Communication: Mailboxes
• Synchronized threads using a mailbox and events
An additional handshake is required if the producer and the consumer are to be run in
lock-step
The two threads should use a handshake so
program automatic mbx_evt; that the Producer does not get ahead of the
event handshake;
class Producer; Consumer. This is done by blocking the
task run; Producer on the handshake event (the
for(int i=1; i<4; i++) begin consumer already blocks on the mailbox)
$display(“Producer: before put(%0d)”,i);
mbx.put(i);
@handshake; Producer blocks on the handshake event to
$display(“Producer: after put(%0d)”,i); ensure that the producer stops after sending
end the transaction
endtask
endclass
class Consumer;
task run;
int i;
repeat(3) begin
mbx.get(i);
$display(“Consumer: after get(%0d)”,i);
-> handshake; Consumer triggers the handshake event to
end allow the Producer to advance
endtask
endclass
…
endprogram
Example: Producer-consumer synchronized with an event
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 26
Interprocess Communication: Mailboxes
• Synchronized threads using a mailbox and events
Producer
program automatic mbx_evt;
Producer: before put(1)
event handshake;
class Producer;
Producer: 3
after put(1)
Producer: before put(2)
task run; Producer: 2
after put(2)
for(int i=1; i<4; i++) begin Producer: before put(3)
$display(“Producer: before put(%0d)”,i); Producer: 1
after put(3)
mbx.put(i);
@handshake;
$display(“Producer: after put(%0d)”,i);
end
Mailbox
endtask
endclass
class Consumer;
task run;
int i;
repeat(3) begin
mbx.get(i);
$display(“Consumer: after get(%0d)”,i);
-> handshake; Consumer: after get(1)
end
endtask Consumer: after get(2)
endclass
… Consumer: after get(3)
endprogram
Example: Producer-consumer synchronized with an event Consumer
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 27
Building a Testbench with Threads and IPC
Testbench
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 28
Building a Testbench with Threads and IPC
• Basic Tansactor
class Agent;
mailbox gen2agt, agt2drv; Create mailboxes to send transactions
from generator to agent, agent to driver
Transaction tr;
function new(mailbox gen2agt, agt2drv);
this.gen2agt = gen2agt;
this.agt2drv = agt2drv;
endfunction
function build;
// Empty for now
endfunction
task run;
forever begin
Get transaction from upstream block
gen2agt.get(tr);
//Do some processing
Send transaction to downstream block
agt2drv.put(tr);
end
endtask
task wrapup;
// Empty for now
endtask
endclass
Example: Basic Transactor for Agent that sits between Generator and Driver
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 29
Building a Testbench with Threads and IPC
• Environment Class
The Generator, Agent, Monitor, Checker and function void Environment::gen_cfg;
Scoreboard classes are instantiated in the assert(cfg.randomize);
endfunction
Environment class function void Environment::build;
Instantiate Generator, gen.build;
class Environment;
Agent, Monitor, Checker agt.build;
Generator gen;
Agent agt; and Scoreboard classes drv.build;
Driver drv; mon.build;
Monitor mon; chk.build;
Create mailboxes to send scb.build;
Checker chk;
transactions from endfunction
Scoreboard scb;
Config cfg; generator to agent, agent task Environment::run;
mailbox gen2agt, agt2drv, mon2chk; to driver and monitor to fork
gen.run(run_for_n_trans);
extern function new; checker
extern function void gen_cfg; agt.run;
extern function void build; Run Generator, agent, drv.run;
extern task run; driver, monitor and mon.run;
extern task wrapup; chk.run;
checker in parallel scb.run(run_for_n_trans);
endclass
join
function Environment::new; endtask
gen2agt = new; task Environment::wrapup;
agt2drv = new; Initialize mailboxes fork
mon2chk = new; gen.wrapup;
gen = new(gen2agt); agt.wrapup;
agt = new(gen2agt, agt2drv); drv.wrapup;
Initialize transactors mon.wrapup;
drv = new(agt2drv);
mon = new(mon2chk); chk.wrapup;
chk = new(mon2chk); scb.wrapup;
scb = new; join
cfg = new; endtask
endfunction
Example: Environment Class Example: Environment Class, continued
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 30
Building a Testbench with Threads and IPC
• Test Program
The main test goes in the top level program
program automatic test;
Environment env;
initial begin
env = new;
env.gen_cfg;
env.build;
env.run;
env.wrapup;
end
endprogram
Example: Basic Test Program
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 31
Building a Testbench with Threads and IPC
• Conclusion
Model your testbench to generate multiple stimulus streams and check the
responses using parallel threads
Organize your testbench in a layered fashion and orchestrate it by the top-level
environment
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 32
Thank You
©2008, Dr. Meeta Yadav, www4.ncsu.edu/~myadav/Research 33