Maggie • 来自相关话题

2月前

静态时序分析基础

建立时间,保持时间    为了确保寄存器在时钟沿稳定采集数据,那么必须要满足寄存器的建立,保持时间要求。建立时间要求:在寄存器有效时钟沿之前至少Tsetup时间,数据必须到达且稳定。如下图所示。     保持时 ...查看全部

建立时间,保持时间
    为了确保寄存器在时钟沿稳定采集数据,那么必须要满足寄存器的建立,保持时间要求。

建立时间要求:在寄存器有效时钟沿之前至少Tsetup时间,数据必须到达且稳定。如下图所示。


     保持时间要求:在数据采集有效时钟沿之后,数据必须维持最短Thold时间不变。如下图所示。


      建立时间裕量计算
同步时序电路如下图所示。这里对后面一个寄存器进行建立时间裕量分析。


     其中寄存器的输出延时为Tcq,即时钟有效沿之后延时Tcq时间,数据才到达寄存器Q端。第一个寄存器有效时钟沿之后Tcq时间,数据才到达Q1端,如下图所示。


     再经过组合逻辑运算,得到数据C1,其中组合逻辑的延时为Tlogic。时序如下图。


      从这里可以看到,如果clk2和clk1之间没有延时。那么对于后面的寄存器而言,数据提前于采样时钟沿之前Tcycle-(Tcq+Tlogic)时间到来,且稳定。其中Tcycle为时钟周期。


     由于寄存器要求的建立时间为Tsetup,故电路必须满足 Tcycle -(Tcq+Tlogic)- Tsetup>0 。建立时间裕量 Tset_slack = Tcycle -(Tcq+Tlogic)- Tsetup

如果clk1与clk2之间存在skew,如下图所示,如正skew。得建立时间裕量为

Tset_slack = Tcycle -(Tcq+Tlogic)- Tsetup + Tskew

这是对建立时间有益的。


    保持时间裕量计算
在时钟没有skew的情况下。在后一个寄存器有效采样时钟沿之后,同时新的C1只有经过Tcq+Tlogic之后才会到达。故要满足保持时间要求,只需要满足Tcq+Tlogic>Thold。保持时间裕量Thold_slack = Tcq+Tlogic-Thold


如果clk1与clk2之间存在skew。如下图所示(正skew)。


可以看到,C1_NEW在clk2时钟沿后,Tlogic+Tcq-Tskew就改变了。此时保持时间裕量Thold_slack = Tcq+Tlogic-Thold - Tskew 。可以看到,正skew对保持时间是无益的。

总结
可以看到,必须满足建立时间要求和保持时间要求,级满足如下关系式。

Tset_slack = Tcycle -(Tcq+Tlogic)- Tsetup + Tskew>0

Thold_slack = Tcq+Tlogic-Thold - Tskew > 0

从中可以看出,建立时间与保持时间要求是一对矛盾的关系,在设计电路时,我们要折中考虑。

Maggie • 来自相关话题

2月前

UVM验证平台中的常用机制(总结&概述)

    一个完整的UVM验证平台应该具有一些基本功能:产生并驱动激励,与DUT行为一致的理想参考模型reference_model(golden_model),监测并收集DUT和reference_mode的输入/输出信号,比较并判断DUV ...查看全部

    一个完整的UVM验证平台应该具有一些基本功能:产生并驱动激励,与DUT行为一致的理想参考模型reference_model(golden_model),监测并收集DUT和reference_mode的输入/输出信号,比较并判断DUV和reference_mode的输出是否一致且符合预期结果,等。这些基本的功能由相应的组件来完成。那么,这些组件之间是如何可靠、高效的完成验证工作的?或者说,UVM方法学中有哪些特有的机制可以保证验证平台自动的、可靠的并且高效率的完成验证工作呢?这篇内容将对一些常见的UVM机制进行一些简单的总结。
    常见的UVM机制:
    1.field_automation机制
    对field_automation最直观的感受是,他可以自动实现copy、compare、print等三个函数。当使用uvm_field系列相关宏注册之后,可以直接调用以上三个函数,而无需自己定义。这极大的简化了验证平台的搭建,尤其是简化了driver和monitor,提高了效率。    
    2.config_db机制
    config_db机制在UVM验证平台中主要用于参数传递。它们通常都是成对出现的,set函数是寄信,而get函数是收信。函数格式如下:
    uvm_config_db# (Type)::set(this,"inst_path","field",value); //将value寄送给 “field(字段)”
    uvm_config_db# (Type)::get(this,"","field",field)      //field将接收参数的传递
    结果得到:field = value;    
    所谓“字段”,是变量名包含的内容。在set和get函数中为第三个参数,字段在set和get中必须保持一致,才能保证参数的正确传递。例如要将参数值10,传递给int类型的变量data,则:
    uvm_config_db# (int)::set(this,"env.i_agt.drv","dat",10); //将10寄送给env组件中的成员变量“data”
    uvm_config_db# (int)::get(this,"","dat",data)      //字段“dat”保持一致,成员变量data将接收参数的传递
    结果得到:data = 10;    
    3.objection机制
    UVM中通过objection机制来控制验证平台的关闭,需要在drop_objection之前先raise_objection。验证在进入到某一phase时,UVM会收集此phase提出的所有objection,并且实时监测所有objection是否已经被撤销了,当发现所有都已经撤销后,那么就会关闭此phase,开始进入下一个phase。当所有的phase都执行完毕后,就会调用$finish来将整个验证平台关掉。如果UVM发现此phase没有提起任何objection,那么将会直接跳转到下一个phase中。
    UVM的设计哲学就是全部由sequence来控制激励生成,因此一般情况下只在sequence中控制objection。另外还需注意的是,raise_objection语句必须在main_phase中第一个消耗仿真时间的语句之前。
    4.factory机制
    factory机制的优势在于其具有重载功能。重载并不是factory机制的发明,只是factory机制的重载与这些重载都不一样。要想使用factory机制的重载功能,必须满足以下要求:
    1) 无论是重载的类(parrot)还是被重载的类(bird),都要在定义时注册到factory机制中。
    2) 被重载的类(bird)在实例化时,要使用factory机制的方式进行实例化,而不能使用传统的new的方式。
    3) 最重要的是,重载的类(parrot)要与被重载的类(bird)有派生关系。重载的类必须派生自被重载的类,被重载的类必须是重载类的父类。
    4) component与object之间互相不能重载。虽然uvm_component是派生自uvm_object,但是这两者根本不能重载。因为,从两者的new参数的函数就可以看出来,二者互相重载时,多出来的一个parent参数会使factory机制无所适从。
    当然,factory机制的实现被集成在了一个宏中:uvm_component_utils。这个宏最主要的任务是,将字符串登记在UVM内部的一张表中,这张表是factory功能实现的基础。只要在定义一个新的类时使用这个宏,就相当于把这个类注册到了这张表中。这样,factory机制可以实现:根据一个字符串自动创建一个类的实例,并且调用其中的函数(function)和任务(task),这个类的main_phase就会被自动调用。
    5.callback机制
    在UVM验证平台中,callback机制最大的用处就是提高验证平台的可重用性。很多情况下,验证人员期望在一个项目中开发的验证平台能够用于另外一个项目。但是,通常来说,完全的重用是比较难实现的,两个不同的项目之间或多或少会有一些差异。如果把两个项目不同的地方使用callback函数来做,而把相同的地方写成一个完整的env,这样重用时,只要改变相关的callback函数,env可完全的重用。
    除了提高可重用性外,callback机制还用于构建异常的测试用例,VMM用户会非常熟悉这一点。只是在UVM中,构建异常的测试用例有很多种方式,如factory机制重载,而callback机制只是其中的一种。
    6.phase机制
    在不同的时间做不同的事情,这就是UVM中phase的设计哲学。但是仅仅划分成phase是不够的,phase的自动执行功能才极大方便了用户。当new语句执行完成后,后边的connect语句肯定就会自动执行。现引入phase概念,将前面的new的部分包裹进build_phase里面,把后边的connect语句包裹进connect_phase里边,很自然的,当build_phase执行结束后就应该自动执行connect_phase。
    phase的引入在很大程度上解决了因代码顺序杂乱可能会引起的问题。遵循UVM的代码顺序划分原则(如build_phase做例化工作,connect_phase做连接工作等),可以在很大程度上减少验证平台开发者的工作量,使其从一段杂乱的工作中解脱出来。
    UVM中的phase按照其是否消耗仿真时间($time打印出的时间)的特性,可以分成两大类:一类是function_phase,如产生build_phase、connect_phase等,这些phase都不消耗仿真时间,通过函数来实现;另一类是task_phase,如run_phase等,他们消耗仿真时间,通过任务来实现。给DUT施加激励、监测DUT的输出都是在这些phase中完成的。
    所有的phase按照以下顺序自上而下自动执行:
    build_pase
    connect_phase
    end_of_elaboration_phase
    start_of_simulation_phase
    *run_pase*
    extract_phase
    check_phase
    report_phase
    final_phase

    其中,*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

    7.sequence机制
    sequence机制用于产生激励,它是UVM中最重要的机制之一。sequence机制有两大组成部分:sequence和sequencer。在整个验证平台中sequence处于一个比较特殊的位置。sequence不属于验证平台的任何一部分,但是它与sequencer之间有着密切的关系。只有在sequencer的帮助下,sequence产生的transaction才能最终送给driver;同样,sequencer只有在sequence出现的情况下才能体现出其价值,如果没有sequence,sequencer几乎没有任何作用。除此之外,sequence与sequencer还有显著的区别。从本质上说,sequencer是一个uvm_component,而sequence是一个uvm_object。与my_transaction一样,sequence也有其生命周期。它的生命周期比my_transaction要更长一点,其内部的transaction全部发送完毕后,它的生命周期也就结束了。
    8.通信机制TLM1.0
    UVM中,通常使用TLM(Transaction Level Modeling)实现component之间transaction级别的通信。TLM现在已经发展到TLM2.0,是对TLM1.0的扩展。但是TLM1.0足以满足大多数完整的验证平台的通信要求,所以主要学习TLM1.0。关于TLM1.0通信机制,后续将会详细介绍。

