1.1 Interface Connection
1.1.1 DUT I/O Interfafce Connection
DUT I/O interfaces need to be connected to the virtual interfaces in the testbench components. This is normally achieved with the following steps:
1. Declare interfaces in the top module where DUT is instantiated
2. Connect the interfaces to DUT interfaces when instantiating the DUT
3. Assign the interfaces to the virtual interfaces in testbench components
The step1 and 2 are very straightforward. Below we talk about the step 3.
As mentioned in “5.2.7 Virtual Interface Assignment”, we would like to separate the DUT hierarchy from the testbench hierarchy. In other words, we should be able to instantiate a VIP and connect it to the design interface within different design hierarchy with the same virtual interface setting method. This can be achieved by using the uvm_config_prop_pkg described in the book “A Practical Guide to Adopting the Universal Verification Methodology (UVM)”[Page 107].
The following utility is the uvm_config_prob_pkg:
1. package uvm_config_prop_pkg;
2. import uvm_pkg::*;
3. class uvm_config_prop #(type T=int) extends uvm_object;
4. T value;
5. function new(T value);
6. this.value = value;
7. endfunction
8.
9. static function void set_config(string target,
10. string field,
11. T value,
12. uvm_component cntxt=null);
13. uvm_config_prop#(T) wrapper = new(value);
14. if(cntxt == null) cntxt = uvm_root::get();
15. cntxt.set_config_object(target, field, wrapper, 0);
16. endfunction: set_config
17. static function bit get_config(uvm_component cntxt,
18. string field,
19. output T value);
20. uvm_config_prop#(T) wrapper;
21. uvm_object obj;
22. if(cntxt == null) cntxt = uvm_root::get();
23. if(!cntxt.get_config_ojbect(field, obj, 0)) return 0;
24. if(!$cast(wrapper, obj)) return 0;
25. value = wrapper.value;
26. return 1;
27. endfunction
28. static function bit get_config_object(uvm_component cntxt,
29. string field,
30. inout T value, bit clone=0);
31. uvm_object obj;
32. T tmp;
33. if(cntxt == null) cntxt = uvm_root::get();
34. if(!cntxt.get_config_ojbect(field, obj, clone)) return 0;
35. if(!$cast(tmp, obj)) return 0;
36. value = tmp;
37. return 1;
38. endfunction
39. endclass
40. endpackage
Line 3: Wrapper class for configuring a single standalone object. The object can be any type that can be passed as an input to and output from a function. This allows things like virtual interfaces, dynamic arrays, and so on to be used as configuration properties. The setter and the getter must agree on the type being passed, and the type must match exactly. There are two static functions.
Line 9: The set_config() function has the same signature as the various set_config_* functions (an option context is available if the method is being used from inside of a component context). This is needed to enable a higher level component to override a sub-component settings.
Line 17: The get_config() function has a similar signature to the get_config_* functions, but requires that a context e provided. The context is the component fetching the value from the configuration table.
Line 28: The wrapper also provides a simplified get_config_object interface so that the user doesn’t have to recreate a temporary object and do a cast in order to get a configuration object.
The following code shows the usage of the uvm_config_prop_pkg in the X_DUT VIP:
1. class x_dut_apb_vif_bdl extends uvm_object;
2. virtual apb_if apb_vif;
3. virtual p_bus_if p_bus_vif;
4. endclass
5. class x_dut_ahb_vif_bdl extends uvm_object;
6. virtual ahb_if ahb_vif;
7. virtual h_bus_if h_bus_vif;
8. endclass
9. class x_dut_axi_vif_bdl extends uvm_object;
10. virtual axi_if axb_vif;
11. virtual x_bus_if x_bus_vif;
12. endclass
// Bundle class that holds all the virtual interfaces
// for x_dut I/O ports
13. class x_dut_io_vif_bdl extends uvm_object;
14. virtual apb_if apb_vif;
15. virtual p_bus_if p_bus_vif;
16. virtual ahb_if ahb_vif;
17. virtual h_bus_if h_bus_vif;
18. virtual axi_if axb_vif;
19. virtual x_bus_if x_bus_vif;
20. endclass
21. module x_dut_tb_top; // X_DUT Testbench Top Module
22. import uvm_config_prop_pkg::*;
23. …
24. apb_if apb_if0(p_clock, reset); // interfafce instance
25. p_bus_if p_bus_if0;
26. ahb_if ahb_if0(h_clock, reset);
27. h_bus_if h_bus_if0;
28. axi_if axi_if0(x_clock, reset);
29. x_bus_if x_bus_if0;
30. x_dut_io_vif_bdl io_vif_bdl
31. X_DUT u_x_dut ( … );// assign interfaces to DUT IO
32. …
33. initial begin
// assign interfaces to the vif bundle object
34. io_vif_bdl = new();
35. io_vif_bdl.apb_if0 = apb_if0;
36. io_vif_bdl.p_bus_if0 = p_bus_if0;
37. io_vif_bdl.ahb_if0 = ahb_if0;
38. io_vif_bdl.h_bus_if0 = h_bus_if0;
39. io_vif_bdl.axi_if0 = axb_if0;
40. io_vif_bdl.x_bus_if0 = x_bus_if0;
// set_config the vif bundle object to x_dut_env
41. uvm_config_prop#(x_dut_io_intf_bdl)::
set_config(“uvm_test_top.x_dut_tb.x_dut_env”, “x_dut_io_vif_bdl”, io_vif_bdl);
42. end
43. initial begin
44. #1 run_test();
45. end
46. endmodule: x_dut_tb_top
47. // x_dut_env: build() method
48. function void x_dut_env::build();
49. super.build();
50. void`(uvm_config_prop #(x_dut_io_vif_bdl)::get_config(this,”x_dut_io_vif_bdl”, io_vif_bdl));
51. // Call get_config() to unwrap the container, and assign the
52. // io_vif_bdl in x_dut_tb_top to the io_vif_bdl in x_dut_env.
53. // allocate virtual interfaces to sub-boundles.
54. apb_vif_bdl.apb_vif = io_vif_bdl.apb_vif;
55. apb_vif_bdl.p_bus_vif = io_vif_bdl.p_bus_vif;
56. ahb_vif_bdl.ahb_vif = io_vif_bdl.ahb_vif;
57. ahb_vif_bdl.h_bus_vif = io_vif_bdl.h_bus_vif;
58. axi_vif_bdl.axi_vif = io_vif_bdl.axi_vif;
59. axi_vif_bdl.x_bus_vif = io_vif_bdl.x_bus_vif;
60. if(io_vif_bdl != null)begin
61. uvm_config_prop #(apb_vif_bdl)::set_config(“apb_agent”,”apb_vif_bdl”, apb_vif_bdl, this);
62. // This static function call creates a wrapper class and
63. // set_config that class to
64. // uvm_test_top.x_dut_tb.x_dut_env.apb_agent.
65. uvm_config_prop #(ahb_vif_bdl)::set_config(“ahb_agent”,”ahb_vif_bdl”, ahb_vif_bdl, this);
66. uvm_config_prop #(axi_vif_bdl)::set_config(“axi_agent”,”axi_vif_bdl”, axi_vif_bdl, this);
67. end
68. endfunction: build
69. // x_dut_env: run() task (OPTIONAL)
70. function void x_dut_env::run();
71. if(io_vif_bdl == null) `uvm_fatal (“NOVIF”, “Virtual interfaces have not been set!”)
72. endtask: run
73. // apb_agent: build() method
74. function void apb_master_agent::build();
75. super.build();
76. void`(uvm_config_prop #(apb_vif_bdl)::get_config(this, “apb_vif_bdl”, apb_vif_bdl));
77. // Call get_config() to unwrap the container, and assign the
78. // apb_vif_bdl to the apb_vif_bdl in x_dut_env.apb_agent.
79. if(apb_vif_bdl != null)
80. uvm_config_prop #(virtual apb_if)::set_config(“apb_drv”, “apb_vif”, apb_vif_bdl.apb_vif, this);
81. // This static function call creates a wrapper class and
82. // set_config that class to
83. // uvm_test_top.x_dut_tb.x_dut_env.apb_agent.apb_drv.
84. …
85. endfunction: build
86. // apb_drv: build() method
87. function void apb_master_driver::build();
88. super.build();
89. void`(uvm_config_prop #(virtual apb_if)::get_config(this, “apb_vif”, apb_vif));
90. // Call get_config() to unwrap the container, and assign the
91. // apb_vif to the apb_vif in x_dut_env.apb_agent.apb_drv.
92. endfunction: build
93. // ahb_agent: build() method
94. function void ahb_master_agent::build();
95. super.build();
96. void`(uvm_config_prop #(ahb_vif_bdl)::get_config(this, “ahb_vif_bdl”, ahb_vif_bdl));
97. if(ahb_vif_bdl != null)
98. uvm_config_prop #(virtual ahb_if)::set_config(“ahb_drv”, “ahb_vif”, ahb_vif_bdl.ahb_vif, this);
99. …
100. endfunction: build
101. // ahb_drv: build() method
102. function void ahb_master_driver::build();
103. super.build();
104. void`(uvm_config_prop #(virtual ahb_if)::get_config(this, “ahb_vif”, ahb_vif));
105. endfunction: build
106. // axi_agent
107. …
108. // axi_drv
109. …
1.1.2 DUT Internal Interface Connection
When a VIP is reused at different hierarchical environment, especially when integrating a module VIP into a subsystem or chip level environment, we expect the signal connection to the DUT internal singal can be done by simply prepending HDL path to signal names instead of rewriting all the signal connection code.
As for signal connection between VIP and DUT’s in/out ports, we would not do this because we should check the signal connection in a “higher level DUT” by reconnecting the VIP’s interface signal to the internal signals in this “higher level DUT”. This is to make sure the DUT’s in/out ports are connected correctly when instantiated.
Let’s consider the following example:
Module PCE is the DUT. It has an internal interface pce_intf_c, which is defined as follows (in PCE block level verification environment):
interface pce_intf_c(input bit clk);
logic sig0;
logic sig1;
clocking aclk @(posedge clk);
output sig0;
input sig1;
endclocking: aclk
clocking pclk @(posedge clk);
output sig0;
input sig1;
endclocking: pclk
modport drv(clocking aclk);
modport mon(clocking pclk);
endinterface: pce_intf_c
Figure 7 Block Level Interface Signal Connection
Figure 7 shows the PCE verification environment. The top module is pce_tb_top, which instances DUT PCE (as u_pce) and interface pce_intf_c .
First we need to connect this interface instance to u_pce’s internal interface pce_intf_c.
Then connect this interface instance to the virtual interface pce_vif_c in the module level VIP pce_env(uvm_env) instanced in pce_tb(uvm_env).
We use the following macro to do the first step connection:
// For each internal interface, define a *_conn macro
`define pce_intf_c_conn(intf_name, hdl_path) \
pce_intf_c intf_name(hdl_path.clk) ; \
assign intf_name.sig0 = hdl_path.sig0; \
assign intf_name.sig1 = hdl_path.sig1;
// Provide a single macro that contains all internal interface
// connection macros.
`define pce_intf_conn(module_name, hdl_path) \
`pce_intf_c_conn( module_name``_intf_c, hdl_path)
// other types of PCE internal interface connect
The above macro are used in pce_tb_top as follows:
module pce_tb_top();
logic intfa_in, intfa_out, intfb_in, intfb_out;
logic clk;
pce_intf_c pce_intf_c_inst(clk);
…….
PCE u_pce(…);
`pce_intf_conn( pce , u_pce )
// at this point, we have u_pce’s internal interface pce_intf_c
// is connected to interface instance “pce_intf_c
endmodule
Now suppose we have Module DBM as the DUT, and it instances two PCEs: u_pce0 and u_pce1. Figure 8 shows the top module (dbm_tb_top) of DBM level verification environment. dbm_tb(uvm_env) has two pce_env(uvm_env) instances.
Figure 8 Interface Signal Connection when Block is Integrated into a Higher Level
We need to connect the two pce_intf_c instances in dbm_tb_top module to the pce_intf_c interface in each u_pce. We would like to reuse the PCE interface signal connection definition in PCE verification environment. Below is the interface signal connection macro at DBM level and how the macro is used in dbm_tb_top module:
`define dbm_intf_conn(module_name, hdl_path) \
`pce_intf_conn(module_name``_pce0, hdl_path.u_pce0) \
`pce_intf_conn(module_name``_pce1, hdl_path.u_pce1) \
// other dbm level internal interface definition
module dbm_tb_top();
// signal definitions
// interfaces
pce_intf_c dbm_pce0_intf_c, dbm_pce1_intf_c;
…
DBM u_dbm(…);
`dbm_intf_conn(dbm, u_dbm)
// other dbm external interface definition
// dbm external interface connection
// at this point, we have u_dbm.u_pce0’s pce_intf_c connected
// to interface instance “dbm_pce0_intf_c”, and u_dbm.u_pce1’s
// pce_intf_c connected to interface instance “dbm_pce1_intf_c”.
// “dbm_pce0_intf_c” and “dbm_pce1_intf_c” can be passed
// (as virtual interface parameters) to pce_env0 and pce_env1
// respectively
endmodule
With the interface instanced in the testbench top module connected to DUT internal signals, next we will need to connect/assign the interface to the virtual interface inside the env. In the above DBM example, we have two interfaces (dbm_pce0_intf_c and dbm_pce1_intf_c) need to be passed down to dbm_env and further down to pce_env0 and pce_env1 respectively.
But, suppose we have a large number of interfaces at DBM level need to be connected to the virtual interfaces in dbm_env, we may pass these interfaces as virtual interface parameters to dbm_env. OR, we can define a virtual interface bundle class first, assign those interfaces at dbm_tb_top to the virtual interface bundle object first, then pass the virtual interface bundle object as parameters to dbm_env using set_config/get_config approach, which is similar to what described in “6.10.1 DUT I/O Interfafce Connection”.
In PCE block level environment, suppose we need to pass down virtual interfaces to monitor and driver respectively. We define the following class to include all pce_env required virtual interface.
class pce_vif_bundle;
virtual pce_intf_c.mon pce_vif_c_mon;
virtual pce_intf_c.drv pce_vif_c_drv;
virtual pce_intf_b.mon pce_vif_b_mon;
virtual pce_intf_b.drv pce_vif_b_drv;
endclass
In the DBM level where PCE is instanced twice. Suppose pce_intf_a is the PCE external interface and is internal to DBM. In addition to pce_intf_a at DBM level, we need to include the PCE internal interface pce_intf_c in the DBM level interface bundle class. Here we list all the interface and/or bundles required for DBM env in dbm_vif_bundle class:
class dbm_vif_bundle;
virtual pce_intf_a.mon dbm_pce0_intfa_mon;
virtual pce_intf_a.mon dbm_pce1_intfa_mon;
pce_vif_bundle pce0_vif_bdl;
pce_vif_bundle pce1_vif_bdl;
function new();
pce0_vif_bdl = new();
pce1_vif_bdl = new();
endfunction
endclass
When dbm_env get_config the virtual interface bundle object, it assigns the PCE internal virtual interface bundle and set_config it to pce_env0 and pce_env1.
没有评论:
发表评论