数字集成电路设计(五、仿真验证与 Testbench 编写)(三)


5. 任务和函数

  • 在C语言等高级语言中,我们通常用函数的方式来表示代码,可以减少代码的使用量,编译器会把函数的内容打开变成汇编语言。高级语言通常使用函数和对象的方式来表示
  • 在 Verilog HDL 语言中提供了任务和函数,可以将较大的行为级设计划分为较小的代码段,允许设计者将需要在多个地方重复使用的相同代码提取出来,编写成任务和函数,这样可以使代码更加简洁和易懂

5.1 任务

5.1.1 任务的定义

  • 语法格式:

    (1)任务定义是入在关键字 task 和endtask 之间的,其中关键词 task 标志着–个任务定义结构的开端,endtask 标志着个任务定义结构的结束。“任务名”是所定义任务的名称。在“任务名”后面不能出现输入、输出端口列表。
    (2)“端口和类型声明”用于对任务各个端口的宽度和类型进行声明,其中使用由关键词input、output或inout对任务的端口进行声明
    (3)“局部变量声明”用来对任务内用到的局部变量进行宽度和型声明,该声明语句的语法与进行模块定义时的相应声明语句语法一致
    (4)任务中由begin与end关键词界定的一系列语指明了任务被调用时需要进行的操作,在任务被调用时这些语句将以串行方式得到执行
  • 举例:
    在这里插入图片描述
  • 需要注意:
    (1)在第一行 task 语句中不能列出端口名列表。
    (2)任务中可以有延迟语句、敏感事件控制语句等事件控制语句
    (3)任务可以没有或可以有一个或多个输入、输出和双向端口。
    (4)任务可以没有返回值,也可以通过输出端口或双向端口返回一个或多个值
    (5)任务可以调用其它的任务或函数,也可以调用该任务本身。
    (6)任务定义结构内不允许出现过程块(initial或always 过程块)
    (7)任务定义结构内可以出现 disable 终止语句,这条语句的执行将中断正在执行的任务。在任务被中断后,程序流程将返回到调用任务的地方继续向下执行。

5.1.2 任务的调用

在这里插入图片描述

  • 首先写出任务名,然后按位置写出端口列表就可以了
    在这里插入图片描述
  • !!区分任务和模块(从语法角度):(1)模块调用有实例名,任务调用没有;(2)在模块的调用时,每一个模块都是独立的,通过实例名把模块调用过来之后,被调用的模块就会形成当前模块的子模块或者下一级模块。而任务时写在模块内部的,也就是在调用的时候只能调用当前模块内部的任务,而不能调用其他模块里面的任务

5.2 函数

5.2.1 函数的定义

  • 函数定义是入在关键字function 和endfunction之间的,其中关键词function 标志着一个函数定义结构的开端,endfunction 标志着一个函数定义结构的结束。“函数名”是给被定义函数取的名称,这个函数名在函数定义结构内部还代表着一个内部变量,函数调用后的返回值是通过这个函数名变量传递给调用语句的。
    在这里插入图片描述在这里插入图片描述
  • !!会有返回值和位宽。但是只有输入参量并没有输出参量,这是function和task之间很明显的一个区别,function是用过返回值,task是通过输出信号来进行参数传递的
  • 返回值有三种形式:
    (1)[msb:sb]:这种形式说明函数名所代表的返回数据变量是一个多位的存器变量它的位宽由[msb:lsb]指定
    (2)integer:这种形式说明函数名所代表的返回变量是一个整型变量
    (3)real:这种形式说明函数名所代表的返回变量是一个实型变量
  • 参数类型至少有一个输入端口,但是不允许有输出端口和双向端口(与task不同)
  • “局部变量声明”是对函数内部局部变量进行宽度和类型的声明
  • 由begin 与end 关键词界定的一系列语同任务中的一样,用来指明函数被调用时要执行的操作。在函数被调用时,这些语句将以串行方式得到执行
    在这里插入图片描述
  • 需要注意:
    (1)与任务一样,函数定义结构只能出现在模块中,而不能出现在过程块内
    (2)函数至少必须有一个输入端口。
    (3)函数不能有任何类型的输出端口(output端口)双向端口(inout 端口)。
    (4)在函数定义结构中的行为语句内不能出现任何类型的时间控制描述,也不允许使用disable 终止语句(和task有区别)
    (5)与任务定义一样,函数定义结构内部不能出现过程块。
    (6)在1个函数内可以对其它函数进行调用,但是函数不能调用其它任务。
    (7)在第一行 function 语句中不能出现端口名列表。
    (8)函数声明的时候,在 Verilog HDL 的内部隐含地声明了一个名为function identifier(函数标识符)的寄存器类型变量,函数的输出结果将通过这个寄存器类型变量被传递回来。

