UVM面经

Phase机制

简单来说就是不同时间做不同的事,把UVM仿真阶段层次化,build phase和fianl phase都是自上而下,其他事自下而上。除了run phase耗时是task phase,其他都是function phase。

Run phase可以细分12个phase,run phase与细分的phase是并行的,但是12个phase是按照先后顺序来的。

UVM

其中,run_phase按照以下顺序自上而下执行:
pre_reset_phase
reset_phase
post_reset_phase
pre_configure_phase
configure_phase
post_configure_phase
pre_main_phase
main_phase
post_main_phase
pre_shutdown_phase
shutdown_phase
post_shutdown_phase

Phase机制怎么同步?

只有所有的component的每一个小的task phase完成,整个仿真平台才开始下一个小task phase的执行。

例如 objection 机制没有 drop objection 会发生什么情况?例如 2 个 componentA 和 B,A 啥事都没干,B 没有 drop objection,那么 A 会不会跳转到后边?

不会跳转,理由上面已经说了

OOP(面向对象编程)的特性?

封装、继承和多态

封装:把数据和使用数据的方法封装在一个集合里,成为class。

继承:允许通过现有类去得到一个新的类,并且可以共享现有类的属性和方法。现有类叫做基类,新类叫做派生类或者扩展类。

多态:得到扩展类后,有时我们会使用基类句柄去调用拓展类对象,这时候怎么判断调用对象?通过对类中方法进行virtual声明。

其实跟C++中的super很像,就是父类和子类的方法名一样,但是你可以准确调用其中的任意一个。

local数据只能自己访问,protected数据可以自己和子类访问。

  • 一个多态的例子

    animal类中有print_hometown:

    class animal;
        function void print_hometown();
            $display("my hometown is on the earth!");
        endfunction
    endclass

    同时bird和non_bird中也有自己的print_hometown函数

    class bird extends animal;
        function void print_hometown();
            $display("my hometown is in sky!");
        endfunction
    endclass
    
    class non_bird extends animal;
        function void print_hometown();
            $display("my hometown is on the land!");
        endfunction
    endclass

    再来一个print_animal的函数:

    PS: automatic就是动态,里面的变量会随着fouction的声明周期建立而摧毁。static function则是初始化只伴随其生命周期发生一次,不会因为多次调用被多次初始化。

    function automatic void print_animal(animal p_animal);
        p_animal.print();
        p_animal.print_hometown();
    endfunction

    print_animal的参数是一个animal类型的指针,如果实例化了一个bird,并且将其传递给print_animal函数,这样做是完全允许的,因为bird是从animal派生的,所以bird本质上是个animal:

    initial begin
        bird members[20];
        members[0] = new();
        members[0].init("parrot", 20091021, "bird", 20, 1);
        print_animal(members[0]);
    end

    打印出来的结果是earth,而不是想要的sky。想要正确的结果,就需要在print_animal函数中调用print_hometown之前进行类型转换。

    function automatic void print_animal2(animal p_animal);
        bird p_bird;
        non_bird p_nbird;
        p_animal.print();
        if($cast(p_bird, p_animal))
            p_bird.print_hometown();
        else if($cast(p_nbird, p_animal))
            p_nbird.print_hometown();
    endfunction

    cast是个类型转换函数,把animal向bird和non_bird转换。但是有点麻烦,重用性不高。

    含义
    父类站的高,子类在底下。

         从父类向子类的转换称为向下类型转换 (即 child_handle = father_handle,直接这样复制是不行的)
    
         反之则称为向上类型转换(即 father_handle = handle_handle)
    
         $cast 则是用于向下类型转换。
    
         在向下类型转换时,只有当父类指针指向的对象和待转换的类型一致时,cast才会成功。
    

    所以该咋做呢,就是在定义bird和non_bird时加上virtual关键字:

    class animal;
        virtual function void print_hometown2();
            $display("my hometown is on the earth!");
        endfunction
    endclass
    
    class bird extends animal;
        virtual function void print_hometown2();
            $display("my hometown is in sky!");
        endfunction
    endclass
    
    class non_bird extends animal;
        virtual function void print_hometown2();
            $display("my hometown is on the land!");
        endfunction
    endclass

    接着在print_animal3中调用此函数:

    function automatic void print_animal3(animal p_animal);
        p_animal.print();
        p_animal.print_hometown2();
    endfunction

    如果将members[0]传递给此函数后,打印结果就是”sky”,如果在initial中实例化了一个non_bird,并传递给print_animal3:

    initial begin
        non_bird members[20];
        members[0] = new();
        members[0].init("tiger", 20091101, "non_bird", 2000, 1);
        print_animal(members[0]);
    end

    打印的结果就是”land”。调用同一个函数,输出的结果却不同,表现出不同的形态,这就是多态。多态要依赖虚函数,普通函数是不能实现多态的。

