7 program automatic unsynchronized;
class Producer;
task run;
for (int i=1; i<4; i++) begin
$display("Producer: before put(%0d)", i);
mbx.put(i);
end
endtask
endclass
206 System
verilog for Verification
Example 7-30 Producer–consumer without synchronization, continued
class Consumer;
task run;
int i;
repeat (3) begin
mbx.get(i); // Get integer from mbx
$display("Consumer: after get(%0d)", i);
end
endtask
endclass
mailbox mbx;
Producer p;
Consumer c;
initial begin
// Construct mailbox, producer, consumer
mbx = new;
p = new;
c = new;
// Run the producer and consumer in parallel
fork
p.run;
c.run;
join
end
endprogram
so the Producer puts all three integers into the mailbox before the Consumer can get the first one. This is
because a thread continues running until there is a blocking statement, and the
Producer has none.
This is done by adding a blocking statement to the Producer such as an event, a semaphore, or a second mailbox. Example 7-32 uses an event to block the Producer after it puts data in the mailbox. The Consumer triggers the event after it consumes the data
If you use wait(handshake.triggered) in a loop, besure to advance the time before waiting again. This waitblocks only once in a given time slot, so you need move intoanother.
uses the edge-sensitive blocking
statement @handshake instead to ensure that the Producer stops after sending
the transaction.The edge-sensitive statement works multiple times in a time
slot but may have ordering problems if the trigger and block happen in the
same time slot.
program automatic mbx_evt;
event handshake;
class Producer;
task run;
for (int i=1; i<4; i++) begin
$display("Producer: before put(%0d)", i);
mbx.put(i);
@handshake;
$display("Producer: after put(%0d)", i);
end
endtask
endclass
class Consumer;
task run;
int i;
repeat (3) begin
mbx.get(i);
$display("Consumer: after get(%0d)", i);
->handshake;
end
endtask
endclass
...
endprogram
//because of no time information , so we use the @ not the wait(xxxx.triggered)
An event is the simplest construct, followed by blocking on a
variable. A semaphore is comparable to using a second mailbox, but no
information is exchanged.
class Agent;
mailbox gen2agt, agt2drv;
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 it to downstream block
agt2drv.put(tr);
end
endfunction
task wrapup;
// Empty for now
endtask
endclass
class Environment;
Generator gen;
Agent agt;
Driver drv;
Monitor mon;
Checker chk;
Scoreboard scb;
Config cfg;
mailbox gen2agt, agt2drv, mon2chk;
extern function new;
extern function void gen_cfg;
extern function void build;
extern task run;
extern task wrapup;
endclass
function Environment::new;
// Initialize mailboxes
gen2agt = new;
agt2drv = new;
mon2chk = new;
// Initialize transactors
gen = new(gen2agt);
agt = new(gen2agt, agt2drv);
drv = new(agt2drv);
mon = new(mon2chk);
chk = new(mon2chk);
scb = new;
cfg = new;
endfunction
function void Environment::gen_cfg;
assert(cfg.randomize);
endfunction
function void Environment::build;
gen.build;
agt.build;
drv.build;
mon.build;
Chapter 7: Threads and Interprocesses Communication 213
chk.build;
scb.build;
endfunction
task Environment::run;
fork
gen.run(run_for_n_trans);
agt.run;
drv.run;
mon.run;
chk.run;
scb.run(run_for_n_trans);
join
endtask
task Environment::wrapup;
fork
gen.wrapup;
agt.wrapup;
drv.wrapup;
mon.wrapup;
chk.wrapup;
scb.wrapup;
join
endtask
program automatic test;
Environment env;
initial begin
env = new;
env.gen_cfg;
env.build;
env.run;
env.wrapup;
end
endprogram