SV系统集成篇之五(终):初论环境的复用性

本文转自:http://www.eetop.cn/blog/html/28/1561828-2331492.html

伴随着模块验证和MCDF设计的子系统应用,在最终的芯片级验证中,MCDF会跟其它的模块一并组合在一起形成一个统一的芯片级验证环境。在之前的底层验证过程中,主要将功能侧重于模块或者子系统一级,而在芯片级验证中则侧重于子系统之间的连接和互动测试上面。伴随着验证系统的逐层提高,我们的验证环境也会发生变化,这一点可以在之前的《验证环境的组装》中得到说明。但是不管验证什么子系统,它的验证环境都会包括stimulator、monitor、checker等。为了最大限度的降低验证环境构建的工作量,我们期望可以用于便于复用的测试组件(《灵活化的配置》)和易于组合调度的测试向量(《测试场景的生成》)。有了可复用的验证组件,这会在减少重复工作的同时,补充验证师们在验证场景构成和检查的时间。

重用的策略

重用的好处就是避免“重复制造轮子”,而是用车去做更紧迫更有效的事情,让验证团队减低重复开发验证环境的成本,多投入到验证场景和检查中去。从MCDF的例子来看,一个有规模的公司在同一时间段很有可能运行两个以上的项目,如果MCDF的设计复用性好,那么设计的集成时间是很快的,而这对于验证的要求就更紧迫了。

在上面的示图中,芯片A和芯片B的结构中都会集成MCDF,在芯片A中的MCDF slave接口是三个,输入数据位宽是32位,对应着之前的设计,而在芯片B中的MCDF slave接口是四个,输入数据位宽是64位。那么芯片B的设计组如果已经拥有一个参数化的设计,那么对于调整slave接口数量和数据位宽不会是难事,而一旦MCDF的接口发生了变化,这对验证环境也会带来新的要求。在芯片B中MCDF的接口变化,会要求MCDF子系统验证的环境也要相应做调整,如果像设计一样使得环境可以参数化配置,做出快速的响应和验证检查,那么在不同项目之间,验证环境的迁移就可以完成水平复用了。

而无论是在芯片A中MCDF设计,还是芯片B中的MCDF设计,一旦集成到芯片系统结构中,那么它的接口即完全“浸入”在芯片结构中,原来的MCDF子系统环境不再需要提供任何激励了。那么MCDF的子系统验证环境是否在芯片一级中还有用武之地,可以继续发挥它的功能来检查MCDF自身功能呢?

这就需要考虑从低一层的验证环境到高一层的验证环境复用性了,即垂直复用。

在之前《验证环境的组装》中我们讨论了通过agent组件来组装stimulator和monitor的必要性,这会使得模块级组件的初衷复用可以跨接到MCDF子系统一级。类似地,我们也可以同参考《灵活化的配置》一节中通过绝对结构化的变量来完成MCDF从子系统一级到芯片一级的垂直复用,让MCDF的子系统验证环境在芯片级验证中继续发挥它的作用。

下面的两段例码用来初步讨论环境在水平复用和垂直复用方向上的一些考虑,希望对读者有一点启发。在后面的篇章中我们也会深入讨论验证环境的可复用性。

水平复用的应用

下面的这段代码是经过简化后的,以此来说明MCDF中的模块arbiter在MCDF的slave接口从芯片A中的3个变化为芯片B中的4个,以及数据宽度从32位拓宽为64位的水平复用环境。

package mcdf_global_pkg;

parameter data_width_p = 64;

parameter slave_num_p = 4;

endpackage

interface arb_ini_if;

parameter data_width_p = mcdf_global_pkg::data_width_p;

logic [data_width_p-1 :0] data;

endinterface

interface arb_rsp_if;

parameter data_width_p = mcdf_global_pkg::data_width_p;

logic [data_width_p-1 :0] data;

endinterface

首先是定义了MCDF的参数包mcdf_global_pkg,其中规定了数据的宽度为64,MCDF slave接口数目是4。

接下来是对arbiter的两个接口arb_ini_if和arb_rsp_if的定义,关于信号端口data的宽度通过参数进行位宽的可变控制,且来自于mcdf_global_pkg::data_width_p。