Maggie #面试经验 • 来自相关话题

2月前

数字IC笔试题之复旦微

1、Recovery check 和 Removal check的概念? 怎么保证不出现问题 ?(有关异步信号/异步复位)  对于一个异步复位寄存器来说,异步复位信号需要和时钟满足recovery time和removal time 才能有 ...查看全部

1、Recovery check 和 Removal check的概念? 怎么保证不出现问题 ?(有关异步信号/异步复位)

  对于一个异步复位寄存器来说,异步复位信号需要和时钟满足recovery time和removal time 才能有效进行复位和复位释放操作,防止输出亚稳态。

 1.1 Recovery time:恢复时间

 撤销复位时,恢复到非复位状态的电平必须在时钟有效沿来临之前的一段时间到来,才能保证有效地恢复到非复位状态,此段时间为recovery time。类似于同步时钟的setup time。

12.png

 如图所示,rst_n为0表示复位,clk上升沿触发,rst_n从0到1的上升沿与时钟上升沿之间地时间差必须不小于recovery time才能保证寄存器恢复到正常状态。

 1.2 Removal time:去除时间

 复位时,在时钟有效沿来临之后复位信号还需要保持的时间为去除时间removal time。类似同步时钟hold time。

13.png

 如图所示,rst_n为0表示复位有效,clk为上升沿触发,rst_n保持为0经过clk上升沿后仍需要保持一段时间,才能保证寄存器有效复位,防止亚稳态。

2、 画出 Y=A+BC 的 COMS 电路?

 

14.png

 

15.png

3、SDF 文件在 IC 设计中哪个阶段使用?文件包含了哪些信息?

 SDF : Standard delay format,标准延时格式,是IEEE标准,它描述设计中的时序信息,包括cell delay 和wire delay。cell delay是指模块内部的延时,wire delay是器件互连的延时。 

4、画出 4 分频电路?

16.png 


5、画出 assign out = (a[3:0] != 4’b0001)的门电路?

a[3:0] == 4’b0001  ->  out = 1’b0 ;

a[3:0] != 4’b0001   ->  out = 1’b1 ;

 

17.png

6、分析以下电路功能?

 18.png

上图左边为一个16分频的电路。

当 A 为低电平时,5个D触发器全部复位,Y输出低电平

当 A 为高电平时,左边4个触发器开始分频计数,计数到第4个触发器输出高电平(上升沿,分频器半个周期,8个CLK周期)时,Y输出高电平。

19.png

即个人觉得功能是检测输入A高电平脉冲宽度

7、写 verilog 代码。

输入 in,输出 out,对输入 in 维持的周期进行计数 N:

如果 N<4,则 out 为 0,

如果 N>4,则将 out 拉高,并保持 N/4个周期数,限定 N/4 不大于 6

 

20.png

使用状态机,

current_state == 0 : IDLE

current_state == 1 : 计数

current_state == 2 :  计算out高电平持续周期

current_state == 3 : 拉高out

Maggie #面试经验 • 来自相关话题

2月前

数字IC设计笔试问题系列--1(50题)

1、如何将XOR门转换为buffer和inverter(仅使用一个XOR门)?                          ...查看全部

1、如何将XOR门转换为buffer和inverter(仅使用一个XOR门)?

                                                    Buffer

1.png

                                                    Inverter

2.png

2、使用2x1 MUX实现2输入AND

3.png

3、什么是多路复用器(mux)?

多路复用器是一种组合电路,它从许多输入信号中选择一个作为输出。

4、什么是环形振荡器?

环形计数器是一种由循环移位寄存器组成的计数器。最后一个移位寄存器的输出被馈送到第一个寄存器的输入。例如,在4寄存器计数器中,初始寄存器值为1100,重复模式为:1100,0110,0011,1001,1100,依此类推。

5、比较同步和异步复位。

同步复位逻辑将综合较小的触发器,但是组合逻辑门数会增加。时钟可以滤除复位信号的毛刺,但是如果这些毛刺发生在有效时钟边沿附近,则触发器可能会变成亚稳态。在某些设计中,复位必须由一组内部逻辑生成的。建议对这些类型的设计进行同步复位,因为它会过滤时钟之间的毛刺。

同步复位的问题在于综合工具不能容易地将复位信号与任何其他数据信号区分开。同步复位需要在数据路径中增加额外的逻辑和延迟以处理同步复位。 

异步复位:使用异步复位,不会在数据路径增加额外的逻辑门。异步复位的主要问题是复位释放。如果复位的释放发生在时钟边沿或者接近时钟边沿,则触发器可能进入亚稳态。

