Document

嵌入式系统工程系
第二章 Verilog HDL基础











Verilog HDL历史与现状
Verilog HDL与VHDL
系统建模概述
简单的Verilog HDL示例
语言要素
表达式、操作数、操作符
门级建模
数据流建模
行为建模
Verilog HDL源代码设计
Testbench验证
嵌入式系统工程系
Verilog HDL历史
 1983年,Gateway Design Automation公司推出Verilog语言,开发
了仿真与验证工具;
 1985年,GDA推出Verilog仿真器Verilog-XL:仿真速度快,处理能
力强,具有交互式调试手段;
 1987年,Synopsys公司的综合软件开始接受Verilog输入;
 1989年,Cadence公司收购GDA,进一步扩大Verilog的影响;
 1990年,Open Verilog International(OVI)成立,推广Verilog
HDL和Veriog-XL被广泛推广;
 1993年,OVI推出Verilog2.0,作为IEEE提案提出申请;
 1995年,IEEE(Institute of Electrical and Electronics Engineers)
通过Verilog HDL标准IEEE Std.1364-1995;
 2001年,IEEE 发布了Verilog IEEE 1364-2001标准。
本课程以IEEE Std.1364-1995为主
嵌入式系统工程系
Verilog HDL现状
Verilog HDL是最广泛使用的、具有国际标准支
持的硬件描述语言,绝大多数的EDA厂商都支持;
在工业界和ASIC设计领域,Verilog HDL应用更
加广泛。
嵌入式系统工程系
Verilog HDL与VHDL
VHDL
 Very-High-Speed Integrated
Description Language
Circuit
Hardware
诞生于1982年;1987年底被IEEE和美国国防部
确认为标准硬件描述语言 。
IEEE 1076(1983)
IEEE 1076-1995
……
嵌入式系统工程系
Verilog HDL与VHDL
 建模层次
 系统级(system): 用高级语言结构实
现设计模块的外部性能的模型。
 算法级(algorithmic): 用高级语言结
构实现设计算法的模型。
 RTL级(Register Transfer Level):
描述数据在寄存器之间流动和如何
处理这些数据的模型。
 门级(gate-level): 描述逻辑门以及
逻辑门之间的连接的模型。
 开关级(switch-level): 描述器件中
三极管和储存节点以及它们之间连
接的模型。
系统级
算法级
算法级
RTL 级
RTL 级
门级
门级
逻辑级
电路级
开关级
Verilog HDL
行为级
VHDL
嵌入式系统工程系
Verilog HDL与VHDL
相同点:




都能形式化抽象表示电路行为和结构;
支持逻辑设计中层次与范围的描述;
具有电路仿真和验证机制;
与工艺无关。不专门面向FPGA设计
不同点:
 Verilog与C语言相似,语法灵活;VHDL源于Ada语言,
语法严格;
 Verilog更适合ASIC设计。
嵌入式系统工程系
SystemVerilog与SystemC
SystemVerilog:IEEE 1364 Verilog-2001 标准
的扩展增强,兼容Verilog 2001,将硬件描述语
言(HDL)与现代的高层级验证语言(HVL)结
合。
SystemC:一种软/硬件协同设计语言 ,既是系
统级语言,也是硬件描述语言。
嵌入式系统工程系
系统建模
设计方法学
 自顶向下
 自底向上
 混合式
描述方式




数据流描述:描述电路数据流行为:assign
行为描述:描述功能:initial,always
结构化描述:描述元器件间连接关系:例化
混合描述:Verilog允许多描述方式共存于同一模块。
嵌入式系统工程系
简单的Verilog程序
module trist1(out,in,enable);  三态门
output out; /*输出信号*/
 模块trist1 调用
input in, enable; //输入信号
模 块 mytri 的 实
mytri tri_inst(out,in,enable); 例元件tri_inst;
endmodule
通过这种结构性
模块构造可构成
module mytri(out,in,enable);
特大型模块。
output out;
input in, enable;
assign out = enable? in : 'bz;
endmodule
嵌入式系统工程系
简单的Verilog程序
三态门(综合)
嵌入式系统工程系
简单的Verilog程序
Verilog HDL程序是由模块构成的;
每个模块要进行端口定义,并说明输入输出口,然
后对模块的功能进行逻辑描述;
Verilog HDL程序的书写格式自由,一行可以写几
个语句,一个语句也可以分写多行;
除了endmodule语句外,每个语句和数据定义的最
后必须有分号。
嵌入式系统工程系
同一电路的多种描述方法
RTL级行为描述
module muxtwo(out,a,b,sl);
 二选一的选择器
input a, b, sl;
门级(结构化)描述
output out;
module muxtwo(out,a,b,sl);
reg out;
input a, b, sl;
always @(sl or a or b)
output out;
if(!sl) out=a;
else out=b;
wire nsl, sela,selb;
not #1 u1(nsl,sl); // #1是仿真延迟 endmodule
布尔代数级行为描述
and #1 u2(sela,a,nsl);
module muxtwo(out,a,b,sl);
and #1 u3(selb,b,sl);
input a, b, sl;
or #1 u4(out, sela, selb);
output out;
endmodule
assign out = sel?b:a;
endmodule
嵌入式系统工程系
同一电路的多种描述方法
 二选一的选择器(综合)
嵌入式系统工程系
混合描述
 混合设计方式的1位全加器实例
module FA_Mix (A, B, Cin, Sum, Cout);
input A, B, Cin;
output Sum, Cout;
reg Cout;
reg T1, T2, T3;
wire S1;
xor X1(S1, A, B);
// 门实例语句。
always @ ( A or B or Cin )
// always 语句。
begin
T1 = A & Cin;
T2 = B & Cin;
T3 = A & B;
Cout = (T1 | T2) | T3;
end
assign Sum = S1 ^ Cin;
// 连续赋值语句。
endmodule
嵌入式系统工程系
混合描述
 混合设计方式的1位全加器实例(综合)
嵌入式系统工程系
模块基本结构
module 模块名(端口列表);
端口I/O说明
内部信号声明
功能定义
endmodule
module block (a,b,c,d);
input a,b;
output c,d;
assign c= a | b ;
assign d= a & b;
endmodule
a
c
b
d
嵌入式系统工程系
时延
 Verilog HDL模型中的所有时延都根据时间单位定义。下
