Hi, Dear,
Recently I run into a problem as :
task body();
for (int i = 0; i < 8; i++) begin
automatic link = i;
if (p_sequencer.rx_cfg[i].enable == 1) fork
// do something pre ...
....
fork : data_send
begin
forever begin
`uvm_do_with(data_seq, {slice == link;})
end
end
begin
`uvm_do_with(check_seq, {slice == link;})
end
join_any
disable data_send;
// do something post ...
....
join_none
end
endtask
I thought the disable data_send will only kill particular thread generated from every “i”, however I found that when first disable data_send; executed, all “data_send” threads (8 threads totally) were killed simultaneously, looks like the thread name “data_send” was shared for every thread, and disable data_send; is visible for them, kill them all …
Could you please help explain what’s going on here and gave me a solution for this issue ?
The disable <named_block> construct is very simple - just makes every <named_block> jump to the end of the block. It does not consider any threading. You probably want to use disable fork instead. That construct terminates all threads that are the children of the parent thread (the thread executing the disable fork)
There are a few other issues with your example code.
You probably want the code inside the fork/join_none wrapped in a begin/end block. Otherwise the statements after the "do something pre/post, the disable fork, and the fork/join_any will all execute in parallel threads.
There is never a need to use the automatic keyword inside a class. All variables have automatic lifetime by default.
Abruptly killing a thread running a sequence might result in a hang. See Sequences/Stopping | Verification Academy
In reply to dave_59:
Hi, Dave,
Thanks so much for your response, it helps a lot :) Two more questions :
-
The reason I do not use disable fork is that I have some other threads on “do something pre” and “do something post” block, looks to me that disable fork will kill the thread in “do something pre” as well, but it is not my expectation, that is why I use disable data_send; instead. Any suggestion to handle this ?
-
I am really curious about what you said “abruptly killing a thread running a sequence might result in a hang.” I didn’t get too much information from the link you pointed out, could you please elaborate more here ? I really want to know the potential risk for this code.
Thanks so much,
WangYang
In reply to caowangyang:
For Q1 Two things you can do. You can wrap the fork/join_any in a fork/begin/end/join to isolate the threads you want affected by disable.
task body();
for (int i = 0; i < 8; i++) begin
link = i;
if (p_sequencer.rx_cfg[i].enable == 1) fork
// do something pre ...
....
fork begin : isolation_block
fork : data_send
begin
forever begin
`uvm_do_with(data_seq, {slice == link;})
end
end
begin
`uvm_do_with(check_seq, {slice == link;})
end
join_any
disable fork; // only disables the threads that are children of isolation_block
end : isolation_block join
// do something post ...
....
join_none
end
endtask
or you could get a handle to the process you want to terminate. See 9.7 Fine-grain process control in the LRM
task body();
process data_send;
for (int i = 0; i < 8; i++) begin
link = i;
if (p_sequencer.rx_cfg[i].enable == 1) fork
// do something pre ...
....
fork
begin
data_send = process::self();
forever begin
`uvm_do_with(data_seq, {slice == link;})
end
end
begin
`uvm_do_with(check_seq, {slice == link;})
end
join_any
data_send.kill();
// do something post ...
....
join_none
end
endtask
For Q2, you just need to be aware that the driver might have to deal with a sequence being terminated in the middle of the protocol with the sequence. Most of the time, this is not a problem because the driver is only blocked waiting for get_next_item(), but more advanced scenarios, such has using blocking put() for the response could result in a hang. I would not worry about it untill you get to that point.
In reply to dave_59:
Got it, thanks for your response.