FPGA EDA软件的位流验证

打印 上一主题 下一主题

主题 844|帖子 844|积分 2532

        位流验证,对于芯片研发是一个非常告急的测试本领,对于纯软件开辟职员,最难理解的就是位流验证。在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:流程图


我们先来表明一下上面的典范流程:


  • 测试用例RTL文件
        利用FPGA的器件的用例。测试用例文件,一般是DeviceModel提供的一些适合于验证功能的标准用例。作为待测用例。(这些用例,一般是由简到繁)


  • EDA Flow
        FPGA的软件工具的运行flow,在主动化运行时,一般是采用无界面的tcl命令来执行。
        这里必要生存多个输出:
        综合/映射后的网表:这是针对全部Primitve的电路实现,可以理解为罗列出,我们的设计终极实际用到了哪些逻辑的实例。就是可以看到设计终极利用的Instance,有明确的标识名称。
        Route后输出的路由文件:这个用来定位全部实例Instance在芯片中实际的物理位置。也就是Impl后,实际上物理芯片上利用的逻辑块和相应的布线。联合上一步的输出,可以明确查到实例具体的位置,以及走线的路径。
        Bitstream 位流:这个是天生的bitstream,这个作为Dut的输入,直接加载到测试平台(如:VCS),加上提前准备好的芯片的原生的仿真库,仿真平台可以实际的模拟芯片的运行,并且得到相应的运行中结果和中间输出。


  • 用例的TestBench
       根据实际情况,天生的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—— 对应的参考模子。
        相干的配置信息——大概存在一些配置,大概待测内容不同,配置会有所不同。略
        输出目录——包括位流输出目录(包括一些中间文件输出),终极输出目录。


  • DUT Testbench
        每个待测工程,都必要有一个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 代码编译       
        针对 test bench代码,建议采用makefile进行编译/构建,这样比较容易针对VCS进行动态链接和引用,也定义输出的动作。


  • test bech 仿真运行
        利用仿真平台,输入激励,进行仿真,输出波形,输出代码覆盖度。
        对Golden Case 进行运行,输入激励,执行输出。
        两者进行比较,在仿真log中输出
4.6:结果输出

       收集结果,确认是否成功,输出终极报告。
       大概还会输出一些必要的数据,波形,代码覆盖度之类。

五:其它

5.1:硬件设计验证

        对于硬件部的位流验证,实际上根本上是划一的。不同之处主要有:
        1:在EDA的位流天生功能不成熟前,必要有其它方式天生位流。
        2:硬件验证不通过的情况,会关注硬件自己的设计,而不是位流的天生。关注点会有所不同。

5.2:生产测试中心位流验证

        对于生产测试中心,必要实际上板测试。以是。
        1:位流必要download到实际芯片上运行,激励必要实际输入到芯片。
        2:参考模子与软件一样,但注意,大多数时间要思量时序,而不是简单的组合逻辑。
        3:对于实际芯片的运行输入/输出,一般必要做特别处理,比如:设计专用电路,完成激励输入,设计驱动程序将输出数据写入当地磁盘,然后通过磁盘文件获取结果,进行比较。
        输出的文件必要表达时序信息。
        4:生产测试发现问题,关注的是硬件的可靠性。

        大概就是这样了,对于仿真的实际运行的细节和实际的硬件相干,有空再说。



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

卖不甜枣

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表