面是带时延的连续赋值语句实例。
assign #2 Sum = A ^ B;
 # 2指2个时间单位。使用编译指令将时间单位与物理时间相关联。
这样的编译器指令需在模块描述前定义,如下所示:
` timescale 1ns /100ps
 此语句说明时,延时间单位为1ns并且时间精度为100ps (时间精
度是指所有的时延必须被限定在0.1ns内)。如果此编译器指令所
在的模块包含上面的连续赋值语句, #2 代表2ns。
 如果没有这样的编译器指令, Verilog HDL 模拟器会指定
一个缺省时间单位。IEEE Verilog HDL 标准中没有规定
缺省时间单位。
嵌入式系统工程系
语言要素:标识符
 所谓标识别符就是用户为程序描述中的Verilog 对象所起
的名字。
 模块名、变量名、常量名、函数名、任务名
 标识符必须以英语字母(a-z, A-Z)起头,或者用下横线
符( _ )起头。其中可以包含数字、$符和下划线符。
 标识符最长可以达到1023个字符。
 模块名、端口名和实例名都是标识符。
 Verilog语言大小写敏感,
 sel 和 SEL 是两个不同的标识符。
 所有的关键词都是小写的。
嵌入式系统工程系
语言要素:系统任务和函数
以$字符开始的标识符表示系统任务或系统函数。
任务可以返回0个或多个值,函数除只能返回一个
值以外与任务相同。
函数在0时刻执行,即不允许延迟,而任务可以带
有延迟。
常用于测试模拟,一般不用于源代码设计。
$display ("Hi, you have reached LT today");
/*$display系统任务在新的一行中显示。* /
$time
//该系统任务返回当前的模拟时间。
嵌入式系统工程系
语言要素:编译指令
以`(反引号)开始的某些标识符是编译器指令。
 `define 和`undef,很像C语言中的宏定义指令
 `ifdef、`else 和`endif,用于条件编译
 ` include 文件既可以用相对路径名定义,也可以绝对
路径
 ` timescale 编译器指令将时间单位与实际时间相关联。
该指令用于定义时延的单位和时延精度。
嵌入式系统工程系
语言要素:值集合
Verilog HDL有下列四种基本的值:




0:逻辑0或“假”
1:逻辑1或“真”
x:未知
z:高阻 (x,z不区分大小写)
Verilog HDL中有三类常量:
 整型
 实数型
 字符串型
嵌入式系统工程系
语言要素:常量
1. 整数
 表达方式:
 <位宽>’<进制><数字>:标准方式
 ’<进制><数字>:默认位宽,与机器类型有关
 <数字>:不指明进制默认为十进制
 进制




二进制(b或B):8’b10101100, ’b1010
十进制(d或D):4’d1543, 512
十六进制(h或H): 8’ha2
八进制(o或O):6’O41
 x和z值
 x:不确定:4’b100x
 z:高阻:16’hzzzz,没有驱动元件连接到线网,线网的缺省值为z。
嵌入式系统工程系
语言要素:常量
 负数:
 在位宽表达式前加一个减号,如 -8’d5
 减号不可以放在位宽和进制之间,也不可以放在进制和具体的数之间,
如8’d-5
 下划线:
 只能用在具体的数字之间,如16’b1010_1111_1010
 位数指的是二进制位数。
 数位扩展:(定义的长度比为常量指定的长度长)
 最高位是0、1,高位用0扩展:8’b1111 等于 8’b00001111
 最高位是z、x,高位自动扩展:4’bz 等于 4’bzzzz
 数位截断:
 如果长度定义得更小,最左边的位被截断,如:
 3 ‘ b1001_0011 等于 3’b011,5'H0FFF等于5'H1F
嵌入式系统工程系
语言要素:常量
2. 实数
 十进制计数法;例如
2.0
5.68
 科学计数法;
23_5.1e2 其值为23510.0,忽略下划线
3.6E2
其值为360.0 ( e与E相同)
实数通常不用于FPGA源代码的常量
嵌入式系统工程系
语言要素:常量
3. 字符串
 字符串是双引号内的字符序列。字符串不能分成多行
书写。例如:
"INTERNAL ERROR"
" REACHED->HERE “
 用8位ASCII值表示的字符可看作是无符号整数。
为存储字符串“INTERNAL ERROR”,变量需要8 *14位。
reg [1 : 8*14] Message;
(Message = “INTERNAL ERROR“)
字符串较少用于FPGA源代码的常量
嵌入式系统工程系
语言要素:数据类型
 两大类数据类型
wire [msb:lsb] reg1, reg2, ... regN;
 线网型
 寄存器型
 线网型
 包含下述不同种类的线网子类型
wire
//FPGA设计中,通常只用wire型
tri
wor
trior
wand
triand
trireg
tri1
tri0
supply0
supply1
msb和lsb 定义了范围,
并且均为常数值表达式。
范围定义是可选的;如果
没有定义范围,缺省值为
1位线网
wire Reset;
wire [3:0] data_in;
wire [3:2] select;
wire [0:2] point;
嵌入式系统工程系
语言要素:数据类型
reg [msb:lsb] reg1, reg2, ... regN;
寄存器型
 5种不同的寄存器类型。
reg //FPGA设计中,通常只用
reg型,默认初始值x。
integer //其他类型用于仿真
time
real
realtime
msb和lsb 定义了范围,并且
均为常数值表达式。范围定
义是可选的;如果没有定义
范围,缺省值为1位寄存器
reg Reset;
reg [3:0] data_in;
reg [3:2] select;
reg [0:2] point;
嵌入式系统工程系
语言要素:数据类型
存储器
 存储器是一个寄存器数组。存储器使用如下方式说明
reg [msb: lsb] memory1 [upper1: lower1] ,
memory2 [upper2: lower2] ,... ;
reg [3:0] MyMem [63 : 0]
// MyMem为64个4位寄存器的数组。
reg Bog [1 : 5]
// Bog为5个1位寄存器的数组。
存储器赋值不能在一条赋值语句中完成,寄存器可以。
存储器常用于FPGA外围器件的仿真建模
嵌入式系统工程系
语言要素:数据类型
 存储器赋值
 2)系统任务赋值
reg [3:0] RomB [7:0] ;
$readmemb ("ram.patt", RomB);
Romb是存储器。文件“ram.patt”必须包含二进制值。文件也可以
包含空白空间和注释。下面是文件中可能内容的实例。
1101
1110
1000
0111
0000
1001
0011
0001
嵌入式系统工程系
语言要素:数据类型
存储器赋值
 1)对每个单元逐一赋值
reg [0:3] Xrom [0:2];
...
Xrom[0] = 4'hA;
Xrom[1] = 4'h8;
Xrom[2] = 4‘hF;
嵌入式系统工程系
语言要素:参数
参数是一个常量,常用于定义时延和变量的宽度。
parameter LINELENGTH = 132;
parameter ALL_X_S = 16'bx;
parameter BIT=1, BYTE = 8, PI = 3.14;
parameter STROBE_DELAY = (BYTE + BIT) / 2;
参数值也可以在编译时被改变。改变参数值可以
使用参数定义语句或通过在模块初始化语句中定
义参数值
嵌入式系统工程系
语言要素:空白符、注释
除了字符串中的空白符,其他空白符编译被忽略
注释
 多行注释 /*
 单行注释 //
*/ (不允许嵌套)
嵌入式系统工程系
嵌入式系统工程系
嵌入式系统工程系
嵌入式系统工程系
嵌入式系统工程系
嵌入式系统工程系
习题1
 1 使用`timescale 编译器指令的目的是什么?
 2 写出产生下图所示波形的变量BullsEye的初始化语句。
 3 使用数据流描述方式编写下图所示的异或逻辑的Verilog HDL描述,
