自打说了《玩转 IP Core》和写了《IP 核芯志》之后,老衲就沉沦了,沉迷于“秋风功”修炼。俗话说:“坐吃山空”,见到荷包渐渐干瘪,和尚我不得不再次出山了。施主们多多捧场为好。本想说说理论的事情,奈何在下道行还浅,就只得简单的讲讲 Verilog 语言这个事情了,大伙儿见谅。
这个语言本不难,道和歪嘴念经的太多,念着念着就成了 C 语言的附属了,这个着实的大谬也。看看整套书的题目便知:从电路里来,到 Verilog 里去!
老僧一贯不喜欢说古,对于 Verilog 语言的历史,施主只需要知道几个事实:
1)1983 年诞生,此时 EDA 设计尚处于幼年,难免有很多误区。所以,Verilog 语言里面也存在许多“拧巴”的地方。
2)1995 年,首次被 IEEE 标准化,这就是所谓的 Verilog 95 版本。
3)2001 年,IEEE 对于 Verilog 重大更新,Verilog 2001 面世。
4)2005 年,IEEE 又进行了细微更正,出品了 Verilog 2005。
本书里面介绍了 2001 版本,也就是应用最广、软件兼容性最强的版本。
1. 模块为本,链接系统
在概念设计过程中,设计人员总是将复杂的功能划分为简单的功能,模块是提供每个简单功能的基本结构。设计人员可以采取“自顶向下”的思路,将复杂的功能模块划分为低层次的模块。这一步通常是由系统级的总设计师完成,而低层次的模块则由下一级的设计人员完成。自顶向下的设计方式有利于系统级别层次划分和管理,并提高了效率、降低了成本。在实际代码编写过程中,会采用“自下向上”的工作流程。也就是先从前面划分好的最下层的模块开始写作代码以及完成相应的单元测试,逐次联线打包和测试,直至完成最高层模块及其测试。这个“自顶向下”再“自下向上”的过程,是项目管理研究的范畴,这里稍带提一下。
Verilog 语言被设计用来描述复杂的硬件电路。由于上述项目管理模式的普遍采用,使用 Verilog 描述硬件的基本设计单元是模块(module)。构建简单的子电路,然后通过子电路的连接形成较为复杂的上层电路,最终达到系统的设计需求。实际的自电路元件,Verilog 语言能够提供输入、输出端口,可以实例调用其他模块,也可以被其他模块实例调用。模块中可以包括组合逻辑部分、过程时序部分。
一个模块如图 1 所示,模块一般用方框表示,但是也有例外。图中,方框内的名字为模块名,指向模块的带肩头线表示输入信号,由模块向外的箭头线表示模块的输出信号。输入信号和输出信号上面一般标记信号名称。例如,图 1.3 中就是一个具有三个输入信号(“输入 11”,“输入 12”和“输入 13”)和两个输出信号(“输出 11”和“输出 12”)的模块,模块被命名为“模块 1”。
图 1 模块的图例
模块之间的连接如图 2 所示,如果模块的输出为另一个模块的输入,在图中会合并两个联线(也就是说,用一个从第一个模块指向第二个模块的带箭头线来表示)。例如,图 2 中“模块 1”的两个输出连接到了“模块 2”的两个输入。没有在模块之间连接的信号,则表示该信号是外部信号,例如“模块 2”的输入 3 信号。
图 2 模块之间的连接
多个模块可以打包为一个上层的模块,如图 3 所示。图中,把图 3 里面的两个模块打包成为了一个上层模块:“模块 3”。生成的上层模块的输入和输出一定包含低层模块之间未连接的信号,但是也可以包括低层模块之间的连接信号。
图 3 模块打包为上层模块
具体这些模块设计、模块连接以及模块打包如何完成,后文书自有交待。
2. 电路设计,二虎挡道
在任何一本《数字电子技术》教材的某个犄角旮旯,总有类似下面的论述。
“组合逻辑电路中,同一信号经不同的路径传输后,到达电路中某一会合点的时间有先有后,这种现象称为逻辑竞争,而因此产生输出干扰脉冲的现象称为冒险。
竞争(Competition)定义为:在组合逻辑电路中,某个输入变量通过两条或两条以上的途径传到输出端,由于每条途径延迟时间不同,到达输出门的时间就有先有后,这种现象称为竞争。把不会产生错误输出的竞争的现象称为非临界竞争。把产生暂时性的或永久性错误输出的竞争现象称为临界竞争。
冒险(Risk)定义为:信号在器件内部通过连线和逻辑单元时,都有一定的延时。延时的大小与连线的长短和逻辑单元的数目有关,同时还受器件的制造工艺、工作电压、温度等条件的影响。信号的高低电平转换也需要一定的过渡时间。由于存在这两方面因素,多路信号的电平值发生变化时,在信号变化的瞬间,组合逻辑的输出有先后顺序,并不是同时变化,往往会出现一些不正确的尖峰信号,这些尖峰信号称为‘毛刺’。如果一个组合逻辑电路中有‘毛刺’出现,就说明该电路存在冒险。
竞争冒险都是由于延迟时间的存在,当一个输入信号经过多条路径传送后又重新会合到某个门上,由于不同路径上门的级数不同,或者门电路延迟时间的差异,导致到达会合点的时间有先有后,从而产生瞬间的错误输出。”
为什么每次读教科书,都觉得是大筒木輝夜的惊天阴谋呢?有一种不让读者进入无限月读,誓不罢休的霸气外露(典故见《火影忍者疾风传》)。当然这是题外话,大家一乐一乐(非拉面广告啊)。列位稍安勿躁,待贫道说人话解释一番。
竞争就是三字经“人之初,性本善。性相近,习相远”的道理。同一个服务器的数据包,有的是电驴 P2P(点对点,point to point)几乎及时到达,有的却莫名其妙被发配去粮食基地米国周游了一圈五分钟才到您老的计算机网口。如果这个是上网浏览一点网页,还倒罢了,这就是非临界的。假使这个是看视频,这个就卡那里了,用户会很不爽,这就算临界竞争了。
冒险的情况,坏就坏在“分进合击”上面。一组本来好好的输入,非要经过不同道路到达器件,结果可好有了时间差了,再被已处理后果不堪设想。
3. 时序器件,建立保持
问题从最简单的开始解决,这是一个原则。先来瞧瞧一个 D 触发器对于输入信号的要求,就是耳朵都听出茧子的建立时间和保持时间问题。建立时间(英文 Set up time,简写 Tsu),是指触发时钟沿(一般为时钟信号的上升沿,本书中也沿用这个约定)到达 D 触发器之前,要求输入信号必须已经达到稳定的时间。对应的,保持时间(英文 Hold time,简写 Th),是指触发时钟沿到达 D 触发器之后,要求输入信号还需要保持必须稳定的时间。建立时间、保持时间相对于触发时钟沿的关系,如图 4 所示。
图 4 建立时间、保持时间相对于触发时钟沿的关系
“我们都是木头人,不许说话不准动”,建立时间和保持时间就是输入信号的“木头人时间”。做游戏的时候,“木头人时间”内有了晃动或者出声,那游戏就输了。电路里面,如果输入信号在建立时间或者保持时间里面发生了变化,那就是仿真(尤其是时序仿真)里面所谓“全国山河一片红”了,那个绝对是灾难(声明:贫道与中国台湾没有关系)。
不推导“复杂”公式,直接给结论:要满足建立与保持时间,时序系统才能稳定工作。
对于 FPGA 设计,这个“满足”是靠写时序约束,来教导综合软件和布局布线软件实现的。
首先,这里解答“什么是布局布线”这个问题。简单理解,“布局布线”就是在器件(FPGA/ASIC)里面画 PCB。布局是把器件放到相应的位置上,布线是把有关联的器件连接起来。
机器都是笨的,即使是号称“电脑”的计算机,需要人来操作才能完成需要的任务。如果仅仅把设计给了设计环境,不做任何设计上的说明(也即所谓的“时序约束”),机器绝对会搞出一些工程师不希望的结果出来。记住:任何工具都不会注重尝试寻找到达最快的布局布线结果。这个正如画 PCB 的工程师绝对不会使用软件提供的所谓“自动功能”。
最常用的时序约束有时钟、输入输出时延和“虚假路径”几种,下面一一介绍:
时钟的频率和性质决定了其他时延的上限。时钟频率越高、时钟抖动越大,允许的布局布线时延以及组合逻辑时延也就越小。
时钟信号包括三个性质:周期与占空比、时钟间的偏移和时钟的抖动。如图 5 所示,时钟的周期定义为理想时钟的两个触发沿之间的时间间隔;时钟的占空比定义为一个周期内,理想时钟高电平“1”信号占整个时钟周期的比例;时钟之间的偏移定义为两个同源时钟之间的、固定的出发沿的时间差;时钟的抖动定义为一个时钟信号相对于理想时钟,上升沿与下降沿可能变换的前后范围。最后多嘴一句,这些实际系统中的时钟与理想时钟的差别是芯片内的“时钟树”造成的。
图 5 时钟的时序约束参数
下面两折比较类似,现在合并演出(台下:偷工减料,退票!):输入 / 输出路径时延表标记芯片的前端 / 后端芯片的时序特性 / 要求。这个基本上就是,把前端芯片看作 D 触发器,告诉工具前端芯片的输出时延,这样可以保证系统对于输入可以可靠采样。同样的,把后端芯片看作 D 触发器,通知工具后端芯片的建立时间和保持时间,可以保证可靠的信号输出。整个系统的链接情况,如图 6 所示。
图 6 输入 / 输出路径时延
下一出是《珍珠塔》,“皇帝家也有三门草鞋亲,谢谢”(这就完了!?台下骚动:“退票!退票!”)。实际系统中,很少有必须工作时间都一致的,总会存在一些“急先锋”遇到的“慢郎中”的。别抬杠,您老没遇到,只能说明您还毛嫩。多周期的时序约束,实际上是减少布局布线压力的,这个和前面 存在差异。对于这些需要计算较慢的单元 / 路径,可以降低要求,把好的资源让渡给需要快速的部分使用。图 7 就是一个例子,上面是需要快速解决的单元们,下面是可以“慢慢来”的部分。多周期约束定义了到底可以多慢做完这个工作。
图 7 多路径约束
最后出场的就是“虚假路径”这个草包,主要表现时钟域之间路径和测试逻辑,工具对这类任务也采取不闻不问、听之任之的态度。
4. 定制器件,细节入手
FPGA 才是“上得厅堂,下得厨房,安得系统,打得流氓”的多面手,其结构如图 8 所示。
图 8 某 FPGA 的功能结构图
其中包括可编程输入输出单元(IOB,Input Output Block)、可配置逻辑块(CLB,Configurable Logic Block)、数字时钟管理模块(DCM,Digital Clock Management) 和嵌入式块 RAM(BRAM,Block RAM) 等部分。这个里面与 Verilog 语言设计电路有关的部分是 CLB,揭开 CLB 的盖头来,您老将会看到如图 9 所示的一副尊容。以 Xilinx 公司的 FPGA 器件为例,CLB 由多个(一般为 2 个或 4 个)相同的 Slice(这个词还真不好翻译,查了半天度娘,看到唯一靠点谱的说法是逻辑片)和附加逻辑构成。其中,Slice 是基本的逻辑电路实现单元,属于中等后面介绍;外围电路中,开关网络主要完成一般信号的可配置连接,快速连接线进行时钟和复位等关键信号的连接。
图 9 Xilinx 公司 FPGA 中 CLB 的结构图
Slice 的结构如图 10 所示,一个 Slice 由两个 4/6 输入的 LUT(查找表,Look Up Table)、进位算术逻辑、D 触发器和函数复用器组成。算术逻辑包括一个异或门和一个专用与门,一个异或门可以使一个 Slice 实现 2bit 全加操作,专用与门用于提高乘法器的效率(这个后文书有详细讲解,这里属于“看热闹”的内容);进位逻辑由专用进位信号和函数复用器(MUXC)组成,还包括两条快速进位链,用于提高 CLB 模块的处理速度,用于实现快速的算术加减法操作。4 输入 LUT 用于实现组合逻辑运算。
图 10 Xilinx 公司某款 FPGA Slice 的结构
同学们有没有感到惊讶呢?如果有,说明你的《数字电子技术》的学费没白交。说好的用于组合逻辑的门级电路呢?还有编码器、译码器等等,等等?这些器件都没有,怎么设计组合逻辑啊?“风萧萧兮易水寒,虞兮虞兮奈若何?”
LUT 本质上就是一个 RAM,里面存着真值表。目前 FPGA 中,多数采用 4 输入的 LUT,所以每一个 LUT 可以看成一个有 4 位地址线的 的 RAM。 当用户通过原理图或 HDL 语言描述了一个逻辑电路以后,EDA 软件会自动计算逻辑电路的所有可能结果,并把真值表事先写入 RAM。这样,每输入一个信号进行逻辑运算就等于输入一个地址进行查表,找出地址对应的内容,然后输出即可。对于输入高于 4 比特的输入,EDA 软件会自动把输入拆分成若干 LUT,最后联合输出。(真值表是什么,这个不用讲了吧。不懂的人,数电考试肯定作弊的,回去重修!)官方说法是:“由于基于 LUT 的 FPGA 具有很高的集成度,其器件密度从数万门到数千万门不等,可以完成极其复杂的时序与逻辑组合逻辑电路功能,所以适用于高速、高密度的高端数字逻辑电路设计领域” 。所以本书介绍的结构竟然是流行趋势,好极好极!
最后来看一看开发流程里面的“综合”这个过程,那些“优化”什么的亮瞎钛合金狗眼的概念,对于 FPGA 的组合逻辑方面还存在吗?我们估计独立思考。贫道闻到了退化的味道。这也不是说 EDA 软件没用,LUT 组合方面的算法还是可圈可点的嘛。
5. 语言缺陷,电路子集
Verilog 设计之初就号称可以一次打两只鸟:设计和验证,殊不知这两个方向的思路却相差很大。数字逻辑的设计是基于数电的,受限制于电路器件的特性;反观数字逻辑系统的验证,这是一个纯粹软件仿真的活计,工程师更加喜欢一般程序设计语言的思路。在老衲参与的很多技术讨论里面发现,这“两鸟”不仅仅没有打到,这把“米”是一定“蚀”了。大多数初学者完全分不清那些可以综合,那些不可以。
老僧也在总结这个现象的原因,吾称之为:“教不得法,学不得法”。“教不得法”是指,很多关于 Verilog 的书继承了程序语言教材的结构,把语句分为:算术运算、条件判断和循环等等部分,完全不区分可综合性,这样有误导的嫌疑;“学不得法”是一个历史原因,大多数学习者实际上具有很强的程序语言,例如 C 语言,的基础,这反而是有害的。人的思维是有习惯性的,Verilog 的形式又与 C 语言很类似,所以多数人都把 C 语言设计里面的习惯不自觉的带入了 Verilog 语言的电路实现。于是乎,综合软件上报的错误“如滔滔江水,绵绵不绝”,实现与想象不一致也是“如黄河泛滥,一发不可收拾”。以上是一家之言啊,切勿对号入座,否则必有受伤者,老衲不负责医药费!
数字电路和程序语言的区别,别告诉我您不知道。下面姑且总结几条,叫大家以后面试的时候,可以说得头头是道。
1. 对应于仿真:C 语言的代码是一行一行执行的,属于顺序结构;而 Verilog 是一种硬件描述语言,语句同时进行,属于并行结构。
这个特点说起来有点抽象,我们举一个例子吧。就对于某家上面举出的代码,减震器记得时刻到了。
表 1 两种语言的仿真方式差别
|
|
Verilog |
代码 |
int a, b, c; …… c = a; a = b; b = c; …… |
reg [0:7] a, b, c; …… always @ (posedge Clock) begin …… c <= a; a <= b; b <= c; …… end |
初始值 |
a = 1; b = 2; c =3 |
|
结果 |
a = 2; b = 1; c = 1 |
第一节拍:a = 2; b = 3; c = 1; 第二节拍: a = 3; b = 1; c = 2; 第三节拍: a = 1; b = 2; c = 3; 第四节拍:a = 2; b = 3; c = 1; …… |
“不看不知道,世界真奇妙”,套用以前《正大综艺》的台词。见到黄河了?佛主说:回头是岸,南无阿弥陀佛。
避繁就简,结合网上度娘出来的资料,老衲针对 Verilog 可综合性设计,分别 FPGA 和 ASIC 的情况,给列位总结出了表 2(内部资料,注意保密)。表里面可能有一些超前的概念,例如 always、循环和阻塞 / 非阻塞赋值等,这些会在后文书里面介绍,这里先说声对不起。
表 2 Verilog 可综合性设计原则汇总表
原则 |
FPGA 适用 |
ASIC 适用 |
不使用初始化赋值 |
不适用 |
适用 |
不使用时延 |
适用 |
适用 |
不使用循环次数不确定的循环语句 |
适用 |
适用 |
不使用移位次数不确定的移位操作 |
不适用 |
适用 |
不使用复杂运算,如乘法、除法,幂等 |
依赖于综合软件 |
适用 |
不使用用户自定义原语 |
适用 |
适用 |
尽量使用同步方式设计电路 |
适用 |
适用 |
除非是关键路径的设计,一般不采用调用门级元件来描述设计的方法,建议采用行为语句来完成设计 |
适用 |
不适用 一些组合逻辑优化可使用门电路简化 |
用 always 过程块描述组合逻辑,应在敏感信号列表中列出所有的输入信号 |
仅适用于 Verilog 95 |
仅适用于 Verilog 95 |
所有的内部寄存器都应该能够被复位,在使用 FPGA 实现设计时,应尽量使用器件的全局复位端作为系统总的复位 |
适用 |
适用 |
对时序逻辑描述和建模,应尽量使用非阻塞赋值方式 |
适用 |
适用 |
对组合逻辑描述和建模,既可以用阻塞赋值,也可以用非阻塞赋值 |
适用 |
适用 |
同一个过程块中,最好不要同时用阻塞赋值和非阻塞赋值 |
适用 |
适用 |
不能在一个以上的 always 过程块中对同一个变量赋值 |
适用 |
适用 |
对同一个赋值对象不能既使用阻塞式赋值,又使用非阻塞式赋值 |
适用 |
适用 |
避免混合使用上升沿和下降沿触发的触发器 |
适用 |
适用 |
同一个变量的赋值不能受多个时钟控制,也不能受两种不同的时钟条件(或者不同的时钟沿)控制 |
适用 |
适用 |
避免在 case 语句的分支项中使用 x 值或 z 值 |
与综合软件有关 |
与综合软件有关 |
话说两头,老衲从来不提倡先写代码,然后去看能不能综合的设计步骤。老僧把那叫做“C Style”、“逻辑派”或者“语法党”,某家提倡的是“电路门”。早在《IP 核芯志》里面,吾就说过了:“看图说话基本法,电路映射程序中。”,这是不二法门。听不懂吗?就是先画电路 / 时序 / 状态转移图,再去写代码的意思!还不懂?完全无语了,容我冷静一下。
这正是:
“
远看石塔黑乎乎,上面细来下面粗。有朝一日翻过来,下面细来上面粗。
都晓倒着很悬乎,重心不稳难以扶。不知语言学习路,也要数电加基础。
”
与非网原创内容,谢绝转载!
如果有任何技术问题,欢迎留言提问~~