5.2.2 函数的调用

在这里插入图片描述

  • 需要注意:
    (1)函数的调用不能单独作为一条语句出现,它只能作为一个操作数出现在调用语句内。!!在C语言中还可以操作指针,可以返回一个指针,但是在Verilog里面,函数就是返回了一个数,不能单独存在,必须把这个数进行赋值给另一个才有意义
    (2)函数的调用既能出现在过程块中,也能出现在 assign 连续赋值语句中
    (3)函数定义中声明的所有局部寄存器都是静态的,即函数中的局部寄存器在函数的多个调用之间保持它们的值
    在这里插入图片描述在这里插入图片描述
  • 该例由函数定义和initial过程块构成,其中定义了一个名为 factorial 的函数,该函数是一个进行阶乘运算的函数,具有一个4位的输入端口,同时返回一个 32 位的寄存器类型的值;在initial 块中定义了两个寄存器变量,分别为32位的result 和4位的n,imitial 块对1至9进行阶乘运算,并打印出结果值
    在这里插入图片描述

5.3 函数和任务的区别

在这里插入图片描述

  • !!函数的应用范围可能比任务更窄一些,因为任务在硬件描述语言中既可以延迟,又可以调用函数也可以调用本身,同时还有输入输出接口,这显示出来任务比函数更有优势。而函数实际上就是计算一个值(函数不是一个非常重要的概念)
  • 第二行说明:任务有时间概念,任务没有时间概念

6. 典型测试向量的设计

  • 学习如何写测试向量,通过典型设计来贯穿前面说的

6.1 变量初始化

  • 在 Verilog HDL语言中,有两种方法可以初始化变量:一种是利用初始化变量;另一种是在定义变量时直接赋值初始化。(显式定义和隐式定义,显示连续赋值语句和隐式连续赋值语句区别在于:在信号定义的时候,如果是隐式定义,在信号定义过程中直接对它进行赋值)
  • !!实际中,还是建议用初始化变量的方式进行定义。因为写成过程语句的话会相对来说更清楚一点
  • 这两种初始化任务是不可综合的,主要用于仿真过程。
  • 1.initial初始化方式
    (1)在大多数情况下,Testbench 中变量初始化的工作通过 initial 过程块来完成,可以产生丰富的仿真激励。在硬件语言中,引入了延迟的概念,所以可以得到初始化信号的量,也可以得到任意一个时刻对信号进行赋值的过程
    (2)initial 语句只执行一次,即在设计被开始模拟执行时开始(0 时刻)直到过程结束,专门用于对输入信号进行初始化和产生特定的信号波形。一个Testbench 可以包含多个initial过程语句块,所有的initial 过程都同时执行。
    (3)注意:initial 语句中的变量必须为 reg 类型(因为是过程赋值语句)
    在这里插入图片描述在这里插入图片描述
  • 2.定义变量时初始化
    在这里插入图片描述

6.2 数据信号测试向量的产生

  • !!在硬件描述语言中,经常会有议席没有规则的信号,这些信号怎么产生呢?或者对于有规则的,也可以产生这种方式,只是没有规则的更麻烦,有规则地还可以通过有规则的寻找
  • 数据信号的产生有两种形式:其一是初始化和产生都在单个imitial 块中进行:其二是初始化在 initial 语句中完成,而产生在 always 语句块中完成。前者适合不规则数据序列,并且要求长度较短:后者适合具有一定规律的数据序列,长度不限。
  • !!initial语句适合不规则信号的产生,同时序列不能特别长,因为序列长写的东西就特别多;如果是循环或者有规律的东西,可以用过always语句
    在这里插入图片描述
  • 可以得到两个事情:
    (1)!!对于没有规则的序列,我们可以采用直接加固定延迟对它进行操作的过程
    (2)!!对于循环语句,在可综合逻辑设计的时候是一个非常害怕的东西,但是在测试仿真,循环语句能起到很大作用