Callback机制?

Callback机制其作用是提高TB的可重用性,其还可进行特殊激励的产生等,与factory类似,两者可以有机结合使用。与factory不同之处在于 callback的类还是原先的类,只是内部的callback函数变了,而factory这是产生一个新的扩展类进行替换。

  1. UVM组件中内嵌callback函数或者任务
  2. 定义一个常见的uvm_callbacks class
  3. 从UVM callback空壳类扩展uvm_callback类
  4. 在验证环境中创建并登记uvm_callback

Factory机制

其存在的意义就是为了能够方便的替换TB中的实例或者已注册的类型。一般而言,在搭建完TB后,我们如果需要对TB进行更改配置或者相关的类信息,我们可以通过使用factory 机制进行覆盖,达到替换的效果,从而大大提高TB的可重用性和灵活性。要使用factory机制先要进行:

  1. 将类注册到factory表中
  2. 创建对象,使用对应的语句 (type_id::create)
  3. 编写相应的类对基类进行覆盖。

SV中的interface的clock blocking的功能,如果clock blocking定义在下沿,最后的结果应该是什么样?

Interface是一组接口,用于对信号进行一个封装,捆扎起来。如果像 verilog中对各个信号进行连接,每一层我们都需要对接口信号进行定义,若信号过多,很容易出现人为错误,而且后期的可重用性不高。因此使用interface接口进行连接,不仅可以简化代码,而且提高可重用性,除此之外,interface内部提供了其他一些功能,用于测试平台与DUT之间的同步和避免竞争。

Clocking block:在interface内部我们可以定义clocking块,可以使得信号保持同步,对于接口的采样和驱动有详细的设置操作,从而避免TB与DUT的接口竞争,减少我们由于信号竞争导致的错误。采样提前,驱动落后,保证信号不会出现竞争。
如果clock blocking定义在下沿,可能会出现竞争

动态数组和联合数组的区别?

队列:队列结合了链表和数组的优点,可以在一个队列的任何位置进行增加或者删除元素。其通过[$]这样的符号进行申明: int q[$];

定宽数组:属于静态数组,编译时便已经确定大小。其可以分为压缩定宽数组和非压缩定宽数组:压缩数组是定义在类型后面,名字前面;非压缩数组定义在名字后面。Bit 7:0 name; bit[7:0] name [3:0];

动态数组:其内存空间在运行时才能够确定,使用前需要用new[]进行空间分配。

关联数组:其主要针对需要超大空间但又不是全部需要所有数据的时候使用,类似于hash,通过一个索引值和一个数据组成: bit [63:0] name[bit[63:0]];索引值必须是唯一的。

  1. 【关联数组】可以用来保存稀疏矩阵的元素。当你对一个非常大的地址空间寻址时,该数组只为实际写入的元素分配空间,这种实现方法所需要的空间要小得多。
  2. 此外,关联数组有其它灵活的应用,在其它软件语言也有类似的数据存储结构,被称为哈希(Hash)或者词典(Dictionary),可以灵活赋予键值(key)和数值(value) 。

