参数化接口和可重用VIP:第二部分

描述

在本系列的第一部分中,介绍了SystemVerilog接口的基本概念,并描述了这些接口的参数化给测试平台代码带来的问题。这篇文章将描述此问题的可能解决方法,但需要付出代价...

特洛伊木马:偷袭方法

虚拟接口不支持多态性,因为它们与静态设计元素相关联。但是,SystemVerilog 类确实支持多态性,这一事实可用于创建接口访问器类。

创建一个虚拟类,该类声明在 SystemVerilog 接口上执行特定操作的纯虚拟访问器方法。然后,参数化类扩展此类以执行对强类型接口的实际访问。VIP 代码仅与非参数化基访问器类交互,因此无需参数化 VIP 代码即可使用强类型接口。测试平台必须设置强类型访问器类并将其传递给 VIP,但在此之后,无需参数化 VIP 即可执行与 VIP 的所有交互。以下代码段演示如何进行此设置。

首先,我们定义参数化虚拟接口,该接口与上周代码段中使用的接口相同:

1
2
3
4
5
6
7
8
9
10
11
12
13
interface param_if#(int width = 8);
  logic clk;
  logic[width-1:0] data;
 
  clocking active_cb @(posedge clk);
    default input #1 output #1;
    output data;
  endclocking
 
  modport active_mp (clocking active_cb);
endinterface
 
typedef virtual param_if param_vif

接下来,我们定义非参数化接口访问器类和参数化以使用参数化接口的扩展类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//=======================================================================
virtual class port_wrapper extends uvm_object;
  pure virtual task do_write(logic[31:0] data);
  pure virtual task do_read(output logic[31:0] data);
  pure virtual task sync_driver_cb();
endclass
 
//=======================================================================
class port_wrapper_typed#(type vif_t=param_vif) extends port_wrapper;
  vif_t vif;
 
  function new(vif_t vif);
    this.vif = vif;
  endfunction
 
  virtual task do_write(logic[31:0] data);
    vif.active_cb.data <= data;
  endtask
 
  virtual task do_read(output logic[31:0] data);
    data = vif.active_cb.data;
  endtask
 
  virtual task sync_driver_cb();
    @(vif.active_cb);
  endtask
 
endclass

在此之后,实现 VIP 代码以使用此访问器类,而不是直接访问虚拟接口。下面的代码段展示了驱动程序和代理类,并演示了必须使用访问器方法的各个位置。关于此VIP代码需要注意的一件事是,它通过配置数据库而不是接口接受非参数化访问器类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  //=======================================================================
  class cust_driver extends uvm_driver#(cust_data);
    `uvm_component_utils(cust_driver)
 
    port_wrapper port;
 
    task consume_from_seq_item_port();
      forever begin
        seq_item_port.get_next_item(req);
        port.do_write(req.data);
        port.sync_driver_cb();
    task sample_signals();
      forever begin
        bit[31:0] sampled_data;
 
        port.do_read(sampled_data);
        port.sync_driver_cb();
    function void build_phase(uvm_phase phase);
      if (!uvm_config_db#(port_wrapper)::get(this, "", "port", port))
        `uvm_fatal("build", "A valid port wrapper was not received.");
 
  //=======================================================================
  class cust_agent extends uvm_agent;
    `uvm_component_utils(cust_agent)
 
    cust_driver driver;
 
    function void build_phase(uvm_phase phase);
      port_wrapper port;
 
      if (!uvm_config_db#(port_wrapper)::get(this, "", "port", port))
        `uvm_fatal("build", "A valid port wrapper was not received.");
 
      uvm_config_db#(port_wrapper)::set(this, "driver", "port", port);
      driver = cust_driver::type_id::create("driver", this);

  最后,给出了测试平台代码。测试用例可以在不参数化的情况下访问 VIP,但实例化接口的顶级模块确实必须实现一些额外的步骤。它不仅必须实例化参数化接口,还必须创建参数化接口访问器类并将其传递给 VIP 实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//=======================================================================
class cust_test extends uvm_test;
  `uvm_component_utils(cust_test)
 
  cust_agent agent8;
  cust_agent agent16;
  cust_agent agent32;
 
  virtual function void build_phase(uvm_phase phase);
    agent8 = cust_agent::type_id::create("agent8", this);
    agent16 = cust_agent::type_id::create("agent16", this);
    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
    port_wrapper_typed#(virtual param_if#(8)) port_wrapper8;
    port_wrapper_typed#(virtual param_if#(16)) port_wrapper16;
    port_wrapper_typed#(virtual param_if#(32)) port_wrapper32;
 
    port_wrapper8 = new(if8);
    port_wrapper16 = new(if16);
    port_wrapper32 = new(if32);
 
    uvm_config_db#(port_wrapper)::set(uvm_root::get(), "uvm_test_top.agent8", "port", port_wrapper8);
    uvm_config_db#(port_wrapper)::set(uvm_root::get(), "uvm_test_top.agent16", "port", port_wrapper16);
    uvm_config_db#(port_wrapper)::set(uvm_root::get(), "uvm_test_top.agent32", "port", port_wrapper32);
 
    run_test("cust_test");
  end
endmodule

如您所见,此解决方案确实解决了参数化接口的使用模型问题。创建正确类型的端口包装器需要一些额外的代码,但是测试平台编写者可以访问使用不同专用接口的代理,而无需知道这些接口是如何专用的。

但是,此解决方案对 VIP 如何使用虚拟接口施加了严格的限制。驱动程序和监视器类不能直接与接互,而必须将这些访问推迟到强类型包装类。通过将驱动程序和监视器类的大部分功能移动到强类型包装类,可以抵消这种生产力损失;然而,这种更复杂的结构可能会令人困惑且不太灵活。上面的代码段仅显示了驱动程序类可能需要的几个访问方法,但在实际环境中,此列表可能会增长许多倍,并且监视器类也需要完整的访问器方法。

经过反思,处理参数化接口的蛮力方法和特洛伊木马方法都不是理想的。使用前一种方法,向类添加参数会使 VIP 的使用模型复杂化。在后面的方法中,必须提前规划端口包装类支持的访问方法,并且对虚拟接口的 VIP 访问受到限制。如果 VIP 代码可以在不实际使用参数化接口的情况下启用类似于参数化接口的功能,该怎么办?

审核编辑:郭婷

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分