6.3 时钟信号的测试向量的产生

  • !!!!时钟信号在硬件描述语言是一个非常重要的信号,这个信号将会贯穿整个时序电路中间的产生。时钟信号的参数量特别多,类型也特别多,随着集成电路的发展,它的形态也完全不一样(例:在印象中,时钟信号都是一直存在的,但是一直产生一个高频信号就会有 功 耗 和 电 磁 干 扰 功耗和电磁干扰 的问题,从高频看有可能就是一个天线,这个在医疗,军工等有非常严格的要求。所以对这种信号,时钟有三个变化:!!!!(1)如果芯片和芯片之间有通信,一般会随着随路时钟打过来,$现在这个时钟有可能不是一直存在,而是需要多少个给你多少个,在单片机,dsp都已经用了(2)以前芯片和芯片之间有一个时钟相连,现在不连了,现在有两个本地振荡的环,是放在芯片内部的,也就是时钟信号不再引出来,通过信号线上编码的方式去同步当前芯片里面的两个时钟,同步两个时钟以后就内部产生振荡,对外就没有信号了(3)还有信号端口,但是信号端口不是以简单的0101进行了,而是传输过去一种经过压缩编码的信号,然后在本地恢复,然后形成数据)
  • !!在数字电路中间,现在门道越来越多,毕竟占了百分之30以上的功耗,而且板级的高速信号如果处理不好,时钟的问题将成为EMI问题的致命问题

6.3.1 例:产生占空比为50%的时钟信号

在这里插入图片描述

6.3.2 例:产生占空可调的时钟信号

在这里插入图片描述在这里插入图片描述

6.3.3 例:产生具有相位偏移的时钟信号

在这里插入图片描述

  • !!!!!!掩图是数字电路中很重要的概念,不想让信号采到错误的位置或者容易受到干扰的位置,需要去理解掩图的概念(数字集成电路的基础知识,不知道这个什么都做不了)

6.3.4 例:产生固定数目的时钟信号

在这里插入图片描述

6.4 总线信号测试向量的产生

  • !!总线特别重要,在数字集成电路设计尤其是处理器设计,我们说得总线者得天下。总线不光要和CPU内核挂在一起,更主要的是它提供了高端的交互机制
  • !!总线的仿真压力非常大。目前做数字集成电路设计的时候,都会先放到FPGA里面跑一下,看一下时序是否正确。但是如何把多个设备挂在一起?只能通过高阻状态。FPGA芯片内部是不具备高阻状态的,只有一圈IO具备高阻状态。而在ASIC里面,可以在内部集成高阻状态IO接口,但是FPGA不行。那怎么办呢?所有的总线到FPGA指令集验证的时候都要产生一个控制逻辑,产生一个时分的这样一个东西,就把每一个总线用定时器分成一个片一个片,也就是总线部分的代码是要重新写的。由于总线又是一个非常麻烦的东西,所以重新写会带来几个问题,由于在总线上面做了时间的分片,所以FPGA虽然能跑到很高的频率,甚至跑到喝ASIC一样的频率,但是只要牵扯到总线的逻辑,FPGA的速度就降下来了
    -
  • 在总线中,对于每个请求端,有一个输入来选择驱动该总线所对应的请求端。选择多个请求端时会产生总线冲突,根据不同的总线类型,会产生不同的冲突结果。当有多个请求端发出请求时,相应的操作由总线的类型决定。在 Verilog HDL 测试中,总线测试信号通常是通过将片选信号、读(或者写)使能信号、地址信号以及数据信号以 task 任务的形式来描述,并通过调用 task 形式的总线信号测试向量来完成相应的总线功能

在这里插入图片描述

  • 在AHB总线中,每三个时钟输出一个地址数据

  • !!在很多cpu包括dsp来讲,总线一般就是内核的一半或者四分之一。就是我的芯片工作频率是100MHz的话,外设的工作频率一般只有50MHz。

  • !!我们希望单指令周期输出信号,也就是1个clk输出信号,没有任何的延迟,这种的性能是最好的,但是性能好,操作方便意味着成本会越来越高(体现在价格)
    在这里插入图片描述在这里插入图片描述

  • task会把需要产生的波形的状态记录下来,只要重复调用输入信号的东西去改变task中间的值就可以改变输出信号,这种方式就会让效率得到极大提高
    在这里插入图片描述

  • 在测试上,有一种方式没有讲,就是在超大规模测试的时候,如果用人手写会很麻烦,这时候会有VCD的方式,会形成一个波形文件,然后通过波形信号的读取,把信号读出来

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

普通的晓学生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值