UVM从哪里启动

UVM的启动
总结:1)在导入uvm_pkg文件时,会自动创建UVM_root所例化的对象/UVM_top,UVM顶层的类会提供run_test()方法充当UVM世界的核心角色,通过UVM_top调用run_test()方法。 2)在环境中输入run_test来启动UVM验证平台,run_test语句会创建一个my_case0的实例,得到正确的test_name 2)依次执行uvm_test容器中的各个component组件中的phase机制,按照顺序, 1.build-phase(自顶向下构建UVM 树) 2.connet_phase(自低向上连接各个组件) 3.end_of_elaboration_phase 4.start_of_simulation_phase 5.run_phase() objection机制仿真挂起,通过start启动sequence(每个sequence都有一个body任务。当一个sequence启动后,会自动执行sequence的body任务),等到sequence发送完毕则关闭objection,结束run_phase()(UVM_objection提供component和sequence共享的计数器,当所有参与到objection机制中的组件都落下objection时,计数器counter才会清零,才满足run_phase()退出的条件(UVM入门P45)) 5.执行后面的phase

Untitled

接口如何传递到环境中

  • 传递virtual interface到环境中;
  • 配置单一变量值,例如int、string、enum等;
  • 传递配置对象(config_object)到环境;
  • 传递virtual interface到环境中;
  1. 虽然SV可以通过层次化的interface的索引完成传递,但是这种传递方式不利于软件环境的封装和复用。通过使用uvm_config_db配置机制来传递接口,可以将接口的传递与获取彻底分离开。

    uvm_config_db#(int)::get(this, "", "pre_num", pre_num);

  2. 接口传递从硬件世界到UVM环境可以通过uvm_config_db来实现,在实现过程中应当注意:
  3. 接口传递应发生在run_test()之前。这保证了在进入build_phase之前,virtual interface已经被传递到uvm_config_db中。
  4. 用户应当把interface与virtual interface区分开来,在传递过程中的类型应当为virtual interface,即实际接口的句柄。
  • 配置单一变量值,例如int、string、enum等;

在各个test中,可以在build_phase阶段对底层组件的各个变量加以配置,进而在环境例化之前完成配置,使得环境可以按照预期运行。

  • 传递配置对象(config_object)到环境;

在test配置中,需要配置的参数不只是数量多,可能还分属于不同的组件。对这么多层次的变量做出类似上边的单一变量传递,需要更多的代码,容易出错且不易复用。如果整合各个组件中的变量,将其放置在一个uvm_object中,再对中心化的配置对象进行传递,将有利于整体环境的修改维护,提升代码的复用性。

UVM优势,为什么要用UVM

UVM其实就是SV封装,把测试中一些要用到重复性和重要的工作进行封装,让我们能够快速搭建一个测试平台。

componet与object区别

UVM中 component也是由object派生出来的,不过相比于object, component有很多其没有的属性,例如phase机制和树形结构等。在UVM中,不仅仅需要component 这种较为复杂的类,进行TB的层次化搭建,也需要object这种基础类进行TB的事务搭建和一些环境配置等。

item属于object

说一下ref类型,你用到过嘛

Ref参数类型是引用

  • 向子程序传递数组时应尽量使用ref获取最佳性能,如果不希望子程序改变数组的值,可以使用const ref类型
  • 在任务里可以修改变量而且修改结果对调用它的函数随时可见。

Virtual sequencer 和sequencer区别

Virtual sequencer主要用于对不同的agent进行协调时,需要有一定顶层的sequencer对内部各个agent中的sequencer进行协调

sequence, sequence,sequencer,driver之间的通信

TLM通信

Untitled

Untitled

Untitled

代码覆盖率、功能覆盖率和断言覆盖率的区别

代码覆盖率——是针对RTL设计代码的运行完备度的体现,包括行覆盖率、条件覆盖率、FSM覆盖率、跳转覆盖率、分支覆盖率,只要仿真就可以收集,可以看DUT的哪部分代码没有动,如果有一部分代码一直没动看一下是不是case没有写到。