并使用规定的时延。
 4下列表达式的位模式是什么?
7'o44, 'Bx0, 5'bx110, 'hA0, 10'd2, 'hzF
嵌入式系统工程系
表达式
表达式由操作数和操作符组成;
表达式可以在出现数值的任何地方使用;
表达式是数据流描述的基础。
A&B
Addr1[3:0] +Addr2[3:0]
Count + 1
(a[0] ^ b[0] ) | (a[1] & ~b[1])
嵌入式系统工程系
表达式:操作数
操作数可以是以下类型中的一种:








常数
参数
线网
寄存器
位选择
部分选择
存储器单元
函数调用
嵌入式系统工程系
表达式:操作数
 常数
表达式中的整数值可被解释为有符号数或无符号数;
如果整数是基数型整数,作为无符号数对待。
12
01100的5位向量形式
(有符号)
-12
10100的5位向量形式
(有符号)
5‘b01100 十进制数12
(无符号)
 参数
参数类似于常量,并且使用参数声明进行说明。例如
parameter LOAD = 4'd12, STORE = 4'd10;
LOAD和STORE为参数,值分别被声明为12和10。
嵌入式系统工程系
表达式:操作数
 线网
线网中的值被解释为无符号数,
表达式中可使用:标量线网(1位)和向量线网(多位)。
wire [3:0] led; //4位向量线网。
wire line;
//标量线网。
assign led = 4‘ha; //被赋于位向量1010,为十进制10。
嵌入式系统工程系
表达式:操作数
 寄存器
integer型的值被解释为有符号的二进制补码数,
reg型或time型的值被解释为无符号数,
real型和realtime的值被解释为有符号浮点数。
reg [4:0] state;
State = 5‘b01011; // 值为位向量01011,十进制值11。
State = 9;
// 值为位向量01001,十进制值9。
为何没有用assign语句赋值?
嵌入式系统工程系
表达式:操作数
 位选择
位选择从向量中抽取特定的位。形式如下:
net_or_reg_vector[bit_select_expr]
State[1] && State[4]
led[0] | line
//寄存器位选择。
//线网位选择。
如果选择表达式的值为x、z或越界,则位选择的值为
State[x]值为x。(FPGA设计中禁用)
嵌入式系统工程系
表达式:操作数
 部分选择
net_or_reg_vector[msb_const_expr:lsb_const_expr]
State [4:1]
//寄存器部分选择。 reg [4:0] state;
led [2:0]
//线网部分选择。 wire [3:0] led;
选择范围越界或为x、z时,部分选择的值为x。
(FPGA设计中禁用越界)
嵌入式系统工程系
表达式:操作数
 存储器单元
存储器单元从存储器中选择一个memory[word_address]
reg [7 : 0] Dram [63 : 0];
Dram [60];
//存储器的第61个单元。
不允许对存储器变量值部分选择或位选择。
(思考:在存储器中读取一个位或部分选择一个字?)
 函数调用
表达式中可使用函数调用。
$time + SumOfEvents (A, B)
/* $time是系统函数,并且SumOfEvents是在别处定义的
用户自定义函数。*/
嵌入式系统工程系
表达式:操作符
Verilog HDL中的操作符可以分为下述类型:









算术操作符
关系操作符
相等操作符
逻辑操作符
按位操作符
归约操作符
移位操作符
条件操作符
连接和复制操作符
嵌入式系统工程系
表达式:操作符
操作符从最高优先级到最低优先级排列。同一行
中的操作符优先级相同。
嵌入式系统工程系
表达式:操作符
除条件操作符从右向左关联外,其余所有操作符
自左向右关联。
 A+B-C
 A?B:C?D:F
等价于:(A + B ) - C
等价于:A?B:(C?D:F)
圆扩号能够用于改变优先级
 (A?B:C)?D:F
//自左向右
//从右向左
嵌入式系统工程系
表达式:操作符
算术运算符
 + (加)
 -(减)
 * (乘)
 / (除)
 %(取模)
1) 后三种不常用,是具体设计结构而定;
2) 任意操作数是X或Z,那么整个结果为X;
3) 结果的长度由最长的操作数决定;
4) reg和wire保存无符号数。
嵌入式系统工程系
表达式:操作符
关系操作符有:




>(大于)
<(小于)
>=(不小于)
<=(不大于)
习题:求下列表达式真值
23 > 45
52 < 8'hxFF
'b1000 > = 'b01110
1) 关系操作符的结果为真(1)或假(0);
2) 如果操作数中有一位为X或Z,那么结果为X。
嵌入式系统工程系
表达式:操作符
等式运算符(相等关系运算符)
习题:求下列表达式真值
 = =(逻辑相等)
