硬件描述语言Verilog设计经验总结
一、硬件描述语言Verilog
粗略地看Verilog与C语言有许多相似之处。分号用于结束每个语句,注释符也是相同的(/* ... */和// 都是熟悉的),运算符“==”也用来测试相等性。Verilog的if..then..else语法与C语言的也非常相似,只是Verilog用关键字 begin和end代替了C的大括号。事实上,关键字begin和end对于单语句块来说是可有可无的,就与C中的大括号用法一样。Verilog和C都对大小写敏感。
当然,硬件和软件的一个重要区别是它们的“运行”方式。硬件设计中用到的许多单元都是并行工作的。一旦设备电源开启,硬件的每个单元就会一直处于运行状态。虽然根据具体的控制逻辑和数据输入,设备的一些单元可能不会改变它们的输出信号,但它们还是一直在“运行”中。相反,在同一时刻整个软件设计中只有一小部分(即使是多软件任务也只有一个任务)在执行。如果只有一个处理器,同一时间点只能有一条指令在执行。软件的其它部分可以被认为处于休眠状态,这与硬件有很大的不同。变量可能以一个有效值而存在,但大多数时间里它们都不在使用状态。
软硬件的不同行为会直接导致硬件和软件代码编程方式的不同。软件是串行执行的,每一行代码的执行都要等到前一行代码执行完毕后才能进行(中断的非线性或操作系统的命令除外)。
二、模块(module)
一个Verilog模块的开头是关键字module,紧跟其后的是模块名称和端口列表,端口列表列出了该模块用到的所有输入输出名称。接下来是端口声明部分。注意:所有的输入输出既出现在模块第一行的端口列表中,也会出现在端口声明(declaration)部分中。
以下三个模块分别以∶ 结构式(structural)、数据流式(data-flow)及行为式(behavioral)来描述一个二输入与门。
//structural
module AND2 (in1,in2,out);
input in1;
input in2;
output out;
wire in1,in2,out;
and u1 (out,in1,in2);
endmodule
//data flow
module AND2 (in1,in2,out);
input in1;
input in2;
output out;
wire in1,in2,out;
assign out=in1&in2;
endmodule
//behavioral
module AND2 (in1,in2,out);
input in1;
input in2;
output out;
wire in1,in2;
reg out;
always@(in1 or in2);
out=in1 & in2;
endmodule
结构式的描述∶在这层次中模块是由逻辑闸(Gate Level)连接而成,在这层次的设计工作就好像以前用描绘逻辑闸来设计线路一样。例1中的AND是Verilog的基本元件 (primitive),是Verilog语言预先定义好的函式。
数据流式的描述∶ 它是一种模拟组合函式的方法。当任何输入有所改变时,输出会被重新计算而跟著改变。数据流式只能用来实践组合函式。在这个层次中,要说明数据如何在暂存器中储存与传送,如何处理数据。例2数据流式使用关键字assign进行描述。
行为式的描述∶它是一种使用高阶语言来描述硬件的方式。因Verilog提供很普遍及功能强大的描述方式。在这个层次的设计工作就好像写C语言一样,使用行为式所描述的模块其描述与真实的电路可能毫无关连。甚至某些行为式模块可能硬件实践。这种特性是Verilog的优点也是缺点。 例3中的行为式描述有一个使用关键字always进行描述。
三、Verilog中端口的描述
1、端口的位宽最好定义在I/O说明中,不要放在数据类型定义中;
Example1:
module test(addr,read,write,datain,dataout)
input[7:0] datain;
input[15:0] addr;
input read,write;
output[7:0] dataout; //要这样定义端口的位宽!
wire addr,read,write,datain;
reg dataout;
Example2:
module test(addr,read,write,datain,dataout)
input datain,addr,read,write;
output dataout;
wire[15:0] addr;
wire[7:0] datain;
wire read,write;
reg[7:0] dataout; // 不要这样定义端口的位宽!!
2、端口的I/O与数据类型的关系:
module内部 module外部
input wire wire或reg
output wire或reg wire
inout wire wire
3、assign语句的左端变量必须是wire;直接用"="给变量赋值时左端变量必须是reg!
Example:
assign a=b; //a必须被定义为wire!!
********
begin
a=b; //a必须被定义为reg!
end
在Verilog中有二种类型的内部信号用得比较多,它们是reg和wire。它们具有不同的功能。wire是线网形变量,它不能存储值,必须受到驱动器或者连续赋值语句的驱动。reg是数据存储单元的抽象,通过赋值语句可以改变寄存器存储的值,其作用与改变触发器存储的值相当。所有端口都有一个名称相同且声明为wire的信号。因此连线line被声明为wire不是必要的。reg会保持上次的赋值,因此不需要每次都进行驱动。wire型信号用于异步逻辑,有时也用来连接信号。因为 reg可以保持上次的值,因此输入不能被声明为reg类型。在Verilog模块中可以在任何时候异步地将输入改变为任何事件。reg和wire的主要区别是,reg类型的信号只能在过程块(后面会谈到)中赋值,而wire类型的信号只能在过程块外赋值。这两种信号类型都可以出现在过程块内部和外部的赋值运算符右边。 使用关键字reg并不一定意味着编译器会创建一个寄存器,理解这一点是非常重要的。
四、敏感变量的描述完备性
Verilog中,用always块设计组合逻辑电路时,在赋值表达式右端参与赋值的所有信号都必须在 always @(敏感电平列表)中列出,always中if语句的判断表达式必须在敏感电平列表中列出。如果在赋值表达式右端引用了敏感电平列表中没有列出的信号,在综合时将会为没有列出的信号隐含地产生一个透明锁存器。这是因为该信号的变化不会立刻引起所赋值的变化,而必须等到敏感电平列表中的某一个信号变化时,它的作用才表现出来,即相当于存在一个透明锁存器,把该信号的变化暂存起来,待敏感电平列表中的某一个信号变化时再起作用,纯组合逻辑电路不可能作到这一点。综合器会发出警告。
Example1:
input a,b,c;
reg e,d;
always @(a or b or c)
begin
e=d&a&b; /*d没有在敏感电平列表中,d变化时e不会立刻变化,直到a,b,c中某一个变化*/
d=e|c;
end
Example2:
input a,b,c;
reg e,d;
always @(a or b or c or d)
begin
e=d&a&b; /*d在敏感电平列表中,d变化时e立刻变化*/
d=e |c;
end
Verilog中用于上升沿和下降沿的关键字分别是posedge和negedge。这二个关键字经常被用于敏感列表。
五、条件的描述完备性
如果if语句和case语句的条件描述不完备,也会造成不必要的锁存器。
Example1:
if (a==1'b1)
q=1'b1;//如果a==1'b0,q=? q将保持原值不变, 生成锁存器!
Example2:
if (a==1'b1)
q=1'b1;
else
q=1'b0;//q有明确的值, 不会生成锁存器!
Example3:
reg[1:0] a,q;
....
case (a)
2'b00 : q=2'b00;
2'b01 : q=2'b11;//如果a==2'b10或a==2'b11,q=? q将保持原值不变, 锁存器!
endcase
Example4:
reg[1:0] a,q;
....
case (a)
2'b00 : q=2'b00;
2'b01 : q=2'b11;
default: q=2'b00;//q有明确的值. 不会生成锁存器!
endcase
六、描述的规范性
以触发器为例说明描述的规范性
1、无置位/清零的时序逻辑
always @( posedge CLK)
begin
Q<=D;
end
2、有异步置位/清零的时序逻辑
异步置位/清零是与时钟无关的,当异步置位/清零信号到来时,触发器的输出立即被置为1或0,不需要等到时钟沿到来才置位/清零。所以,必须要把置位/清零信号 列入always块的事件控制表达式。
always @( posedge CLK or negedge RESET)
begin
if (!RESET)
Q=0;
else
Q<=D;
end
3、有同步置位/清零的时序逻辑
同步置位/清零是指只有在时钟的有效跳变时刻置位/清零,才能使触发器的输出分别转换为1或0。所以,不要把置位/清零信号列入always块的事件控制表达式。但是必须在always块中首先检查置位/清零信号的电平。
always @( posedge CLK )
begin
if (!RESET)
Q=0;
else
Q<=D;
end
七、非阻塞赋值和阻塞赋值
always块中的赋值运算符与以关键字assign开头的连续赋值语句中用到的运算符不一样。"<="运算符用于非阻塞性(nonblocking)赋值,而"="运算符用于阻塞性(blocking)赋值。在一组阻塞性赋值语句中,在下一个阻塞性赋值语句执行前需要计算并赋值第一个赋值语句。这一过程就象C语言中语句的顺序执行。而非阻塞语句在执行时,所有赋值语句的右边被同时计算和赋值,在always块结束后才完成赋值操作。连续赋值语句必须使用阻塞赋值语句(否则编译器会报错)。
为了减少代码出错的概率,建议在顺序逻辑(例如希望以寄存器方式实现的逻辑)always块中的所有赋值语句使用非阻塞性赋值语句。大多数always块应该使用非阻塞性赋值语句。如果always块都是组合逻辑,那么就需要使用阻塞性赋值语句。
现列举八条“非阻塞赋值”和“阻塞赋值”指导方针,谨遵这些方针可以帮助Verilog设计者减少所遇到的90-100%的Verilog竞争:
1: 当为时序逻辑建模,使用“非阻塞赋值”。
2: 当为锁存器(latch)建模,使用“非阻塞赋值”。
3: 当用always块为组合逻辑建模,使用“阻塞赋值”
4: 当在同一个always块里面既为组合逻辑又为时序逻辑建模,使用“非阻塞赋值”。
5: 不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”。
6: 不要在两个或两个以上always块里面对同一个变量进行赋值。
7: 使用$strobe以显示已被“非阻塞赋值”的值。
8: 不要使用#0延迟的赋值。
八、其他一些设计规范和原则
1:不使用初始化语句,用复位脉冲初始化信号和变量。
2:不使用延时语句;
3:不使用循环次数不确定的语句,如:forever,while等;
4:尽量采用同步方式设计电路;
5:尽量采用行为语句完成设计;
6:always过程块描述组合逻辑,应在敏感信号表中列出所有的输入信号;
7:所有的内部寄存器都应该可以被复位;避免使用内部生成的异步置位/清零信号,内部生成的置位/清零信号会引起测试问题。使某些输出信号被置位或清零,无法正常测试。
8:用户自定义原件(UDP元件)是不能被综合的。
9: if...else if ... else 语句是有优先级的,一般说来第一个if的优先级最高,最后一个else的优先级最低。 而case语句是"平行"的结构,所有的case的条件和执行都没有“优先级”。而建立优先级结构会消耗大量的组合逻辑,所以如果能够使用case语句的地方,尽量使用case替换if...else结构。
10: 状态机的一般设计原则,Biary, gray-code 编码使用最少的触发器,较多的组合逻辑。而one-hot编码反之。所以CPLD多使用GRAY-CODE, 而FPGA多使用ONE-HOT编码。另一方面,小型设计使用GRAY-CODE和BINARY编码更有效,而大型状态机使用ONE-HOT更有效。
11:fpga设计中不要使用门时钟,内部生成的时钟称为门生时钟(gated clock)。时钟信号必须连接到全局时钟管脚上。
12:不要使用内部三态信号,否则增加功耗。
13:避免使用负延触发的双稳态多谐振荡器(flip flop)。
14:不要在代码中使用buffer 类型的端口读取输出数据;要使用out 类型,再增加另外变量或信号,以获取输出值。这是因为buffer 类型的端口不能连接到其他类型的端口上,因此buffer 类型就会在整个设计的端口中传播下去。
15:对变量要先读后写;如果先写后读,就会产生长的组合逻辑和锁存器(或寄存器)。这是因为变量值是立即获取的。
硬件描述语言Verilog设计经验总结的更多相关文章
- 上一步是硬件描述语言,下一步是FPGA
上一步是硬件描述语言,下一步是FPGA. 学习了硬件描述语言(Verilog或者VHDL)之后,FPGA该如何继续. 世上没有捷径,每一步都得踏踏实实的走.学习FPGA也是这样,在有了硬件描述语言的基 ...
- 我的 FPGA 学习历程(04)—— 练习 verilog 硬件描述语言
这篇讲的是使用 verilog 硬件描述语言编写一个 3 - 8 译码器. 3 - 8 译码器是一个简单的组合逻辑,用于实现并转串,其输入输出关系如下: | 输入 | 输出 | -------- ...
- 【转】Swift 语言的设计错误
Swift 语言的设计错误 在『编程的智慧』一文中,我分析和肯定了 Swift 语言的 optional type 设计,但这并不等于 Swift 语言的整体设计是完美没有问题的.其实 Swift 1 ...
- 专訪阿里陶辉:大规模分布式系统、高性能server设计经验分享
http://www.csdn.net/article/2014-06-27/2820432 摘要:先后就职于在国内知名的互联网公司,眼下在阿里云弹性计算部门做架构设计与核心模块代码的编写,主要负责云 ...
- c++学习书籍推荐《C++语言的设计与演化》下载
百度云及其他网盘下载地址:点我 编辑推荐 <C++语言的设计与演化>由C++语言的设计者Bjarne Stroustrup著就,是一本阐述C++语言的设计及开发过程的无可争辩的内情手册.S ...
- [转]C语言指针学习经验总结浅谈
指针是C语言的难点和重点,但指针也是C语言的灵魂 . 这篇C语言指针学习经验总结主要是我入职以来学习C指针过程中的点滴记录.文档里面就不重复书上说得很清楚的概念性东西,只把一些说得不清楚或理解起来比较 ...
- Web API接口设计经验总结
在Web API接口的开发过程中,我们可能会碰到各种各样的问题,我在前面两篇随笔<Web API应用架构在Winform混合框架中的应用(1)>.<Web API应用架构在Winfo ...
- web设计经验<一> 提升移动设备响应式设计的8个建议
今天看到一些关于web设计的一些建议和设计经验,拿出来分享分享. 第一篇: 提升移动设备响应式设计的8个建议 一.直观性和易用性 在使用移动设备时,对于杂乱.复杂或者不直观的设计造成的混乱不佳的用户体 ...
- 学生管理系统-火车订票系统 c语言课程设计
概要: C 语言课程设计一---学生管理系统 使使用 C 语言实现学生管理系统.系统实现对学生的基本信息和考试成绩的 管理.采用终端命令界面,作为系统的输入输出界面.采用文件作为信息存储介质. 功能描 ...
随机推荐
- 在PHP5.3以上版本运行ecshop和ecmall出现的问题及解决方案
ecshop 问题一:商城首页报错 Strict Standards: Only variables should be passed by reference in D:\wamp\ecshop\i ...
- 按照 where id in ()排序
select * from ibs6_terminal_adv_inf where id in (16,14,15) order by find_in_set(id,'16,14,15')
- linux地址空间划分
LDD讲的很明白了: Linux 是一个虚拟内存系统, 意味着用户程序见到的地址不直接对应于硬件使用的物理地址. 虚拟内存引入了一个间接层, 它允许了许多好事情. 有了虚拟内存, 系统重运行的程序可以 ...
- solr5.5教程-tomcat布署
tomcat和solr在各自官网下载,版本如下: tomcat版本:8.0.24 solr版本:5.5.0 1.solr解压后,目录结构如下: 2.tomcat的webapps里新建solr目录, 把 ...
- dig out deledted chat messages
One of my friends asked me to do a favor for her. She said her friend deleted some important chat me ...
- SSDT Hook
一.效果图 二.分析 这里对NtCreateProcessEx做拦截,用WinDbg来定位该函数在SSDT中的记录地址: : kd> dd KeServiceDescriptorTable 80 ...
- iOS 微信支付平台集成
https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_5
- Windows Phone 资源管理与换肤思考
新入手一台Windows 8的笔记本,安装了VS2013后,终于又可以开发WP了.公司暂时不愿意开发WP,那么咱就自行研究吧! 在没有WP开发环境的时候,曾经在WPF尝试了一下换肤功能的实现.最简单的 ...
- JS匿名函数自执行函数
JS匿名函数自执行函数:(function(){})();(function(){}) 这是一个函数,函数后面接(),则是调用函数 比如(function(arg){console.log(arg); ...
- 在peopletools里面测试文件上传
Using the PeopleTools Test Utilities Page Select selectPeopleTools, then selectUtilities, then selec ...