package arb_pkg;

import mcdf_global_pkg::*;

class arb_ini_trans; endclass

class arb_ini_stm;

virtual interface arb_ini_if #(.data_width_p(data_width_p)) vif;

task run(); endtask

endclass

class arb_ini_mon;

virtual interface arb_ini_if #(.data_width_p(data_width_p)) vif;

mailbox #(arb_ini_trans) mb;

task run(); endtask

endclass

class arb_ini_agent;

virtual interface arb_ini_if #(.data_width_p(data_width_p)) vif;

arb_ini_stm stm;

arb_ini_mon mon;

function new();

stm = new();

mon = new();

endfunction

task run(); endtask

function void assign_vi(virtual interface arb_ini_if #(.data_width_p(data_width_p)) intf);

vif = intf;

stm.vif = intf;

mon.vif = intf;

endfunction

endclass

class arb_rsp_trans; endclass

class arb_rsp_stm;

virtual interface arb_rsp_if #(.data_width_p(data_width_p)) vif;

task run(); endtask

endclass

class arb_rsp_mon;

virtual interface arb_rsp_if #(.data_width_p(data_width_p)) vif;

mailbox #(arb_rsp_trans) mb;

task run(); endtask

endclass

class arb_rsp_agent;

virtual interface arb_rsp_if #(.data_width_p(data_width_p)) vif;

arb_rsp_stm stm;

arb_rsp_mon mon;

function new();

stm = new();

mon = new();

endfunction

task run(); endtask

function void assign_vi(virtual interface arb_rsp_if #(.data_width_p(data_width_p)) intf);

vif = intf;

stm.vif = intf;

mon.vif = intf;

endfunction

endclass

class arb_checker;

mailbox #(arb_ini_trans) ini_mb[slave_num_p];

mailbox #(arb_rsp_trans) rsp_mb;

function new();

foreach(ini_mb[i]) ini_mb[i] = new();

rsp_mb = new();

endfunction

function void connect(); endfunction

task run(); endtask

endclass

class arb_env;

arb_ini_agent arb_ini_agt[slave_num_p];

arb_rsp_agent arb_rsp_agt;

arb_checker chk;

function new();

foreach(arb_ini_agt[i]) arb_ini_agt[i] = new();

arb_rsp_agt = new();

chk = new();

endfunction

function void connect();

foreach(arb_ini_agt[i]) begin

arb_ini_agt[i].mon.mb = chk.ini_mb[i];

end

arb_rsp_agt.mon.mb = chk.rsp_mb;

endfunction

task run(); endtask

endclass

endpackage

上面的arb_pkg中定义了各个组件,而最后arb_env将各个组件进行了例化和连接。值得注意的是,由于接口arb_ini_if和arb_rsp_if是参数化的接口,所以在各个组件中声明虚接口时,应该使用的是

  • virtual interface arb_ini_if #(.data_width_p(data_width_p)) vif
  • virtual interface arb_rsp_if #(.data_width_p(data_width_p)) vif

来声明,而不是

  • virtual interface arb_ini_if vif
  • virtual interface arb_rsp_if vif

因为SV关于虚接口传递的规定是,传递的接口类型必须严格一致,如果是参数接口,则虚接口的类型中也应该表明接口的参数传递值与随后被传入的物理接口的参数值是一致的。

再来看看由于MCDF slave接口数量的变化导致的arbiter输入端口组数的变化。这一变化使得arbiter arb_env中的arb_ini_agt[slave_num_p]和arb_checker中的ini_mb[slave_num_p]也变为参数化了,并且在arb_env中monitor与checker的通信管道连接也通过foreach完成了全部连接。

module arb_tb;

import arb_pkg::*;

event build_end_e;

event connect_end_e;

arb_env env;

arb_ini_if arb_ini_if0();

arb_ini_if arb_ini_if1();

arb_ini_if arb_ini_if2();

arb_ini_if arb_ini_if3();

arb_rsp_if arb_rsp_if();

... // DUT例化

initial begin : build

env = new();

-> build_end_e;

end

initial begin : connect

wait(build_end_e.triggered());

env.arb_ini_agt[0].assign_vi(arb_ini_if0);

env.arb_ini_agt[1].assign_vi(arb_ini_if1);

env.arb_ini_agt[2].assign_vi(arb_ini_if2);

env.arb_ini_agt[3].assign_vi(arb_ini_if3);

env.arb_rsp_agt.assign_vi(arb_rsp_if);

env.connect();

->connect_end_e;

end

initial begin : run

wait(connect_end_e.triggered());

fork

env.run();

join_none

end

endmodule

最后看看arb_env在顶层arb_tb中的例化和连接关系。在例化了4个arb_ini_if接口之后,需要在connect过程语句块中连接对应的接口到正确的agent。而其它的非参数化的例化和连接则不需要做出变动。从上面的arb_tb实现中,完成了从之前的芯片A MCDF arbiter模块环境到芯片B MCDF arbiter模块环境的水平复用。与之类似地,我们相信可以在MCDF子系统一级的验证环境中做出同样的水平复用,使得在芯片A到芯片B的MCDF复用中,不但设计可以很好地完成复用,对于验证环境也可以贡献同样的复用价值。

垂直复用的应用

下面的例码是从之前《验证环境的组装》中移植过来的代码,经过稍加改善,我们已经可以使得mcdf_env的环境结构同时支持MCDF_TB模式(MCDF子系统验证)和CHIP_TB模式(芯片系统验证)两种模式。做的更新在于添加模式变量mcdf_env::tb_mode,并且在例化mcdf_env的时候传递模式值。如果是MCDF_TB模式,那么会在其后对子组件的例化中,令子组件都变为active模式;如果是CHIP_TB模式,那么会在子组件的例化中,让其也都例化为passive模式。

通过这种层次化的模式传递,可以在顶层例化时,灵活地控制整体的环境结构。而在后面的connect()和run()阶段则无需做出更新,因为对于mcdf_env子组件而言,各个agent会在内部进一步基于active模式还是passive模式对自身的结构做出调整。

typedef enum {MCDF_TB, CHIP_TB} tb_mode_t;

class mcdf_env;

regs_ini_agent regs_ini_agt;

slv_ini_agent slv_ini_agt[3];

fmt_rsp_agent fmt_rsp_agt;

mcdf_checker chk;

tb_mode_t tb_mode;

virtual interface mcdf_if vif;

function new(tb_mode_t m = MCDF_TB);

bit active_mode;

tb_mode = m;

if(m == MCDF_TB)

active_mode = 1;

else

active_mode = 0;

regs_ini_agt = new(.mod(active_mode));

foreach(slv_ini_agt[i]) slv_ini_agt[i] = new(.mod(active_mode));

fmt_rsp_agt = new(.mod(active_mode));

chk = new();

endfunction

function void connect();

chk.connect();

regs_ini_agt.mon.mb = chk.regs_ini_mb;

foreach(slv_ini_agt[i]) slv_ini_agt[i].mon.mb = chk.slv_ini_mb[i];

fmt_rsp_agt.mon.mb = chk.rsp_mb;

endfunction

task run();

fork

regs_ini_agt.run();

foreach(slv_ini_agt[i]) slv_ini_agt[i].run();

fmt_rsp_agt.run();

chk.run();

join_none

endtask

function void assign_vi(virtual interface mcdf_if intf);

vif = intf;

chk.vif = intf;

endfunction

endclass

至此,关于SV的系统集成篇就结束了。在这一篇中,读者懂得了package的意义,懂得如何集簇底层模块的package。在整理了各个模块环境的之后,将其各个模块环境的组件通过MCDF子系统环境的组织可以进一步搭建成为子系统验证结构。有了房子之后,如何“通水通电”,让这个大房子能够正常运转,则是要考虑测试场景如何生成和调度的问题。最后,考虑到验证环境的水平复用和垂直复用,我们需要引入更灵活的结构变量和模式变量来提供便捷的配置方式。

猜你喜欢

转载自blog.csdn.net/qq_41394155/article/details/83446970