假定
Data = ‘b11x0;
 ! = (逻辑不等)
Addr = ‘b11x0;
 = = =(全等)
求
Data = = Addr
 ! = =(非全等)
Data = = = Addr
1) 如果比较结果为假则结果为0,为真结果为1;
2) 在全等比较中,值x和z严格按位比较。
===
0
1
x
z
==
0
1
x
z
0
1
0
0
0
0
1
0
x
x
1
0
1
0
0
1
0
1
x
x
x
0
0
1
0
x
x
x
x
x
z
0
0
0
1
z
x
x
x
x
嵌入式系统工程系
表达式:操作符
逻辑操作符有:
 && (逻辑与)
 || (逻辑或)
 ! (逻辑非)
习题:求下列表达式真值
假定:
求
1) 只对逻辑值运算,结果一位,逻辑
值1、0或x;
2) 对于向量操作, 非0向量作为1处理;
3) 如果任意一个操作数包含x,结果
也为x。
C = 'b0; //0为假
D = ‘b1; //1为真
A_Bus = 'b0110;
B_Bus = 'b0110;
C && D
C || D
!D
A_Bus && B_Bus
A_Bus || B_Bus
!A_Bus
嵌入式系统工程系
表达式:操作符
按位操作符有:
• ~(一元非)
• &(二元与)
• |(二元或)
• ^(二元异或)
• ~^或^~(二元异或非)
操作数对应位上按位操
作,并产生向量结果。
习题:求下列表达式真值
假定
求
A = 'b0110;
B = 'b0100;
A|B
A&B
嵌入式系统工程系
表达式:操作符
归约操作符有:






& (归约与)
~& (归约与非)
| (归约或)
~| (归约或非)
^ (归约异或)
~^ (归约异或非)
习题:求下列表达式真值
假定:
求
A = ‘b0110;
B = ‘b0100;
MyReg = 4‘b01x0;
~&A
^A
|B
&B
|MyReg
^MyReg
在单一操作数的所有位上操作,并产生1位结果。
嵌入式系统工程系
表达式:操作符
移位操作符有:
习题:求Qreg的值
假定:
reg [7: 0] Qreg;
Qreg = 4'b0111;
Qreg >> 2
 •<< (左移)
移位
 >> (右移)
1)左侧操作数移动右侧操作数表示的次数,逻辑移位,
空闲位添0补位;
2)如果右侧操作数的值为x或z, 移位操作的结果为x。
应用实例:使用移位操作为2 - 4解码器建模
wire [3:0] DecodeOut;
assign DecodeOut = 4'b1 << Address [1:0];
嵌入式系统工程系
表达式:操作符
条件操作符
 根据条件表达式的值选择表达式,形式:
cond_expr ? expr1 : expr2
三目运算符
wire [2 : 0] Student;
assign Student = Marks > 18 ? Grade_A : Grade_C;
嵌入式系统工程系
表达式:操作符
位拼接运算符(连接和复制操作)
 连接操作:将小表达式合并形成大表达式的操作。形式:
{expr1, expr2, . . .,exprN}
 复制操作:指定重复次数来执行操作。如下:
{repetition_number {expr1, expr2, ...,exprN}
实例:
wire [7:0] Dbus, [11:0] Abus;
assign Dbus [7:4] = { Dbus[0], Dbus[1], Dbus[2], Dbus[3] };
assign Abus = {3{4'b1011}};
/ /位向量12'b1011_1011_1011)
assign Abus = {{4{Dbus[7]}}, Dbus};
/*符号扩展*/
嵌入式系统工程系
习题2
 1.说明参数GATE_DELAY, 参数值为5。
 2.假定长度为64个字的存储器, 每个字8位,编写Verilog 代码,按逆序
交换存储器的内容。即将第0个字与第63个字交换,第1个字与第62个
字交换,依此类推。
 3. 假定32位总线Address_Bus, 编写一个表达式,计算从第11位到第
2 0位的归约与非。
 4. 假定一条总线Control_Bus [15 : 0],编写赋值语句将总线分为两条
总线: Abus [0 : 9 和Bbus[6 : 1]。
 5. 编写一个表达式,执行算术移位,将Qparity 中包含的8位有符号数
算术移位。
 6. 使用条件操作符, 编写赋值语句选择NextState的值。如果Current
State的值为RESET, 那么NextState的值为GO;如果CurrentState的
值 为 GO , 则 NextState 的 值 为 BUSY ; 如 果 CurrentState 的 值 为
BUSY ;则NextState的值为RESET 。
 7.如何从标量变量A,B,C和D中产生总线BusQ[0:3]? 如何从两条总
线B usA [0 : 3]和BusY[20 : 15]形成新的总线BusR[1 0 : 1] ?
嵌入式系统工程系
模块与端口
模块:基本单元定义成模块形式
module module_name (port _list) ;
Declarations_and_Statements
endmodule
 端口队列port_list列出了该模块通过哪些端口与外部
模块通信。
嵌入式系统工程系
模块与端口
端口
 模块的端口可以是
input(输入端口)、output (输出端口) 或者inout (双向
端口);
 缺省的端口类型为wire型;
 output或inout能够被重新声明为reg型,但是input不
可以;
 线网或寄存器必须与端口说明中指定的长度相同。
嵌入式系统工程系
模块与端口
 例:
module Micro (PC, Instr, NextAddr);
//端口说明
input [3:1] PC;
output [1:8] Instr;
inout [16:1] NextAddr;
//重新说明端口类型:
wire [16:1] NextAddr;
//该说明是可选的,但如果指定了,就必须与它的端口说明保持相同
长度。
reg [1:8] Instr;
//Instr已被重新说明为reg型,因此能在always语句或在initial语句
中赋值。
...
endmodule
嵌入式系统工程系
模块与端口
 模块实语句
 一个模块能够在另外一个模块中被引用,这样就建立了描述的层次。模
块实例语句形式
module_name instance_name (port _associations) ;
 信号端口可以通过位置或名称关联;但是关联方式不能够混合使用。端
口官廉形式
port_expr
//通过位置,隐式关联
.PortName (port_expr)
//通过名称,显示关联,强烈推荐!
port_expr可以是以下的任何类型:
 1) 标识符(reg型或wire型)
 2) 位选择
 3) 部分选择
 4) 上述类型的合并
 5) 表达式(只适用于input型信号)
