I want to use the following SystemVerilog concepts:
- clocking block: to avoid race conditions between driver and monitor, i want to have it in a central place, in the interface
- modport: normally (e.g. passing a modport instead of the interface to a module) I could use it to ensure that the interface signals could only be accessed through the clocking block
Now in UVM we instantiate a virtual interface and make it available through the config database, so I don't see how it would be possible for the driver to only get a reference to the modport instead of the virtual interface. What is the standard solution to avoid accidentally accessing a signal without the modport?
I could only come up with the following (untested) code sample, however I'm not sure whether there is a better way:
interface my_interface(input clk);
logic I;
logic O;
clocking cb_drv @(posedge clk);
output I;
input O;
endclocking
clocking cb_mon @(posedge clk);
input I;
output O;
endclocking
modport drv(clocking cb_drv, output clk);
modport mon(clocking cb_mon, input clk);
endinterface
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver);
virtual my_interface.drv vif;
...
virtual function void build_phase(uvm_phase phase);
virtual my_interface temp;
super.build_phase(phase);
if(!uvm_config_db#(virtual my_interface)::get(this, "", "my_interface", temp))
`uvm_fatal(...);
vif = temp.drv;
endfunction
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(transaction);
@(vif.cb_drv);
vif.cb_drv.I = transaction.I;
seq_item_port.item_done();
end
endtask
endclass
The issue with this solution is, that I have functions/tasks, that are implemented in the interface (e.g. read/write functions), which become unaccessible this way.