本文基于MOOC课《芯动力——硬件加速设计方法》课程整理所得。如有需要,请前往MOOC观看
文章目录
前言
简单记录Verilog语言的coding style。本文承接《00.硬件加速设计方法 ——概述》记录,如有需要可以点击前往。
02-1 VerilogHDL可综合描述原则,常见语法描述对应的硬件电路结构
一、VerilogHDL 可综合概述
- Verilog HDL 的基本功能之一是
描述可综合的硬件电路
。如何合理使用 Verilog HDL 描述高性能的可综合电路是本次课的主旨之一。相比 C 语言,最显著的区别在与 HDL 语言具备以下硬件设计的基本概念:互连(connectivity ):wire 型变量描述各个模块之间的端口与网线连接关系。并发(concurrency): 可以有效地描述并行的硬件系统。时间(time): 定义了绝对和相对的时间度量,可综合操作符具有物理延迟。C 语言是一种软件语言,所以并无上述硬件对象。
- 硬件描述语言描述的电路,最终将通过逻辑综合工具、布局布线工具将其实现为芯片。但是,并不是 verilog 中所有的关键词都可以被综合为硬件电路。通常,只有四个关键字能够被综合为电路,分别是always、if-else、case、assign,我们称之为可综合四大法宝。除此以外,其他关键字都不可以被综合为硬件电路,比如左侧的几个。那么 verilog中众多的关键字有什么存在价值呢?这些不可综合的关键字都可以用于书写 testbench。
二、常见可综合语法与硬件的映射关系
- if-else 通常会被逻辑综合其映射为多路选择器,输出结果由输入的条件决定。需根据设计目标,小心设计:先“加”后“选”,先“选”后“加”。
- 对于单 if 语句,逻辑综合工具会将其映射为无优先级的判断结构。推荐初学者尽量使用单 if 语句(if…else if… .else if…) 描述多条件判断结构。
- 多 if 语句,逻辑综合工具会将其映射为有优先级的判断结构。最后一级选择信号具有最高优先级。具有优先级的多选结构会消耗组合逻辑。不推荐这种写法,有点乱。
- 若某些设计中,有些信号要求先到达(如关键便能信号、选择信号等),而有些信号需要后到达(如慢速信号、有效时间较长的信号等),此时则需要使用 if…if…结构。设计方法:最高优先级给最迟到达的关键信号。
- case 是一种无优先级的判断结构。与单 if 语句的区别是,case 的条件互斥。多用于指令译码电路。
三、电路设计需要注意的基本事项
- 慎用 latch。逻辑综合工具很难解释 latch,因此,除非特殊用途,一般避免引入 latch。
- 易引入 latch 的途径,一般是使用不完备的条件判断语句。防止产生非目的性 Latch 的措施主要有:使用完备的 if…else 语句;为每个输入条件设计输出操作,为 case 语句设置 default 操作;仔细检查综合器生成的报告,latch 会以 warning 的形式报告。
- 在电路设计的过程中,如果发现某个电路的负载比较多,则可以通过逻辑复制,降低关键信号的扇出,进而降低该信号的传播延迟,提高电路性能。
- 如果我们发现电路中存在较多的公共单元,也可以通过资源共享的方式,减小面积。但是,一般来说,共享会导致性能下降,所以还要根据性能和面积进行取舍。
- 在设计电路的时候,我们也可以根据数据的延迟,对这些资源进行顺序重排,降低传播延迟。如图所示,A 信号到来比较晚,我们就可以把它尽可能放到后面,隐藏其延迟。
- 最后一个小建议。在代码书写过程中,尽可能使用 always 描述电路,assign 仅仅用来连线。少用
:?
这种形式,因为这种形式通常难以阅读,且多层嵌套后很难被综合器解释。
- 所有的组合逻辑或锁存的 always 结构必须有敏感信号列表。这个敏感信号列表必须包含所有的输入信号。综合过程将产生一个取决于除敏感列表中所有其它值的结构,它将可能在行为仿真和门级仿真见产生潜在的失配。
- 在综合过程中,每个 Verilog always 块敏感信号列表只能对应一个时钟。原因:这是将每一个过程限制在单一寄存器类型的要求,有利于逻辑综合和静态时序分析。
- 不允许 Wait 声明和# delay 声明。Wait 声明语句,不论是清楚还是含糊,都不能用于可综合设计。从 RTL 级转换到 gate 级的综合工具一般都不支持 Wait 声明和# delay 声明,为了有效的综合,这些语句应该避免。在不需要进行综合的行为模块中,如测试模块、表示行为的虚拟器件模块中可以使用。
- 在时序电路中必须使用非阻塞赋值(<=),组合逻辑电路必须使用阻塞赋值(=),这个一定要注意,严格按这个要求写。
- 分开异步逻辑与同步逻辑。避免综合和静态时序时碰到问题,简化约束和编码难度。
- 不可应用于非综合模块中(例如:总线模块,总线监视器或是模拟模块)除非他们被设计来综合仿真。
- 分开控制逻辑和存储器,建议控制逻辑和存储器逻辑分成独立的模块。通常来说,存储器是用 memory compiler 生成的,其综合方式与 RTL 代码不同,所以混在一起,不利用综合,不利于很方便地更换工艺库和平台。
四、总结
初学者通常会有一个误区:把 verilog 代码当做了程序,把电路设计当成了编程。那怎么样写好 verilog 呢?牢记并理解可综合“四大法宝”所对应的硬件结构。写前,确认电路指标是什么:性能?面积?根据指标再来设计电路。硬件思维方式,代码不再是一行行的代码而是一块一块的硬件模块;对所需实现的硬件电路“胸有成竹”。
电路优化的质量取决于代码的书序方式。请不要把电路性能优化寄希望于综合工具。
02-2 在RTL书写中如何考虑延迟、面积等
可综合风格——在 RTL 书写中如何考虑延迟、面积等。
- 多路选择器构成的级联电路中,如果有个别信号到来的比较晚。那如何针对这个信号进行优化呢?尽可能把这个延迟较大的分支单独拿出来,放到离出口最近的选择器中。
对于代码中的延时较大的信号,一般分为几类,一类是选择器中信号,这类信号中对于到来较晚的信号可以尽量放在离出口较近的选择器中,特别注意的是,在修改选择分支顺序时,要注意考虑判断条件:
module case_in_if_01(A,DATA_is late_arriving,C,sel,Z): input [8:1] A; input DATA_is late_arriving; input [2:0] sel; input [5:1] C; output Z; reg Z; always @ (sel or C or A or DATA is late_arriving)begin if (C[1]) Z=A[5]; else if (C[2]==1'bo) Z=A[4] ; else if (C[3]) Z-A[1] ; else if (C[4]) case (sel) 3'b010:Z=A[8]; 3'b011:Z=DATA is late arriving; 3'b101:Z=A[7]; 3'b110:Z=A[6]; default:Z=A[2]; endcase else if (C[5]==1'b0) Z=A[2]; else Z=A[3]; end endmodule
这里可以看到,case中条件为011的情况的信号到来比较晚,我们将这个信号调整到最后一级的选择器中:
调整前的电路
调整后的电路
修改后的代码如下:
还有一类延时是在一个计算流中产生的,这时候主要考虑先选后加、先比后加、先加后乘的原则对计算顺序进行调整。
-
注意“先加后选”和“先选后加”两种方法对数据通道延迟的影响。
-
重点关注电路中的加法器、乘法器等较为复杂的逻辑单元,尽可能少使用。
-
随着芯片工艺的进步和生产成本的降低,面积显得没有时序问题重要。但减少设计面积意味着成本降低、功耗降低,特別是对于 FPGA 的设计,直接决着 FPGA 的选型。一般综合过程中可以对面积进行优化,但在 RTL 编码中如果注意节约设计面积,往往可以达到事半功倍的效果。
-
要减少设计的面积,首先应该学会估计设计使用资源的数量,如使用了多少个触发器、加法器、乘法器。这个过程也可以借助一些工具完成。最终应该知道设计中哪些部分占用了较大的面积,进而分析改进的方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qIePkJOK-1646400926656)(…/…/…/…/…/…/AppData/Roaming/Typora/typora-user-images/image-20220304211550341.png)]
-
一般来说,触发器的数量由功能决定,很难减少,同时触发器的面积比较好估计,因此和时延问題一样,组合逻辑是改进的攻点。对应到 RTL 代码,就是各种操作符。因此,应该了解各种操作符会产生的电路。要知道,RTL 代码屮的一个“+ ”可能对应着一个 64位的加法器。以下这些操作符都可能产生较大 的组合逻辑,使用时应加以重视,如“ + ”、 “-”、“×”、“÷”以及条件语句中 的比较运算。对于这些操作,首先应该判断其必要性,是否能用更简单的运算代替。
-
如果,必须使用复杂的运算符,则应考虑是否可以资源共享。尽管电路逻辑综合工具也会在综合的过程中采用资源共享的方法进行优化,但是,综合器的策略是有限的,因此,在编写 RTL 的时候,应该尽量考虑共享,而不是把这项工作完全留给综合工具。
-
除了操作符,多比特的信号也往往会占用较大的资源,因为使用这些信号的操作都是对所有的比特进行的,相当于成倍使用资源,因此,对这类信号的操作也应重视。
-
除此之外,针对不同的设计,还有可能有各种各样的优化和改进的方法,但是,归结到一点,就是编写代码时,应对操作符有足够的重视,对有可能简化的地方尽量简化。逻辑简化往往在减少面积的同时也减少了延迟,因此,是值得花费一些时间的。
-
对于功耗控制,主要措施包括如下:(1)门控时钟,门控时钟是电路设计最常用也是最有效的方法,在逻辑综合阶段可以让综合工具自行插入。(2)增加使能信号,使得部分电路只有在需要工作时才工作;(3)对芯片各个模块进行控制,在需要工作时才工作;(4)除了有用信号和时钟的翻转会消耗功耗,组合逻辑产生的毛刺也会大量消耗功耗。但是,毛刺在设计中无法避免,因此,只有尽量减少毛刺在电路中的传播,才可以减少功耗。即,在设计中,尽量把产生毛刺的电路放在传播路径的最后。另外,可以使用一些减少毛刺的技术。(5)对于有限状态机,可以通过低功耗编码来减少电路的翻转。总的来说,使用这些技术时,应首先考虑全局的功耗控制;然后再 RTL 编码中,注意消耗功率较多的电路,如状态机、译码器、多路选择器等。最后,在综合中,使用门控时钟和其他减少功耗的优化技术。这里要注意下门控时钟和增加使能控制的区别。增加使能仅仅是使得电路的信号不在翻转,但是时钟每个周期还会继续翻转。而门控时钟则是直接关掉时钟,这种方法效果更好。
- 如果可以在 RTL 编码 阶段考虑代码可能对布线产生的影响,就可能避免最后出现无法布通的情况。布线阶段,通常热点是一个影响布线质量的问题。我们在 RTL 阶段应该重视这种电路,及早发现可能在布线阶段产生的问题。如果设计的功能中确实需要采用大的 mux,可以通过其他方式改变他的结构。其基本的思路是将一个大的 mux 分解为多级较小的 mux。
02-3 RTL设计指导原则
RTL 设计指导原则。
一、概述
RTL 级设计的评判标准很多,如时序性能、所占面积、可测试性、可重用性、功耗、时钟域的分配、复位信号设计以及是否与所用 EDA 工具匹配等。如果设计目标是在 FPGA 或CPLD 等可编程逻辑器件上实现,则还需考虑是否能发挥这些 PLD 的结构特点等。根据这些目标的组合和优先级设置,可以派生出很多不同的设计原则。这里仅讨论一般意义的指导原则。通常来说,主要有三个指导原则:面积与速度互换、乒乓操作、流水线设计。
我们先来讲面积与速度互换原则。这里的“面积”是指一个设计所消耗的目标器件(如FPGA 、 CPLD 和 ASIC 等)的硬件资源数量或者 ASIC 芯片的面积。对于 FPGA 来说,可以用所消耗的触发器 CFF) 和查找表 CLUT)数量来衡量;对于 ASIC 来说,则可以用设计的面积、门数等衡量。"速度"指设计在芯片上稳定运行时所能够达到的最高频率,这个频率由设计的时序状况决定,与设计满足的时钟周期、PAD to PAD Time 、Clock Setup Time 、Clock Hold Time 和 Clock-to-Output Delay 等众多时序特征量密切相关。
面积和速度这两个指标贯穿于 RTL 设计的始终,是衡量设计质量的终极标准。这里讨论一下设计中关于面积和速度的基本原则,即面积和速度的平衡与互换原则。作为矛盾的两个方面,面积和速度的地位是不一样的。相比之下,满足时序、工作频率的要求更重要一些,所以当两者发生冲突时,应采用速度优先的原则。
面积和速度的互换是 RTL 设计的一个重要思想。从理论上讲,一个设计如果时序余量较大,所能跑的频率远远高于设计要求,那么就能通过功能模块复用减少整个设计所消耗的芯片面积,也就是用速度的优势对换面积的节约。
反之,如果一个设计的时序要求很高,普通方法达不到设计频率,那么一般可以通过将数据流串并转换,井行复制多个操作模块,对整个设计采取"乒乓操作"和"串并转换"的思想进行处理,在芯片输出模块处再对数据进行"井串转换"。
从宏观上看,整个芯片满足了处理速度的要求,这相当于用面积复制换取速度的提高。面积和速度互换的操作技巧很多,比如模块复用
、"乒乓操作"
、"串井转换"
等,这些技巧需要不断积累。
二、乒乓操作
乒乓操作的最大特点是通过“输入数据选择单元”和“输出数据选择单元”按节拍、相互配合的切换,将经过缓冲的数据流没有停顿地送到“数据流运算处理模块”进行运算与处理。把乒乓操作模块当做一个整体,站在这个模块的两端看数据,输入数据流和输出数据流都是连续不断的,没有任何停顿,因此非常适合对数据流进行流水线式处理。所以乒乓操作常常应用于流水线式算法,完成数据的无缝缓冲与处理。
乒乓操作的第二个优点是可以节约缓冲区空间。另外,巧妙运用乒乓操作还可以达到用低速模块处理高速数据流的效果。如视频中实例展示,通过乒乓操作实现低速模块处理高速数据的实质是:通过 DPRAM 这种缓存单元实现了数据流的串并转换,并行用“数据预处理模块 1”和“数据预处理模块 2”处理分流的数据,是面积与速度互换原则的体现!
三、流水线
流水线的参数设计中,系统时钟取决于最慢的流水线级的延时。流水线分割点及级数的确定要考虑的因素包括:单元延迟时间及时钟频率的大小决定了数据通过速率,过多的级数不一定能产生最快的结果;太多寄存器的插入会导致芯片面积增加,布线困难,时钟偏差增加。
总结
如何写出高质量的Verilog代码对于一个数字设计工程师来说还真的挺重要的,但是不同的场景对于coding style的要求是不同的,这里的有些观点也不是所有情况下都适用的,相比于细节来说,更重要是知道为什么会这样写,以及知道代码不可以 随心所欲 的写。