Micro M1 ( UdIn[3:0], {WrN, RdN}, Status[0], Status[1] ,
&UdOut[0:7], TxData) ;
嵌入式系统工程系
模块与端口
 使用两个半加器模块构造全加器
module HA (A , B , S , C);
input A , B;
output S, C;
assign S = A ^ B;
assign C = A & B;
endmodule
module FA (P, Q, Cin, Sum, Cout) ;
input P, Q, Cin;
output Sum, Cout;
wire S1, C1, C2;
HA h1 (P, Q, S1, C1);
//通过位置关联。
HA h2 (.A(Cin), .S(Sum), .B(S1), .C(C2)); //通过端口与信号的名字关联。
or O1 (Cout, C1, C2) ;
//或门实例语句
endmodule
考虑如何模块参数化?
嵌入式系统工程系
模块与端口
使用两个半加器模块构造全加器(模块参数化)
module HA (A , B , S , C);
input A , B;
output S, C;
parameter AND_DELAY = 1, XOR_DELAY = 2;
assign #XOR_DELAY S = A ^ B;
assign #AND_DELAY C = A & B;
endmodule
module FA (P, Q, Cin, Sum, Cout) ;
input P, Q, Cin;
output Sum, Cout;
parameter OR_DELAY = 1;
wire S1, C1, C2;
HA h1 (P, Q, S1, C1);
//通过位置关联。
HA h2 (.A(Cin), .S(Sum), .B(S1), .C(C2)); //通过端口与信号的名字关联。
or #OR_DELAY O1 (Cout, C1, C2) ;
//或门实例语句
endmodule
嵌入式系统工程系
模块与端口
 悬空端口
 通过将端口表达式表示为空白来指定为悬空端口
DFF d1(.Q(QS), .Qbar(), .Data(D),.Preset(), .Clock(CK));
 端口长度不同
 通过无符号数的右对齐或截断方式进行匹配
module Child(Pba, Ppy) ;
input [5:0] Pba;
output [2:0] Ppy;
...
endmodule
module Top;
wire [1:2] Bdl;
wire [2:6] Mpr;
Child C1 (.Pba(Bdl), .Ppy(Mpr));
endmodule
嵌入式系统工程系
模块与端口
模块参数值改变
 1)参数定义语句(defparam)
module TOP (NewA , NewB , NewS , NewC) ;
input New A , New B;
output New S , New C;
defparam Ha1. XOR_DELAY = 5,
//实例Ha1中的参数XOR_DELAY。
Ha1. AND_DELAY = 2;
//实例Ha1中参数的AND_DELAY。
HA Ha1 (NewA, NewB, NewS, NewC) ;
endmodule
嵌入式系统工程系
模块与端口
模块参数值改变
 2) 带参数值的模块引用
module TOP (NewA , NewB , NewS , NewC) ;
input New A , New B;
output New S , New C;
HA #(5,2) Ha1(NewA , NewB , NewS , NewC) ;
//第1个值5赋给参数AND_DELAY,该参数在模块HA中说明。
//第2个值2赋给参数XOR_DELAY,该参数在模块HA中说明。
endmodule
嵌入式系统工程系
模块与端口
 外部端口
 显式地指定外部端口。(较少使用)
module Scram_B
( .Data(Arb), .Control(Ctrl),.Mem_Word(Mem_Blk),
.Addr(Byte)
);
input [0:3] Arb;
input Ctrl;
input [8 : 0] Mem_Blk;
output [0:3] Byte;
...
endmodule
嵌入式系统工程系
习题3
1 .模块实例语句与门实例语句的区别是什么?
2 .当端口悬空时,即端口没有被连接时,端口的
值是什么?
3 .用本章讲述的模块FA编写执行加法和减法的4
位ALU的结构模型。
嵌入式系统工程系
门级建模
 FPGA设计中较少使用
 Verilog HDL中提供下列内置基本门:
 1) 多输入门:
and, nand, or, nor, xor, xnor
 2) 多输出门:
buf, not
 3) 三态门:
bufif0, bufif1, notif0, notif1
 4) 上拉、下拉电阻:
pullup, pulldown
 5) MOS开关:
cmos, nmos, pmos, rcmos, rnmos, rpmos
 6) 双向开关:
tran, tranif0, tranif1, rtran, rtranif0, rtranif1
嵌入式系统工程系
用于定义原语(UDP)
FPGA设计中通常不
使用;
UDP 实 例 语 句 的 语
法与基本门的实例
语句语法一致;
UDP 中 可 以 描 述 组
合电路和时序电路。
Primitive D_Edge_FF (Q, Clk, Data) ;
output Q;
reg Q ;
input Data, Clk;
initialQ = 0;
table
// Clk Data Q (State) Q(next )
(01) 0 : ? :
0;
(01) 1 : ? :
1;
(0x) 1 : 1 :
1;
(0x) 0 : 0 :
0;
// 忽略时钟负边沿:
(?0) ? : ? :
-;
// 忽略在稳定时钟上的数据变化
(??) ? : ? :
-;
endtable
endprimitive
嵌入式系统工程系
数据流建模
连续赋值用于数据流建模(描述),生成组合逻辑
电路。
连续赋值使用连续赋值语句 assign语句,格式为:
assign LHS_target = RHS_expression;
例如
wire Z1, Preset, Clear; //线网说明
assign Z1 = Preset & Clear; //连续赋值语句
wire [15:0] data_in;
wire [15:0] data_tmp;
wire data_tmp = {data_in[7:0], data_in[15:8]};
嵌入式系统工程系
数据流建模
连续赋值语句在什么时候执行呢?
只要在右端表达式的操作数上有事件发生(值
变化),表达式立即被计算,新结果就赋给左边的
线网。
连续赋值的目标类型(左侧操作数类型)





1) 标量线网
assign Z1 = … ;
2) 向量线网
assign data_tmp = … ;
3) 向量的常数型位选择
assign data_tmp[2] = … ;
4) 向量的常数型部分选择 assign data_tmp[7:0] = … ;
5) 上述类型的任意的拼接运算结果
assign { Z1, data_tmp[15]} = 2’b10;
嵌入式系统工程系
数据流建模
 例:数据流描述的一位全加器
