from #22 a discussion on where and how to use objections has been started.
to not clutter that PR too much I have taken discussion this into its own issue.
The original suggestion is to avoid objections in drivers and monitors and instead add them in the scoreboard.
I will try to make an argument why monitors is the perfect place for objections and why they should only go into phase_ready_to_end()
one of the Arguments of why it is nice to have objections in the scoreboard is given by @sriyerg
I took the liberty of quoting it here:
I see some valid usecases in allowing raising/dropping objections in the scoreboard; example: in xbar type DUT, raise obj when transaction enters the DUT and drop only when it exits. This objection (that helps ensure that the test does not exit prematurely) cannot be captured in the monitor and has to be captured in a higher level component. It cannot be captured in the test either since it only generates and sends C-R stimulus to the driver. It has no knowledge of when the transactions actually enter and exit the DUT.
We keep the performance impact low by raising objection exactly once (dont raise if it is already raised), which is what the line implies. You can find that logic in our base scoreboard (hw/dv/sv/dv_lib/dv_base_scoreboard.sv
). If by deadlocks you mean hangs, then there is an obvious problem either in the design or dv, that needs fixing. We avoid hanging forever through reasonable test timeouts.
I have two arguments against this - the first one being simply performance.
every time you raise and drop an objection is causing the component hierarchy to be traversed.
so for shallow environments this might be a minor impact but as the hierarchy gets deeper this quickly gets expensive.
The other argument is that implementing this in the scoreboard requires a watchdog timer to be implemented a long the side to avoid deadlocks.
Here is what I suggest
Most modules have deterministic input to output latency and does not need any objections besides test case objections and a good choice of drain time. this should be the default go to.
objections should only be used outside the test when the DUT latency is not detministic!
Some modules does not have deterministic latency. an example can be some sort of packet builder where input packets are grouped in larger chunks and these chunks are only forwarded when reaching a certain size.
Modules like this usually have an internal timeout to flush out undersized chunks.
In such a scenario it will be hard to raise objections in a scoreboard as the scoreboard cannot easily predict the number of outputs based on the number of inputs.
and the suggested objection method would be insufficient.
instead I suggest putting the objetion inside the phase_ready_to_end() function.
this is called once all modules have dropped their run_phase objections and the test is trying to end the run phase.
here is what is currently in my scoreboard (AES)
virtual function void phase_ready_to_end(uvm_phase phase);
if (phase.get_name() != "run") return;
if ( (dut_fifo.num() != 0 ) || (ref_fifo.num() != 0) ) begin
phase.raise_objection(this, "fifos still has data");
fork begin
wait_fifo_empty();
phase.drop_objection(this);
end
join_none
end
endfunction
virtual task wait_fifo_empty();
wait ((dut_fifo.num() == 0 ) && (ref_fifo.num() == 0));
endtask
// ...
endclass
This ensures the Objection is ever only raised once!
obviously this still has the deadlock issue so a time would need to be implemented in parallel to the ''' wait_for_fifo_empty ''' function.
But this is still sub-optimal because the scoreboard should be a 0-time and there for it does not have any knowledge of clock frequency and one would have to guess what number of milliseconds is enough for any used clock frequency.
I believe the better choice is to implement a function in the monitor that detects traffic and keeps the phase from ending for /# number of clocks.
this is very similar to drain time but where drain time is for the full environment this implementation will work even when multiple environments are cascaded to form a top level verification environment.
the idea is to start a timer when phase_ready_to_end is called.
if no traffic is seen before a timer runs out - it is ok to end.
here is an example I did
function void phase_ready_to_end(uvm_phase phase);
uvm_phase current_phase;
string str ="";
uvm_object objectors[$];
current_phase = phase;
if (phase.is(uvm_main_phase::get())) begin
// Start watchdog
fork
watchdog_ok_to_end();
join_none;
if (!ok_to_end) begin
phase.raise_objection(this);
fork begin
wait_for_ok_end();
phase.drop_objection(this);
// see who is still objecting
current_phase.get_objection().get_objectors(objectors); // get all that has raised objections
str = $sformatf("\n\n\t |--------------------| Who Is Still OBJECTNG |------------------------|");
str = {str, $sformatf("\n\t current phase: %s ", current_phase.get_name())};
str = {str, $sformatf("\n\t Hierarchical Name Class Type")};
foreach(objectors[i]) begin
str = {str, $sformatf("\n\t%-60s%s\n\n", objectors[i].get_full_name(), objectors[i].get_type_name())};
end
`uvm_info({LOG_NAME, "*"}, str, UVM_HIGH);
end join_none;
end
end
endfunction
//-----------------------
// Wait until ok_to_end is set. Only checks ok_to_end every timeout_delay cycles since this is the length of the watchdog cycle
task wait_for_ok_end();
forever begin
repeat(timeout_delay) @(posedge vif.clk_i);
if (ok_to_end) break;
end
endtask // wait_for_ok_end
// Watchdog waits timeout_delay cycles while checking if there is any data on the interface. If there is then !ok_to_end else ok_to_end.
task watchdog_ok_to_end();
bit reset_timer = 0;
fork
begin
forever begin
@(posedge vif.clk_i);
if(vif.vif.rd) begin
reset_timer = 1;
end
end
end
begin
forever begin
ok_to_end = 0;
repeat(timeout_delay) @(posedge vif.clk_i); // stay here until #timeout delay clocks has passed
if(!reset_timer) begin
ok_to_end = 1;
break;
end else begin
`uvm_info(LOG_NAME, $sformatf(" Resetting timer"), UVM_FULL);
reset_timer = 0;
end
end
end
join_any
endtask
I hope this at least shows some of the drawbacks of putting objections in the run phase.
and secondly in the scoreboard.
If there is a big desire to but objections in the scoreboard I think that is ok, but I think we should require objections to only go in the phase_ready_to_end function when outside of the test.