功能覆盖率---与spec比较来发现,design是否行为正确,需要按verification plan来比较进度。用来衡量哪些设计特征已经被测试程序测试过的一个指标,首要的选择是使用更多的种子来运行现有的测试程序;其次是建立新的约束,只有在确实需要的时候才会求助于定向测试,改进功能覆盖率最简单的方法是仅仅增加仿真时间或者尝试新的随机种子。验证的目的就是确保设计在实际环境中的行为正确。设计规范里详细说明了设备应该如何运行,而验证计划里则列出了相应的功能应该如何激励、验证和测量

断言覆盖率---用于检查几个信号之间的关系,常用在查找错误,主要是检查时序上的错误,测量断言被触发的频繁程度。

用过断言嘛?写一个断言,a为高的时候,b为高,还有a为高的时候,下一个周期b为高

交叠蕴含操作符符号为 |->

交叠蕴含操作符中,若先验部分验证成功,则后验结果在同一时钟进行验证。如下例所示,若某时钟处a变为高电平,则同一时刻b也应为高电平。

非交叠蕴含操作符(Non-overlapped implication)

非交叠蕴含操作符符号为|=>。

非交叠蕴含操作符在先验部分验证匹配后,在下个时钟沿才进行后验部分的检查匹配。相对于蕴含操作符后验部分有一个时钟的滞后。如下例所示,在时钟边沿a为高电平则一个时钟后b也应为高电平。

a_high_then_b_high:assert property(); //a和b
property a_high_then_b_hight;
    @(posedge clk)
        a|->b;
endproperty

property a_high_then_b_high; //a高,b下一个周期高
    @(posedge clk)
        a|=>b;
endproperty

立即断言和并行断言

Untitled

形式验证

形式验证指从数学上完备地证明或验证电路的实现方案是否确实实现了电路设计所描述的功能。形式验证方法分为等价性验证、模型检验和定理证明等。
形式验证主要验证数字IC设计流程中的各个阶段的代码功能是否一致,包括综合前RTL代码和综合后网表的验证,因为如今IC设计的规模越来越大,如果对门级网表进行动态仿真,会花费较长的时间,而形式验证只用几个小时即可完成一个大型的验证。另外,因为版图后做了时钟树综合,时钟树的插入意味着进入布图工具的原来的网表已经被修改了,所以有必要验证与原来的网表是逻辑等价的

如何保证验证的完备性?

首先不可能百分百完全完备,即遍历所有信号的组合,这既不经济也不现实。所以只能通过多种验证方法一起验证尽可能减少潜在风险,一般有这些验证流程:ip级验证、子系统级验证、soc级验证,除这些以外,还有upf(Unified Power Format,低功耗)验证、fpga原型验证等多种手段。前端每走完一个阶段都需要跟设计以及系统一起review验证功能点,测试用例,以及特殊情况下的波形等。

芯片后端也会做一些检查,像sta(静态时序)、formality(形式验证)、DFM(Design for Manufacturing) ——可制造性设计、DRC(Design rules checking)检查等,也会插入一些DFT(design for test)逻辑供流片回来测试用。流片归来进行测试,有些bug可以软件规避,有些不能规避,只能重新投片

启动Sequence的方法

  • 通过start的方式显示启动
  • 通过default sequence来隐式启动

面向对象编程的优势

1、易维护
采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护也只是在局部模块,所以维护起来是非常方便和较低成本的。
2、质量高
在设计时,可重用现有的,在以前的项目的领域中已被测试过的类使系统满足业务需求并具有较高的质量。
3、效率高
在软件开发时,根据设计的需要对现实世界的事物进行抽象,产生类。使用这样的方法解决问题,接近于日常生活和自然的思考方式,势必提高软件开发的效率和质量。
4、易扩展
由于继承、封装、多态的特性,自然设计出高内聚、低耦合的系统结构,使得系统更灵活、更容易扩展,而且成本较低。

