verilog 新手导航
来源:互联网 发布:增值税发票数据导出 编辑:程序博客网 时间:2024/09/21 08:17
写在前面
verilog不同于其他编程语言,它是一种硬件描述语言。我在verilog的学习中、烧板中走过了许多弯路。
试过写个简单的ALU花了15h+的时间,几天几夜debug无数次。也试过写学号循环,仿真通过了,烧板时灯完全不亮。期间各种google和stackoverflow,最终才渐渐地对verilog有所感悟。
我略总结了几条准则,在写verilog时应该反复在心里确认。
把verilog看成硬件描述语言
简介
verilog这个语言的作用是描述硬件。因此在写代码时,应该反复确认代码能否与硬件构成一一对应的关系。
有些代码能够通过综合(synthesis)和仿真(simulation),但在烧板时却无法达到预期的效果。烧板无法达到预期的代码,称为不可合成的代码(non-systhesizable),这些代码没有真正地”描述“硬件如何工作,无法根据代码产生对应的硬件。
烧板能正确工作的代码,称为可合成代码(synthesizable)。下面我们会讨论如何写出正确的可合成的代码。
写出可合成代码的诀窍是,时刻把verilog看成描述硬件的语言
下面简单举几个例子
延时(delay)不可合成
下列代码
reg [20:0]cnt = 0; always begin #1000000; cnt = cnt + 1; end
在时间单位是1ns的情况下,他企图每1ms增加cnt的值。然而在烧板时它无法正确工作。
试想一下,作者希望延迟
类似的代码还有OUT1 #1 = IN1 & IN2;
假设一个与门的延迟是5ns,你又如何强迫上述代码必须在1ns内完成任务?
所以,在产生实际电路时,所有的延迟将被忽略。延迟不可合成。
延迟存在的意义是,仿真时模拟竞争与冒险。
拒绝for循环
在verilog中,使用for循环被视为bad practise
试想以下,下述代码
reg [3:0] i; for (i = 4'b0000; i <= 4'b1111; i = i + 1) begin //statement end
为了让它运行,该产生多么复杂的硬件呢。
首先我们需要4位寄存器存储i
的初始值,我们需要比较电路来完成i <= 4'b1111
, 我们还需要加法电路实现i = i + 1
。
原本简单的电路会因此变得十分复杂,难以调错。
使用for很不”硬件描述“。
笔者并不反对在TestBench 文件中使用for循环。但谨记,不要在源文件中使用for。虽然for可合成,但为了合成它需要很大的代价。
分清时序电路与组合电路
这是写verilog代码的第二个准则。新手十分容易在此处犯错。
简述
verilog有它基本的语法,例如
always@(A or B or C)
上述代码always后接敏感表,会对应产生组合电路
又如
always@(posedge CLK)
上述代码always后接上升沿敏感,会产生时序电路。
注意,两行代码差异十分微小,但会由此产生截然不同的电路。对电路的分析、使用也截然不同。
组合电路
观察下述代码
reg [3:0] cnt; always@(cnt) begin cnt = cnt + 1; end
上述代码没有任何”语法错误“,但却是个绝对错误的代码。
always里接电平敏感信号,表明这是个组合电路。然而组合电路没有记忆功能,即时的输入决定即时的输出。这种情况下,如何”记忆“cnt的现态,把它加一求出次态呢。
上述代码在仿真时会出现不定态(此处不确定)
值得一提的是,组合电路不会含有寄存器,故reg [3:0] cnt;
不会产生寄存器。此处用reg
声明的原因是,always
语句里必须使用reg
型变量。若声明为wire
会报错
此处是因语法需要而将cnt
声明为reg
。本质上组合电路中,cnt
与wire
型变量的行为是一致的。
时序电路
时序电路会为reg
型变量产生寄存器。
观察下述代码
always@(posedge CLK or negedge CLK) begin //statement end
上述代码表面上没有错误,但也是错误的代码。
“在CLK的上升沿或下降沿触发”
与
“在CLK发生改变时触发”
两者是完全一致的。故这个电路实质上是组合电路。
上述代码的合成将会报错(待确认)
组合电路(2)
组合电路还有一种写法
assign OUT1 = IN1 & IN2;
assgin
的含义是连线。相当于在实验板上,用一根电线把某些接口连接在一起。
显然即时的输入决定即时的输出,构成一种组合电路。
应注意,assign
语句构成的组合电路是”每时每刻”都发生变化的。故构成并行语句。
不难理解assign
与always
语句应该处于同等地位(平行关系)。故不能将assign
语句放置在always
中。
推荐写法
有时verilog的警告会出现 infer latch
(构成锁存器)字样。下面讨论一些good practise 以及与锁存器相关的内容。
if语句必须带有else
以下讨论组合电路。
假设if
不带else
,则表明在某些情况下(不进入所有的if体
),某些变量的值不会被修改。
换句话说,某些情况下,某些变量的值会保留它上一时刻的值。
这在组合电路中是无法发生的。vivado
会为这种情况产生寄存器,或把输出反接到输入端。构成锁。(待确认)
故所有的if
语句都应该带有else
if
语句的硬件实现是优先译码器(prior decoder)
case语句一定带有default
原因同上。 case
语句的硬件实现是多路分配器(multipexer)
组合电路禁止出现反馈
这点具体可见组合电路的定义。
组合电路的定义本身禁止门的输出直接相连,禁止将输出反接到输入,禁止成环等。
技巧与注意
- 声明变量不要忘记线宽。笔者曾经试过
reg a
忘记加线宽而出现难以调试出的bug - 位拼接运算符很好用。
{ A, B, C, ..., X}
会将各个变量的位按次序拼接在一起。用位拼接运算符可以简化代码。 - 禁止部分初始化
reg
。考虑这个代码
reg [3:0]ID;initial begin ID[3:0] = 4'b0000;end
烧板时会造成错误,但
reg [3:0] ID;initial begin ID = 4'b0000;end
则是正确的。原因不明(请求信息)。
4. delay(#)
和real
不可合成。real指实数,是一种数据类型。建议大家google verilog synthesizable
.学习如何写出可合成代码。
5. initial
在FPGA
上可合成。单独提出这点是因为,并非所有的实验板都支持initial
语句。我们(中山大学16届计科)用的板是FPGA
,可以根据initial
语句产生正确的电路。但不保证其余情况下initial
语句能正确工作。
写在最后
always语句中的reg解疑
明明always@(A or B)
会被看作组合电路,为何always
语句中还要使用带有歧义的reg
类型变量呢?
verilog
语言的创始人犯了一个很愚蠢的错误。他想当然地认为always
语句中的变量都应该是reg
,于是在设计语言时将其作为一个语法。
事实上always
语句有可能被合成为组合电路,故早期的verilog
就在这个矛盾中发展起来了。
对reg
类型的讨伐越来越多,修改的呼声越来越高。新版的system verilog
里不再使用reg
类型,而是使用取而代之的logic
类型。(待确认)相信这种情况下,初学者会对verilog
更易掌握。
可合成代码解疑
许多初学者对verilog
有过声讨。为何它的代码要分可合成和不可合成?
原因在于,使用verilog
语言的人不完全都是为了烧板。verilog
作为一个硬件描述语言,有它自己的存在意义,烧板只是它的功能的一个子集。
许多程序员会希望verilog
语言拥有更强大的功能,帮助设计硬件,哪怕这个语法暂时还无法合成。
笔者相信,无法区分可合成与不可合成,是写verilog
致错的重要原因。
例如下述代码
always@ (posedge CLK) begin $monitor("%h, %0t", cnt, $time); cnt = cnt + 1; end
作用是,在每次cnt
变量发生修改时,都会在log
上打印cnt
变量的值以及当前的时间。
这段代码是很好的调错代码。显然,$monitor
无法被合成。你不可能希望在烧板后,凭空产生一个屏幕为你显示调错信息。
类似的调错代码还有$display()
等。
但不得不强调:通过仿真不代表烧板后行为正确。你必须确保你的代码全都是可合成的代码。
全文完。
via YB
- verilog 新手导航
- Verilog新手误区
- 新手导航
- SHELL版新手导航
- Verilog RTL 代码设计新手上路
- 新手向导--logo学习导航
- 高手经验:一个新手的verilog学习经验
- 如何使用ISE高效开发Verilog项目(新手)
- 数据库的基本入门---新手导航
- MacBook 新手帮助导航, 一起从零开始
- 关于idea 2016开发环境新手导航
- verilog
- verilog
- Verilog
- ESL综合之C-to-Verilog 免费工具(新手必看 )
- web开发网址导航,建站新手必备主页
- 菜鸟新手们得福音纯css菜单导航
- .Net Micro Framework导航总贴(新手必看)
- 使用HBase Coprocessor协处理器
- 常用知识点
- 用GDB调试程序
- c++实验六
- Integer to Roman【LeetCode】
- verilog 新手导航
- java-一维二维数组的定义、使用二维数组、遍历数组
- 区块链搭建以及智能合约的运行
- POJ1149_PIGS_网络流
- 程序员所具备的素质[转]
- 你要工作多久,才能赚够一份早餐钱?
- FileWriter无法指定文件输出编码
- 详解Google Code Prettify代码高亮Prettify.js库使用及其应用
- jQuery获取Select选中的Text和Value,根据Value值动态添加属性等