6、什么是Johnson计数器?

Johnson计数器将最后一个移位寄存器的输出反相连接到它的输入。例如,在包含4个寄存器Johnson计数器中,重复模式是:0000,1000,1100,1110,1111,0111,0011,0001,依此类推。

7、在4Johnson计数器中有多少未使用的状态?

4Johnson计数器:0000,1000,1100,1110,1111,0111,0011,0001,0000

存在8个未使用的状态。

8、使用最少数量的2个输入与非门设计3输入与非门。

4.png

9、如何将JK触发器转换为D触发器?

5.png

10、触发器和锁存器之间有什么区别?         

触发器是边沿敏感,锁存器是电平敏感。触发器不受毛刺的影响,锁存器对毛刺很敏感。锁存器比触发器需要更少数量的门,功耗更低。锁存器比触发器快。

11、Mealy型状态机和Moore 型状态机有什么区别?

Mealy型状态机输出取决于输入和状态。Moore 型状态机输出仅取决于状态。

12、什么是状态编码技术? 解释一下。

One-Hot encoding每个状态由1位触发器表示。 如果有四种状态,那么它需要四个触发器来表示当前状态。 有效状态值为1000,0100,00100001.如果值为0100,则表示第二个状态是当前状态。

One-Cold encoding::与One-Hot encoding:相同,只是'0'是有效值。如果有四种状态,那么它需要四个触发器来表示当前状态。有效状态值是0111,1011,1101和1110。

Binary encoding:每个状态由二进制代码表示。具有“2^N”状态的有限状态机仅需要N个触发器。 

  Gray encoding: 每个状态由格雷码表示。具有“2^N”状态的有限状态机仅需要N个触发器。

13、什么是时钟偏移(Clockskew),负时钟偏移,正时钟偏移?

时钟偏移是同步电路中的一种现象,时钟信号在不同时间到达不同的时序器件。这可能是由许多不同的因素造成的,例如导线互连长度,温度变化,电容耦合等。

时钟偏差有两种类型:负偏斜和正偏斜。当时钟到达接收寄存器比到达发送寄存器时,会发生正偏移。负偏斜是相反的:接收寄存器比发送寄存器更早地获得时钟触发。

14、给出CMOS NAND门的晶体管级电路。

6.jpg

15、什么是亚稳态(Metastability)。

如果在任何时序电路中存在建立(setup time)和保持时间(hold time)违例,则输出会进入不可预测的状态,该状态称为亚稳状态。在亚稳状态结束时,触发器稳定至逻辑高或逻辑低。

16、什么是setup时间和hold时间

setup时间是时钟边沿到达之前的数据需要稳定的时间。

hold时间是在时钟边沿到达之后数据需要稳定的时间。

当触发器中存在setuphold时间违例时,它就进入输出不可预测的状态,这被称为亚稳状态。

17、画出二分频电路

7.jpg

18、给出两种将两输入NAND门转换为反相器的方法。

8.png

19、使用2x1多路复用器实现D锁存器。

9.jpg


20、如果A * B = CC * A = B那么,布尔运算符*是什么?

  * is 异或

21、使用PLA实现功能:X = A'BC+ ABC + A'B'C'Y = ABC + AB'C

可编程互连线

10.jpg

22、什么是LUT

LUT  - 查找表。查找表可以用多路复用器实现,其选择是LUT的输入。LUT可以通过真值表来编码任何布尔函数。LUT实际上是现代FPGA的关键组件。

23FPGA在现代电子产品中的重要意义是什么?

  • ASIC原型设计:由于ASIC芯片的高成本,首先在FPGA中验证。

  • 数字信号处理

24、什么是bus

总线是一组承载数据,地址和控制信号的线。

25、什么是三态逻辑?

使用三个逻辑电平,它们是高,低和高阻抗状态。高和低是逻辑1和零电平。高阻抗状态是电气开路状态。三态逻辑有enable端。

26、什么是冯·诺依曼架构?

冯·诺依曼体系结构是一种计算机设计模型,它使用单独的存储结构来保存指令和数据。从存储器读取指令/数据并由处理单元执行。

27function和task有什么区别?

function                                                                                    

可以调用其他function,但是不能调用其他task  

消耗0仿真时间                     

不得包含任何延迟和时序控制语句。         

必须至少有一个输入参数。 

函数始终返回单个值。他们不能有outputinout参数。 

task

可以调用其他function 和 task

消耗非0仿真时间

可以包含任何延迟和时序控制语句。

可以有零个或多个类型为inputoutputinout参数。

outputinout参数。 任务不返回值,但可以通过outputinout参数传递多个值。

28$ display$ monitor有什么区别?

$ monitor持续监视参数列表中指定的变量或信号的值,并在任何一个变量/参数的值发生变化时执行该语句。

$ display不同,$ monitor只需要调用一次。

29wirereg有什么区别?

Wire是一种net数据类型,表示硬件元素之间的连接。 它的默认值是z reg是寄存器数据类型,表示数据存储元素。 寄存器保留值,直到将另一个值放在它们上面。 它的默认值是x

30、阻塞赋值和非阻塞赋值有什么区别?

阻塞赋值语句按顺序块中指定的顺序执行。 阻塞赋值不会阻止并行块中的语句执行。“=”运算符用于指定阻塞赋值。

非阻塞赋值不阻止顺序块中的语句。“<=”运算符用于指定非阻塞赋值。

31、使用和不使用临时寄存器写一个verilog代码来交换两个寄存器的内容。

With a temporary register:

always @ (posedge clock) begin
  temp_reg=b;
  b=a;
  a=temp_reg;
  end

Without using a temporary register:

always @ (posedge clock) begin
  a < = b;
  b < = a;
 end

