verilog设计--基础部分:概述、设计方法流程、语法
各种电路从本质上来讲是为了解决特定的数学问题而存在。目前,通用处理器能解决大部分的问题,如滤波、加密、解密、编码、解码、纠检错等,那为什么还需要通过Verilog HDL来设计复杂数字逻辑系统呢?因为通用处理器的总线带宽、指令周期等已经被固化,而不会加入一些有相当严格的特殊要求(如严格时延、并发等速度性能方面)电路。
1- 设计流程:
设计-->验证
Verilog HDL设计需要从算法、编程语言、计算机系统结构以及仿真验证几个方面入手。
首先,为准确表达要分析的问题信息,我们往往需要抽象出数学模型,而这些数学模型也就是数据结构;接着,我们使用编程语言把数据结构串起来,表述问题的求解过程,在这类复杂电路设计过程中,我们尽量避免迭代、递归、指针等不易于通过简单物理单元电路(可综合)搭建的高级编程语言语法,从而产生更简化、方便重复调用的Verilog语言;另外,由于专用电路设计仍是基于计算机系统结构高性能硬件逻辑系统,Verilog HDL设计用来砌墙的砖头就是微机系统结构;然后,设计初步完成后,可通过仿真分析工具来进行仿真验证,降低了复杂数字电路系统的设计风险。
2- 设计方案主体
通常·Verilog HDL设计有如下几种方案:
1)以已有微处理器为中心,一般在其C程序代码已验证条件下将编译好的代码加载到RAM进行算法仿真验证,周期短、受限微处理器及其外围电路;
2)以 FPGA(从几万门到百万门)为中心,需配备 FPGA 开发环境(仿真综合)、布局布线和编程工具,仍属于通用可编程器件,在更严苛的条件下可使用ASIC;
3)以专用的大规模集成电路(ASIC)为中心,利用现有IP核辅之专门设计的高速ASIC 运算电路,需熟悉半导体厂家基本器件库和 IP 库、配备开发环境(仿真综合),周期长、投入大,适合高预算、大批量生产;
由于电路结构对运算速度影响巨大,常常需要多轮仿真验证才能投产:
C 语言功能仿真;
C 语言并行结构仿真;
Verilog HDL 行为仿真;
Verilog HDL RTL 级仿真;
综合后门级结构仿真;
布局布线后仿真;
电路实验验证;
随着制造技术进步,设计效率跟不上而分解出逻辑设计(前端)和电路实现(后端),优秀的常用数字逻辑电路和系统部件(如FFT、DCT)被建成宏单元(Megcell)或软核(Soft-Core)库供设计者引用,电路实现则由综合工具和布局布线工具完成。
我们也常常会听说硬核、软核这样的概念,硬核是指固定的5000门以上电路结构编码文件,软核功能经过验证的、可综合的、实现后电路结构总门数在5000门以上的Verilog HDL模型。它们的区别在与软核可以根据电路工艺来微调,硬核是固化的。
3- 设计方法
3-1 设计方法:
设计方法上,复杂数字系统的设计通过层次化(自顶向下/自底向上)、结构化设计可以细化分解不同的模块,利于工程管理。
设计层级:
系统级(system):用高级语言结构实现设计模块的外部性能的模型。
算法级(algorithm):用高级语言结构实现设计算法的模型。
RTL级(Register Transfer Level):描述数据在寄存器之间流动和如何处理这些数据的模型。
门级(gate-level):描述逻辑门以及逻辑门之间的连接的模型。
开关级(switch-level):描述器件中三极管和储存节点以及它们之间连接的模型。
3-2 模块结构:
接口描述+功能逻辑
1- 模块端口定义:
module 模块名(口1,口2,口3,口4, ………);
2- I/O说明:
输入口: input 端口名1,端口名2, ………,端口名i; //(共有i个输入口)
输出口: output 端口名1,端口名2, ………,端口名j; //(共有j个输出口)
3- 内部信号说明:
在模块内用到的和与端口有关的wire 和 reg 变量的声明。
如:
reg [width-1 : 0] R变量1,R变量2 。。。。;
wire [width-1 : 0] W变量1,W变量2 。。。。;
4- 功能定义:
模块中最重要的部分是逻辑功能定义部分。有三种方法可在模块中产生逻辑。
1).用“assign”声明语句
如: assign a = b & c;
2).用实例元件
如: and and_inst( q, a, b );
采用实例元件的方法象在电路图输入方式下,调入库元件一样。键入元件的名字和相连的引脚即可, 表示在设计中用到一个跟与门(and)一样的名为and_inst的与门,其输入端为a, b,输出为q。要求 每个实例元件的名字必须是唯一的,以避免与其他调用与门(and) 的实例混淆。
3).用“always”块
如:always @(posedge clk or posedge clr) begin
if(clr) q <= 0; else if(en) q <= d;
end
----assign描述组合逻辑,always既可以描述组合逻辑,也可以描述时序电路;
4- 数据类型&运算
4-1 数据类型、变量、常量、参数化
parameter格式:
parameter 参数名1=表达式,参数名2=表达式, …, 参数名n=表达式;
如:
parameter msb=7; //定义参数msb为常量7
parameter e=25, f=29; //定义二个常数参数
parameter r=5.7; //声明r为一个实型参数
parameter byte_size=8, byte_msb=byte_size-1; //用常数表达式赋值
parameter average_delay = (r+f)/2; //用常数表达式赋值
多层次模块构成的电路,在一个模块中改变另一个模块的参数时,需要使用defparam命令
wire\reg\memery
memory型数据和reg型数据的定义格式很相似, 但要注意其不同之处。如一个由n个1位寄存器构 成的存储器组是不同于一个n位的寄存器的。见下例:
reg [n-1:0] rega; //一个n位的寄存器
reg mema [n-1:0]; //一个由n个1位寄存器构成的存储器组
一个n位的寄存器可以在一条赋值语句里进行赋值,而一个完整的存储器则不行。 见下例:
rega =0; //合法赋值语句 mema =0; //非法赋值语句
如果想对memory中的存储单元进行读写操作,必须指定该单元在存储器中的地址。下面的写法是正确 的。
mema[3]=0; //给memory中的第3个存储单元赋值为0
4-2 运算符
4-2-1 运算符
算术运算符(+,-,×,/,%)
赋值运算符(=,<=)
关系运算符(>,<,>=,<=)
逻辑运算符(&&,||,!)
条件运算符(?:)
位运算符(,|,^,&,^)
移位运算符(<<,>>)
拼接运算符({ })
4-2-2 赋值
注意事项:
(1)时序电路建模时,用非阻塞赋值。
(2)锁存器电路建模时,用非阻塞赋值
(3)用always 块建立组合逻辑模型时,用阻塞赋值。
(4)在同一个always 块中建立时序和组合逻辑电路时,用非阻塞赋值。
(5)在同一个 always 块中不要既用非阻塞赋值又用阻塞赋值。
(6)不要在一个以上的 always块中为同一个变量赋值。
(7)用$strobe系统任务来显示用非阻塞赋值的变量值,
(8)在赋值时不要使用#0延迟。
(1).非阻塞
非阻塞(Non_Blocking)赋值方式( 如 b <= a; )
块结束后才完成赋值操作。
b的值并不是立刻就改变的。
这是一种比较常用的赋值方法。(特别在编写可综合模块时)
(2).阻塞
阻塞(Blocking)赋值方式( 如 b = a; )
赋值语句执行完后,块才结束。
b的值在赋值语句执行完后立刻就改变的。
可能会产生意想不到的结果。
[例1]:
always @( posedge clk )
begin
b<=a;
c<=b;
end
[例1] 中的"always"块中用了非阻塞赋值方式,定义了两个reg型信号b和c,clk信号的上升沿到来时,b就等于a,c就等于b,这里应该用到了两个触发器。请注意:赋值是在"always"块结束后执行的,c应为原来b的值。这个"always"块实际描述的电路功能如下图所示:
[例2]:
always @(posedge clk)
begin
b=a;
c=b;
end
[例2]中的 "always"块用了阻塞赋值方式。clk信号的上升沿到来时,将发生如下的变化:b马上取a的值,c马上取b的值(即等于a),生成的电路图如下所示只用了一个触发器来寄存器a的值,又输出给b和c。
4-2-3 语句
if else
case
assign
(1) case
case分支项的一般格式如下:
分支表达式: 语句
缺省项(default项): 语句
case语句与if_else_if语句的区别主要有两点:
与case语句中的控制表达式和多分支表达式这种比较结构相比,if_else_if结构中的条件表达式更为直观一些。
对于那些分支表达式中存在不定值x和高阻值z位时,case语句提供了处理这种情况的手段。
下面例子介绍了处理x,z值位的case语句。
例1]:case ( select[1:2] )
2 'b00: result = 0;
2 'b01: result = flaga;
2 'b0x,2 'b0z: result = flaga? 'bx : 0;
2 'b10: result = flagb;
2 'bx0,2 'bz0: result = flagb? 'bx : 0;
default: result = 'bx;
endcase
(2) 锁存器
1)在"always"块内,如果在给定的条件下变量没有赋值,这个变量将保持原值,也就是说会生成一个锁存器!
[图片上传失败...(image-f7547e-1724403082957)]
2)另一种偶然生成锁存器是在使用case语句时缺少default项的情况下发生的。
[图片上传失败...(image-8db69-1724403082957)]
为避免锁存器生成,应保证语句条件完备;
(3) always
always 的时间控制可以是沿触发也可以是电平触发的,可以单个信号也可以多个信号,中间需要用关键字 or 连接,如:
沿触发:
always @(posedge clock or posedge reset) //由两个沿触发的always块
begin ……
end
电平触发:
always @( a or b or c ) //由多个电平触发的always块
begin
……
end
沿触发的always块常常描述时序逻辑, 如果符合可综合风格要求可用综合工具自动转换为表示时序逻辑的寄存器组和门级逻辑,而电平触发的always块常常用来描述组合逻辑和带锁存器的组合逻辑,如果符合可综合风格要求可转换为表示组合逻辑的门级逻辑或带锁存器的组合逻辑。
(4) 条件编译
条件编译命令ifdef、
else、`endif
条件编译命令有以下几种形式:
`ifdef 宏名 (标识符)程序段1
`else程序段2
`endif
它的作用是当宏名已经被定义过(用`define命令定义),则对程序段1进行编译,程序段2将被忽略;否则编译程序段2,程序段1被忽略。
小结:本文从Verilog HD发展起因、设计方案、仿真类型、基础语法等方面进行了概括性描述。