第二章 HDL攻略
模块
模块是Verilog 的根本描绘单位,用于描绘某个规划的功用或结构及其与其他模块通讯的外部端口。一个规划的结构可运用开关级原语、门级原语和用户界说的原语方法描绘; 规划的数据流行为运用接连赋值句子进行描绘; 时序行为运用进程结构描绘。一个模块能够在另一个模块中运用。
一个模块的根本语法如下:
module module_name (port_list);
DeclaraTIons:
reg, wire, parameter,
input, output, inout,
funcTIon, task, . . .
Statements:
IniTIal statement
Always statement
Module instanTIation
Gate instantiation
UDP instantiation
Continuous assignment
endmodule
阐明部分用于界说不同的项,例如模块描绘中运用的寄存器和参数。句子界说规划的功用和结构。阐明部分和句子能够分布在模块中的任何地方;可是变量、寄存器、线网和参数等的阐明部分有必要在运用前呈现。为了使模块描绘明晰和具有杰出的可读性, 最好将一切的阐明部分放在句子前。本书中的一切实例都恪守这一规范。
以下为建模一个半加器电路的模块的简略实例。
module HalfAdder (A, B, Sum, Carry);
input A, B;
output Sum, Carry;
assign #2 Sum = A ^ B;
assign #5 Carry = A B;
endmodule
模块的姓名是HalfAdder。 模块有4个端口: 两个输入端口A和B,两个输出端口Sum和Carry。因为没有界说端口的位数, 一切端口巨细都为1位;一起, 因为没有各端口的数据类型阐明, 这四个端口都是线网数据类型。
模块包括两条描绘半加器数据流行为的接连赋值句子。从这种意义上讲,这些句子在模块中呈现的次序无关紧要,这些句子是并发的。每条句子的履行次序依赖于发生在变量A和B上的事情。
在模块中,可用下述方法描绘一个规划:
1) 数据流方法;
2) 行为方法;
3) 结构方法;
4) 上述描绘方法的混合。
下面几节经过实例叙述这些规划描绘方法。不过有必要首要对Verilog HDL的时延作扼要介绍。
时延
Verilog HDL模型中的一切时延都依据时间单位界说。 下面是带时延的接连赋值句子实例。
assign #2 Sum = A ^ B;
#2指2个时间单位。
运用编译指令将时间单位与物理时间相相关。这样的编译器指令需在模块描绘前界说,如下所示:
` timescale 1ns /100ps
此句子阐明时延时间单位为1ns并且时间精度为100ps (时间精度是指一切的时延有必要被限定在0.1ns内)。 假如此编译器指令地点的模块包括上面的接连赋值句子, #2 代表2ns。
假如没有这样的编译器指令, Verilog HDL 模仿器会指定一个缺省时间单位。IEEE Verilog HDL 规范中没有规则缺省时间单位。
数据流描绘方法
用数据流描绘方法对一个规划建模的最根本的机制便是运用接连赋值句子。在接连赋值句子中,某个值被指派给线网变量。 接连赋值句子的语法为:
assign [delay] LHS_net = RHS_ expression;
右边表达式运用的操作数不管何时发生改动, 右边表达式都从头核算, 并且在指定的时拖延改动值被赋予左面表达式的线网变量。时延界说了右边表达式操作数改动与赋值给左面表达式之间的持续时间。假如没有界说时延值, 缺省时延为0。
下面的比如显现了运用数据流描绘方法对2-4解码器电路的建模的实例模型。
`timescale 1ns/ 1ns
module Decoder2x4 (A, B, EN, Z);
input A, B, EN;
output [ 0 :3] Z;
wire Abar, Bbar;
assign #1 Abar = ~ A; / / 句子 1。
assign #1 Bbar = ~ B; / / 句子 2。
assign #2 Z[0] = ~ (Abar Bbar EN) ; / / 句子 3。
assign #2 Z[1] = ~ (Abar B EN) ; / / 句子 4。
assign #2 Z[2] = ~ (A Bbar EN) ; / / 句子 5。
assign #2 Z[3] = ~ (A B EN) ; / / 句子 6。
endmodule
以反引号“ ` ”开端的第一条句子是编译器指令, 编译器指令`timescale 将模块中一切时延的单位设置为1 ns,时间精度为1 ns。例如,在接连赋值句子中时延值#1和#2分别对应时延1 ns和2 ns。
模块Decoder2x4有3个输入端口和1个4位输出端口。线网类型阐明晰两个连线型变量Abar和Bbar (连线类型是线网类型的一种)。此外,模块包括6个接连赋值句子。
当EN在第5 ns改动时,句子3、4、5和6履行。这是因为EN是这些接连赋值句子中右边表达式的操作数。Z[0]在第7 ns时被赋予新值0。当A在第15 ns改动时, 句子1、5和6履行。履行句子5和6不影响Z[0]和Z[1]的取值。履行句子5导致Z[2]值在第17 ns变为0。履行句子1导致Abar在第16 ns被从头赋值。因为Abar的改动,反过来又导致Z[0]值在第18 ns变为1。
请留意接连赋值句子是怎么对电路的数据流行为建模的;这种建模方法是隐式而非显式的建模方法。此外,接连赋值句子是并发履行的,也便是说各句子的履行次序与其在描绘中呈现的次序无关。
行为描绘方法
规划的行为功用运用下述进程句子结构描绘:
1) initial句子:此句子只履行一次。
2) always句子:此句子总是循环履行, 或者说此句子重复履行。
只需寄存器类型数据能够在这两种句子中被赋值。寄存器类型数据在被赋新值前坚持原有值不变。一切的初始化句子和always句子在0时间并发履行。
下例为always句子对1位全加器电路建模的示例。
module FA_Seq (A, B, Cin, Sum, Cout);
input A, B, Cin;
output Sum, Cout;
reg Sum, Cout;
reg T1, T2, T3;
always
@ ( A or B or Cin ) begin
Sum = (A ^ B) ^ Cin;
T1 = A Cin;
T2 = B Cin;
T3 = A B;
Cout = (T1| T2) | T3;
end
endmodule
模块FA_Seq 有三个输入和两个输出。因为Sum、Cout、T1、T2和T3在always 句子中被赋值,它们被阐明为 reg 类型(reg 是寄存器数据类型的一种)。always 句子中有一个与事情操控(紧跟在字符@ 后边的表达式)。相相关的次序进程(begin-end对)。这意味着只需A、B或Cin 上发生事情,即A、B或Cin之一的值发生改动,次序进程就履行。在次序进程中的句子次序履行,并且在次序进程履行结束后被挂起。次序进程履行完结后,always 句子再次等候A、B或Cin上发生的事情。
在次序进程中呈现的句子是进程赋值模块化的实例。模块化进程赋值鄙人一条句子履行前完结履行。进程赋值能够有一个可选的时延。
时延能够细分为两种类型:
1) 句子间时延: 这是时延句子履行的时延。
2) 句子内时延: 这是右边表达式数值核算与左面表达式赋值间的时延。
下面是句子间时延的示例:
Sum = (A ^ B) ^ Cin;
#4 T1 = A Cin;
在第二条句子中的时延规则赋值推迟4个时间单位履行。便是说,在第一条句子履行后等候4个时间单位,然后履行第二条句子。下面是句子内时延的示例。
Sum = #3 (A^ B) ^ Cin;
这个赋值中的时延意味着首要核算右边表达式的值, 等候3个时间单位,然后赋值给Sum。
假如在进程赋值中未界说时延,缺省值为0时延,也便是说,赋值当即发生。这种方法以及在always 句子中指定句子的其他方法将在第8章中具体评论。
下面是initial句子的示例:
`timescale 1ns / 1ns
module Test (Pop, Pid);
output Pop, Pid;
reg Pop, Pid;
initial
begin
Pop = 0; // 句子 1。
Pid = 0; // 句子 2。
Pop = #5 1; // 句子 3。
Pid = #3 1; // 句子 4。
Pop = #6 0; // 句子 5。
Pid = #2 0; // 句子 6。
end
endmodule
initial句子包括一个次序进程。这一次序进程在0 ns时开端履行,并且在次序进程中一切句子悉数履行结束后, initial句子永久挂起。这一次序进程包括带有界说句子内时延的分组进程赋值的实例。句子1和2在0 ns时履行。第三条句子也在0时间履行,导致Pop 在第5 ns时被赋值。句子4在第5 ns履行,并且Pid 在第8 ns被赋值。相同,Pop在14 ns被赋值0,Pid在第16 ns被赋值0。第6条句子履行后,initial句子永久被挂起。
结构化描绘方法
在Verilog HDL中可运用如下方法描绘结构:
1) 内置门原语(在门级);
2) 开关级原语(在晶体管级);
3) 用户界说的原语(在门级);
4) 模块实例 (创立层次结构)。
经过运用线网来彼此衔接。下面的结构描绘方法运用内置门原语描绘的全加器电路实例。
module FA_Str (A, B, Cin, Sum, Cout);
input A, B, Cin;
output Sum, Cout;
wire S1, T1, T2, T3;
xor
X1 (S1, A, B),
X2 (Sum, S1, Cin);
and
A1 (T3, A, B),
A2 (T2, B, Cin),
A3 (T1, A, Cin),
or
O1 (Cout, T1, T2, T3);
endmodule
在这一实例中,模块包括门的实例句子,也便是说包括内置门xor、and和or 的实例句子。门实例由线网类型变量S1、T1、T2和T3互连。因为没有指定的次序, 门实例句子能够以任何次序呈现;图中显现了纯结构;xor、and和or是内置门原语;X1、X2、A1等是实例称号。紧跟在每个门后的信号列表是它的互连;列表中的第一个是门输出,余下的是输入。例如,S1与xor 门实例X1的输出衔接,而A和B与实例X1的输入衔接。
4位全加器能够运用4个1位全加器模块描绘。下面是4位全加器的结构描绘方法。
module FourBitFA (FA, FB, FCin, FSum, FCout );
parameter SIZE = 4;
input [SIZE:1] FA, FB;
output [SIZE:1] FSum
input FCin;
input FCout;
wire [ 1: SIZE-1] FTemp;
FA_Str
FA1( .A (FA[1]), .B(FB[1]), .Cin(FCin),
.Sum(FSum[1]), .Cout(FTemp[2])),
FA2( .A (FA[2]), .B(FB[2]), .Cin(FTemp[1]),
.Sum(FSum[2]), .Cout(FTemp[2])),
FA3(FA[3], FB[3], FTemp[2], FSum[3], FTemp[3],
FA4(FA[4], FB[4], FTemp[3], FSum[4], FCout);
endmodule
在这一实例中,模块实例用于建模4位全加器。在模块实例句子中,端口能够与称号或方位相关。前两个实例FA1和FA2运用命名相关方法,也便是说,端口的称号和它衔接的线网被显式描绘(每一个的方法都为“.port_name (net_name))。最终两个实例句子,实例FA3和FA4运用方位相关方法将端口与线网相关。这儿相关的次序很重要,例如,在实例FA4中,第一个FA[4]与FA_Str 的端口A衔接,第二个FB[4]与FA_Str 的端口B衔接,余下的由此类推。
混合规划描绘方法
在模块中,结构的和行为的结构能够自在混合。也便是说,模块描绘中能够包括实例化的门、模块实例化句子、接连赋值句子以及always句子和initial句子的混合。它们之间能够彼此包括。来自always句子和initial句子(牢记只需寄存器类型数据能够在这两种句子中赋值)的值能够驱动门或开关,而来自于门或接连赋值句子(只能驱动线网)的值能够反过来用于触发always句子和initial句子。
下面是混合规划方法的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 ) begin // always 句子。
T1 = A Cin;
T2 = B Cin;
T3 = A B;
Cout = (T1| T2) | T3;
end
assign Sum = S1 ^ Cin; // 接连赋值句子。
endmodule
只需A或B上有事情发生,门实例句子即被履行。只需A、B或Cin上有事情发生,就履行always 句子,并且只需S1或Cin上有事情发生,就履行接连赋值句子。
规划模仿
Verilog HDL不只供给描绘规划的才干,并且供给对鼓励、操控、存储呼应和规划验证的建模才干。鼓励和操控可用初始化句子发生。验证运转进程中的呼应能够作为“改动时保存”或作为选通的数据存储。最终,规划验证能够经过在初始化句子中写入相应的句子主动与希望的呼应值比较完结。
下面是测验模块Top的比如。该比如测验2.3节中讲到的FA_Seq模块。
‘timescale 1ns/1ns
module Top; // 一个模块能够有一个空的端口列表。
reg PA, PB, PCi;
wire PCo, PSum;
// 正在测验的实例化模块:
FA_Seq F1(PA, PB, PCi, PSum, PCo); // 定位。
initial
begin: ONLY_ONCE
reg [3:0] Pal;
//需求4位, Pal才干取值8。
for (Pal = 0; Pal 8; Pal = Pal + 1)
begin
{PA, PB, PCi} = Pal;
#5 $display (“PA, PB, PCi = %b%b%b”, PA, PB, PCi,
“ : : : PCo, PSum=%b%b”, PCo, PSum);
end
end
endmodule
在测验模块描绘中运用方位相关方法将模块实例句子中的信号与模块中的端口相衔接。也便是说,PA衔接到模块FA_Seq的端口A,PB衔接到模块FA_Seq的端口B,依此类推。留意初始化句子中运用了一个for循环句子,在PA、PB和PCi上发生波形。for 循环中的第一条赋值句子用于表明兼并的方针。自右向左,右端各相应的位赋给左端的参数。初始化句子还包括有一个预先界说好的体系使命。体系使命$display将输入以特定的格局打印输出。
体系使命$display调用中的时延操控规则$display使命在5个时间单位后履行。这5个时间单位根本上代表了逻辑处理时间。便是输入向量的加载至观察到模块在测验条件下输出之间的推迟时间。
这一模型中还有别的一个细微差别。Pal在初始化句子内被部分界说。为完结这一功用,初始化句子中的次序进程(begin-end)有必要符号。在这种情况下, ONLY_ONCE是次序进程符号。假如在次序进程内没有部分声明的变量,就不需求该符号。下面是测验模块发生的输出。
PA, PB, PCi = 000 ::: PCo, PSum = 00
PA, PB, PCi = 001 ::: PCo, PSum = 01
PA, PB, PCi = 010 ::: PCo, PSum = 01
PA, PB, PCi = 011 ::: PCo, PSum = 10
PA, PB, PCi = 100 ::: PCo, PSum = 01
PA, PB, PCi = 101 ::: PCo, PSum = 10
PA, PB, PCi = 110 ::: PCo, PSum = 10
PA, PB, PCi = 111 ::: PCo, PSum = 11
验证与非门穿插衔接构成的RS_FF模块的测验模块如下例所示。
`timescale 10ns/1ns
module RS_FF (Q, Qbar, R, S);
output Q, Qbar;
input R, S;
nand #1 (Q, R, Qbar);
nand #1 (Qbar, S, Q,);
//在门实例句子中,实例称号是可选的。
endmodule
module Test;
reg TS, TR;
wire TQ, TQb;
//测验模块的实例句子:
RS_FF NSTA (.Q(TQ), .S(TS), .R(TR), .Qbar(TQb));
//选用端口名相相关的衔接方法。
// 加载鼓励:
initial
begin:
TR = 0;
TS = 0;
#5 TS = 1;
#5 TS = 0;
TR = 1;
#5 TS = 1;
TR = 0;
#5 TS = 0;
#5 TR = 1;
end
//输出显现:
initial
$monitor (At time %t , , $time,
TR = %b, TS=%b, TQ=%b, TQb= %b, TR, TS, TQ, TQb);
endmodule
RS_FF模块描绘了规划的结构。在门实例句子中运用门时延;例如,第一个实例句子中的门时延为1个时间单位。该门时延意味着假如R或Qbar假定在T时间改动,Q将在T+1时间取得核算结果值。
模块Test是一个测验模块。测验模块中的RS_FF用实例句子阐明其端口用端口名相关方法衔接。在这一模块中有两条初始化句子。第一个初始化句子只简略地发生TS和TR上的波形。这一初始化句子包括带有句子间时延的程序块进程赋值句子。
第二条初始化句子调用体系使命$monitor。这一体系使命调用的功用是只需参数表中指定的变量值发生改动就打印指定的字符串。下面是测验模块发生的输出。请留意`timescale指令在时延上的影响。
At time 0, TR=0, TS=0, TQ=x, TQb= x
At time 10, TR=0, TS=0, TQ=1, TQb= 1
At time 50, TR=0, TS=1, TQ=1, TQb= 1
At time 60, TR=0, TS=1, TQ=1, TQb= 0
At time 100, TR=1, TS=0, TQ=1, TQb= 0
At time 110, TR=1, TS=0, TQ=1, TQb= 1
At time 120, TR=1, TS=0, TQ=0, TQb= 1
At time 150, TR=0, TS=1, TQ=0, TQb= 1
At time 160, TR=0, TS=1, TQ=1, TQb= 1
At time 170, TR=0, TS=1, TQ=1, TQb= 0
At time 200, TR=0, TS=0, TQ=1, TQb= 0
At time 210, TR=0, TS=0, TQ=1, TQb= 1
At time 250, TR=1, TS=0, TQ=1, TQb= 1
At time 260, TR=1, TS=0, TQ=0, TQb= 1