位流验证,对于芯片研发是一个非常告急的测试本领,对于纯软件开辟职员,最难理解的就是位流验证。在FPGA芯片研发中,位流验证是在做什么,在哪些阶段必要做位流验证,如何做?都是问题。
我们先整体的说一下:
首先:在硬件设计阶段,位流验证是设计验证部的告急工作,它是为了验证硬件设计的数字电路部分的合理性,对于FPGA芯片,主要就是对标准的架构组件进行验证。如果相应的EDA软件未成形之前,相应的位流必要通过手工单独天生。
然后:在FPGA配套的EDA软件开辟阶段,位流验证也非常告急。由于EDA软件的最大目的就是天生正确的位流,位流输出是否符合预期,关系到软件各个环节的正确性。以是,一般会将位流验证做为告急的回归测试本领,一方面,可以连续验证最新输出软件的正确性,另外,如果芯片的底层架构发生变化,模子变更,仿真库变更,也可以通过位流验证,保证架构更改的正确性(架构主要是按照硬件设计对应的软件模子,而硬件设计的这些模子,都会有对应的仿真库和参考模子,用于验证)。
末了:在生产测试阶段,位流验证同样好坏常好的测试本领,它可以测试位流写入芯片后上板上芯片的效果,确认芯片实际的运行情况。在软件中只是保证了逻辑上的正确,实际联合电气特性和各种不恻隐况是否能正常可靠运行,大概通过位流加载后的效果进行验证。加载到实际板上的效果,也可以通过驱动程序取出,与预期参老模子的结果做对比,验证其正确性。
当然,位流验证大概并不但限于上面的三种情况(限于我相识的内容有限),但通过上面的描述,你可以发现,位流验证自己是就是会跨多个部分的比较特别的本领,在不同部分做验证时,一定会存在共同,和一些有相似的工作。但是,由于各自的目的不同,发现错误后,定位和纠错的方向也很不一样。反以,反过来说,选择的相应示例也会有些不同。
今天,我们只讲在EDA软件如何利用位流验证,完成对EDA软件的告急回归测试。
大概的讲法是:
:我们会讲解位流验证依靠的框架UVM
:会讲一下UVM的实现平台。
:会讲一下针对EDA软件的验证,会对哪些部分进行验证,会利用什么样的示例 。
:发现问题后,会如何通过输出去定位错误。
:由于用于主动化回归,也讲一下相应的主动化平台应该如何拱建。
首先,我们还是得再来理解一下位流验证必要用到的UVM框架。(之前在单讲测试时,也说过一次,但讲得并不仔细)
一:UVM框架
1.1:理论
Universal Verifacation Methodology:是一套基于SystemVerilog的标准验证方法学。它提供了一套完备的框架,用于构建复杂的验证情况,应用于芯片设计范畴的功能验证。旨在提高验证的可重复性、可重用性和主动化程度。应该说,对于硬件设计的验证,UVM是最基础,最常用的平台,离不开它。
2.1.1:DUT
待测设计——Design Under Test,就是你要验证的设计单元。
如果你是要验证/测试一颗 FPGA 芯片,DUT 一般会分为三种,一种是单Tile的设计,一种是多Tile的设计,还有就是整个芯片的设计。
注意这里我加了个bitstream,这并不是UVM必须的,加上bitstream是和我们今天要讲的位流验证扣题,也就是我们在验证时,利用FPGA的位流文件,来形成DUT的功能。
env:Testbenc Environment ,除了DUT,其它就是UVM的验证情况了,它包含了一大堆东西,反面一个一个介绍。
- Sequencer:测试数据序列,由于测试时必要很多的输入数据,我们按序列提供。
- Driver:如何数据输入DUT的过程,可以理解为激励的过程。
- Monitor:监控DUT的输入/输出信号
- Reference Model:参考模子,对应DUT的设计,提供相偕行为的给果。
- 这里参考模子的运行,一般我们会利用仿真工具的内置实现来完成。
- Compare:比较DUT的输出和参考模子的输出,判断是否匹配。
2.1.2:Sequencer
Sitmulus:天生测试数据序列。我们必要基于测试的需求,产生数据,交给Driver来驱动到DUT。比如:你要测试DSP的乘法,那就是提供相应的输入数值对。
class my_sequence extends uvm_sequence #(my_transaction);
`uvm_object_utils(my_sequence)
task body();
my_transaction tr;
foreach (tr.data) begin
tr.data = $urandom_range(0, 255); // 随机天生 8 位数据
start_item(tr); // 发送数据
finish_item(tr);
end
endtask
endclass
2.1.3:Driver
接收Sequencer的测试数据并将其转换为DUT的输入信号。
这里大概必要模拟时序,比如:时钟的同步,握手协议等。
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req); // 接收 sequencer 的数据
@(posedge clk); // 时钟同步
dut_input = req.data; // 将数据驱动到 DUT
seq_item_port.item_done();
end
endtask
endclass
2.1.4:Monitor
实时监控DUT的输入和输出信号,将监控到的数据送出到比较系统Compare,进行输出分析。
class my_monitor extends uvm_monitor #(my_transaction);
`uvm_component_utils(my_monitor)
task run_phase(uvm_phase phase);
forever begin
@(posedge clk);
tr.data = dut_output; // 收罗 DUT 的输出数据
analysis_port.write(tr); // 将数据发送给 Scoreboard
end
endtask
endclass
2.1.5:Reference Model
参考模子,用于天生和DUT雷同逻辑功能的输出(Golden Model)。
基于雷同的输入激励信号,获得抱负的输出。
function bit [7:0] ref_model(input bit [7:0] data);
return data + 1; // 示例:参考模子输出为输入加 1
endfunction
2.1.6:Compare
这里的Compare,大概是简单的比较结果的输出,也大概是比较复杂的Scoreboard(一个记录数据表)。根据比较结果,输出不匹配的情况,并输出报告。
class my_scoreboard extends uvm_scoreboard;
`uvm_component_utils(my_scoreboard)
task run_phase(uvm_phase phase);
my_transaction ref_tr, dut_tr;
forever begin
ref_tr = ref_fifo.get(); // 从参考模子接收预期数据
dut_tr = dut_fifo.get(); // 从 DUT 获取实际输出
if (ref_tr.data !== dut_tr.data)
`uvm_error("Mismatch", $sformatf("Expected: %0h, Got: %0h", ref_tr.data, dut_tr.data));
end
endtask
endclass
2.1.7:示例
我们再来一个完备的示例,说明一下各部分做的事情。
DUT设计—— 一个简单的8位加法器。
DUT代码:(对于位流验证,输入不是这个,反面会举例说明)
module adder(
input logic [7:0] a,
input logic [7:0] b,
output logic [7:0] sum
);
assign sum = a + b;
endmodule
UVM情况:
这里必要定义一个Transaction,由于每次激励的执行都是不同的事务,事务标明输入和输出。
Transaction 定义:
class my_transaction extends uvm_sequence_item;
rand bit [7:0] a, b; // 输入信号
bit [7:0] expected_sum; // 参考模子天生的盼望输出
`uvm_object_utils(my_transaction)
// 构造函数
function new(string name = "my_transaction");
super.new(name);
endfunction
endclass
Sequencer:
class my_sequence extends uvm_sequence #(my_transaction);
`uvm_object_utils(my_sequence)
task body();
my_transaction tr;
repeat (10) begin
tr = my_transaction::type_id::create("tr");
tr.a = $urandom_range(0, 255); // 随机天生输入 a
tr.b = $urandom_range(0, 255); // 随机天生输入 b
start_item(tr); // 开始传输数据
finish_item(tr); // 结束传输数据
end
endtask
endclass
Driver:
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
// DUT 的接口
virtual adder_if dut_if;
// 构造函数
function new(string name = "my_driver", uvm_component parent);
super.new(name, parent);
endfunction
// 运行阶段
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req); // 从 Sequencer 获取数据
@(posedge dut_if.clk); // 等待时钟上升沿
dut_if.a = req.a; // 驱动 DUT 输入 a
dut_if.b = req.b; // 驱动 DUT 输入 b
seq_item_port.item_done(); // 标志数据已完成
end
endtask
endclass
Monitor:
输入监控
class input_monitor extends uvm_monitor;
`uvm_component_utils(input_monitor)
// DUT 接口
virtual adder_if dut_if;
uvm_analysis_port #(my_transaction) analysis_port; // 分析端口
function new(string name = "input_monitor", uvm_component parent);
super.new(name, parent);
endfunction
// 运行阶段
task run_phase(uvm_phase phase);
forever begin
@(posedge dut_if.clk); // 等待时钟上升沿
my_transaction tr = my_transaction::type_id::create("tr");
tr.a = dut_if.a; // 收罗 DUT 输入 a
tr.b = dut_if.b; // 收罗 DUT 输入 b
analysis_port.write(tr); // 将交易数据发送给分析端口
end
endtask
endclass
输出监控:
class output_monitor extends uvm_monitor;
`uvm_component_utils(output_monitor)
// DUT 接口
virtual adder_if dut_if;
uvm_analysis_port #(my_transaction) analysis_port; // 分析端口
function new(string name = "output_monitor", uvm_component parent);
super.new(name, parent);
endfunction
// 运行阶段
task run_phase(uvm_phase phase);
forever begin
@(posedge dut_if.clk); // 等待时钟上升沿
my_transaction tr = my_transaction::type_id::create("tr");
tr.expected_sum = dut_if.sum; // 获取 DUT 输出 sum
analysis_port.write(tr); // 发送到分析端口
end
endtask
endclass
Compare:
class my_scoreboard extends uvm_scoreboard;
`uvm_component_utils(my_scoreboard)
uvm_analysis_imp #(my_transaction, my_scoreboard) input_analysis_imp;
uvm_analysis_imp #(my_transaction, my_scoreboard) output_analysis_imp;
my_transaction expected_tr, actual_tr;
function new(string name = "my_scoreboard", uvm_component parent);
super.new(name, parent);
input_analysis_imp = uvm_analysis_imp #(my_transaction, my_scoreboard)
::type_id::create("input_analysis_imp", this);
output_analysis_imp = uvm_analysis_imp #(my_transaction, my_scoreboard)
::type_id::create("output_analysis_imp", this);
endfunction
// 输入数据分析
function void write(input my_transaction tr);
expected_tr = tr;
expected_tr.expected_sum = expected_tr.a + expected_tr.b; // 参考模子
endfunction
// 输出数据分析
function void write(output my_transaction tr);
actual_tr = tr;
// 比较 DUT 输出和参考模子的盼望值
if (actual_tr.expected_sum !== expected_tr.expected_sum) begin
`uvm_error("Mismatch", $sformatf("Expected: %0d, Got: %0d",
expected_tr.expected_sum, actual_tr.expected_sum));
end
endfunction
endclass
2.1.8:位流验证
那对于位流验证,UVM是如何利用的呢?
1:首先,必须准备好你的FPGA的EDA软件(准备好运行时必要的 Flow 的tcl命令)。给出你的芯片仿真时必要的Primitve仿真库。
2:针对你要测试的全芯片或者部分tile,选择你要测试的设计用例(必要有一定的代表性噢,可以是N多,必要有一定覆盖度),用 flow 天生仿真用的门级网表文件,并输出用例的route结果,输出终极的bitstream。
3:开始对要测试的用例,编写参考模子(也是另外一种实现方案)。
4:编写激励(测试)程序,提供相应的激励数据序列。
5:在TestBench上,针对 DUT 的 RefModel模子进行仿真,验证两者输出是否划一,如果划一,说明位流测试正常。比较的方法,要看测试用例的功能。
6:如果测试不正常,可以利用中间输出,日志输出,波形输出等信息进行定位,查察出错原因。
好了,那UVM框架是如何实现的呢?有什么支撑平台和工具吗?
1.2:支撑
那UVM这种框架是如何通平台来支撑它的呢?我们以Synopsys的VCS为例,来说明它是如何支持的(当然,也可以利用Mentor提供的QuestaSim)。
1.2.1:工具
VCS(Verlog Compiler Simulator)是业界领先的仿真工具,原生支持UVM,它提供了一系列的工具和功能,全而覆盖了UVM测试平台中的各项工作。
UVM 组件VCS 支持工具/功能SequenceSystemVerilog 编译器直接支持 UVM sequence 的随机天生和调试。Driver通过 VCS 的时钟精确仿真,支持 UVM driver 精确地驱动 DUT 信号。MonitorUVM 信号分析器,与 DVE 图形工具集成,监控信号流。 Compare
Scoreboard
与覆盖率分析工具集成,支持寄存器覆盖率和功能覆盖率的查抄。Debug 提供图形化的 UVM Phase 调试工具,支持动态调试和波形交互分析。Verification 集成 DVE 和 Verdi 工具,查察波形和调试测试平台的运行时行为。
基于VCS的功能,要实现UVM平台,还必要封装一些标准的命令,告竣常见的功能。
1.2.2:平台
为了实现UVM的基础功能,我们必要提供一些封装功能,我们称之为平台。由于VCS并不是直接针对UVM的,以是,我们必要针对它做一些封装,然后我们基于封装来利用,这样,看起来就更像是一个针对UVM实现的平台了。
根本功能如下:(封装方法可以利用Python或其它脚本语言来实现)
确认 测试平台的主目录,测试工程目录,测试用例目录,工具目录,输出目录……
测试的代码列表库
测试用例的分组
运行次数
是否调试模式:提供更多log输出
提供随机种子数:用于控制输入参数的随机种子,控制测试数据的天生。
……
我们的testbench的代码还是相对比较复杂的,另外,它也依靠于背后的仿真平台提供的大量的库文件,以是,我们一般必要对testbench的代码进行构建,输出各种我们必要的情况。
首先要对DUT代码进行编译,一般是SystemVerilog代码。
一般会利用 Makefile,必要编写相应的makefile,主要是必要将用到的UVM的动态库和引用资源进行构建。VCS有一些针对UVM的支撑库。
直接调用VCS的仿真,有低功耗的仿真模式可选
输出波形,用于Verdi波形调试
统计测试的代码覆盖率
……
1.2.3:运行
在运行期,为了保证示例可以并行执行,一般会利用HPC集群情况。我们可以利用LSF集群管理,通过 bsub 来将相应的任务,提交到HPC中运行。
比如:
‘一般会把任务分为多条,按顺序将任务放入同一个队列,保证先后次序
bsub -n 1 -M 20480 -q dv_test -Is "上面封装的命令,完成编译或仿真或波形分析的功能"
以上是运算支撑最根本的要求。具体的实现,我们反面会展开给个实例来讲。
二:位流验证
对于位流验证,我们今天要讲的主要是 FPGA的EDA工具测试,在EDA工具的Flow已根本成形的情况下,就可以开始搭建位流验证平台了,用于回归/验证EDA工具的功能。
2.1:流程图
我们先来表明一下上面的典范流程:
利用FPGA的器件的用例。测试用例文件,一般是DeviceModel提供的一些适合于验证功能的标准用例。作为待测用例。(这些用例,一般是由简到繁)
FPGA的软件工具的运行flow,在主动化运行时,一般是采用无界面的tcl命令来执行。
这里必要生存多个输出:
综合/映射后的网表:这是针对全部Primitve的电路实现,可以理解为罗列出,我们的设计终极实际用到了哪些逻辑的实例。就是可以看到设计终极利用的Instance,有明确的标识名称。
Route后输出的路由文件:这个用来定位全部实例Instance在芯片中实际的物理位置。也就是Impl后,实际上物理芯片上利用的逻辑块和相应的布线。联合上一步的输出,可以明确查到实例具体的位置,以及走线的路径。
Bitstream 位流:这个是天生的bitstream,这个作为Dut的输入,直接加载到测试平台(如:VCS),加上提前准备好的芯片的原生的仿真库,仿真平台可以实际的模拟芯片的运行,并且得到相应的运行中结果和中间输出。
根据实际情况,天生的tb文件。包含激励,定位,比较的逻辑。
测试用例表:原始的测试用例
Primitive行为模子:明确这个Primitive的行为是什么,在探求参考模子时,必要找到对应的参考实现。
解析输入/输出管脚:找出输入IO 和 输出IO,这个可以在Route 中找到。
编写用例参考模子:根据Primitive的行为和你的设计用例,写等价的功能。注意,这里的等价模子,一般是仿真平台直接执行出结果(并不是基于FPGA的逻辑电路来执行)。
设计输入的激励信号:设计测试的输入数据和相应的序列。
编写比较函数:看如何比较两者的输出。对于返回值 ,可以简单比较输出。对于时序,大概必要查抄波形,一般是选择几个关键的点,不大概全部查抄。
联合位流文件,仿真库,testbech ,利用UVM平台进行实际的运行。完成代码的编译,构建,仿真,结果比较的功能。
实际比较结果,将结果输出到指定位置,并形成报告。
对于失败的报告,还可以通过查察其它输出(日志,波形等),具体进行问题定位。
对于覆盖度的统计,可以查察现在回归用例的覆盖度,进而逐渐提升。
2.2:验证的问题
如果2.1验证出问题,那大概有哪些问题呢?我们可以看看。
- 文件的问题
大概是测试用例的问题,route输出的问题(net丢失之类),postmap输出的问题……
- IOPackage的问题
大概是IO Pad的错误,这大概是IO Package的问题。大概是输入Pad,也大概是输出Pad不对。IO配置不对,配置信号不对,绑定约束的处理问题。
- 路由的问题
提供的switch box 的连接不对。
- 逻辑单元不对
模块的配置有问题,配置问题导致功能出错,模块到Switch box的连线问题
- 其它
DeviceModel建模的问题,也有大概是Primitive实现的问题(硬件方面的)
具体的定位,可以通过源代码,PostMap文件,route文件来查到具体的实例,然后在VCS的仿真器上,查察具体时点的输出,这里大概是必要利用工具查察波形。
route文件,postmap文件(这个一般是打平的flattern文件)的具体格式就不方便给出样例了,这个每家FPGA的格式会有所不同。
三:FPGA芯片验证方法
我们设计出来的FPGA芯片,是否能满意预期。我们要保证我们提供给用户的FPGA上每一个器件的功能是符合预期的,也就是单个tile的功能,以及Tile之间的连接,整体芯片的逻辑是完全正常的。
以是,我们必要针对单Tile,多Tile,fullchip做位流验证。方法就是利用上面的位流验证平台。针对这三种方法,我们分别来说明,说明应该如何设计样例 ,来告竣相应的结果。
3.1:单Tile位流验证
对于每一种Tile做单独的测试(全部的Primitive),比如:CLB(LUT,CLA,DFF,SFTR……),CMT,IO,DSP,BRAM,EMRAM,FIFO,OBUF,IBUF,IOBUF,……
并且也包括Tile内部的连接信号路由的验证。
我们以CLB为例:
对于CLB,里面主要涉及不同的LUT,DFF,CLA,SFTR,必要设计一系列的用例,去覆盖相干的器件利用。
我们以最简单的 Lut2为例,来看看如何构建测试用例,参考用例,激励数据,结果比较。
3.1.1:CLB —— LUT2
首先,我们要相识Lut2的原理,它是由2输入1输出组成,可以完成 2个单bit输入1个输出的任何逻辑。比如:a & b,a | b , a xor b。
一般来说,厂商会提供 LUT2的Primitive IP,我们如果提供的IP就叫做 YY_LUT2。
那么,对于Dut,那就很简单了。注意 a & b 的真值 表是 1000b 也就是 h8
Dut的设计代码:
moudule And2(
input a,
input b,
output result
);
YY_LUT2 #(.INIT(4'h8)) lut2(.Io(a), .I1(b), .O(result));
end module
将该代码利用 eda flow 执行,天生 bitstream。
我们来看一下参考用例的写法:
module RefModel(input A,
input B,
output Y);
assign Y = A & B ; // 参考模子的布尔函数
endmodule
以上的代码,仿真工具在运行时,会在每个仿真周期,根据输入数据的变化来重新输出结果。对应的逻辑表达式的运算是在仿真器中执行(实际上就是语法解析,然后交给CPU来执行了)。
当然,你的示例还可以是 A | B,A xor B,若干的样例。
另外:
- INIT=0 IS_C_INVERTED = 0 IS_CLR_INVERTED=1 可以验证 DFFCE 的功能
- 1bit加,1bit 减 可以验证CLA功能
- wclk极性不反转 验证 SFTR32
- 将INIT、IC_C_INVERTED、IS_PRE(CLR/R/S/)_INVERTED的值进行随机,inst约束到FFA0 验证 LPQCE
3.1.2:DSP
我们再以DSP为例,选择符合的用例(针对DSPX18 的 2个输入乘法)
直接调用 YY_DSP48_CPLX18 ,具体就不写了。
具体如何设置用例,必要根据DSP48_CPLX18的输入参数来设置,尽量保证相干的参数可覆盖。
3.2:多Tile位流验证
必要验证 Tile间路由的正确性,在多个tile分配资源,全局信号,时钟可以正常共享,跨tile的优化结果符合预期。主要是对一组tile,某个功能区域进行验证。验证多个tile的功能及其之间的交互。可以用来验证协同工作,比如:DSP 与 BRAM的协同工作情况,验证范围有限,无法覆盖全芯片的全局资源,比如:时钟网络,全局布线等。
必要构造如下用例:
- 长路径信号传输:
- Tile间级联:多个tile之间的逻辑级联(比如:多级加法器)
- Tile内外通讯:在不同Tile实现的模块之间进行通讯(如:FIFO,AXI总线)
3.3:全芯片位流验证
对于全局资源(如:时钟,全局线)的利用进行验证。关注边界行为(I/O引脚,DDR,PCIe)。
必要构建的用例 :
- 设计一条覆盖尽大概多Tile的信号路径,从一个边界Tile流传到对角线另一侧。
- 在设计中启动全局时钟。
- 包含外部接口(I/O,DDR,PCIe)在设计中。
选用的用例,一般会是一些经典的电路设计,比如:FPGA一般会和闪存共同利用,那我们就选用一些标闪的闪存器件来搭建用例(由于闪存用例是有公开的行为模子和仿真库的)
由于是针对整个芯片的测试,以是,激励数据,应该采用jtag方式输入,涉及到PRAM的操纵。相对比较复杂,这里不再展开细说。
3.4:代码的测试覆盖率
测试是否有用,主要看覆盖率,以是,在仿真运行中输出测试覆盖率,好坏常告急的指标。
覆盖率报告通常包括以下信息:
- 总体覆盖率:
- 显示整个设计的总体覆盖率(如行覆盖率、分支覆盖率等)。
- 模块级覆盖率:
- 代码行覆盖率:
- 分支覆盖率:
- 条件覆盖率:
- 状态机覆盖率:
- 翻转覆盖率:
- 显示信号是否发生了 0 到 1 或 1 到 0 的翻转。
代码覆盖度能够查察测试的完备度。好坏常告急的参考指标。
末了我们来看看,要搭建一个主动化的验证平台,必要做一些什么样的工作:
这部分是一个封装动作,和验证自己的关系并不是很大,但如果对验证的细节不清晰,也不能搭建一个好的,可扩展的平台,由于平台的功能也不是一成稳定的,必要有一个演进的过程。如果有好的设计,自然最佳。
四:位流验证的主动化平台
4.1:平台架构
这软件EDA的位流验证的架构。如果验证是上板验证(生产测试中心)。那位流的运行就不是在仿真情况,而是在板上的物理芯片运行,然后想办法把物理芯片的运行结果,按时序取回,然后进行对比。
4.2:资源管理
必要管理的资源:
主要是流程控制的代码,Python,Shell脚本,主要是存放到Git中,应该必要管理版本,
一般会是表明性脚本语言,不会有编译/构建的过程。
每一次回归测试,我们定义为一个工程。而工程会描述全部运行的输入,输出。
工程的定义:针对某个芯片,某种验证方式,某个器件的测试,我们认为是一个验证工程。
待验证芯片——芯片的系列(架构),具体输出的芯片版本号(锁定的版本)
待验证的内容—— FullChip,或者 TILE_CLB_LUT2,或者 BRAM & DSP 的联合工程。
对应的仿真库——不同验证点,必要提供不同的仿真库,比如 fullchip和单tile必要的仿真库是不同的。但相对固定,根本上是提前准备好的。但是如果芯片的架构发生变化,必要做相应的更换。也可以理解芯片系列的版本发生变化。
对应的待测式DUT——待测试的DUT的测试用例。
DUT 对应的RefModel—— 对应的参考模子。
相干的配置信息——大概存在一些配置,大概待测内容不同,配置会有所不同。略
输出目录——包括位流输出目录(包括一些中间文件输出),终极输出目录。
每个待测工程,都必要有一个TestBench 代码,TestBench 用来定义整个测试的过程。包括如何利用天生的位流进行仿真运行,如何调用RefModel(Golden case)获得结果,两者如何进行比较,如何输出。
注意,不同测试工程,大概代码会有所不同。
测试用例是针对芯片待测单元的调用。大概必要覆盖待测单元的大多数输入组合。这些用例必要进行同一管理,在必要时,在测试工程中配置。
比如:针对 LUT1的测试用例 (只必要初始化一下真值表即可)
YY_LUT1 # ( INIT (2'h3) inst_lut1() // o = a
针对测试用例,与之对应的另外一种实现(不依靠于FPGA),直接在VCS中运行。如果必要思量时序,该模子代码必要打拍。
比如:assign o = a 就是针对上面的LUT1用例的Golden case。
配置EDA工具的运行情况,这个是可以按需配置,由于对于EDA,研发每天都会有新的版本。
4.3:调度平台
由于必要主动化,必须要有调度,这样可以定时主动执行。
可以通过脚本控制执行的入口和过程,控制超时时间,控制运行的队列(串行还是并行),可以将大任务发到计算集群。
调度很简单,一般利用 Jekins的 Pipeline即可,Jekins支持Shell,可以进行任务的编排。
4.4:位流天生
位流天生,就是利用j最新EDA和相应的flow tcl ,用来天生位流。
位流天生必要注意几个点:
大概会添加固定的物理约束,简化输出,明确实际的 IOPAD。
4.5:仿真运行
对于test bencch 运行,会有多个步调。
这里要看一下,是否必要形成有针对性的激励数据。大概和位置转换有关(实际位置可以通过位流输出的 instance和 route信息,获取实际的位置信息)。
针对 test bench代码,建议采用makefile进行编译/构建,这样比较容易针对VCS进行动态链接和引用,也定义输出的动作。
利用仿真平台,输入激励,进行仿真,输出波形,输出代码覆盖度。
对Golden Case 进行运行,输入激励,执行输出。
两者进行比较,在仿真log中输出
4.6:结果输出
收集结果,确认是否成功,输出终极报告。
大概还会输出一些必要的数据,波形,代码覆盖度之类。
五:其它
5.1:硬件设计验证
对于硬件部的位流验证,实际上根本上是划一的。不同之处主要有:
1:在EDA的位流天生功能不成熟前,必要有其它方式天生位流。
2:硬件验证不通过的情况,会关注硬件自己的设计,而不是位流的天生。关注点会有所不同。
5.2:生产测试中心位流验证
对于生产测试中心,必要实际上板测试。以是。
1:位流必要download到实际芯片上运行,激励必要实际输入到芯片。
2:参考模子与软件一样,但注意,大多数时间要思量时序,而不是简单的组合逻辑。
3:对于实际芯片的运行输入/输出,一般必要做特别处理,比如:设计专用电路,完成激励输入,设计驱动程序将输出数据写入当地磁盘,然后通过磁盘文件获取结果,进行比较。
输出的文件必要表达时序信息。
4:生产测试发现问题,关注的是硬件的可靠性。
大概就是这样了,对于仿真的实际运行的细节和实际的硬件相干,有空再说。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |