《设计与验证 Verilog HDL》
设计与验证的发展历程
早期:卡诺图设计,面包板验证
中期:原理图设计,EDA工具仿真验证
后期:硬件描述语言HDL设计,EDA工具仿真验证
抽象层次不断提高
HDL可以从算法/系统级>功能级>行为级>寄存器传输级(RTL)>门级和开关级不同的层次描述数字电路系统。
通过EDA工具将高层次的电路描述解析到门级等低层次的电路描述(网表)的过程就叫做"综合"(Synthesize),或者称逻辑综合(综合时会根据"约束条件"优化)
最成熟的综合是RTL级到门级的综合
Verilog不仅定义了语法,还对每个语法结构都清晰定义了仿真语义,从而便于仿真调试
Verilog集成了C语言的语法结构和操作符,易学易用,具有很强的扩展性
VHDL(Very High Speed Integrated Circuit HDL,超高速集成电路描述语言)的特点是描述严谨
几种设计方法:
原理图直观,便于理解,但在大型设计中可维护性差,不利于提高模块重用性
HDL输入:利于自顶向下设计,利于模块划分和重用,可移植性好,通用性好,设计不因芯片工艺和结构变化而变化,利于向ASIC移植
波形输入和状态机输入:是最常用的辅助设计方法,EDA工具可自动根据两种输入生成HDL代码,应用受局限,维护性不高
推荐初学者:首选HDL方式,在某些要求使用图形描述设计顶层的情况下才使用原理图,不要在设计顶层以外的其他层次使用原理图,不要依赖波形设计工具(因为复杂的测试激励无法用波形工具描述)
Verilog和VHDL:
两者都能胜任数字电路系统设计任务
VHDL最初用作文档来描述数字硬件的行为,描述性和抽象性更强,更适合描述更高层次(行为级,系统级)
Verilog最初为更简捷、有效的描述数字电路和仿真而设计,易学易懂
在这两者基础上又发展处很多更抽象的硬件描述语言:SystemVerilog、Superlog、SystemC、CoWare C等
Verilog寄存器和线网两种数据类型定义清楚,时序和组合电路描述简洁,能帮助快速了解硬件设计的基本概念。
但VHDL和Verilog各有所长,并无优劣之分,最重要的是建模方法与思想
推荐初学者:从Verilog学起
Verilog和C语言:
区别:
互连(connectivity):wire型变量配合驱动结构可有效表示网线互连
并发(concurrency):并行执行
时间(time):Verilog定义了绝对和相对的时间度量,仿真时可描述信号之间的时间关系
HDL语言与软件语言(C,C++等)有本质区别,评判HDL代码的最终标准是实现的硬件电路的性能(面积和速度)
一个硬件设计的最终性能,很大程度上取决于设计工程师所构想的硬件实现方案的合理性
从软件设计转行的初学者,片面追求代码的整洁、简短是错误的,是与评价HDL的标准背道而驰的
正确的方法是,首先对所要实现的硬件电路有一个清楚的认识,对硬件结构与连接了解十分清楚,再用HDL语句描述出来
验证时,常用C语言编写测试向量,与Verilog在仿真器中通信,用于验证
描述分级
系统级:对系统整体功能和性能指标进行衡量
功能级:将系统功能划分为可实现的具体功能模块,大致确定模块间的接口,描述每个模块的时序约束
行为级:明确每个模块所有的接口和边界,模块内部功能,外部接口和行为都已经清晰。常用于编写仿真测试激励(延时描述、监视描述)
寄存器传输级:不关注寄存器和组合逻辑的细节,使用HDL语言描述寄存器到寄存器间的逻辑功能描述电路——一般对此设计仿真
门级:目前要直接使用门级描述的情况一般是ASIC和FPGA设计中有面积或时序要求较高的模块
布局规划与布局布线:门级描述映射到目标器件中
时序仿真:将布局布线的延时信息反标注到设计网表中进行仿真,简称后仿真。包含门延时和线延时,能较好反映芯片实际工作情况。主要目的是发现时序违规的情况
所有的电路单元是并行工作的,相互之间没有顺序关系,即使是最小的单元也是如此
模块中只有定义了时钟边沿的行为是与跳变沿有关的,其余(例如+,-,×,÷,多路选择器,译码器等)如果没有定义边沿触发,就与边沿无关,其输出与输入之间的延时只有门延时和线延时,线路越长,此延时越大
assign:连续赋值语句
#3:模拟组合逻辑的延时,表示经过3个延时单位,再进行赋值
timescale 1ns/100ps:定义时间单位/仿真时间精度
{}:是Verilog的合并符号,将多个变量合并成一个组合变量,可用于批量赋值
xor:是Verilog自带的逻辑门原语
实例化:调用功能模块
在模块中实例化其他模块的描述方式称为结构化描述
Verilog的3种描述方法:
数据流描述:assign语句——连续赋值语句
行为描述:always或initial语句——其中包含的语句称为过程赋值语句
结构化描述:实例化已有的模块(包括Module实例化、门实例化、用户定义原语UDP实例化)
基本词法:
Verilog对大小写敏感,书写时要格外注意
Verilog中所有关键字都是小写
Verilog中标识符(内部信号名,变量名)由字母、数字、$、和_组成,第一个字符必须是字母或_
//和/* */表示注释,某些控制编译过程的指令也是以注释的形式出现,例如/* synthesis syn_black_box */
间隔:空格、制表符、换行符
转义字符:\n换行符;\t制表符;\\表示\本身
模块和端口:
Verilog中,module是基本的组成单元
建议在一个Verilog文件中只放一个module定义,且使文件名与module名称一致
module 模块名称 (端口列表)
//变量声明
input [1:0], output, inout,
reg, wire, parameter,
function, task, …
//语句,以下语句在module中是并行的关系,无任何顺序关系,其文本上的顺序不改变module的功能
initial
always
module实例化
门实例化
UDP实例化
assign
endmodule
有些module不包含输入输出端口,一般用于内部已经实例化了激励的封闭系统,只用于仿真,不用于实际系统
input默认为wire类型(貌似不可以声明为reg类型);output在always或initial中赋值时默认是reg,否则是wire;inout一般设为tri类型(表示有多个驱动源,如无驱动则为三态)
建议将所有的声明放到所有语句之前,增加可读性
always的用法
1.always@后面内容是敏感变量,always@(*)里面的敏感变量为*,意思是说敏感变量由综合器根据always里面的输入变量自动添加,不用自己考虑。
2.如果没有@,那就是不会满足特定条件才执行,而是执行完一次后立马执行下一次,一直重复执行
3.通常情况下,使用方法是:always@(posedge clk_out_0, negedge reset_n)
Error (10028): Can't resolve multiple constant drivers for net……
上面的代码在quartusII里面就会出现题目的错误提示,其原因就是在两个always语句里面都对out1,out2信号赋值了,而两个always是并行快,所以提示出现多重驱动的情况
解决办法:将两个always合并成一个
结构化描述
结构化描述就是在设计中实例化已有的功能模块,包括门原语、用户自定义原语UDP,和其他模块module
需要将模块实例与外部信号相连接,模块实例的端口连接规则:
Input:缺省为线网wire类型
Output:寄存器(在过程赋值语句中被赋值),或者是线网类型
Inout:缺省为线网类型,定义为tri双向
与之相连的信号类型为:
与input相连的,可以使一个线网或寄存器
与output,相连的一定是驱动一个线网
与inout先练,输入时从一个线网驱动而来,输出时驱动到一个线网(切记,只有线网类型可以驱动inout端口,否则编译出错)
实例的端口对应方式有两种:
(1)名称对应:外部信号与模块端口名称相对应,顺序可随意
模块名 实例名称(
.X(E_X),
.Y(E_Y), //没有可留空
……
);
(2)位置对应:按顺序对应(与函数传递参数类似),没有则留空
模块名 实例名称(E_X, E_Y, E_SUM, E_C_out);
参数化
上面讲了实例化,这里讲参数化
module中的参数一般用作定义常量
当实例化某个模块时,可以修改参数的值,实现不同的特性,是通过"新参数直接代入"或"参数重定义"来完成的。
方法:
1、defparam关键字重新定义模块参数——Altera自动生成的通用模块定制采用
此方法在某些综合器中失效(应采用第2种)
defparam
module.parameter = ,
……
2、直接在实例化时代入参数——Xilinx自动生成的通用模块定制采用
#(……,……,……)参数要按顺序列出,不能遗漏和颠倒