module FA_Df (A, B, Cin, Sum, Cout) ;
input A, B, Cin;
output Sum, Cout ;
assign Sum = A^B ^Cin;
assign Cout = (A & Cin) | (B & Cin) | (A & B) ;
endmodule
1)assign语句之间是并发的,与其书写的顺序无关;
2)线网的赋值可以在声明时赋值,例如
wire Sum = A^B ^Cin;
嵌入式系统工程系
数据流建模
数据流建模的时延
assign #2 Sum = A ^ B ^ Cin;
 #2表示右侧表达式的值延迟两个时间单位赋给Sum;
 时间单位是多少?由谁来决定?
`timescale 1ns/100ps
 FPGA设计中的时延仅在功能仿真时有效,不影响实际
电路生成。
嵌入式系统工程系
数据流建模
数据流建模注意事项:




1)wire型变量如果不赋值,默认值为z;
2)数据流建模没有存储功能,不能保存数据;
3)wire型变量只能在声明时赋值或者assing语句赋值;
4)assign语句并发执行,实际的延迟又物理芯片的布线
结果决定。
 5)最基本的FPGA设计源代码描述语句之一,用于生成
组合逻辑,定制LUT的逻辑功能。常作为中间信号的描
述用于控制寄存器的输入输出。
嵌入式系统工程系
习题4
1. 使用assign语句描述一个时钟信号clk,频率
为100MHz。
assign #5 clk = ~clk;
2. 请指出下列语句是否合法?描述了怎样的功能?
assign Mux = (S = = 0)? A : 'bz;
assign Mux = (S = = 1)? B : 'bz;
assign Mux = (S = = 2)? C : 'bz;
assign Mux = (S = = 3)? D : 'bz;
嵌入式系统工程系
行为建模
过程赋值用于行为建模(描述)
行为建模的主要机制:
 1) initial 语句
 2) always 语句
主要用于仿真文件(模拟)
用于源文件和仿真文件
所有initial语句和always语句之间都是并发执行;
执行顺序与其在模块中书写顺序无关。
嵌入式系统工程系
行为建模:initial语句
 initial 语句只执行一次;
 在模拟开始时执行,即在0时刻开始执行;
 不能嵌套使用。
initial [timing_control] procedural_statement
procedural_statement可以是:
procedural_continuous_assignment 过程赋值(阻塞或者非阻塞)
conditional_statement ->
if
case_statement
->
case
loop_statement
->
for, forever, repeat, while
wait_statement
->
wait
disable_statement
->
disable(相当于C中的break)
event_trigger
->
@ (event)
sequential_block
->
begin ... end
parallel_block
->
fork ... join
task_enable (user or system)
嵌入式系统工程系
行为建模:initial语句
 例:
reg Curt;
...
initial #2 Curt = 1;
 例:
parameter SIZE = 1024;
reg [7:0] RAM [0 : SIZE-1] ;
reg RibReg;
Initial
begin: SEQ_BLK_A
//顺序过程的标记,如果没有局部声明,则不需要
integer Index;
RibReg = 0;
for (Index = 0; Index < SIZE; Index = Index + 1)
RAM [Index] = 0;
end
嵌入式系统工程系
行为建模:initial语句
 initial语句在仿真文件产生时钟和构造数据简单示例
parameter APPLY_DELAY = 5;
reg [0 : 7] port_A;
reg clk;
...
initial
begin
Port_A = 'h20 ;
#APPLY_DELAY Port_A= 'hF2;
#APPLY_DELAY Port_A= 'h41;
#APPLY_DELAY Port_A= 'h0A;
initial
begin
clk = 0;
while(1)
//或者 forever
clk = #5 ~clk;
//或者 #5 clk = ~clk;
end
嵌入式系统工程系
行为建模:always语句
always语句重复执行,语法和initial语句相同:
always [timing_control] procedural_statement
procedural_statement可以是:
procedural_continuous_assignment 过程赋值(阻塞或者非阻塞)
conditional_statement ->
if
case_statement
->
case
loop_statement
->
wait_statement
->
disable_statement
->
event_trigger
->
sequential_block
->
parallel_block
->
task_enable (user or system)
for , forever, repeat, while
wait
disable(相当于C中的break)
@ (event)
begin ... end
fork ... join
嵌入式系统工程系
行为建模:always语句
两种典型的always语句
 1) 组合逻辑(电平触发)
reg c;
always @ ( a or b or sel )
c = sel ? a : b;
说明:
(1)虽然c是reg型,但综合的
结果是组合电路;
(2)等同于数据流描述
wire c;
assign c = sel ? a : b;
(3) FPGA设计中不建议使用;
此外,容易产生锁存器
嵌入式系统工程系
行为建模:always语句
 说明:
(1) 在 always 语
句中所有被赋值的信号
 2) 时序逻辑(时钟沿触发)
必须是reg型;

reg [8:0]count;
(2) 综合为触发器,
always @ (posedge clk or negedge reset)
推荐使用;
begin
if (~reset)
(3) 异步时序逻辑
两种典型的always语句
count = 0;
else
begin
if (count == 511)
count = 0;
else
count = count + 1;
end
end
嵌入式系统工程系
常见过程语句
时序控制语句
 仅用于仿真测试
 1)时序控制
reg Stream ;
initial
Begin
Stream = 0;
#12 Stream = 1;
#5 Stream = 0;
#3 Stream = 1;
#4 Stream = 0;
#2 Stream = 1;
#5 Stream = 0;
end
嵌入式系统工程系
常见过程语句
时序控制语句
 2)事件控制
 边沿触发事件
reg [9:0]addr;
integer i;
initial
begin
for (i=0; i<5 ;i=i+1)
@ (posedge clk)
addr = addr +1;
end
电平触发事件
initial
begin
wait (Sum > 22)
Sum = 0;
end
嵌入式系统工程系
常见过程语句
顺序语句块
 begin … end
 源程序、测试文件
 块内语句顺序执行
思考题:
initial语句若使用
fork…join如何描
述右图时序?
并行语句块
 fork … join
 测试文件
 块内语句并行执行
嵌入式系统工程系
常见过程语句
问题:
(1)时序电路的行为具有并行特性:寄存器
都受到时钟的控制,流水线…
(2)既然fork … join不能在源文件中使用,
在行为描述中如何描述并行语句?
(3)begin … end中的语句是顺序执行,在
同一时钟边沿触发下,每个寄存器变量为何赋值
有先有后?这与实际电路是否矛盾?
嵌入式系统工程系
常见过程语句
过程赋值语句
 定义:initial和always语句中的赋值语句
 区别于数据流描述的连续赋值语句(assign)
 分为阻塞过程赋值和非阻塞过程赋值两种
 阻塞过程赋值
 非阻塞过程赋值
always @ (posedge clk or
negedge rst)
begin
if (~rst)
…//寄存器复位
else if (… )
begin
a = 1‘b1;
b = a;
end
end
b = ?
always @ (posedge clk or
negedge rst)
begin
if (~rst)
…//寄存器复位
else if (… )
begin
a <= 1‘b1;
b <= a;
end
end
嵌入式系统工程系
常见过程语句
结论:
 源代码设计推荐使用非阻塞过程赋值“ <= ”
• 可以有效综合为寄存器逻辑电路
• 符合实际,时序分析简单
• 语句之间并行执行,不再有顺序关系
 阻塞过程赋值“ = ”多用于仿真测试文件
• 适合构造仿真模型和仿真行为
• 不容易直接综合为FPGA资源
嵌入式系统工程系
常见过程语句
initial
begin
Clr = #5 0;
Clr = #4 1;
Clr = #10 0;
end
initial
begin
Clr <= #5
Clr <= #4
Clr <= #10
end
1;
0;
0;
嵌入式系统工程系
常见过程语句
过程赋值与连续赋值的比较
嵌入式系统工程系
常见过程语句
if语句
 与C语言类似
If (condition_1)
procedural_statement_1
{else if(condition_2)
procedural_statement_2}
{else
procedural_statement_3}
注:
1)条件语句必须在过程块语句中使用,不能单独使用;
2) if后面的表达式的值只有为1时才按“真”处理。
嵌入式系统工程系
常见过程语句
 if语句的嵌套
if(表达式1)
if (表达式2)
语句1;
else
语句2;
else
if (表达式3)
语句3;
else
语句4;
注:
1) else总是与它最上面的最近的if配对;
2) 如果if与else的数目不一样,为了实现程序设计者的目
的,可以用begin…end语句确定配对关系;
3) 强烈建议保留else分支。
嵌入式系统工程系
常见过程语句
 例:
always @ (posedge clk or negedge rst)
begin
if (~rst)
begin
ctrl
flag
<= #1 2’b00;
<= #1 0;
end
else if(~flag)
begin
ctrl
flag
<= #1 2’b01;
<= #1 1;
end
else
flag
end
<= #1 0;
嵌入式系统工程系
常见过程语句
综合电路
嵌入式系统工程系
常见过程语句
case语句
 类似C语言的switch case语句
case (case_expr)
case_item_expr{ ,case_item_expr} :
procedural_statement
...
...
[default: procedural_statement]
endcase
嵌入式系统工程系
常见过程语句
reg e;
always @ (posedge clk or negedge rst)
begin
if (~rst)
e <= #1 0;
else
case ( {a, b} )
2’b00:
2’b01:
2’b11:
2’b11:
e <= #1 d;
e <= #1 ~c;
e <= #1 1’b0;
e <= #1 1’b1;
default: ;
endcase
end
//空语句,强烈建议保留default分支表达式
嵌入式系统工程系
常见过程语句
综合电路
嵌入式系统工程系
常见过程语句
casex语句和casez语句
 语法与case非常相似
 不建议使用
casez(ir)
8b’1???????: instruction1(ir);
8b’01??????: instruction2(ir);
8b’00010???: instruction3(ir);
8b’000001??: instruction4(ir);
endcase
嵌入式系统工程系
常见过程语句
循环语句




forever语句
repeat语句
while语句
for语句
思考题:
用四种循环语句分别实现
initial中的时钟产生:
1)在100ns出开始;
2)周期10ns。
(1)连续执行的循环;
(2)只用于测试程序的initial块中;
(3)综合工具很难综合成FPGA的逻辑电路。
嵌入式系统工程系
习题5
1. 描述电路行为:该电路在每一个时钟下跳沿
(负沿)检查输入数据,当输入数据Usg为1011
时,输出Asm被置为1。
2. 描述电路行为:输入为12位的向量。如果其中
1的数量超过0的数量,输出设置为1。当
Data_Ready为1时,才对输入数据进行检查。
提示:输入信号均有clk和rst,采用时序逻辑设计
(always语句)
嵌入式系统工程系
Verilog HDL源代码设计
基于本章内容,可以设计FPGA可实现的Verilog
源代码;
自顶向下设计;
采用数据流建模、行为建模、结构化建模三种方
式;
开始你的第一个Verilog功能模块源代码设计!
嵌入式系统工程系
Verilog HDL源代码设计
基本设计流程





1)根据需求,进行模块功能划分,自顶向下设计;
2)定义各个模块的接口信号(包括方向、类型、宽度);
3)定义全局时钟信号和全局复位信号;
4)编写顶层模块,例化子模块;
5)子模块功能设计,以时序逻辑设计为主;
与软件源代码设计最大的不同:时序的严格性!
嵌入式系统工程系
Verilog HDL源代码设计
如何验证源代码设计的正确性?
功能仿真(前仿真)
 进行语法检查,error和warning
 设计testbench,根据激励输入验证逻辑功能
逻辑综合
…
…
嵌入式系统工程系
Testbench验证
Testbench
 模拟实际环境的输入激励和输出校验的一种“虚拟平
台”
 以输入激励为主 ,输出校验可以通过波形观测
Testbench
(Board) CLK
RST
Peripheral_1
Top Module
(FPGA)
……
Peripheral_n
signals
1)Testbench和源
代码都是.v文件
2) Testbench和源
代码都是module
3) Testbench不能
综合成FPGA内部
电路
嵌入式系统工程系
Testbench验证
接口信号定义
 被测试模块的输入激励设置为reg型;
 被测试模块的输出设置为wire型;
 双向端口inout在测试中需要进行特殊处理。
为什么信号方向与类型的对应关系与之前的要求不同?
源代码看作testbench子模块
源代码顶层的输入是testbench的输出
源代码顶层的输出是testbench的输入
嵌入式系统工程系
Testbench验证
 Testbench中inout信号的使用
 本质上是三态门
inout[15:0] data;
wire [15:0] data;
reg [15:0] data_out;
reg data_enable;
方法1:
assign data = data_enable ? data_out : 16‘hz;
方法2:
IOBUF(.I(data_out), .O(), .T(data_enable), .IO(data));
嵌入式系统工程系
Testbench验证
 Testbench的结构
module testbench();
//信号类型定义(wire或者reg),注意testench没有输入输出。
…
//例化顶层模块
…
//激励行为描述,通常都包含clk和rst的产生描述
initial …
//可使用各种合法语句
always …
assign …
task …
//类似于函数
endmodule
嵌入式系统工程系
Testbench验证
Testbench自动生成模板
 Xilinx ISE工具提供testbench的自动生成模板
 在同一项目中的 Verilog Test Fixture
 与哪个源代码文件关联就生成对应层次的Testbench
嵌入式系统工程系
Testbench验证
空模板样例
 需增加clk的产生
 rst的使能描述
 输入信号的行为
描述
嵌入式系统工程系
Testbench验证
值序列产生
 产生值序列的最简单是使用initial语句。例如:
initial
begin
Reset = 0;
#100 Reset = 1;
#80 Reset = 0;
#30 Reset = 1;
end
嵌入式系统工程系
Testbench验证
值序列产生
 重复序列可由always语句产生,例如:
parameter REPEAT_DELAY = 35;
integer CoinValue;
always
begin
CoinValue = 0;
#7 CoinValue = 25;
#2 CoinValue = 5;
#8 CoinValue = 10;
#6 CoinValue = 5;
# REPEAT_DELAY;
end
嵌入式系统工程系
Testbench验证
重复模式(例如时钟信号)
wire Clock;
assign # (PERIOD/2) Clock = ~ Clock;
 初值多少?
initial
Clock = 0;
 Clock数据类型是否正确?
 建议使用行为描述方式产生时钟
嵌入式系统工程系
Testbench验证
重复模式(例如时钟信号)
reg Clk_A ;
//推荐使用的仿真时钟生成方法
parameter tPERIOD = 10;
initial
Clk_A = 0;
always
# (tPERIOD/2) Clk_A = ~ Clk_A;
嵌入式系统工程系
Testbench验证
状态检测器验证实例
module Top;
reg Data, Clock;
integer Out_File;
//待测试模块的应用实例
Count3_ls F1(Data, Clock, Detect) ;
initial //产生时钟
begin
Clock = 0;
forever
#5 Clock = ~ Clock;
end
initial
begin
Data = 0;
#5 Data = 1;
#40 Data = 0;
#10 Data = 1;
#40 Data = 0;
#20 $stop; // 模拟结束
end
initial
//文件保存监控信息
begin
Out_File = $fopen ("results.vectors") ;
$fmonitor (Out_File ,"Clock=%b,
Data=%b, Detect=%b",
Clock, Data, Detect);
end
endmodule
嵌入式系统工程系
Testbench验证
任务




类似过程,从描述的不同位置执行共同的代码段;
任务可以包含时序控制,也能调用其它任务和函数;
任务参数数量不限(可以是0),需要声明输入输出。
构造复杂的仿真行为,推荐使用!
task task_id;
[declarations]
procedural_statement
endtask
嵌入式系统工程系
Testbench验证
 一个任务实例
parameter MAXBITS = 8;
task Reverse_Bits;
input [MAXBITS - 1:0] Din;
output [MAXBITS - 1:0] Dout;
integer K;
begin
for (K = 0; K < MAXBITS; K = K + 1)
Dout [MAXBITS - K] = Din[K] ;
end
endtask
嵌入式系统工程系
Testbench验证
任务的调用
 如同函数调用
 在initial或者always语句中使用
 任务中可以调用其他任务
 注意:实际仿真时,常常构造带有时序逻辑的任务构
造数据
嵌入式系统工程系
Testbench验证
PCI控制器DMA模式写数据任务
task pci_w;
input start;
input [31:0] data0;
input [31:0] data1;
input [31:0] data2;
input [31:0] data3;
input [31:0] data4;
input [11:0] addr;
reg
flag;
begin
TENABLE <= 0;
@ (posedge clk66);
if (start)
#1 pci_lhold_im <= 1;
else
#1 pci_lhold_im <= 0;
@ (posedge clk66);
#1 flag <= 0;
@ (posedge clk66);
@ (posedge clk66);
if (pci_lholda_om & ~flag)
begin
#1 pci_ads_iw <= 0;
pci_addr_i
<= addr;
pci_data_i
<= 32'h14;
end
while(~flag)
begin
if(~pci_ready_ow)
#1 flag <= 1;
@ (posedge clk66);
begin
#1 pci_ads_iw
<= 1;
if (flag)
pci_data_i
<= data0;
end
end
@(posedge clk66);
#1 pci_data_i <= data1;
@(posedge clk66);
#1 pci_data_i <= data2;
@(posedge clk66);
#1 pci_data_i <= data3;
@(posedge clk66);
begin
#1 pci_data_i
<= data4;
pci_blast_iw
<= 0;
end
@(posedge clk66);
#1 pci_blast_iw <= 1;
@(posedge clk66);
if(pci_ready_ow)
pci_lhold_im <= 0;
T_ENABLE <= 1;
end
endtask
嵌入式系统工程系
Testbench验证
函数
 与任务类似,但是只能返回一个值;仿真中涉猎不多。
 Verilog有丰富的系统函数,与C语言系统函数类似,
请查阅手册;
 系统函数通常用于输出校验和存储器仿真。
嵌入式系统工程系
习题6
1. 产生一个高电平持续时间和低电平持续时间分
别为3 ns和10 ns的时钟。
2. 编写测时序检测器源代码以及测试验证程序。
时序列检测器按模式10010在每个时钟正沿检查
输入数据流。如果找到该模式,将输出置为1;否
则输出置为0。