这里可以看出明显差异,所以,一般在时序电路设计中,建议使用非阻塞赋 值。 为加深理解,可介绍二者混用的例子 例5-7中,虽然l写在前面,但它采用的是非阻塞赋值,因此它的值不会立 刻更新,而bl采用的是阻塞赋值,一旦启动就立刻更新,因此,b1反而al更 早完成更新,这就是二者的区别。当然,像这种混用的写法,我们实际中不会这 样做,可读性太差,一般我们不建议阻塞和非阻塞赋值同时在一个块中出现。 注意:特定顺序的阻塞赋值也可以实现多级移位寄存器电路呢,但是不推荐 使用,可读性差,这也更进一步印证了时序电路中最好采用非阻塞赋值的结论。 总结非阻塞与阻塞赋值 关于阻塞与非阻塞,教材上给出了许多混用例子,但这些例子都只是为了说 明二者的区别而构建的,现实中不建议使用。对于组合电路,为了保证逻辑正确, 最好用阻塞赋值,用非阻塞会导致赋值更新不及时影响后面的判断。而对时序电 路,为了保证寄存器不被优化掉,就最好用非阻塞赋值,以免后面赋值直接用到 新值。 82过程结构总结 1:过程与软件的函数有相似又有差异,过程具有无限循环特性,只要敏感 信号量发生变化,就会自动运行,运行完毕后进入等待状态,等待再次运行。 2、过程中语句具有并行性,从仿真角度看,always块中无论包含了多少语 句,只要过程启动,执行时间无限小,而与是不是阻塞赋值无关(与软件有很大 差异,软件代码越多,执行时间越长,)Verilog要描述硬件电路的行为,所以, Verilog仿真不像软件可以单步调试的,它只能是一个时间轴上的仿真,通过查 看不同时刻变量的变化情况来确定代码功能是否正常。 3、不同过程之间是并行的:多个always块是完全并行的,谁写前面是没有 任何区别。当然不一定大家都在执行,具体每个过程是否在执行状态是看它的敏 感信号量是否满足,这是软件语言无论如何实现不了的。 4、时序过程中只能存在单一时钟沿逻辑:主要是从代码对应的硬件电路角 度来考虑,最终时序电路是要映射到寄存器,而寄存器只有一个触发时钟,如果 既想在上升沿作操作,也想在下降沿作处理,需要分成多个always块,而且阻 18
18 这里可以看出明显差异,所以,一般在时序电路设计中,建议使用非阻塞赋 值。 为加深理解,可介绍二者混用的例子 例 5-7 中,虽然 a1 写在前面,但它采用的是非阻塞赋值,因此它的值不会立 刻更新,而 b1 采用的是阻塞赋值,一旦启动就立刻更新,因此,b1 反而 a1 更 早完成更新,这就是二者的区别。当然,像这种混用的写法,我们实际中不会这 样做,可读性太差,一般我们不建议阻塞和非阻塞赋值同时在一个块中出现。 注意:特定顺序的阻塞赋值也可以实现多级移位寄存器电路呢,但是不推荐 使用,可读性差,这也更进一步印证了时序电路中最好采用非阻塞赋值的结论。 总结非阻塞与阻塞赋值 关于阻塞与非阻塞,教材上给出了许多混用例子,但这些例子都只是为了说 明二者的区别而构建的,现实中不建议使用。对于组合电路,为了保证逻辑正确, 最好用阻塞赋值,用非阻塞会导致赋值更新不及时影响后面的判断。而对时序电 路,为了保证寄存器不被优化掉,就最好用非阻塞赋值,以免后面赋值直接用到 新值。 8.2 过程结构总结 1:过程与软件的函数有相似又有差异,过程具有无限循环特性,只要敏感 信号量发生变化,就会自动运行,运行完毕后进入等待状态,等待再次运行。 2、过程中语句具有并行性,从仿真角度看,always 块中无论包含了多少语 句,只要过程启动,执行时间无限小,而与是不是阻塞赋值无关(与软件有很大 差异,软件代码越多,执行时间越长,)Verilog 要描述硬件电路的行为,所以, Verilog 仿真不像软件可以单步调试的,它只能是一个时间轴上的仿真,通过查 看不同时刻变量的变化情况来确定代码功能是否正常。 3、不同过程之间是并行的:多个 always 块是完全并行的,谁写前面是没有 任何区别。当然不一定大家都在执行,具体每个过程是否在执行状态是看它的敏 感信号量是否满足,这是软件语言无论如何实现不了的。 4、时序过程中只能存在单一时钟沿逻辑:主要是从代码对应的硬件电路角 度来考虑,最终时序电路是要映射到寄存器,而寄存器只有一个触发时钟,如果 既想在上升沿作操作,也想在下降沿作处理,需要分成多个 always 块,而且阻
塞与非阻塞不要混用。 5、关于分支的完整性:在时序电路中,分支可以不完整,但在组合电路中, 如果分支不完整,就很可能有问题(解释为什么会生成锁存器) 8.3移位寄存器设计 回忆数字电路中的移位寄存器知识,介绍其用处(串并转换)。 分析例5-14,在LOAD为高时读入预置数,然后每个时钟周期数据右移 次,QB相当于把DN的数据转化成串行数据,从最低位开始,一位一位输出(分 析时序图)。 其它功能更完整的移位寄存器由学生自学,另外,Verilog中也有专门的移位 运算符>,<(95标准,它移出腾空的位都是用0来填充),在Verilog2001标准中 又新增对有符号数的移位,<小>,更方便。 8.4硬件乘法器设计 这一节内容其中真正的重点并不是要求大家学会如何设计硬件乘法器,而是 让大家学习一些新的Verilog语法,关于硬件乘法器的设计,不要求大家掌握, 事实上,现在FPGA内部都是有现成的IP核,大家直接拿来调用即可。 I、parameter:非常实用的功能,与C语言的宏定义非常相似,使模块具有 可重构性(介绍parameter的用法,并板书实例,如一个参数可变加法器,需要 注意的是parameter只对当前模块范围有效)。 2、整数类型:类似于C语言的imt型,integer,,固定32位位宽,主要用在 循环体变量中。 3、for循环:for循环在C语言中应用的极其普遍,Verilog中虽然也有for 循环,但却很少应用,这也是由于硬件电路的特性决定的,fo循环经过综合之 后相当于把所有循环体展开,这意味着大量的硬件复制,所以一般可综合代码中 很少用for循环,格式与C语言完全相同,只不过Verilog中没有C语言的+、 -运算符,因此这里循环条件不能简写,循环变量一般定义成integer类型(for 循环示例)。 4、repeat循环:直接规定循环次数,使用起来更为简洁 5、while:借用了C语言的语法,只要条件为真就执行(循环示例) 6、还有一种forever,不可综合,主要用于仿真,类似于while(l),也就是 19
19 塞与非阻塞不要混用。 5、关于分支的完整性:在时序电路中,分支可以不完整,但在组合电路中, 如果分支不完整,就很可能有问题(解释为什么会生成锁存器)。 8.3 移位寄存器设计 回忆数字电路中的移位寄存器知识,介绍其用处(串并转换)。 分析例 5-14,在 LOAD 为高时读入预置数,然后每个时钟周期数据右移一 次,QB 相当于把 DIN 的数据转化成串行数据,从最低位开始,一位一位输出(分 析时序图)。 其它功能更完整的移位寄存器由学生自学,另外,Verilog 中也有专门的移位 运算符>>,<<(95 标准,它移出腾空的位都是用 0 来填充),在 Verilog2001 标准中 又新增对有符号数的移位,<<</>>>,更方便。 8.4 硬件乘法器设计 这一节内容其中真正的重点并不是要求大家学会如何设计硬件乘法器,而是 让大家学习一些新的 Verilog 语法,关于硬件乘法器的设计,不要求大家掌握, 事实上,现在 FPGA 内部都是有现成的 IP 核,大家直接拿来调用即可。 1、 parameter:非常实用的功能,与 C 语言的宏定义非常相似,使模块具有 可重构性(介绍 parameter 的用法,并板书实例,如一个参数可变加法器,需要 注意的是 parameter 只对当前模块范围有效)。 2、 整数类型:类似于 C 语言的 int 型,integer,固定 32 位位宽,主要用在 循环体变量中。 3、 for 循环:for 循环在 C 语言中应用的极其普遍,Verilog 中虽然也有 for 循环,但却很少应用,这也是由于硬件电路的特性决定的,for 循环经过综合之 后相当于把所有循环体展开,这意味着大量的硬件复制,所以一般可综合代码中 很少用 for 循环,格式与 C 语言完全相同,只不过 Verilog 中没有 C 语言的++、 --运算符,因此这里循环条件不能简写,循环变量一般定义成 integer 类型(for 循环示例)。 4、 repeat 循环:直接规定循环次数,使用起来更为简洁 5、 while:借用了 C 语言的语法,只要条件为真就执行(循环示例) 6、 还有一种 forever,不可综合,主要用于仿真,类似于 while(1),也就是
我们常说的死循环,后面我们介绍仿真时再详细介绍其用法。 (循环语句总结) 总共4种循环语句,其中for和While都和C语言基本一致,repeat和forever 则是Verilog特有,但总体上,这些循环语句主要是在仿真中使用,RTL级设计 中用处不大,写RL代码的目的是高效的硬件电路实现,而不是追求精致的代 码,教材上虽然给出了多个使用循环语句构建硬件乘法器的例子,但没有实用价 值,如果需要使用硬件乘法器,请直接使用FPGA厂商提供的IP核。 8.51F语句一般用法 1、不完整的分支语句,意味着有记忆,是时序电路,所以一般在寄存器设 计中使用 2、2个分支,即可以是组合也可以是时序,具体情况具体分析(还可以板书, 写完整分支的D触发器) 3、多分支的if else语句,也是完整分支,同样地,可以是组合也可以是时 序 2种条件语句,一种是if else,还有一种是case语句,多重if else语句是有 优先级的,而case则是平级, 通过优先编码器介绍优先级概念PPT17(优先编码器实现) 教材上给这种优先编码器给出了2种实现方式,右边是多分支f else语句, 这个比较容易理解。左边是一种case语句的变种,casez,casez的含义是如果比 较的某一位是高阻态,那么这一位就不再比较,所以左边这种写法也可以达到优 先编码的目的,但是需要说明的是casz语句不在标准的可综合语句之列,也就 是说并非所有的综合器都能对例52完成综合,因此,我们在实际设计中,不要 采用casez的方式(与之相类似,还有一个casex,它只要比较对象中某一位是 不定态,也不作比较) 8.6三态与双向端口设计 inout类型端口在对外接口中十分常用,也是Verilog的另外一个难点。 在学习iout之前,复习数字电路中的知识,介绍高阻态(解释高阻态的含 义),们之所以能实现双向端口,就是因为有高阻态在里面 板书:画三态门符号,并解释其特征。 20
20 我们常说的死循环,后面我们介绍仿真时再详细介绍其用法。 (循环语句总结) 总共 4 种循环语句,其中 for 和 While 都和 C 语言基本一致,repeat 和 forever 则是 Verilog 特有,但总体上,这些循环语句主要是在仿真中使用,RTL 级设计 中用处不大,写 RTL 代码的目的是高效的硬件电路实现,而不是追求精致的代 码,教材上虽然给出了多个使用循环语句构建硬件乘法器的例子,但没有实用价 值,如果需要使用硬件乘法器,请直接使用 FPGA 厂商提供的 IP 核。 8.5 IF 语句一般用法 1、不完整的分支语句,意味着有记忆,是时序电路,所以一般在寄存器设 计中使用 2、2 个分支,即可以是组合也可以是时序,具体情况具体分析(还可以板书, 写完整分支的 D 触发器) 3、多分支的 if else 语句,也是完整分支,同样地,可以是组合也可以是时 序 2 种条件语句,一种是 if else,还有一种是 case 语句,多重 if else 语句是有 优先级的,而 case 则是平级, 通过优先编码器介绍优先级概念 PPT17(优先编码器实现) 教材上给这种优先编码器给出了 2 种实现方式,右边是多分支 if else 语句, 这个比较容易理解。左边是一种 case 语句的变种,casez,casez 的含义是如果比 较的某一位是高阻态,那么这一位就不再比较,所以左边这种写法也可以达到优 先编码的目的,但是需要说明的是 casez 语句不在标准的可综合语句之列,也就 是说并非所有的综合器都能对例 5-2 完成综合,因此,我们在实际设计中,不要 采用 casez 的方式(与之相类似,还有一个 casex,它只要比较对象中某一位是 不定态,也不作比较) 8.6 三态与双向端口设计 inout 类型端口在对外接口中十分常用,也是 Verilog 的另外一个难点。 在学习 inout 之前,复习数字电路中的知识,介绍高阻态(解释高阻态的含 义),们之所以能实现双向端口,就是因为有高阻态在里面。 板书:画三态门符号,并解释其特征
通过实际的例子,来学习如何实现三态门。解释例(5-26)中新的状态Z, 如果ena不为l,也就是为零,那么DOUT就变成高阻态。这就实现了一个4位 的三态门。 关于高阻态:只能用于设计的引脚端口级,而不能用于模块内部信号,因为 FPGA,CPLD内部没有三态门的,只有LUT、寄存器这些资源,所以即使写了 三态,器件也无法实现,而引脚10级则是有三态门的,可以实现这些功能。 理解了三态门后,实现双向端口就很容易了,所谓双向,又能输出,又能输 入,但对应的都是一个端口,从时间上看,其实就是所谓的时分复用,控制端口 中三态门的状态,在三态门打开时,可以输出信号,对方接收,在三态门处于高 阻态时,这时候输出被禁用,外面的信号就可以通过输入缓冲器进来,这样就实 现了双向端口。 (双向端口电路设计一assign语句) 实现双向端口的核心就是控制好三态门的状态。通过例5一27学习。该模块 是双向端口的一个完整描述,可以作为设计中的一个子模块直接调用。解释代码 及功能(略),只有在ctnl为0时输入数据才是有效的,ena为1时输入,反之输 入。 (双向端口电路设计一always语句) 用always块也可以实现,例5-29就是如此,靠Ctrl来控制端口方向,为零 时Q置为高阻,也就是输入模式,反之为输出模式。 (双向端口电路设计错误实例) 教材上例5-30给出了一个错误的例子,还有例5-31/5-32,这些课堂上都不 细讲了,本质上,大家只要控制好三态门的状态,不要出现分支遗漏情况,也尽 量不要使用多重赋值(像教材上的例5-31/5-32虽然可以实现正常功能,但从代 码风格的角度看,并不是一个好的设计,实际上,用一个多分支if/else或者Case 语句就可以了,大家可以自行写一下)。 8.7 Verilog设计准则 l:Verilog的语法很多,可综合语法只占Verilog语法的很小一部分,但正是 这一部分才是Verilog语法的精髓,要强调的是,Verilog不是软件代码,描述的 是硬件电路结构及其行为。在设计代码之前,最好先自己画一个结构图出来,设 21
21 通过实际的例子,来学习如何实现三态门。解释例(5-26)中新的状态 Z, 如果 ena 不为 1,也就是为零,那么 DOUT 就变成高阻态。这就实现了一个 4 位 的三态门。 关于高阻态:只能用于设计的引脚端口级,而不能用于模块内部信号,因为 FPGA,CPLD 内部没有三态门的,只有 LUT、寄存器这些资源,所以即使写了 三态,器件也无法实现,而引脚 IO 级则是有三态门的,可以实现这些功能。 理解了三态门后,实现双向端口就很容易了,所谓双向,又能输出,又能输 入,但对应的都是一个端口,从时间上看,其实就是所谓的时分复用,控制端口 中三态门的状态,在三态门打开时,可以输出信号,对方接收,在三态门处于高 阻态时,这时候输出被禁用,外面的信号就可以通过输入缓冲器进来,这样就实 现了双向端口。 (双向端口电路设计-assign 语句) 实现双向端口的核心就是控制好三态门的状态。通过例 5-27 学习。该模块 是双向端口的一个完整描述,可以作为设计中的一个子模块直接调用。解释代码 及功能(略),只有在 ctrl 为 0 时输入数据才是有效的,ena 为 1 时输入,反之输 入。 (双向端口电路设计-always 语句) 用 always 块也可以实现,例 5-29 就是如此,靠 Ctrl 来控制端口方向,为零 时 Q 置为高阻,也就是输入模式,反之为输出模式。 (双向端口电路设计错误实例) 教材上例 5-30 给出了一个错误的例子,还有例 5-31/5-32,这些课堂上都不 细讲了,本质上,大家只要控制好三态门的状态,不要出现分支遗漏情况,也尽 量不要使用多重赋值(像教材上的例 5-31/5-32 虽然可以实现正常功能,但从代 码风格的角度看,并不是一个好的设计,实际上,用一个多分支 if/else 或者 Case 语句就可以了,大家可以自行写一下)。 8.7 Verilog 设计准则 1:Verilog 的语法很多,可综合语法只占 Verilog 语法的很小一部分,但正是 这一部分才是 Verilog 语法的精髓,要强调的是,Verilog 不是软件代码,描述的 是硬件电路结构及其行为。在设计代码之前,最好先自己画一个结构图出来,设
计包含哪些模块,模块有哪些端口,各个模块完成什么功能,必要时还要画时序 图,然后确定没有问题了再开始写代码,尤其是在大型项目中,一上来就写代码 是最不可取的,一方面思路不可能考虑得面面俱到,写着改着,最后改得面目全 非。 而且,Verilog语言不像软件语言那么精妙,像C语言,指针、多维数组 递归,很多高级算法在里面,往往程序写得很精致,执行效率比较高,但Verilog 恰恰相反,我们可以看到,基本上在可综合语句中,就是assign语句和always 语句,所有的功能都是在这些语句中完成,所以,Verilog不追求面子工程,只 考虑综合效果好不好。 2:对现代电子设计是尤其重要的,一定要有同步设计的理念在里面,同步 设计要求设计中的所有元器件都依赖于单一的或者有明确相位关系的时钟,所有 寄存器的翻转是依赖于固定时钟沿的,就是说它有一个稳定的工作节拍,最忌讳 异步信号,但实际上异步信号却不可避免,怎么办?同步化,像复位信号,如果 复位信号释放和时钟沿离得很近,那就会导致亚稳态的严重问题,因此,我们在 外部异步复位进来后都要先用寄存器打2拍,就样就做到了异步复位,同步释放。 同样地,如果有些信号是异步输入,也要先打2拍再使用。具体为什么打2 拍就可以,现在我们先不介绍,大家先知道应该怎么用,后面高级教程中我们会 详细解释。 3:关于输出信号,在数字电路早期,寄存器是比较宝贵的资源,因此输出 变量能用逻辑型就不用寄存型,但逻辑型有很大的问题,竞争、冒险不可避免, 经常会产生输出毛刺,这些毛刺一般都出现在其相关输入逻辑变量多位同时翻转 时,这是组合逻辑电路的先天特点,但事实上,如果采用寄存器型输出,只在每 个时钟沿采样一次,就是说寄存器型输出,只要保证时钟沿附近没有毛刺就行( 般毛刺是在时钟沿之后)就可以有效地避免毛刺现象,也正因为寄存器可以有效 滤除毛刺,现在FPGA中寄存器资源一般都是极其丰富的,所以大家不要怕浪费, 省个寄存器,出来一堆毛刺,后果更严重,这也是现代高速设计的一个基本准则。 从这个角度看,现在FPGA中已经很少有纯粹的组合逻辑电路了,寄存器占的比 例越来越大。 第9章Verilog系统设计优化 22
22 计包含哪些模块,模块有哪些端口,各个模块完成什么功能,必要时还要画时序 图,然后确定没有问题了再开始写代码,尤其是在大型项目中,一上来就写代码 是最不可取的,一方面思路不可能考虑得面面俱到,写着改着,最后改得面目全 非。 而且,Verilog 语言不像软件语言那么精妙,像 C 语言,指针、多维数组、 递归,很多高级算法在里面,往往程序写得很精致,执行效率比较高,但 Verilog 恰恰相反,我们可以看到,基本上在可综合语句中,就是 assign 语句和 always 语句,所有的功能都是在这些语句中完成,所以,Verilog 不追求面子工程,只 考虑综合效果好不好。 2:对现代电子设计是尤其重要的,一定要有同步设计的理念在里面,同步 设计要求设计中的所有元器件都依赖于单一的或者有明确相位关系的时钟,所有 寄存器的翻转是依赖于固定时钟沿的,就是说它有一个稳定的工作节拍,最忌讳 异步信号,但实际上异步信号却不可避免,怎么办?同步化,像复位信号,如果 复位信号释放和时钟沿离得很近,那就会导致亚稳态的严重问题,因此,我们在 外部异步复位进来后都要先用寄存器打 2 拍,就样就做到了异步复位,同步释放。 同样地,如果有些信号是异步输入,也要先打 2 拍再使用。具体为什么打 2 拍就可以,现在我们先不介绍,大家先知道应该怎么用,后面高级教程中我们会 详细解释。 3:关于输出信号,在数字电路早期,寄存器是比较宝贵的资源,因此输出 变量能用逻辑型就不用寄存型,但逻辑型有很大的问题,竞争、冒险不可避免, 经常会产生输出毛刺,这些毛刺一般都出现在其相关输入逻辑变量多位同时翻转 时,这是组合逻辑电路的先天特点,但事实上,如果采用寄存器型输出,只在每 个时钟沿采样一次,就是说寄存器型输出,只要保证时钟沿附近没有毛刺就行(一 般毛刺是在时钟沿之后)就可以有效地避免毛刺现象,也正因为寄存器可以有效 滤除毛刺,现在 FPGA 中寄存器资源一般都是极其丰富的,所以大家不要怕浪费, 省个寄存器,出来一堆毛刺,后果更严重,这也是现代高速设计的一个基本准则。 从这个角度看,现在 FPGA 中已经很少有纯粹的组合逻辑电路了,寄存器占的比 例越来越大。 第 9 章 Verilog 系统设计优化