32`timescale 1 ns / 1ps verilog代码中表示什么?

这意味着时间单位为1ns,精度最高可达1ps

33、在纯组合电路中,是否有必要在敏感列表中的列入所有输入?为什么?

 是的,在组合电路中,如果其中一个输入发生变化,则输出重新计算。因此,必须将所有输入信号放在敏感列表中。

34、如何使用verilog编码生成正弦波?

使用CORDIC算法。

35、如何在Verilog中描述同步和异步复位?

Synchronous reset:always @(posedge clk)begin  
        -- 
        if(reset)  
          --
        end

Asynchronous reset:always @(posedge clk or posedge reset)begin 
        -- 
        if(reset) 
         --
        end

36、是否可以优化Verilog代码,以便实现低功耗设计?

是。尝试优化代码,以减少数据转换和filp-flops的时钟切换。

37、为什么现有的VLSI电路使用MOSFET而不是BJT

BJT相比,MOSFET可以面积更小并且制造相对简单。

38、什么是阈值电压?

栅极和源极之间的电压值,即VGS,其中足够数量的移动电子在沟道区域中累积以形成导电沟道被称为阈值电压(Vt对于NMOS是正的而对于PMOS是负的)。

39、给出CMOS开关功耗的表达式。

Pswitching = (1/2)CVdd2/f
Pswitching = Switchingpower.
C = Load capacitance.
V
dd = Supplyvoltage.
f = Operating frequency.

40VLSI优化的重要方面是什么?

Power, Area, and Speed.

41、功耗的来源是什么?

+动态功耗,逻辑转换导致逻辑门负载电容充电/放电。

+短路电流,在逻辑转换期间短路(一段时间)时发生。

+泄露功耗,由泄漏电流引起的。

42、为什么需要减少功耗?

低功耗可延长电池寿命,降低冷却和封装成本。

43、提供一些低功耗设计技术。

电压调节,晶体管尺寸调整,电源管理模式等。

44、逻辑电路中的毛刺是否会导致功耗泄露?

是的,因为它们会导致逻辑门的转换。

45SRAM中功耗浪费的主要来源是什么?

要读写数据,需要激活字线,这会导致行中的所有列都处于激活状态,即使我们只需要一个字数据。这消耗了大量的功耗。

46、在CMOS数字设计中,为什么PMOS的尺寸通常高于NMOS的尺寸?

PMOS中,载流子是空穴,其迁移率小于NMOS中的载流子,电子。这意味着PMOSNMOS慢。在CMOS技术中,NMOS将输出下拉至地,PMOS将输出上拉至Vdd。如果PMOSNMOS的大小相同,则PMOS需要很长时间才能对输出节点充电。如果我们有更大的PMOS,那么充电更快。所有这些都是为了输出节点的上升时间和下降时间相等。

47、什么是slack

slack是特定路径中要求延迟与实际延迟的时间差。 Slack可以是正的也可以是负的。

48、如果负载电容增加,延迟会发生什么?

延迟增加。

49、为什么逻辑综合最好读入floorplan信息?

提高逻辑综合和物理实现的时序相关性

50、在设计中,有多少种时序路径?

reg2reginput2regreg2outputinput2output

admin #内推职位 • 来自相关话题

2月前

史上最强IC公司招聘

大公司内推岗位:AMD,海光,德州仪器,杭州矽力杰,苏州瑞晟微电子,盛科网络,兆芯的一切职位小公司以及创业ai公司招聘:上海,深圳,珠海,广州,成都,杭州,苏州,北京,西安,数字ic设计工程师,数字验证工程师,模拟ic设计工程师,DFT,FPGA,数字后端工程 ...查看全部

大公司内推岗位:AMD,海光,德州仪器,杭州矽力杰,苏州瑞晟微电子,盛科网络,兆芯的一切职位


小公司以及创业ai公司招聘:上海,深圳,珠海,广州,成都,杭州,苏州,北京,西安,数字ic设计工程师,数字验证工程师,模拟ic设计工程师,DFT,FPGA,数字后端工程师,RFIC,射频工程师,ae,fae,sales.欢迎自荐和推荐。
联系方式:微信chipist1

Maggie • 来自相关话题

2月前

verilog重点解析(13题)

本文来自于数字芯片实验室。1、连续赋值和过程赋值之间有什么区别?2、initial和always中的赋值有什么区别?    initial和always中的赋值都是过程赋值。3、阻塞和非阻塞赋值之间有什么区别?阻塞和非阻塞赋值都是过程赋值。 ...查看全部

本文来自于数字芯片实验室。

1、连续赋值和过程赋值之间有什么区别?

40.jpg

2、initial和always中的赋值有什么区别?

    initial和always中的赋值都是过程赋值。

41.jpg

3、阻塞和非阻塞赋值之间有什么区别?

阻塞和非阻塞赋值都是过程赋值。

42.jpg

44.png

4、如何使用连续赋值建模双向nets?

assign语句构成一个连续赋值。 RHS变化立即影响LHS。 然而,LHS的任何变化都不会影响RHS。

例如,在以下声明,rhs的更改将更新到lhs,但反之不会。

      wire rhs,lhs;

      assign lhs=rhs;

System Verilog引入了一个关键字alias,能定义双向nets。

例如,在以下代码中,rhs的任何变化都会更新到lhs,反之亦然。

module test_alias;

     wire [3:0] lhs,rhs;

     alias lhs=rhs;

   initial begin

         force rhs=4'h2;

         $display("lhs=%0h,rhs=%0h",lhs,rhs);

         release rhs;

         force lhs=4'hc;

        $display("lhs=%0h,rhs=%0h",lhs,rhs);

        release lhs;

    end

endmodule //test_alias

如果上述alias换成assign,则输出如下:

lhs = 2 , rhs = 2

lhs = c , rhs = z

 

然而,由于定义了alias,输出如下:

lhs = 2 , rhs = 2

lhs = c, rhs = c

在上面的示例中,对net任何一侧的更改都会更新到另一侧。

5、task和function之间有什么区别?

Verilog中的task和function都可以实现常用功能,有助于代码的清晰和可维护,避免在不同位置复制大量代码。 本质上,task和function都提供了在模块中不同位置重用相同代码段的“子程序”机制。

但是,task和function在以下方面有所不同:

46.jpg

6、静态task和动态task有何不同?

   动态task在关键字task和名称之间有automatic关键字。 动态task在每个task调用期间,自动分配变量内存空间,即每次调用都不会覆盖这些值。没有automatic关键字,变量是静态分配的,这意味着这些变量在不同的task调用之间共享,因此可以被覆盖。

以下示例说明了关键字automatic的效果。 这是一个不可综合的代码。

module modify_taskval;

    integer out_val;

    task automatic modify_value;

       input [1:0] in_value;

       output [3:0] out_value;

       reg [1:0] my_value;

       begin

          //syntax error to use nonblocking assignment with automatic variables            

          my_value=in_value; //blocking assigment

          $display("my_value=\t%0d,t=%0d",my_value,$time);

          out_value=my_value+2;

       end

   endtask

  initial begin

      fork 

         begin  //first parallel call

           #1        

             $display("in1=\t\t%0d,t=%0d",2,$time);

             modify_value(2,out_val);

          end 

         begin  //second parallel call

           #2        

             $display("in2=\t\t%0d,t=%0d",3,$time);

             modify_value(3,out_val);

          end

        join

   end

endmodule

  在上面的示例中,my_value是task中的局部变量。 每当调用此task时,输入in_value在5个仿真时间单位之后赋值给局部变量。 在initial begin中,有一个fork-join,它启动两个并行进程,分别在仿真时间单位#1和#2之后开始。 第1个进程赋值2给my_value,第2个进程赋值3给my_value。 假设没有automatic关键字,使用上面的代码运行仿真,会输出以下内容:

in1      =2 ,t=1 //passed value is 2

in2      =3 ,t=2

my_value   =3 ,t=6 //retained value is 3

my_value   =3 ,t=7

没有automatic关键字的事件序列如下:

1. 从仿真时间0开始启动fork-join两个进程。

2.第一个进程在#1之后调用modify_value,并赋值局部变量my_value为2.  此时 t = 1。

3.第二个进程在#2之后调用modify_value,并赋值局部变量my_value为3.  此时t = 2。

请注意现在赋值给局部变量my_value的值被3覆盖。

4.再经过4个时间单位,即在t = 1 + 5 = 6时,第1个task调用$display。由于最新值现在是“3”, my_value显示“3”,而不是 “2”。

类似地,对于第二个过程,即t =2 + 5 = 7,第2个task调用$display。由于最新值仍为“3”,因此此处my_value显示“3”。

现在,在task和task名称之间使用关键字automatic,仿真输出一下内容:

in1      =2 ,t=1 //passed value is 2

in2      =3 ,t=2

my_value   =2 ,t=6 //passed value 2 preserved

my_value   =3 ,t=7

按照上述相同的步骤,这次,由于存在关键字automatic,变量不会被其他进程覆盖。

下表总结了动态task和静态task之间的差异:

47.jpg

7、如何覆盖automatic task中的变量?

默认情况下,module中的所有变量都是静态的。 但是, task/function中的变量都可以定义为static或automatic。

以下示例组合了static或者automatic的task/function和其变量:

1、task/function和其变量都没有定义为automatic

在Verilog-1995中,task/function和其变量都是隐式静态的。 变量仅分配一次内存,多次调用将覆盖其变量。

2、static task/function

System Verilog引入了关键字static。 当task/function被明确定义为static,它的变量只分配一次内存,并且多次调用将覆盖其变量。 、

3、automatic task/function

从Verilog-2001开始,当task/function定义为automatic,其变量也是隐式automatic的。 因此,在多次调用task/function时,变量每次都会分配内存并不会覆盖。

4、static  task/function和automatic变量

SystemVerilog还允许在静态task/function中使用automatic变量。 那些没有automatic定义的变量会保持隐含的静态。 这在变量需要在task/function调用之前初始化,并且自动分配内存的情况下很有用

5、automatic task/function和static变量

SystemVerilog还允许automatic task/function中使用静态变量。没有static声明的那些变量将保留隐式automatic。 这在静态变量需要为每次调用更新变量值的场景中很有用

8、如何没有返回值地调用function?

      在Verilog 2001之前,任何function调用都必须返回一个值,调用function的代码必须接收返回值。 例如,以下是语法错误:

 function my_funct;

    ...

  endfunction

   intial begin

      my_funct(..)  //MUST have a destination

   end

上面例子中的一行是语法错误,因为调用了my_funct,却没有返回值。 只有task可以在没有返回值的情况下调用。

SystemVerilog引入了void来支持没有返回值的function调用。 这使得function调用类似于task调用。

以下示例说明void的function调用:

module func_1bit;

  reg [31:0] int_result;

   function void my_func;

       input [31:0] in1;

       input [31:0] in2;

       output [31:0] out1;

       int_result=in1+in2;

   endfunction

  initial begin

     my_func(3,4,int_result);

     $display("int_result=%0d",int_result);

  end

endmodule

上面的例子显示了结果:

int_result = 7

同时:

1、function不能使用#,@等结构

2、function不能使用非阻塞赋值。

3、function返回值默认为1比特位宽

9、

如何在例化时修改模块的parameter值?

如果Verilog模块使用parameter,有两种方法可以修改它

值。

1)按顺序列表:

在此方法中,parameters的修改顺序和模块中声明它们的顺序一样。 例如,parameter_list模块包含两个参数,即width和depth,已在模块中分配默认值。 并且在example_parameter_list模块中实例化,并且这些parameter在不同的实例化中被不同的值覆盖。

module parameter_list(addr,data);//1995 format

   parameter width=32;

   parameter depth=64;

   parameter num_buses=44;

   input [width-1:0] addr;

   input [depth-1:0] data;

   ...

endmodule

module example_ordered_list;

   reg [127:0] a;

   reg [255:0] b;

   reg [63:0]  c;

   reg [31:0]  d;

  //Instantiating parameter_list module and 

 //overriding width only

  parameter_list #(128) U0 (a,c);

  //Instantiating parameter_list module and 

 //overriding width and depth only

  parameter_list #(128,256) U1(a,b); 

  //Instantiating parameter_list module and 

 //overriding num_buses only

 parameter_list #(32,256,8)  U2(d,b);

endmodule

使用上述方法的限制是:

parameter修改值必须被按顺序修改覆盖。 例如,在上面代码中,U2实例化parameter_list,不能跳过width 和depth直接修改num_buses

我们有两种方法克服这种限制:

1、  在声明模块内的parameter时,将后面例化时需要改变的parameter声明在不需要改变的parameter之前。 例如,在上面的代码中,U0和U1实例化了parameter_list .num_buses不需要更改,所以最后声明,分配给它的是默认值。

2、  在模块实例化时,为所有的parameter分配值,包括不需要修改的parameter。 在U2实例化中,虽然只有num_buses参数需要改变,但width and depth仍然需要分配模块中定义的相同的默认值。

2)按名称指定:

这是Verilog-2001开始提供的一项新功能,可以通过显式指定parameter来更好地修改模块的parameter。 这样,parameter值就链接到它的名字,而不是声明时的位置顺序。

使用与上面相同的模块parameter_list,

下面的示例显示了按名称指定的相同的parameter修改覆盖。

module example_by_name;

  reg [127:0] a;

  reg [255:0] b;

  reg [63:0] c;

  reg [31:0] d;

  //Instantiating parameter_list module and 

 //overriding width only

  parameter_list #(.width(128)) U0(a,c);

  //Instantiating parameter_list module and 

 //overriding width and depth

  parameter_list #(.width(128),.depth(256)) U1(a,b);

  //Instantiating parameter_list module and 

  //overriding depth only

  parameter_list #(.depth(256)) U2(d,b);

endmodule

请注意,显式地按parameter名称指定修改方式,括号中的值是修改的值。 在在U2实例化中,只需指定depth即可,无需为width指定任何修改值。

3)使用 defparam:

在此方法中,模块中的parameter根据其层次结构名称访问。 在下面的示例中,低层次模块parameter_list在example_defparam模块中实例化。 但是width和depth的值使用defparam修改。

module example_defparam;

  reg [127:0] a;

  reg [255:0] b;

  reg [63:0] c;

  reg [31:0] d;

  //Instantiating parameter_list module and 

 //overriding width only

  parameter U0.width=128;

  //Instantiating parameter_list module and 

 //overriding width and depth

 parameter_list U1(a,b);

 defparam U1.width=128;

 defparam U1.width=256;

  //Instantiating parameter_list module and 

 //overriding depth only

  parameter_list U2(d,b);

  defparam U2.depth=256;

endmodule

以下总结了使用defparam方法的优点:

1、修改parameter值是不需要遵循parameter声明顺序

2、可以修改特定parameter,而不是重新指定所有parameters

3、通过对defparam进行分组,可以帮助进行代码维护

10、如果阻止模块例化时parameters不被改变

如果需要阻止模块中的特定parameters被改变,应该使用localparam,而不是parameter。 localparam在Verilog-2001中引入。在以下示例中,localparam用于声明num_bits,因此尝试改变它会给出Error。

module localparam_list(addr,data);

   parameter width=32;

   parameter depth=64;

   localparam num_bits=width*depth;

   input [width-1:0] addr;

   input [depth-1:0] data;

  ...

endmodule

请注意,由于width和depth是使用parameter指定的,它们可以在实例化时被改变。

通常,localparam定义本地化标识符,其值来自常规parameters。

11、使用`define和parameter有什么区别?