约束的几种形式

随机化是SV中极其重要的一个知识点,通过设定随机化和相关约束,我们可以自动随机想要的数据。

权重约束 dist:有两种操作符::=n :/n 第一种表示每一个取值权重都是n,第二种表示每一个取值权重为n/num。
条件约束 if else 和->(case):if else 就是和正常使用一样;->通过前面条件满足后可以触发后面事件的发生。
范围约束inside:inside{[min:max]};范围操作符,也可以直接使用大于小于符号进行,不可以连续使用,如 min<wxm<max 这是错误的

哪些继承于component,哪些继承于object

除了driver、monitor、agent、model、scoreboard、env、test之外全部用uvm_object。

get_next_item()和try_next_item()有什么区别

get_next_item()是一个阻塞调用,直到存在可供驱动的sequence item为止,并返回指向sequence item的指针。
try_next_item()是非阻塞调用,如果没有可供驱动的sequence item,则返回空指针。

UVM的树形结构

Untitled

断言 and 和 intersect 区别

And 指的是两个序列具有相同的起始点,终点可以不同。
Intersect 指的是两个序列具有相同的起始点和终点。
Or 指的是两个序列只要满足一个就可以
Throughout 指的是满足前面要求才能执行后面的序列

Task和function的区别

  • 函数能调用另一个函数,但不能调用任务,任务能调用另一个任务,也能调用另一个函数
  • 函数总是在仿真时刻0就开始执行,任务可以在非零时刻执行
  • 函数一定不能包含任何延迟、事件或者时序控制声明语句,任务可以包含延迟、事件或者时序控制声明语句
  • 函数至少有一个输入变量,可以有多个输入变量,任务可以没有或者多个输入(input)、输出(output)和双向(inout)变量
  • 函数只能返回一个值,函数不能有输出(output)或者双向(inout)变量,任务不返回任何值,任务可以通过输出(output)或者双向(inout)变量传递多个值

触发器和锁存器的区别

触发器:时钟触发,受时钟控制,只有在时钟触发时才采样当前的输入,产生输出。
锁存器由电平触发,非同步控制。在使能信号有效时锁存器相当于通路,在使能信号无效时锁存器保持输出状态。触发器由时钟沿触发,同步控制。
锁存器对输入电平敏感,受布线延迟影响较大,很难保证输出没有毛刺产生;触发器则不易产生毛刺

一个简单的UVM验证平台

Untitled

怎么编写测试用例?

主要是编写sequence,然后在body里面根据测试功能要求写相应的激励,然后再通过reference model和checker判断功能是否实现?

测试用例很多,怎么自动执行?

脚本呗,makefile,shell方法多的很。

断言$past的用法

Untitled

如何打开和关闭约束

通过constraint_mode(0)关闭默认范围的约束块
constraint_mode(1)是打开约束
可以用soft关键字修饰特定的约束语句,这样既可以让变量在一般的情况下取默认值,也可以直接给变量赋默认值范围外的取值。

队列的使用方法,以及push back和pop front的区别

队列的使用方法:insert,delete,push_back和pop_front
Push插入,pop取出
Front前边,back后边

Rand 和randc的区别

rand修饰符:rand修饰的变量,每次随机时,都在取值范围内随机取一个值,每个值被随机到的概率是一样的,就像掷骰子一样。
randc修饰符:randc表示周期性随机,即所有可能的值都取到过后,才会重复取值

组件之间的通信机制,analysis port和其它的区别1

1、通信分为,单向通信,双向通信和多向通信
单向通信:指的是从initiator到target之间的数据流向是单一方向的
双向通信:双向通信的两端也分为initiator和target,但是数据流向在端对端之间是双向的
多向通信:仍然是两个组件之间的通信,是指initiator与target之间的相同TLM端口数目超过一个时的处理解决办法。
2、blocking阻塞传输的方法包含:
Put():initiator先生成数据Tt,同时将该数据传送至target。
Get():initiator从target获取数据Tt,而target中的该数据Tt则应消耗。
Peek(): initiator从target获取数据Tt,而target中的该数据Tt还应保留。

