将VMM方法应用于工业控制系统验证
📑 目录
- 1. **将VMM应用于工业控制系统验证**
- 2. **目录**
- 3. **1. 入门准备**
- 4. - 解释待测设计的关键特性
- 5.  with"创建,因此您可以指定特
- 16. ```systemverilog
- 17. ```systemverilog
- 18. **7. 评估工作:验证结果**
- 19. **8. 结论**
- 20. **9. 致谢**
将VMM方法应用于工业控制系统验证
会议: SNUG Boston 2008
作者: Iman Abdo, Ryan Yuan Chen (Patni Americas)
页数: 20
源文件: SNUG_2008_Boston_Abdo_Applying_VMM_to_an_industrial_control_system_paper.pdf
Page 1
将VMM应用于工业控制系统验证
Iman Abdo, Ryan Yuan Chen Patni Americas Iman.Abdo@patni.com, Ryan.Chen@patni.com
摘要
本文详细介绍了一个高度可扩展的多FPGA工业控制器验证中使用的一些机制。描述了VMM 验证方法学手册如何使在随机环境和定向测试用例中执行动态检查变得更加容易。
通过分析具体示例,本文还展示了回调在检查和传递数值到系统各点时的强大功能。最后总结了本次验证项目过程中遇到的问题和经验教训。
Page 2
目录
1. 入门准备 ...................................................................................... 3 2. 奠定基础:扩展VMM ............................................................... 4 3. 问题引入:设计 .......................................................................... 6 4. 提供灵活性:回调 ...................................................................... 7 5. 制造混沌:约束随机测试 ........................................................ 12 6. 精确聚焦:定向测试 ................................................................ 14 7. 评估工作:验证结果 ................................................................ 18 8. 结论 ............................................................................................ 20 9. 致谢 ............................................................................................ 20 10. 参考文献 .................................................................................... 20
图表目录
图1:设计的简化视图,显示三个PM和多个IOM 图2:PVM VIP结构
Page 3
1. 入门准备
在开始分析待测设计之前,我们需要完成所有基于VMM 验证方法学手册项目共有的一些任务。我们首先搭建了验证环境的基础设施。新开发的Patni验证方法学(PVM)类库扩展了VMM 验证方法学手册类库。
完成这些元素创建后,我们查看了待测设计的具体细节。该特定系统由三片独立的FPGA组成,包括一个可最多实例化三次的处理器模块(PM)和最多可实例化四十八次的输入输出模块(IOM)。
验证该系统的主要挑战包括:
- 全系统的规模 - 测试三个冗余处理器模块在从完全同步到不连贯再到完全不同步的各种情况下的表现 - 复杂的数据流配置,包括: - 一个源到多个不同目的地 - 多个源到一个目的地 - 命令解码与实际执行之间没有固定的时间延迟
本文试图:
Page 4
- 解释待测设计的关键特性 - 详细说明我们的VMM 验证方法学手册实现 - 展示架构如何帮助检查机制和定向测试 - 通过代码示例说明实现过程
2. 奠定基础:扩展VMM
我们基于VMM 验证方法学手册的环境基础设施是在一个自定义扩展中创建的,我们称之为Patni验证方法学(PVM)。PVM旨在进一步定义VMM 验证方法学手册以满足我们的目的,使之可用于本项目及未来的项目。
该环境的基本元素是验证知识产权(VIP),它是对协议从上到下的行为实现,完全独立且可在其他项目中复用。它是从低级到高级的协议接口实现。一个示例(为本项目创建)是高级数据链路控制(HDLC) VIP。从VMM 验证方法学手册扩展而来的VIP结构包括:
- 驱动器和驱动器回调类 - 监视器和监视器回调类 - 事务处理器类 - 生成器类 - 事务类 - VIP顶层类(实例化上述各项) - 配置类
基于VMM 验证方法学手册,为系统中创建的每个VIP设计了以下结构:
Page 5
- 事务类:扩展vmm_data类,定义一些我们所有事务共有的属性,无论VIP类型如何。 - CFG类:配置类用于改变VIP的配置。这在我们的系统中已被证明非常有用。处理器模型与PM通信。PM然后用非归零反转码(NRZI)对命令进行编码并传递到IM/OM。通过允许NRZI编码作为可选配置参数,我们在所有三个环境中复用了相同的处理器模型,只是配置不同。同样,处理器模型的接收端在PM上解码多达24个通道,在IM/OM上仅解码一个通道。CFG类包含一个设置设计中通道数量的参数。 - 生成器类:基于VMM生成器的最简单代码扩展vmm_xactor类。我们的扩展包括定向测试和操控生成的随机流的占位任务。
Page 6
- 事务处理器类:扩展vmm_xactor类,允许在生成器创建数据流之后、在总线上实际驱动之前对数据流进行操控。初始化序列、地址和数据的操控或目标通道也在这一级完成。 - 驱动器类:扩展vmm_xactor类,在连接到DUT的实际物理接口上驱动事务。 - 驱动器回调:扩展vmm_xactor_callbacks类,提供预/后执行和预/后观察的基本任务。 - 监视器类:扩展vmm_xactor类,被动监视总线。 - 监视器回调:扩展vmm_xactor_callbacks类,允许观察监视器从总线上解码出来的内容。
3. 问题引入:设计
考虑到系统规模,第一个需要关注的挑战是性能。我们意识到在每个设计模块周围实例化整个测试平台会严重影响仿真器性能,因为系统包含3个PM和48个IOM共51个RTL模块,以及每个模块周围的测试平台。
为了更快的仿真,我们为每个RTL设计模块创建一个类似系统的环境,以及验证每个设计所需的检查机制,如记分板、监视器等。我们的系统在测试平台中仅实例化1个RTL模块。我们还致力于最大化复用,以确保环境之间的连续性。
另一个挑战是集成检查机制。PM用于与IOM通信的协议要求每个命令都要有一个响应确认,这可能会同时导致:
- IOM内部的寄存器被设置 - 在多个不同接口上启动事务(即一个源到多个不同目的地) - 从多个不同接口和/或内部寄存器读取数据(即多个源到一个目的地)
此外,这些命令的实际执行没有时间限制,因为它取决于命令接收时RTL的状态。
Page 7
4. 提供灵活性:回调
回调是未来代码定制的占位符,在实现我们的检查机制和定向测试用例中被证明是无价的。
通过使用回调,检查机制不会侵入VIP代码,使代码保持简单和可读。它们还允许我们根据回调的扩展清晰地定义检查功能。
回调的策略性定位在实现所有仿真中后台持续运行的检查机制中至关重要。回调允许同一VIP的功能在同一仿真中多次扩展,并用于实现以下检查机制:
回调用例1:命令/响应检查
回调在测试平台的PM模型中实例化。一个在命令被驱动到总线上之后使用(post_ex_trans),另一个在命令在总线上解码之后使用(post_obs_trans)。两个回调都被扩展以将结果事务存储在队列中,由记分板使用来比较实际响应和预期响应。参见示例1.1、1.2和1.3。
示例1.1:从VIP内部调用的发送回调:
task PMDriver::transmitter();
forever begin
PMXaction tr;
bit drop;
bit tx_bit_stream[$];
string temp;
this.wait_if_stopped_or_empty(this.in_chan);
...
this.notify.indicate(this.EXECUTING, tr);
log.trace( tr.psdisplay("Starting Tx Xaction "));
this.hdlc.HDLC_transmit(tr,tx_bit_stream,tr.inter_xaction_delay);
...
// 此回调在顶层环境中被扩展
vmm_callback(PMDriver_callbacks,
post_ex_trans(this, tr));
this.in_chan.remove();
end
endtask: transmitter
Page 8
示例1.2:VIP内部调用的接收回调:
task PMDriver::receiver(int inst_num, ref bit rx_bit_stream[$]);
forever begin
PMXaction tr;
@(frame_ready[(inst_num%24)]);
// driver status
this.notify.indicate(this.OBSERVING, rx_factory);
// Call the HDLC Receive class
this.hdlc.HDLC_receive(rx_factory, rx_bit_stream);
...
$cast(tr, this.rx_factory.copy());
this.out_chan.sneak(tr);
// 此回调在顶层环境中被扩展
vmm_callback(PMDriver_callbacks,
post_obs_trans(this, tr));
this.notify.reset(this.OBSERVING);
end // forever begin
endtask: receiver
示例1.3:在顶层扩展的回调:
class PMDriverSbCb extends PMDriver_callbacks;
PM_Scoreboard sb;
function new(PM_Scoreboard sb);
this.sb = sb;
endfunction
virtual task post_ex_trans(pvm_Driver xactor,
pvm_Xaction tr);
PMXaction hdlc_tr;
hdlc_tr = new;
$cast(hdlc_tr, tr.copy());
// 这将被驱动到总线上的事务添加到记分板的队列中
this.sb.deposit_sentpkt( hdlc_tr );
endtask: post_ex_trans
Page 9
virtual function void post_obs_trans(pvm_Driver xactor,
pvm_Xaction tr);
PMXaction hdlc_tr;
hdlc_tr = new;
$cast(hdlc_tr, tr.copy());
// 这将被存入记分板的通道中
// 这将触发记分板中的比较过程启动
// 注意这里使用sneak而不是put。我们希望回调的扩展
// 对主代码影响最小,并应立即执行
this.sb.in_chan.sneak(hdlc_tr);
endfunction: post_obs_trans
endclass: PMDriverSbCb
回调用例2:整个系统的数据检查
声明一个数据存储单元类,保存系统中的预期值。所有影响预期值并生成实际值的测试平台组件都可以访问这个结构。回调的扩展方式与之前解释的相同,但这次要么在数据被驱动到DUT时更新预期值,要么检索预期值并与实际观察到的值进行比较。
示例2.1、2.2和2.3展示了从不同接口访问数据存储单元的不同回调。
Page 10
示例2.1:IM杂项驱动器回调:
class IMMiscDriverRegChkCb extends IMMiscDriver_callbacks;
// 这是"数据存储"类
IOMRegs iomRegs;
function new(IOMRegs iomRegs);
this.iomRegs = iomRegs;
endfunction
virtual function void post_obs_trans(pvm_Driver xactor,
pvm_Xaction tr);
IMMiscXaction misc_tr;
misc_tr = new;
$cast(misc_tr, tr.copy());
// 访问READY LED"预期"值队列,该队列位于iomRegs结构中
// 该队列由PM的输出命令更新。"实际"值由IMMiscDriver观察
// 并在此回调中记录。如果队列为空,则在TB模块中观察到的
// 变化不是预期的,应打印错误。否则我们将从iomRegs获取预期值
if (this.iomRegs.ready_led_q[0].reg_q.size() == 0) begin
log.error("Unexpected Change on READY LED. reg_q was empty");
else
ready_led_reg = this.iomRegs.ready_led_q[0].reg_q.pop_front();
// 以下将比较"预期"和"实际"值
check_led("READY", ready_led_reg, misc_tr.ready_led);
endfunction: post_obs_trans
endclass: IMMiscDriverRegChkCb
示例2.2:UART回调:
class UARTDriverRegChkCb extends UARTDriver_callbacks;
virtual task post_ex_trans(pvm_Driver xactor,
pvm_Xaction tr);
UARTXaction uart_tr;
uart_tr = new;
$cast(uart_tr, tr.copy());
// 这里不是从iomRegs结构检索数据,而是UART测试平台模块
// 用"预期"值更新iomRegs。"实际"值在PM接口
// 上观察到的响应中看到
this.iomRegs.uart_rx_pkt_q.push_back(pkt_rx);
endtask: post_ex_trans
Page 11
virtual function void post_obs_trans(pvm_Driver xactor,
pvm_Xaction tr);
UARTXaction uart_tr;
uart_tr = new;
$cast(uart_tr, tr.copy());
// 数据被测试平台模块在UART接口上观察后,
// 需要将其关联到PM命令。来自PM命令的"预期"
// 数据存储在iomRegs的队列中。如果队列为空,
// 观察到的数据不是预期的,生成错误。否则,比较
// 此回调中观察到的"实际"事务与"预期"事务
if(iomRegs.iim_tx_pkt_q.reg_q.size != 0) begin
temp_tx = iomRegs.uart_tx_pkt_q.reg_q.pop_front();
if( uart_tr != temp_tx ) begin
log.error($psprintf("UART_TX_PKT: Comparison Error,
Expected %X, Actual %X", temp_tx, uart_tr));
end
else
log.error ("Invalid UART packets detected on PSU UART's txd line");
endfunction: post_obs_trans
endclass: UARTDriverRegChkCb
示例2.3:PM回调:
class PMDriverRegChkCb extends PMDriver_callbacks;
IOMRegs iomRegs;
function new(IOMRegs iomRegs);
endfunction
virtual task post_ex_trans(pvm_Driver xactor,
pvm_Xaction tr);
HDLCXaction hdlc_tr;
hdlc_tr = new;
$cast(hdlc_tr, tr.copy());
case(hdlc_tr.frame_type)
HDLCXaction::SET_MOD: begin
// 这里是之前详细说明的UART回调中使用的"预期"值的实际设置位置
for(int i=0;i<=4;i++)
pkt |= (hdlc_tr.frame_body[i] << (8*i));
uart_tx_pkt_q.reg_q.push_back(pkt);
end
default: begin end
endcase // case (hdlc_tr.frame_type)
endtask: post_ex_trans
// 当接收端完成接收数据包时,调用检查任务
virtual function void post_obs_trans(pvm_Driver xactor,
pvm_Xaction tr);
HDLCXaction hdlc_tr;
hdlc_tr = new;
$cast(hdlc_tr, tr.copy());
hdlc_tr.cmd_rsp = 0;
case(hdlc_tr.frame_type)
HDLCXaction::GET_MOD: begin
// 这里比较"实际"uart数据包与在UART回调的post_ex_trans中设置的"预期"UART数据包
for(int i=0;i<=4;i++)
uart_rx_pkt |= (hdlc_tr.frame_body[i+2] << (i*8));
temp_rx = psup_uart_rx_pkt_q.reg_q.pop_front();
if( uart_rx_pkt != temp_rx ) begin
log.error($psprintf("UART_RX_PKT: Comparison Error,
Expected %X, Actual %X", temp_rx, uart_rx_pkt));
end
end
default: begin end
endcase // case (hdlc_tr.frame_type)
endfunction: post_obs_trans
Page 12
5. 制造混沌:约束随机测试
一旦实现了PMXaction类(PM和IOM之间通信使用的基础事务类),随机测试就相当简单了。这些事务包括命令类型、地址类型、事务间延迟、方向信息(命令或响应)以及与错误注入相关的更多字段。默认约束允许PM生成器创建随机测试中使用的有效事务。
为了创建约束随机测试,我们需要进一步约束PMXaction。示例3.1演示了如何实现一个约束随机测试,目标是在事务之间生成单空闲标志。
示例3.1:约束事务
class PMXaction_ext extends PMXaction;
// 约束事务属性的替代方法
//constraint PMXaction_delay {
// this.inter_xaction_delay == 0;
//}
// 定义post_randomize以修改"随机化后"的事务
function void post_randomize();
inter_xaction_delay = 0;
super.post_randomize(); // gen CRC etc
endfunction: post_randomize
endclass: PMXaction_ext
// 与random_test()几乎相同,除了下面的GenSetup
program automatic random_single_idle_test();
pvm_Logger log = new("random_single_idle", "Main");
// 顶层环境
sysEnv the_env;
initial begin
string testName;
$value$plusargs("testname=%s", testName);
if (testName == "random_single_idle_test") begin
$display("TEST %s CHOSEN", "random_single_idle_test");
// 实例化顶层
the_env = new();
the_env.random_test = 1;
the_env.gen_cfg();
the_env.cfg.PM_0_cfg.stop_after_n_insts = 20;
the_env.cfg.PM_0_cfg.number_of_ioms = 1;
the_env.cfg.disable_all_ioms = 0;
the_env.build();
// 此新代码将默认生成器的事务重写为我们上面扩展的事务
begin: GenSetup
PMXaction_ext xactn;
xactn=new;
$display("Modifying reference of randomized object");
Page 13
the_env.PM_0.gen.randomized_obj=xactn;
end: GenSetup
the_env.PM_0.xactor_A.cvtw = 0;
// 启动测试
the_env.run();
$finish;
end // if (testname == ...)
end // initial
endprogram
"PMXaction"基类首先通过"PMXaction_ext"类进行了扩展。在此子类内部,"post_randomize()"函数被重定义。也可以添加约束块,如上面的注释部分所示。根据其他约束和约束排序的指定方式,有时在"post_randomize()"中覆盖值比解开随机生成器的不直观本质更容易。
最后,在"GenSetup"块内部,生成器的"randomized_obj"蓝图被测试扩展的"PMxaction_ext"类替换。这个概念也在VMM 验证方法学手册第5章的OOP入门:工厂模式中介绍。
6. 精确聚焦:定向测试
尽管VMM 验证方法学手册侧重高覆盖率的随机测试环境,但VMM 验证方法学手册和SystemVerilog语言中固有的特性也为创建定向测试提供了便利。
从测试平台架构开始,VMM 验证方法学手册建议测试用例在验证环境的顶层实现。由于每个测试是其自己的程序块,每个定向测试用例与其他用例完全隔离,并具有底层环境的最大复用潜力。
精心设计的测试步骤(配置、构建、启动、运行等)允许对配置和事务进行定制。在示例4.1中,我们看到PM_0_cfg和UART_cfg在"build()"调用之前被定制。此外,PM_0.gen的randomized_obj被程序块上方定义的自定义对象替换。
示例4.1:定向测试
class PMXaction_ext extends PMXaction;
// 占位符
endclass: PMXaction_ext
Page 14
program automatic random_test1_test();
pvm_Logger log = new("random_test1", "Main");
// 顶层环境
sysEnv the_env;
initial begin
string testName;
$value$plusargs("testname=%s", testName);
if (testName == "random_test1_test") begin
$display("TEST %s CHOSEN", "random_test1_test");
// 实例化顶层
the_env = new();
the_env.gen_cfg(); // 1a. configure defaults
// 1b. custom config
the_env.cfg.PM_0_cfg.number_of_ioms = 1;
the_env.cfg.PM_0_cfg.stop_after_n_insts = 1000;
// 为时钟周期编程一个低于4800ns的随机值
the_env.cfg.rand_iom_clks = 3;
// UART VIP的可编程模式在每个可编程数量数据包后插入可编程数量的周期
foreach(the_env.cfg.UART_cfg[i]) begin
the_env.cfg.UART_cfg[i].driver_mode = UARTCfg::PROGRAMMABLE;
the_env.cfg.UART_cfg[i].num_of_packets = 11;
the_env.cfg.UART_cfg[i].num_of_cycles = 2000;
end
the_env.build(); // 2. build
// 3. 将默认gen的randomized_obj替换为本次测试中定义的
begin: GenSetup
PMXaction_ext xactn;
xactn=new;
$display("Modifying reference of randomized object");
// 这也是跨模块引用的示例
the_env.PM_0.gen.randomized_obj=xactn;
end: GenSetup
// 启动测试
the_env.run(); // 4. run
$finish;
end
end
endprogram
Page 15
VIP的生成器支持inject任务,允许插入事务。事务可以使用"randomize() with"创建,因此您可以指定特定的测试数据来创建测试用例。
在定向测试中,回调可用于获取事务的句柄或修改事务(预执行或后观察)。回调也可用于推进时间。
示例4.2使用"inject"插入一个命令发送到DUT,并附加一个回调等待并"获取"响应。"get"被认为是阻塞的,这意味着定向测试的执行线程在"get"完成并获取响应之前不会继续运行。这些步骤在注释部分展示。
此示例还展示了"randomize() with"以进一步约束定向测试用例中使用的事务。
示例4.2:定向测试:"注入"命令
// 1. 定义您希望"获取"的事务回调的子类
// 这里扩展PMDriver的回调类,因为我们希望
// 分析PM响应事务
class DirectRespCheck extends PMDriver_callbacks;
// 用于传递响应的通道
PMXaction_channel out_chan;
// 扩展new函数以传入用于响应的通道
function new(PMXaction_channel out_chan);
this.out_chan = out_chan;
endfunction:new
// 扩展post_obs,因为它在响应从PM驱动器
// 被捕获之后触发
virtual function void post_obs_trans(pvm_Driver xactor,
pvm_Xaction tr);
PMXaction response;
$cast(response, tr);
if( response.frame_type == PMXaction::GET_ERRORS )
out_chan.sneak(tr);
endfunction: post_obs_trans
endclass:DirectRespCheck
program automatic errors_crc_test();
initial begin
...
// 2. 声明之前定义的响应回调和一个用于接收响应的通道
DirectRespCheck rsp_cb;
PMXaction_channel in_chan;
...
// 3. 实例化响应回调和通道
in_chan = new("PMDriver Input Channel", 0);
Page 16
rsp_cb = new(in_chan);
...
// 4. 注册回调
the_env.PM_0.driver_A.append_callback( rsp_cb );
...
the_env.start();
fork
directed_stimulus;
join_none
the_env.run();
...
end // initial
task directed_stimulus;
...
// ----------------------------------------------------------------
log.note("GET_ERROR To verify the CRC error has been detected");
cmd.randomize() with {
cmd_rsp == 1;
inter_xaction_delay inside {[4:30]};
chan == 1;
frame_type == GET_ERRORS;
};
cmd.display("GET_ERROR To verify the CRC error has been detected");
cmd.rsp_drop = 0;
$cast(cmd_cpy, cmd.copy());
// 5. 注入命令
the_env.PM_0.gen.inject(cmd_cpy);
// ----------------------------------------------------------------
// 6. 等待响应
in_chan.get(cmd_resp);
// 7. 检查响应
if( cmd_resp.frame_body[9] == 1 )
log.note("Got expected A error in the GET_ERRORS body");
else
log.error("Expected bit 9 of GET_ERRORS body to be set. It is NOT");
...
endtask: directed_stimulus
endprogram
层级化和跨模块引用允许测试生成特殊事务、创建不规则条件、关闭错误条件的检查,以及在不需要使用PLI的情况下推送或探查信息。
示例4.3展示了一个定向测试如何无需编写额外支持代码就能直接从监视器访问信息。FET泵监视器不断统计在256个时钟周期内FET泵信号高低电平的次数。
示例4.3:跨模块引用任务
the_env.FET_pump_mon_0.check_spacing(exp_ones);
Page 17
repeat (256*2) @(the_env.FET_pump_0_port.cb);
log.note(the_env.FET_pump_mon_0.psdisplay("FET_pump_mon_0:"));
mon0_ones = the_env.FET_pump_mon_0.get_ones_cnt();
mon0_zeroes = the_env.FET_pump_mon_0.get_zeroes_cnt();
if ( (mon0_ones !== exp_ones) ||
(mon0_zeroes !== exp_zeroes) ) begin
log.error($psprintf("FET_pump_mon_0: Expected %0d ones and %0d zeroes,
instead got %0d ones and %0d zeroes",
exp_ones, exp_zeroes, mon0_ones, mon0_zeroes));
end
示例4.4演示了使用层级引用验证特定引脚在5ms窗口内没有翻转。
示例4.4:信号的层级引用
fork
begin
@(top.UART_0_if.txd);
log.error("PSU UART's tx line changed its level in OFFLINE state");
end
begin
@(top.OMMISC_0_if.grn_led_n);
log.error("one of the GREEN LEDs changed level in OFFLINE state");
end
begin
@(top.I2C_0_if.sda, top.I2C_0_if.scl);
log.error("One of I2C lines has changed its level in OFFLINE state");
end
#5ms;
join_any
Page 18
7. 评估工作:验证结果
VMM 验证方法学手册日志类使设置和控制消息级别变得容易。日志消息具有多个严重级别(跟踪、调试、详细等)。我们向Makefile添加了控制以更改消息级别,如示例5.1所示。
示例5.1:消息级别控制
ifdef MSG_LVL
VCS_ARGS = +vmm_log_default=$(MSG_LVL)
endif
simv: rtl verif
vcs top ${VCS_ARGS}
VMM 验证方法学手册/VCS具有一致的错误标志,如"!ERROR!"。我们还定制了环境,以便完成的测试检查通过/失败标准并打印"PASSED"或"FAILED"。一致的标志允许我们的Perl脚本通过解析特定字符串来收集关键信息,确定回归套件中有多少测试通过或失败。此外,错误和警告的计数内置于VMM 验证方法学手册基类中。
示例5.2是一个示例日志文件。
示例5.2:失败测试的日志文件
[!ERROR!:PM:RegChkCb] @(84667700ns) WRONG TEST abort status:expected(1);received(0)
[Normal:PM_Scoreboard:PM CMD-RSP Scoreboard] @(84667700ns) Expected and Actual Response for GET_MOD Match!!!
...
87970980ns | FAILED | 37 warnings, 6 errors
当然,作为专注的设计和验证工程师,我们一直坚持工作直到看到:
[Normal:PROJ:ENV] @(32256780ns) #### Turning errors off...
[Normal:PROJ:ENV] @(32256780ns) Check scoreboards to verify everything has been received...
[Normal:PROJ:ENV] @(32256780ns) Completed cleanup()
32256780ns | PASSED | 6 warnings, 0 errors
Page 19
8. 结论
本文介绍的技术可以用VMM 验证方法学手册之外的许多其他方式实现,但基于VMM 验证方法学手册的结构在以有组织、结构良好的方式解决工业控制系统的验证问题方面被证明是无价的。回调为所需的不同检查机制提供了很好的解决方案,包括:
- 每个测试平台模块的回调被扩展并为系统数据检查做出贡献,同时通过回调扩展保持该功能的封装性 - 根据功能对一个模块的多个回调进行扩展,实现了更好的控制和可读性 - 这些回调中的队列机制使我们能够解决"命令到响应不定时"的时序问题
使用VMM 验证方法学手册的优势:
- 内置功能,如环境执行顺序 - 回调在维护代码可读性的同时,允许对每个VIP的功能进行多种扩展,用于不同类型的检查和定向测试 - VMM 验证方法学手册涵盖了我们想到和没想到的问题
使用VMM 验证方法学手册的劣势:
- VMM 验证方法学手册难以学习。即使是经验丰富的验证工程师团队且具有面向对象环境专业知识,仍然需要VMM 验证方法学手册培训课程 - VMM 验证方法学手册的许多方面不太直观,但SystemVerilog的VMM 验证方法学手册书籍(圣经!)被证明是非常重要的参考。学习曲线并不如人们期望的那样平缓
Page 20
9. 致谢
Introduction to System Verilog Course - Synopsys Introduction to VMM course - Synopsys Matthew Muresan - Design and Verification Engineer - Patni Americas Vitaly Shilman - Design and Verification Engineer - Patni Americas
10. 参考文献
VMM for SystemVerilog - Bergeron/Cerry/Hunter/Nightingale SystemVerilog for Verification - Spear
图片索引
本文共 1 张图片,存放于 _images/ 目录。
第3页:图1 - 设计的简化视图,显示三个PM和多个IOM 第5页:图2 - PVM VIP结构