This is the third part of a three part series discussing SystemVerilog interfaces and strategies for dealing with parameterization.
You can get more information at Synopsys Verification IP and VC Verification IP Datasheet.
In the first part of this series, the basic concepts of SystemVerilog interfaces were introduced, and the problems that parameterization of those interfaces introduce to testbench code was described. In the second part, the method of using an accessor class to shield the VIP code from the parameterization effects was described, but this solution imposes new restrictions on VIP access to that interface. In this final post of this series, a process is introduced that allows the testbench to make use of parameterized interfaces without imposing any limits on the way that the VIP can access the interface that it is provided.
The problem that parameterized interfaces introduce to dynamic testbench code cannot be resolved using today’s existing SystemVerilog functionality. Therefore we must design a way to avoid exposing these parameters to the VIP code. The previous post in this series introduced the accessor class to achieve this. An alternate solution is to define a maximum footprint style interface that the VIP can interact with, and a parameterized interface that wraps this maximum footprint interface and connects to the signals that it needs from the max footprint interface.
Maximum footprint style interfaces define the maximum width for each signal and individual VIP components can be configured to utilize bit slices from those signals. This allows for multiple VIP instances with different widths and does not require parameterized classes to work with parameterized interfaces. The following code segments demonstrate these concepts.
First, we define the max footprint style interface. Note that this interface is not parameterized because this is the interface that the VIP code will interact with:
// Redefinable max footprint `ifndef MAX_DATA_WIDTH `define MAX_DATA_WIDTH 32 `endif interface max_footprint_if; logic clk; logic[`MAX_DATA_WIDTH-1:0] data_in; logic[`MAX_DATA_WIDTH-1:0] data_out; clocking active_cb @(posedge clk); default input #1 output #1; input data_in; output data_out; endclocking modport active_mp (clocking active_cb); endinterface typedef virtual max_footprint_if max_footprint_vif;
Next, the parameterized interface is defined which is used to wrap the max footprint interface:
interface param_if#(int width = 8); logic clk; logic[width-1:0] data_in; logic[width-1:0] data_out; max_footprint_if internal_if(); assign internal_if.clk = clk; // Z values driven on unused inputs of the max footprint assign internal_if.data_in = {`MAX_DATA_WIDTH'hz, data_in}; // Only selected output values used from the max footprint assign data_out = internal_if.data_out[width-1:0]; endinterface
After this, the VIP code is implemented to work with the max footprint interface rather than the parameterized interface. The one additional class shown below which is not shown in earlier posts is the configuration class which is used to define the signal width for each VIP instance. Another complication that this solution creates is that the VIP must use bitslicing techniques when sampling and driving signals. This is unfortunate, but SystemVerilog is fully equipped to handle this.
//======================================================================= class cust_cfg extends uvm_object; rand int data_width; constraint valid_data_width { data_width inside {8, 16, 32}; } … //======================================================================= class cust_driver extends uvm_driver#(cust_data); max_footprint_vif vif; cust_cfg cfg; `uvm_component_utils(cust_driver) function void build_phase(uvm_phase phase); if (!uvm_config_db#(max_footprint_vif)::get(this, "", "vif", vif)) `uvm_fatal("build", "A valid virtual interface was not received."); if (!uvm_config_db#(cust_cfg)::get(this, "", "cfg", cfg)) `uvm_fatal("build", "A valid configuration was not received."); … task consume_from_seq_item_port(); seq_item_port.get_next_item(req); vif.active_cb.prop_out <= ((req.prop <> (`MAX_DATA_WIDTH-cfg.data_width)); @(vif.active_cb); … task sample_signals(); bit[31:0] sampled_prop_in = ((vif.active_cb.prop_in <> (`MAX_DATA_WIDTH-cfg.data_width)); VM_LOW); @(vif.active_cb); … //======================================================================= class cust_agent extends uvm_agent; `uvm_component_utils(cust_agent) max_footprint_vif vif; cust_driver driver; function void build_phase(uvm_phase phase); if (!uvm_config_db#(max_footprint_vif)::get(this, "", "vif", vif)) `uvm_fatal("build", "A valid virtual interface was not received."); if (!uvm_config_db#(cust_cfg)::get(this, "", "cfg", cfg)) `uvm_fatal("build", "A valid configuration was not received."); uvm_config_db#(max_footprint_vif)::set(this, "driver", "vif", vif); uvm_config_db#(cust_cfg)::set(this, "driver", "cfg", cfg); driver = cust_driver::type_id::create("driver", this); …
Finally, the testbench code is presented. Testcases can access VIPs without parameterization, and the top level module where the interfaces are instantiated can work with parameterized interfaces. The additional step of creating a configuration object for each VIP instance is also shown (this is not an extra step, as this would have been needed with the earlier solutions as well but was left out for conciseness).
Utilizing max footprint style interfaces for VIP access to signals allows VIP code to be created without requiring that the VIP code be parameterized. Defining parameterized interfaces allow testbenches to take advantage of the improved integration capabilities that they enable. The strategy of using parameterized interfaces to wrap max footprint interfaces enables the benefits of both styles.
//======================================================================= class cust_test extends uvm_test; cust_cfg cfg8; cust_cfg cfg16; cust_cfg cfg32; cust_agent agent8; cust_agent agent16; cust_agent agent32; virtual function void build_phase(uvm_phase phase); cfg8 = new("cfg8"); cfg8.data_width = 8; uvm_config_db#(cust_cfg)::set(this, "agent8", "cfg", cfg8); agent8 = cust_agent::type_id::create("agent8", this); cfg16 = new("cfg16"); cfg16.data_width = 16; uvm_config_db#(cust_cfg)::set(this, "agent16", "cfg", cfg16); agent16 = cust_agent::type_id::create("agent16", this); cfg32 = new("cfg32"); cfg32.data_width = 32; uvm_config_db#(cust_cfg)::set(this, "agent32", "cfg", cfg32); agent32 = cust_agent::type_id::create("agent32", this); endfunction endclass //======================================================================= module test_top; param_if#(8) if8(); param_if#(16) if16(); param_if#(32) if32(); initial begin uvm_config_db#(max_footprint_vif)::set(uvm_root::get(), "uvm_test_top.agent8", "vif", if8.internal_if); uvm_config_db#(max_footprint_vif)::set(uvm_root::get(), "uvm_test_top.agent16", "vif", if16.internal_if); uvm_config_db#(max_footprint_vif)::set(uvm_root::get(), "uvm_test_top.agent32", "vif", if32.internal_if); run_test("cust_test"); end endmodule
Utilizing max footprint style interfaces for VIP access to signals allows VIP code to be created without requiring that the VIP code be parameterized. Defining parameterized interfaces allow testbenches to take advantage of the improved integration capabilities that they enable. The strategy of using parameterized interfaces to wrap max footprint interfaces enables the benefits of both styles.
Authored by Aron Pratt
You can get more information at Synopsys Verification IP and VC Verification IP Datasheet.