`define和parameter都可以在设计中用来指定常量。 例如:

`define width 64

  if(`width==64) ...

or

 parameter width=64;

  if(width==64) ...

48.jpg

12、什么是派生parameters?

当一个或多个parameters用于定义另一个parameters时,则结果是派生parameters。 派生parameters可以是parameter或localparam。 在以下示例中,有两个parameter,width和 depth,,用于定义第三个parameter,num_bits。

在这种情况下,num_bits的值为32。

module derived_param;

  parameter width=4;
  parameter depth=8;
 //num_bits is a derived parameter
  localparam num_bits =width*depth;
endmodule
使用派生parameters可以增强RTL代码的可重用性。

13、层次化设计当中连接Ports的方法有哪些?各自的优缺点是什么?

1)按顺序连接

在此方法中,模块实例化中的端口顺序应与模块声明中的端口顺序相同。 例如,在下面的代码中,upper模块实例化lower模块,并且端口是隐式连接的,也就是说,按顺序连接的。

module lower(addr,data);

   input [width-1:0] addr;
   inout [depth-1:0] data;
endmodule  //lower
module upper(in1,out1);
    input [width-1:0] in1;
    output [depth-1:0] out1;
    lower u0 (in1,out1); //implicit connection of
   //in1 to addr and out1 to data ports
 endmodule //upper

2)按名称连接

在此方法中,可以通过port名称,在实例化模块时显式地完成port之间的连接。如下所示,按名称连接连接进行连接port。

 lower U1(

   .data(out1),

   .addr(in1)

);

按名称连接提高了port连接的可读性, 和port声明的顺序不再相关,因为它们是显示连接的。

请注意,两种类型的模块port连接不能混合,也就是说,下面示例的连接方式不正确:

//gives a syntax error

  lower U_wrong (in1,.addr(out1));

3)按Interface连接

SystemVerilog引入了一个interface结构,将一束nets和variables封装到一个组中。使用Interface,有助于创造可维护的代码。 对Interface定义的更改会影响所有实例化interface的模块。 下面是使用Interface结构的示例:

interface basic_con;

   wire [7:0] in1,in2,in3; //bi-dir wire

endinterface:basic_con

module lower (

   basic_con all_ins, //all inputs

   output [7:0] out1,out2

);

assign out1=all_ins.in1&all_ins.in2;

assign out2=all_ins.in1 |all_ins.in3;

endmodule

在上面的例子中,all_ins是实例化的Interface,指定in1到in3端口的ports连接。

Maggie • 来自相关话题

2月前

数字IC设计总线系列之APB实例解析

本文来自于数字芯片实验室。APB是AMBA中相对比较简单的接口协议。采用这种简单的协议,你可以轻松地将自定义外设挂在AMBA总线上。许多APB外设都是慢速器件,例如UART。一般SoC都是通过它们的寄存器进行访问。下面是一个APB slave 的verilog ...查看全部

本文来自于数字芯片实验室。

APB是AMBA中相对比较简单的接口协议

采用这种简单的协议,你可以轻松地将自定义外设挂在AMBA总线上。

许多APB外设都是慢速器件,例如UART。一般SoC都是通过它们的寄存器进行访问

下面是一个APB slave 的verilog实例,大家可以在此基础上,设计自己APB slave接口的自定义模块控制器.

// Sample APB register code
// Standard read/write registers
// Adress offset from psel:
//  0x00 : 32 bit read of status32 port
//  0x04 : 32 bit read & write control32 port
//  0x08 : 16 bit status
//         and 16 bit read & write control16 port
//  0x0C : 8 bit status8
//          and 8 bit read & write control8port
module apb_regs1 (  
    //system   
    //APB   
    //Interface);
    ...
 endmodule

其中,

// system  
     input             reset_n,
     input             enable,  // clock gating

是系统复位和使能信号

// APB  
input             pclk,             
input      [ 3:0] paddr,   // ls 2 bits are unused   
input             pwrite,  
input             psel,  
input             penable,  
input      [31:0] pwdata,  
output reg [31:0] prdata,  
output            pready,  
output            pslverr,

是APB slave的接口信号


// Interface  
  input                 [31:0]   status32,  
  input                 [15:0]   status16,  
  input                 [7:0]    status8,  
  output reg         [31:0]   control32,  
  output reg         [15:0]   control16,  
  output reg         [7:0]    control8

是一个自定义模块的寄存器,其中status32是read_only

wire apb_write = psel & penable &pwrite;wire apb_read  = psel & ~pwrite;

apb_writeapb_read是为了满足APB协议做的读写控制。apb_read信号产生和apb_write不同,具体原因可以查阅APB协议官方文档

assign pready = 1'b1;assign pslverr = 1'b0;

APB slave模块只是对一些控制和状态寄存器进行读写,是无等待传输,同时不生成传输错误信号。

always @(posedge pclk or negedge reset_n) 

   begin    
       if (!reset_n)    
           begin      
              control32   <= 32'h0;  
              control16   <= 16'h1234; // reset/initial value    
             control8   <=  8'h0;        
             prdata    <= 32'h0;    
           end  // reset    
       else if (enable)    
          begin         
             if (apb_write)        
                 begin          
                    case (paddr)        
                        //4'h0 :  status32 read only           
                        4'h4 : control32 <= pwdata;          
                        4'h8 : control16 <= pwdata[15:0];          
                        4'hC : control8  <=pwdata[7:0];          
                    endcase        
                 end // write       
            if (apb_read)        
               begin          
                   case (paddr)          
                       4'h0 : prdata <= status32;      
                     4'h4 : prdata <= control32;       
                     4'h8 : prdata <= {status16,control16};      
                     4'hC : prdata <={8'h0,status8,8'h0,control8};  
                endcase      
              end // read      
            else        
               prdata <= 32'h0; // so we can OR all busses    
        end // clocked  
   end // always

对不同的寄存器做了地址分配,其中status32寄存器只读  

然后我们在Testbench里例化APB slave和一个APB master模型,对该APB slave模块进行验证。

apb_bus0.read(16'h00,32'h9c4e9a31);      
apb_bus0.write(16'h04,32'h11223344);
apb_bus0.write(16'h08,32'hAABB);
apb_bus0.write(16'h0C,32'hDD);
apb_bus0.read(16'h04,32'h11223344);
apb_bus0.read(16'h08,32'h7832AABB);
apb_bus0.read(16'h0C,32'h002a00DD);

36.jpg

Maggie • 来自相关话题

2月前

数字IC设计总线系列之APB

本文来自于公众号数字芯片实验室。APB是AMBA协议的一部分。APB可连接任何低带宽,无需高性能流水线接口的外设。 APB是非流水线协议。 所有信号的转换仅在时钟的上升沿发生,以便能够轻松地将APB外设集成到其他设计流程中。APB每次传输至 ...查看全部

本文来自于公众号数字芯片实验室。

APB是AMBA协议的一部分。

APB可连接任何低带宽,无需高性能流水线接口的外设。 APB是非流水线协议。 

所有信号的转换仅在时钟的上升沿发生,以便能够轻松地将APB外设集成到其他设计流程中。

APB每次传输至少需要两个周期。 

可以使用APB来访问外围设备的寄存器。 

APB包括用于扩展APB传输的PREADY信号和用于指示传输失败的PSLVERR信号。

在APB协议中有两种类型的写传输:无等待写传输和有等待写传输。

无等待写传输:

30.png

在时钟的第一个上升沿,是Setup阶段,地址信号PADDR,数据信号PWDATA,写信号PWRITE,选择信号PSEL开始改变。

在随后的时钟沿之后,使能信号PENABLE拉高,表示进行Access阶段。

地址信号PADDR,数据信号PWDATA,写信号PWRITE,选择信号PSEL在整个Access阶段都保持有效。APB写传输在Access阶段结束时完成。 

使能信号PENABLE在传输结束时拉低。选择信号PSELx也拉低,除非后面紧接着另一个对该外设的传输。

有等待写传输:

31.png

在Access阶段,可以通过拉低PREADY来延长传输。

这可确保对某些外设进行多周期访问。

建议在传输后不立即更改地址信号和写信号,在另一次访问之前保持稳定。这降低了功耗。

无等待读传输:

32.png


上图显示了无等待读传输。APB slave必须在读传输结束之前提供数据。

有等待读传输:

33.png

上图显示了PREADY信号如何扩展读传输。如果在Access阶段PREADY拉低,则写传输会延长。

下图是AMBA APB的工作流程

35.png

IDLE - 这是APB的默认状态。

Setup - 当需要传输时,总线进入SETUP状态,其中相应的选择信号PSELx被置位。 总线仅在一个时钟周期内保持SETUP状态,并始终在时钟的下一个上升沿移至ACCESS状态。 

ACCESS - 使能信号PENABLE在ACCESS状态下被置位。在从SETUP到ACCESS状态的转换期间,PADDR,PWRITE,PSELx和PWDATA信号必须保持稳定。从ACCESS状态退出由来自APB slave的PREADY信号控制

如果APB slave将PREADY保持为低电平,则总线保持在ACCESS状态。

如果APB slave将PREADY驱动为高电平,则退出ACCESS状态。

如果不再需要传输,则总线返回IDLE状态。否则,如果紧跟着另一次传输,则总线直接移动到SETUP状态。

Maggie • 来自相关话题

2月前

IC设计实例解析之“Memory”

本文来自于公众号数字芯片实验室。 为了建模memory,Verilog提供了对二维数组的支持,通过声明寄存器数组来建模memory的行为模型。可以使用数组中的索引访问数组中的任何数据。例如:     reg [words ...查看全部

本文来自于公众号数字芯片实验室。

 为了建模memory,Verilog提供了对二维数组的支持,通过声明寄存器数组来建模memory的行为模型。可以使用数组中的索引访问数组中的任何数据。

例如:

     reg [wordsize:0] array_name [0:arraysize]

这里wordsizes是memory的宽度,arraysize是memory的深度:

我们可以通过:

my_memory [address] = data_in和  data_out = my_memory [address]来分别对memory进行数据存储和读取。

有时候,可能只需要读取或写入某个数据中的一位,这是就需要进行以下操作:

data_out = my_memory [address];
data_out_it_0 = data_out [0];

我们可以使用系统任务$readmemb$readmemh,通过从磁盘读取memory文件来初始化memory。$readmemb用于读取二进制表示的memory,$readmemh用于读取十六进制表示的memory。

 $readmemh(“FILE_NAME”,mem_array,start_addr,stop_addr)

其中start_addr和stop_addr是可选的。

示例 - 简单的memory

module memory();               
   reg [7:0] my_memory [0:255];  
   initial begin  
      $readmemh("memory.list", my_memory);  
endendmodule

其中memory.list中内容为:


1//Comments are allowed 
2 1100_1100   // This is first address i.e 8'h00 
3 1010_1010   // This is second address i.e 8'h01 
4@ 55        // Jump to new address 8'h55 
5 0101_1010   // This is address 8'h55 
6 0110_1001   // This is address 8'h56

19.png

  上图是同步SRAM的Verilog模块。该同步SRAM可存储8个8位数据。同步SRAM模块由8位数据输入dataIn和8位数据输出dataOut组成。该模块使用8位地址线Addr来定位memory阵列中数据字节的位置。利用8位地址线,可以寻址深度为256的SRAM。但在本例中,为了简单起见,设计了一个深度为8的SRAM。该模块使用1位输入时钟线Clk进行时钟控制。该模块还具有1位片选线CS。1位RD信号用于指示同步SRAM上的数据读操作,1位WE信号用于指示同步SRAM上的数据写操作。 RD和WE线均为高电平有效。

  module syncRAM(dataIn, dataOut,Addr,CS,WE,RD,Clk);

  // parameters for the width

   parameter ADR   = 8;

   parameter DAT   = 8;

   parameter DPTH  = 8;

  //ports

   input  [DAT-1:0]  dataIn;

   output reg [DAT-1:0]  dataOut;

   input  [ADR-1:0]  Addr;

   input CS,WE,RD,Clk;

  //internal variables

   reg [DAT-1:0] SRAM [DPTH-1:0];

   always @ (posedge Clk)

     begin

       if(CS == 1'b1) begin

           if(WE == 1'b1 && RD == 1'b0) begin

              SRAM [Addr] = dataIn;

           end

           else if (RD == 1'b1 && WE == 1'b0) begin

              dataOut = SRAM [Addr]; 

           end

           else; 

       end

       else;

     end

endmodule

下面是同步SRAM的Testbench:

`timescale 1ns / 1ps 
 module syncRAM_tb;   //Inputs  
  reg[7:0] dataIn; 
  reg[7:0] Addr;  
  regCS;  
  regWE;  
  regRD;  
  regClk;   //Outputs  
  wire[7:0] dataOut;   //Instantiate the Unit Under Test (UUT)  
  syncRAM uut (  .dataIn(dataIn), 
               .dataOut(dataOut), 
               .Addr(Addr),  
               .CS(CS), 
               .WE(WE), 
               .RD(RD), 
               .Clk(Clk)  );  
  
  initial begin   //Initialize Inputs  
             dataIn  = 8'h0;  
             Addr  = 8'h0;  
             CS  = 1'b0;  
             WE  = 1'b0;  
             RD  = 1'b0;  
             Clk  = 1'b0;    //Wait 100 ns for global reset to finish 
             #100;     //Add stimulus here  
             dataIn  = 8'h0;  
             Addr  = 8'h0;  
             CS  = 1'b1;  
             WE  = 1'b1;  
             RD  = 1'b0;  
             #20; 
             dataIn  = 8'h0;  
             Addr  = 8'h0;  
             #20;  
             dataIn  = 8'h1;  
             Addr  = 8'h1;  
             #20;  
               dataIn  = 8'h10; 
              Addr  = 8'h2; 
              #20;  
              dataIn  = 8'h6;  
              Addr  = 8'h3;  
              #20;  
              dataIn  = 8'h12; 
              Addr  = 8'h4;  
              #40;  
              Addr  = 8'h0;  
              WE  = 1'b0;  
              RD  = 1'b1;  
              #20;  
              Addr   = 8'h1;  
              #20;  
              Addr   = 8'h2;  
              #20;  
              Addr   = 8'h3;  
              #20;  
              Addr   = 8'h4;  
         end    
         always #10 Clk = ~Clk;   
      endmodule

20.jpg

   在FPGA和ASIC中,通常会在设计中实例化RAM,但是这样你的HDL代码就无法移植和可重用。FPGA相关工具提供自动识别RAM的功能,可以根据需要综合出可以推断distributed RAM或者Block RAM。涵盖了RAM的同步写入、使能、异步或同步读取、数据输出锁存、复位、单端口,双端口读写等类型。

综合出的RAM类型取决于其RTL描述。

以下是综合出各种类型RAM的一些Verilog模板:

具有异步读取的单端口RAM:

21.png

module raminfr(clk, we, a, di, do);  
      input clk;  
      input we;   
      input  [4:0] a;  
      input  [3:0] di;  
      output  [3:0]do;   
      reg    [3:0] ram [31:0];   
      always@(posedge clk) begin       
         if(we)         
             ram[a]<= di;     
      end     
      assigndo = ram[a];   
   endmodule

具有“虚假”同步读取的单端口RAM:

22.png

module raminfr(clk, we, a, di, do); 
        input        clk;  
        input        we;  
        input  [4:0]       a;
        input  [3:0]      di; 
        output  [3:0]       do;   
        reg   [3:0]      ram [31:0];   
        reg   [3:0]      do;   
        always@(posedge clk) begin       
            if(we)         
                ram[a]<= di;      
               do<= ram[a];    
        end    
endmodule

具有同步读取的单端口RAM:

23.png

module raminfr(clk, we, a, di, do); 
    input        clk;   
    input         we;   
    input  [4:0] a;  
    input  [3:0] di;  
    output  [3:0]  do;   
    reg   [3:0] ram [31:0];   
    reg    [4:0] read_a;   
    always@(posedge clk) begin       
       if(we)         
           ram[a]<= di;       
           read_a<= a;     
        end     
     assigndo = ram[read_a];   
endmodule

具有使能功能的单端口RAM:

24.png

module raminfr (clk, en, we, a, di, do);   

     input        clk; 
     input         en;   
     input         we;   
     input  [4:0] a;    
     input  [3:0] di;    
     output  [3:0] do;   
     reg   [3:0] ram [31:0];    
     reg   [4:0] read_a;     
     always @(posedge clk) begin           
         if (en)     begin            
             if (we)                    
                ram[a] <= di;                  
                read_a <= a;               
             end         
    end            
   assign do = ram[read_a];   
endmodule

具有异步读取功能的双端口RAM:

module raminfr(clk,we, a, dpra, di, spo, dpo); 
     input           clk;  
     input           we;   
     input  [4:0]  a;  
     input  [4:0]  dpra;  
     input  [3:0]  di;  
     output  [3:0]   spo;   
     output  [3:0]   dpo;   
     reg   [3:0]  ram [31:0];  
     always@(posedge clk) begin       
         if(we)         
            ram[a]<= di;     
      end     
     assignspo = ram[a];     
     assigndpo = ram[dpra];   
endmodule

使能控制的双端口RAM:

25.png

module raminfr(clk,ena,enb,wea,addra,addrb,dia,doa,dob);     
    input        clk;    
    input        ena;     
    input        enb;     
    input        wea;    
    input  [4:0] addra;     
    input  [4:0] addrb;     
    input  [3:0] dia;      
    output [3:0] doa;     
    output [3:0] dob;      
    reg    [3:0]  ram [31:0];       
    reg    [4:0] read_addra;      
    reg    [4:0] read_addrb;         
    always @(posedge clk) begin          
        if (ena) begin                
            if (wea)            
                ram[addra] <= dia;                
               read_addra <= addra;            
        end                     
         if (enb)          
            read_addrb <= addrb;     
  end                      
   assign doa = ram[read_addra];  
   assign dob = ram[read_addrb];                
endmodule

多端口RAM

多端口RAM的不同读端口访问不同地址的RAM内容。但是,只能有一个写端口。

27.png

 module raminfr(clk, we, wa, ra1, ra2, di, do1, do2);  

    input         clk;    
    input          we;     
    input  [4:0] wa;     
    input  [4:0] ra1;      
    input  [4:0] ra2;      
    input  [3:0] di;       
    output  [3:0] do1;      
    output  [3:0] do2;      
    reg    [3:0] ram [31:0];         
    always @(posedge clk) begin           
      if (we)         
          ram[wa] <= di;           
      end           
    assign do1 = ram[ra1];     
    assign do2 = ram[ra2];        
endmodule

我们可以对FPGA进行属性约束,以便综合出distributed RAM或者Block RAM。

Maggie • 来自相关话题

2月前

IC验证系列之分层测试平台

本文来自于公众号数字芯片实验室。在仿真中,测试平台环绕DUT,就像硬件测试仪连接到芯片实体一样。测试平台和硬件测试仪都提供激励和捕获响应。它们之间的区别在于测试平台在各种抽象级别上工作。许多总线功能模型(BFM)通过激励进入DUT,它们是测试平台的一部分,是真 ...查看全部

本文来自于公众号数字芯片实验室。

在仿真中,测试平台环绕DUT,就像硬件测试仪连接到芯片实体一样。

测试平台和硬件测试仪都提供激励和捕获响应。它们之间的区别在于测试平台在各种抽象级别上工作。

13.png

许多总线功能模型(BFM)通过激励进入DUT,它们是测试平台的一部分,是真实设备的等效组件,而不是可综合RTL。

如果使用FPGA进行原型验证,这时候BFM就需要是可综合的。

14.png

分层测试平台

现代验证方法的关键概念都是分层的。虽然分层似乎使测试平台更复杂,实际上有助于简化验证任务。

第一次学习Verilog并开始编写测试平台时,可能会像下面的代码(AMBA总线)一样。

module test(PAddr, PWrite, PSel, PRData,Rst, clk);
  initial begin    
    Rst <= 0;
    #100 Rst <= 1;
 @(posedge clk)
    PAddr <= 16’h50;
    PWData <= 32’h50;
    PWrite <= 1'b1;
    PSel <= 1'b1;
 @(posedge clk)
    PEnable <= 1'b1;
 @(posedge clk)
   PEnable <= 1'b0;
 if (top.mem.memory[16’h50] == 32’h50)
   $display("Success");
 else
   $display("Error, wrong value in memory");
   $finish;
 endendmodule

这样的代码重用性很差,需要编写大量这样的代码,因此可以为常用的操作(如总线)创建了任务,如下所示。

 task write(reg [15:0] addr, reg [31:0]data);
  @(posedge clk)
  PAddr <= addr;
  PWData <= data;
  PWrite <= 1'b1;
  PSel <= 1'b1;
 @(posedge clk)
  PEnable <= 1'b1;
 @(posedge clk)
  PEnable <= 1'b0;
 endtask

现在测试平台变得更简单了。

 module test(PAddr, PWrite, PSel, PRData,Rst, clk);
 initial begin
  reset(); 
  write(16’h50, 32’h50); 
 
 if (top.mem.memory[16’h50] == 32’h50)
  $display("Success");
 else
  $display("Error, wrong value in memory");
 $finish;
 endendmodule

通过将常用操作,例如复位,总线读写放在一个任务中,会让测试平台变得更有效率,减少错误。这种物理层和命令层的创建是分层测试平台的第一步。

15.png

底层是信号层,包含被测设计和将其连接到测试平台的信号。

上一级是命令层。 DUT的输入由总线读写等驱动任务驱动。 DUT的输出被监视器捕获并进行信号转换,输入到命令层。断言跨越命令层和信号层,可以查看单个信号,也可以查看命令层。

功能层

功能层给命令层提供输入。

16.jpg

场景层

什么是一个场景(scenario)?验证工程师的工作就是要确保芯片完成预期任务。每一个任务都是一个场景。

17.jpg

测试平台环境中虚线内模块在项目期间不必重写,可以在代码中留下“hooks”  ,针对不同的测试添加不同的功能。

测试层和功能覆盖

18.jpg

如图所示,功能覆盖率衡量所有测试的进度以使得验证过程满足验证计划中的要求。