3、通信管道:

  • TLM FIFO:可以进行数据缓存,功能类似于mailbox,不同的地方在于uvm_tlm_fifo提供了各种端口(put、get、peek)供用户使用
  • analysis port:一端对多端,用于多个组件同时对一个数据进行处理,如果这个数据是从同一个源的TLM端口发出到达不同组件,则要求该端口能够满足一端到多端,如果数据源端发生变化需要通知跟它关联的多个组件时,我们可以利用观察者模式实现,即广播模式
  • analysis TLM FIFO
  • 由于analysis端口提出实现了一端到多端的TLM数据传输,而一个新的数据缓存组件类uvm_tlm_analysis_fifo为用户们提供了可以搭配uvm_analysis_port端口uvm_analysis_imp端口和write()函数。
  • uvm_tlm_analysis_fifo类继承于uvm_tlm_fifo,这表明它本身具有面向单一TLM端口的数据缓存特性,而同时该类又有一个uvm_analysis_imp端口analysis_export并且实现了write()函数:
  • request & response通信管道

双向通信端口transport,即通过在target端实现transport()方法可以在一次传输中既发送request又可以接收response。

寄存器模型的好处

  1. 复用性更好,将总线访问寄存器抽象成寄存器模型访问的方式,使得后期地址修改(比如基地址更改,因为偏移地址都是固定的)或者域的添加都不会对已有的激励构成影响,从而提高复用性。
  2. 使得配置寄存器序列(reg的sequence)时更加可读(主要是从地址映射成了一个reg名字)。(如果你通过一个总线形式去访问硬件寄存器,那么是按地址来进行访问的,可读性较差(你可以选择用一些verilog定义的全局变量起到像枚举类型的作用),用寄存器模型能直接看出对哪个寄存器、对寄存器中哪个域在做操作(比如在随机化一个32位但是有多个域的reg,直接分段随机化的可读性在不了解设计的情况下很差,而随机化reg_field则很容易读懂)
  3. register model不仅可以发送激励(所以rgm也是个uvm_object),还可以用来做测试(有内建的序列可以测试register model),比如检查每个field如复位值和硬件是否一直
  4. 新增寄存器覆盖率的检查(可以在reg内部或者外部利用继承于uvm_subscriber自定义一个covergroup)。
  5. 用来检查硬件寄存器,以及配合scoreboadr来实时检查DUT功能。(refmodel被集成到scoreboard里,而在模拟的时候可以通过regmodel对设计的配置来模拟打包)
  6. 更方便对寄存器进行操作,前门访问和后门访问。在用前门访问验证了寄存器访问的物理通路工作正常的情况下,可以在后续测试中用后门访问节省时间。
  7. 有mirror value 和desired value 去跟踪寄存器的值,还能起到一个相当于影子存储的作用

Verilog问题

一个最简单的八位加法器应该怎么验证?才有完备性?

  • 首先拿到这个加法器时先看基本逻辑,比如有没有复位信号,有复位信号,验证复位功能是否正常
  • 然后从功能角度,这个加法器是只用来做正数加正数还是负数的运算也可以,所以应验证其正数+正数,正数+负数以及负数+负数
  • 从数据随机的角度来说00001111这种边界情况需要测,还有随机出现数据
  • 最后还要考虑模块出错的情况,如果我们的数据源出现了X值,这个加法器模块会怎么处理
  • 加法器如果没有带时序逻辑进行采样,就是一个组合逻辑电路,还可以验证是否出现毛刺(待定)

如何测试FIFO

  • FIFO深度检查
  • FIFO位宽检查
  • FIFO空满判断
  • FIFO满后再继续写数据,或者是FIFO空后再继续读数据,会发生什么
  • FIFO清空检查,软件复位后可以清空FIFO嘛

异步FIFO的测试点

  • 同时读写,读写数据正确检查
  • FIFO满标志位检查
  • FIFO空标志位检查
  • 写过程中发生写复位,写数据和FIFO满标志位被清空
  • 读过程中发生读复位,读数据和读空标志位被清空
  • 读写时钟相位相同,异步FIFO能正常工作
  • 读写时钟相位不同,异步FIFO能正常工作
  • 写时钟等于读时钟,异步FIFO能正常工作
  • 写时钟快于读时钟,异步FIFO能正常工作
  • 写时钟慢于读时钟,异步FIFO能正常工作
  • 写过程中发生写复位以后,异步FIFO 能继续正常工作
  • 读过程中发生读复位以后,异步FIFO 能正常工作
  • FIFO满以后,继续往FIFO写数据,异步FIFO不会被卡死,数据被读走以后,异步FIFO 能继续正常工作

跨时钟域

单bit(慢时钟域到快时钟域):用快时钟打两拍,直接采一拍大概率也是没问题的,两拍的主要目的是消除亚稳态;
单bit(快时钟域到慢时钟域):握手、异步FIFO、异步双口RAM;快时钟域的信号脉宽较窄,慢时钟域不一定能采到,可以通过握手机制让窄脉冲展宽,慢时钟域采集到信号后再“告诉”快时钟域已经采集到信号,确保能采集到;
多bit:异步FIFO、异步双口RAM、握手、格雷码;
多bit中,强烈推荐异步FIFO,我在实际工程中使用多次,简单方便。

状态机

Moore 输出只与当前状态有关,与Input无关

Mealy 输出与当前状态和Input有关

三段式:在两个always模块描述方法基础上,使用三个always模块,一个always模块采用同步时序描述状态转移,一个always采用组合逻辑判断状态转移条件,描述状态转移规律,另一个always模块描述状态输出(可以用组合电路输出,也可以时序电路输出)。

对于建立时间违例的解决办法按优先级有

1、首先是修改代码,找到关键路径上的源寄存器和目的寄存器,拆分它们之间的组合逻辑。插入寄存器是最简单粗暴的办法,实际上在设计之初,如果我们精心设计的话,是可以在算法级对组合逻辑进行分解的,好的时序是设计出来的,不是约束出来的;
2、如果修改代码实在解决不了,在使用约束关键路径的办法;
3、应该还有其他很多办法,不过我暂时还不知道;
如果上面都解决不了,我们还可以:
4、买更好更快的芯片,更好的芯片意味着更低的Tsu要求,更小的Tpd、Tcomb;
5、降低工作频率,即提高时钟周期。

对于保持时间违例的解决办法按优先级有

1、增加Tdata = (Tcomb+T逻辑组合布线延迟),具体操作是在data line上(两个触发器之间的组合逻辑块部分)插入buffer,反相器或者delay cell去增加Tdata;
2、增加前级D触发器的驱动延迟;
3、减小Tskew,减小时钟偏移;

综合问题

IC验证的流程

1、搞清楚要验证的东西

对设计spec进行阅读和理解,把DUT的结构、功能,时序弄清楚。

2、编写验证计划,指导性的文件

(1)提取验证功能点

(2)明确DUT接口信号(所有信号的名字,位宽,功能,时序关系等)

(3)TB的架构(能够描述每一个组件的功能)

(4)检查点(check point)

(5)功能覆盖率(覆盖点)

(6)测试用例的规划(testcase尽可能的规划完整)

(7)结束标准

3、搭建TB&Debug&调通第一个最基本的testcase,然后再编写测试用例debug,进行主要验证。

4、Regression(回归测试,一天一次,有随机测试)

5、分析代码/功能覆盖率,增加新的测试用例

6、测试报告,测试结果:测试用例(pass/fail),覆盖率报告

基本逻辑门电路和符号

Untitled

Untitled

Last modification:March 11, 2022
恰饭环节