纯SV语言搭建验证平台
2020-04-30张静,卜刚
张 静,卜 刚
(南京航空航天大学 电子信息工程学院,江苏 南京 211106)
0 引 言
芯片的验证[1-7],是提高芯片流片成功的关键。验证工作与设计仿真工作不同,仿真是为了证明设计方案的正确性,而验证是为了证明方案中不存在错误。设计错误很容易造成芯片完全不能工作,而修正错误再重新流片不但需要额外的费用,更会延迟芯片上市时间,这对芯片开发造成了巨大的风险。现如今,芯片制造工艺更加精细,芯片制造费用的增加,芯片功能变得越来越复杂,验证的重要性也在不断增加。
目前来说,SystemVerilog[3,6-11]已经成为比较主流的验证语言,SystemVerilog语言具有许多优点,诸如随机约束[12]、功能覆盖率[2,13]、断言技术和利用面向对象思想构建验证平台的一般方法。这些优点能极大提高芯片验证的效率,保障芯片设计的正确率,缩短芯片上市时间。
1 SystemVerilog语言
此处介绍的验证技术,专注于受约束的随机测试,它利用功能覆盖率来衡量进度并指导验证。SystemVerilog最有意义的优点在于,它允许用户在多个项目中使用连续一贯的语法来构造可靠并且可重复的验证环境。SystemVerilog提供了一种建模语言,简化了自顶向下的设计,可以在SystemVerilog中创建模型,然后在下一层重新定义每个模块,初始的系统级模型也可以被重新用作参考模型,不仅如此,它还引入了直接编程接口(DPI),能更加简单地连接C、C++或者其他非Verilog编程语言。
相比于硬件描述语言(HDL),SystemVerilog硬件验证语言(hardware verification language,HVL)[5,14]具有典型的性质:
●受约束的随机激励生成;
●功能覆盖率;
●更高层次的结构,尤其是面向对象编程;
●多线程及线程的通信;
●支持HDL数据类型,例如Verilog的四状态数值;
●集成了事件仿真器,便于对设计施加控制。
还有其他很多有用的性质,但上述特性已经能够支撑创建高度抽象的测试平台。
采用随机测试[14]的原因如图1所示。
图1 随机测试与定向测试的时间比较
图1是传统验证的定向测试方法和高级验证的随机测试方法的比较[15],对这两种方法所用的验证周期以及功能覆盖率进行对比。采用定向测试方法时,使用的时间与功能覆盖率呈线性关系。随着增加定向测试例的编写数量,功能覆盖率稳步上升。采用随机测试方法时,由于需要准备随机验证环境,开始时会有一段覆盖率没有增加的时间段。当随机验证环境准备完毕,运行随机测试例,可以随机产生大量激励,功能覆盖率会有大幅增加。当覆盖率增加不明显时,分析未覆盖情况,加紧约束或添加定向测试例,对未覆盖的情况进行覆盖,直至功能覆盖率达到100%。由图可以看出,虽然采用随机测试的方法会有一段覆盖率为零的时间段,但从整个验证周期来看,随机测试方法会比定向测试方法更具优势,尤其是随着集成电路规模和复杂度的增加,这种基于覆盖率驱动的受约束随机激励方法会更符合验证的需要。
2 DUT介绍
图2中的DUT是由两个子模块构成的具有两级流水的系统模块[16],第一级为一个预处理器模块(EXECUTE PREPROCESSOR),第二级为一个算术逻辑运算单元(ALU)模块:
图2 DUT结构
●预处理器:对外部输入的数据和控制信号进行处理,并产生对应的ALU可接受的数据和控制信号。
●算数逻辑运算单元:根据输入的opselect,operation和shift_number信号执行相关算数逻辑运算。
这两个子模块都可以具有各自独立的时钟和复位信号,文中这两个子模块的时钟和复位信号是一致的。
基本功能描述:
●复位:复位时,所有的时序逻辑输出都为0,这些信号包括:预处理输出信号中的aluin1,aluin2,operation_out,opselect_out,shift_number enable_arith,enable_shift以及ALU输出的信号aluout,carry。
●预处理器功能描述:所有时序逻辑的输出只有在enable_ex==1的条件下才会发生变化。
●ALU:所有指令信号只在以下两个条件成立之一才有效:enable_arith==1 or enable_shift=1。这些信号均在时钟的上升沿处进入ALU子模块,ALU可执行移位操作和算数逻辑运算,以及数据传输。
3 验证架构
SystemVerilog平台如图3所示。
组件搭建描述[15]:
3.1 Packet类
将数据信息封装进入Packet类,利用随机化和相关约束产生随机数据,创建两个packet对象,一个包在DUT输入端输入,另一个包和DUT输出的数据相参照。
图3 SystemVerilog平台
随机化的部分代码:
rand reg[`REGISTER_WIDTH-1:0] src1;
rand reg[`REGISTER_WIDTH-1:0] src2;
rand reg[`REGISTER_WIDTH-1:0] imm;
…………………….
相关约束的部分代码:
constraint Limit {
src1 inside{[0∶65534]}
src2 inside{[0∶65534]};
imm inside{[0∶65534]};
mem_data inside{[0∶65534]};
opselect_gen inside {[0∶1],[4∶5]};
3.2 Generate类
封装了gen()任务和一个start()方法,其中start()方法的循环由系统全局变量number_packets控制。如果number_packets≤0,则这个循环是无限的。如果number_packets>0,则这个循环在循环这么多次后会停止。在调用gen()之后,会创建一个随机化packet对象(pkt2send)的拷贝,并且通过out_box邮箱发送到Driver。
3.3 Interface
连接验证平台和DUT的端口。但是在类里面不可直接定义interface,须使用virtual来定义。
3.4 Driver类
其中in_box将会用来从Generator向Driver接收Packet对象。out_box将用来从Driver向Scoreboard发送Packet对象。in_box和out_box都是pkt_mbox的类型。在Driver中start()方法是在一个无限的循环中执行的。在这个循环的每一次执行中,都会从in_box中获取一个Packet对象。这个数据包对象的内容将会通过send()发送经过DUT。一旦这个Packet对象的发送过程完成后,这个Packet对象会被发送到scoreboard。
Generator到Driver的mailbox的代码:
typedef mailbox#(Packet) in_box_type;
in_box_type in_box=new;
Driver到Scoreboard的mailbox的代码:
typedef mailbox#(Packet) out_box_type;
out_box_type out_box=new;
3.5 Receiver类
调用recv()来从DUT中重新获取Packet对象,将从DUT中获取的Packet对象复制一份到out_box中去,在receiver中有start()方法,它将会执行一个非阻塞的无限循环。每次循环中,都会从DUT中重新构建一个Packet对象。一旦重新获取后,这个Packet对象将会通过发出邮箱(out mailbox)发送到scoreboard。
Receiver连接到Scoreboard的mailbox的代码:
typedef mailbox#(Packet) rx_box_type;
rx_box_type rx_out_box;
3.6 Scoreboard类
封装check()任务,实现Scoreboard,Driver和Receiver之间的通信。Driver在发送数据包对象到DUT中时,也会将它发送到driver_mbox邮箱。一个Receiver在从DUT接收到数据时,也会发送Packet对象到receiver_mbox邮箱中去。
利用driver_mbox.num()和receiver_mbox.num()查看是否有包传入,如果存在利用get()函数将从Driver传入的包放入pkt2send,receiver传入的包放入pkt2cmp,之后利用check()函数作比较。在Scoreboard这个类中还定义了coveragegroup来收集覆盖率,定义DUT的各个功能点比如ALU的算术和移位功能,采用交叉覆盖率,用sample()采样,最后用get_coverage()收集覆盖率。
Scoreboard用来连接Driver的mailbox的代码:
typedef mailbox#(Packet) out_box_type;
out_box_type driver_mbox;
Scoreboard用来连接Receiver的mailbox的代码:
typedef mailbox #(OutputPacket) rx_box_type;
rx_box_typereceiver_mbox;
覆盖率功能点定义部分代码:
covergroup Arith_Cov_Ver2;
coverpoint pkt_sent.imm;
src1_cov:coverpoint pkt_sent.src1 ;
……………………………………
opselect_cov2:coverpoint pkt_sent.opselect_gen {
bins shift={0};
bins arith={1};
bins mem={[4∶5]};
}
…………………………………
cross src1_cov, src2_cov;
endgroup
covergroup的例化与采样:
Arith_Cov_Ver1=new();
……………………………………
Arith_Cov_Ver1.sample();
……………………………………
覆盖率的收集:
coverage_value1=Arith_Cov_Ver1.get_coverage();
………………………………………….
3.7 Test类
声明并例化各组件,正确连接邮箱,调用每个类的start()方法,设置reset()任务。
部分代码:
组件的声明:
Driver drv;
………………………
组件的例化:
drv=new();
………………………..
组件的启动:
drv.start();
………………………..
3.8 Top模块
声明接口,实现DUT和平台互连,设置时钟信号。
部分代码:
平台互连:
Top dut(.clock(top_if.clock),.enable_ex(top_if.enable_ex),………………………);
设置时钟信号:
#(simulation_cycle/2)
Clock = ~Clock;
4 仿真结果
编写完SystemVerilog代码后开始进行平台验证,主要采用questamsim软件进行仿真验证,使用命令启动平台、生成打印信息报告以及收集覆盖率报告。
根据输出的波形图和生成的验证报告来比较DUT功能的正确与否,并查看输出的功能覆盖率报告来检查各功能点的实现状况。
图4 信息打印
由图4可以发现DUT的输出和模型的输出是一致的,DUT的设计满足功能实现,图4还打印出各个功能点的覆盖率状况以便于分析如何添加测试用例来使功能覆盖率达到100%。
波形图中定位处由随机输入的信号来看实现的是算数逻辑,即将aluin2的低十六位赋值给aluout的高十六位,并将aluout的低十六位置零。由图中aluin2为32’h00003a1e,aluout为32’h00000000可得运算是正确的,且各使能信号也正确无误。
图5 DUT部分波形(1)
图5检测了DUT的算数逻辑,只是DUT其中一个功能点。图6列出了DUT基于随机激励测试的所有功能点实现的波形图,查看整体波形,检查每个功能点实现的具体情况,可以发现DUT的设计是没问题的。
图6 DUT部分波形(2)
图7描述了此DUT的功能覆盖率,可见覆盖率还未达到100%,尤其是算数和移位运算单元比较低,因此可以修改约束条件增加测试用例来使功能覆盖率提高,从而保证验证的完备性。
图7 功能覆盖率(1)
由图8可知,通过增加测试用例以及修改相关约束条件,可以发现覆盖率有了明显的提高,所有的覆盖率都达到了100%,说明该测试点已经得到了充分的验证。由此可见,增加定向测试激励配合随机激励可以大幅提高功能覆盖率,由图8可见所有的测试点的覆盖率都达到了100%。因此可得,基于SV语言的优点,可以减少验证时间并提高验证效率。
图8 功能覆盖率(2)
5 结束语
定向测试每次只能测试一个特性,而无法模拟设备在实际应用中所面对的复杂的激励和配置,为了得到稳健的设计,使用受约束的随机激励加上功能覆盖率,才能在最大限度内创建出最广泛的激励。文中演示了如何使用SV语言面向对象编程的方法建立一个由覆盖率驱动并且受约束的随机分层测试平台。基于SV的验证,充分利用了SV语言中提供的随机约束和断言机制,改变了传统的直接测试的方法,基于覆盖率驱动的验证极大提高了验证的效率和完备性,有效缩短了验证周期。