⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 verilog_2.htm

📁 Verilog HDL硬件描述语言的教程
💻 HTM
📖 第 1 页 / 共 3 页
字号:
下面的例子显示了使用数据流描述方式对2-4解码器电路的建模的实例模型。<br />
<br />
`timescale 1ns/ 1ns<br />
module Decoder2x4 (A, B, EN, Z);<br />
input A, B, EN;<br />
output [ 0 :3] Z;<br />
wire Abar, Bbar;<br />
<br />
assign #1 Abar = ~ A; / / 语句 1。 <br />
assign #1 Bbar = ~ B; / / 语句 2。 <br />
assign #2 Z[0] = ~ (Abar & Bbar & EN) ; / / 语句 3。 <br />
assign #2 Z[1] = ~ (Abar & B & EN) ; / / 语句 4。 <br />
assign #2 Z[2] = ~ (A & Bbar & EN) ; / / 语句 5。 <br />
assign #2 Z[3] = ~ (A & B & EN) ; / / 语句 6。 <br />
endmodule<br />
<br />
  以反引号“ ` ”开始的第一条语句是编译器指令, 编译器指令`timescale 将模块中所有时延的单位设置为1 ns,时间精度为1 ns。例如,在连续赋值语句中时延值#1和#2分别对应时延1 ns和2 ns。<br />
  模块Decoder2x4有3个输入端口和1个4位输出端口。线网类型说明了两个连线型变量Abar和Bbar (连线类型是线网类型的一种)。此外,模块包含6个连续赋值语句。<br />
  当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。<br />
  请注意连续赋值语句是如何对电路的数据流行为建模的;这种建模方式是隐式而非显式的建模方式。此外,连续赋值语句是并发执行的,也就是说各语句的执行顺序与其在描述中出现的顺序无关。<br />
<br />
2.4 行为描述方式<br />
<br />
  设计的行为功能使用下述过程语句结构描述:<br />
  1) initial语句:此语句只执行一次。<br />
  2) always语句:此语句总是循环执行, 或者说此语句重复执行。<br />
  只有寄存器类型数据能够在这两种语句中被赋值。寄存器类型数据在被赋新值前保持原有值不变。所有的初始化语句和always语句在0时刻并发执行。<br />
  下例为always语句对1位全加器电路建模的示例。<br />
<br />
module FA_Seq (A, B, Cin, Sum, Cout);<br />
input A, B, Cin;<br />
output Sum, Cout;<br />
reg Sum, Cout;<br />
reg T1, T2, T3;<br />
always<br />
@ ( A or B or Cin ) begin<br />
Sum = (A ^ B) ^ Cin;<br />
T1 = A & Cin;<br />
T2 = B & Cin;<br />
T3 = A & B;<br />
Cout = (T1| T2) | T3;<br />
end<br />
endmodule<br />
<br />
  模块FA_Seq 有三个输入和两个输出。由于Sum、Cout、T1、T2和T3在always 语句中被赋值,它们被说明为 reg 类型(reg 是寄存器数据类型的一种)。always 语句中有一个与事件控制(紧跟在字符@ 后面的表达式)。相关联的顺序过程(begin-end对)。这意味着只要A、B或Cin 上发生事件,即A、B或Cin之一的值发生变化,顺序过程就执行。在顺序过程中的语句顺序执行,并且在顺序过程执行结束后被挂起。顺序过程执行完成后,always 语句再次等待A、B或Cin上发生的事件。<br />
  在顺序过程中出现的语句是过程赋值模块化的实例。模块化过程赋值在下一条语句执行前完成执行。过程赋值可以有一个可选的时延。<br />
  时延可以细分为两种类型:<br />
  1) 语句间时延: 这是时延语句执行的时延。<br />
  2) 语句内时延: 这是右边表达式数值计算与左边表达式赋值间的时延。<br />
  下面是语句间时延的示例:<br />
<br />
Sum = (A ^ B) ^ Cin;<br />
#4 T1 = A & Cin;<br />
<br />
  在第二条语句中的时延规定赋值延迟4个时间单位执行。就是说,在第一条语句执行后等待4个时间单位,然后执行第二条语句。下面是语句内时延的示例。<br />
<br />
Sum = #3 (A^ B) ^ Cin;<br />
<br />
  这个赋值中的时延意味着首先计算右边表达式的值, 等待3个时间单位,然后赋值给Sum。<br />
  如果在过程赋值中未定义时延,缺省值为0时延,也就是说,赋值立即发生。这种形式以及在always 语句中指定语句的其他形式将在第8章中详细讨论。<br />
  下面是initial语句的示例:<br />
<br />
`timescale 1ns / 1ns<br />
module Test (Pop, Pid);<br />
output Pop, Pid;<br />
reg Pop, Pid;<br />
<br />
initial <br />
begin<br />
Pop = 0; // 语句 1。<br />
Pid = 0; // 语句 2。<br />
Pop = #5 1; // 语句 3。<br />
Pid = #3 1; // 语句 4。<br />
Pop = #6 0; // 语句 5。<br />
Pid = #2 0; // 语句 6。<br />
end<br />
endmodule<br />
<br />
  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语句永远被挂起。<br />
<br />
2.5 结构化描述形式<br />
<br />
  在Verilog HDL中可使用如下方式描述结构:<br />
  1) 内置门原语(在门级);<br />
  2) 开关级原语(在晶体管级);<br />
  3) 用户定义的原语(在门级);<br />
  4) 模块实例 (创建层次结构)。<br />
  通过使用线网来相互连接。下面的结构描述形式使用内置门原语描述的全加器电路实例。<br />
<br />
module FA_Str (A, B, Cin, Sum, Cout);<br />
input A, B, Cin;<br />
output Sum, Cout;<br />
wire S1, T1, T2, T3;<br />
<br />
xor<br />
X1 (S1, A, B),<br />
X2 (Sum, S1, Cin);<br />
<br />
and<br />
A1 (T3, A, B),<br />
A2 (T2, B, Cin),<br />
A3 (T1, A, Cin),<br />
<br />
or<br />
O1 (Cout, T1, T2, T3);<br />
endmodule<br />
<br />
  在这一实例中,模块包含门的实例语句,也就是说包含内置门xor、and和or 的实例语句。门实例由线网类型变量S1、T1、T2和T3互连。由于没有指定的顺序, 门实例语句可以以任何顺序出现;图中显示了纯结构;xor、and和or是内置门原语;X1、X2、A1等是实例名称。紧跟在每个门后的信号列表是它的互连;列表中的第一个是门输出,余下的是输入。例如,S1与xor 门实例X1的输出连接,而A和B与实例X1的输入连接。<br />
4位全加器可以使用4个1位全加器模块描述。下面是4位全加器的结构描述形式。<br />
<br />
module FourBitFA (FA, FB, FCin, FSum, FCout );<br />
parameter SIZE = 4;<br />
input [SIZE:1] FA, FB;<br />
output [SIZE:1] FSum<br />
input FCin;<br />
input FCout;<br />
wire [ 1: SIZE-1] FTemp;<br />
FA_Str<br />
FA1( .A (FA[1]), .B(FB[1]), .Cin(FCin),<br />
.Sum(FSum[1]), .Cout(FTemp[2])),<br />
FA2( .A (FA[2]), .B(FB[2]), .Cin(FTemp[1]),<br />
.Sum(FSum[2]), .Cout(FTemp[2])),<br />
FA3(FA[3], FB[3], FTemp[2], FSum[3], FTemp[3],<br />
FA4(FA[4], FB[4], FTemp[3], FSum[4], FCout);<br />
endmodule<br />
<br />
  在这一实例中,模块实例用于建模4位全加器。在模块实例语句中,端口可以与名称或位置关联。前两个实例FA1和FA2使用命名关联方式,也就是说,端口的名称和它连接的线网被显式描述(每一个的形式都为“.port_name (net_name))。最后两个实例语句,实例FA3和FA4使用位置关联方式将端口与线网关联。这里关联的顺序很重要,例如,在实例FA4中,第一个FA[4]与FA_Str 的端口A连接,第二个FB[4]与FA_Str 的端口B连接,余下的由此类推。<br />
<br />
2.6 混合设计描述方式<br />
<br />
  在模块中,结构的和行为的结构可以自由混合。也就是说,模块描述中可以包含实例化的门、模块实例化语句、连续赋值语句以及always语句和initial语句的混合。它们之间可以相互包含。来自always语句和initial语句(切记只有寄存器类型数据可以在这两种语句中赋值)的值能够驱动门或开关,而来自于门或连续赋值语句(只能驱动线网)的值能够反过来用于触发always语句和initial语句。<br />
  下面是混合设计方式的1位全加器实例。<br />
<br />
module FA_Mix (A, B, Cin, Sum, Cout);<br />
input A,B, Cin;<br />
output Sum, Cout;<br />
reg Cout;<br />
reg T1, T2, T3;<br />
wire S1;<br />
<br />
xor X1(S1, A, B); // 门实例语句。<br />
<br />
always<br />
@ ( A or B or Cin ) begin // always 语句。<br />
T1 = A & Cin;<br />
T2 = B & Cin;<br />
T3 = A & B;<br />
Cout = (T1| T2) | T3;<br />
end<br />
<br />
assign Sum = S1 ^ Cin; // 连续赋值语句。<br />
endmodule<br />
<br />
  只要A或B上有事件发生,门实例语句即被执行。只要A、B或Cin上有事件发生,就执行always 语句,并且只要S1或Cin上有事件发生,就执行连续赋值语句。<br />
<br />
2.7 设计模拟<br />
<br />
  Verilog HDL不仅提供描述设计的能力,而且提供对激励、控制、存储响应和设计验证的建模能力。激励和控制可用初始化语句产生。验证运行过程中的响应可以作为“变化时保存”或作为选通的数据存储。最后,设计验证可以通过在初始化语句中写入相应的语句自动与期望的响应值比较完成。<br />
下面是测试模块Top的例子。该例子测试2.3节中讲到的FA_Seq模块。<br />
<br />
‘timescale 1ns/1ns<br />
module Top; // 一个模块可以有一个空的端口列表。<br />
reg PA, PB, PCi;<br />
wire PCo, PSum;<br />
<br />
// 正在测试的实例化模块:<br />
FA_Seq F1(PA, PB, PCi, PSum, PCo); // 定位。<br />
<br />
initial<br />
begin: ONLY_ONCE<br />
reg [3:0] Pal;<br />
//需要4位, Pal才能取值8。<br />
<br />
for (Pal = 0; Pal < 8; Pal = Pal + 1)<br />
begin<br />
{PA, PB, PCi} = Pal;<br />
#5 $display (“PA, PB, PCi = %b%b%b”, PA, PB, PCi,<br />
“ : : : PCo, PSum=%b%b”, PCo, PSum);<br />
end<br />
end<br />
endmodule<br />
<br />
  在测试模块描述中使用位置关联方式将模块实例语句中的信号与模块中的端口相连接。也就是说,PA连接到模块FA_Seq的端口A,PB连接到模块FA_Seq的端口B,依此类推。注意初始化语句中使用了一个for循环语句,在PA、PB和PCi上产生波形。for 循环中的第一条赋值语句用于表示合并的目标。自右向左,右端各相应的位赋给左端的参数。初始化语句还包含有一个预先定义好的系统任务。系统任务$display将输入以特定的格式打印输出。<br />
  系统任务$display调用中的时延控制规定$display任务在5个时间单位后执行。这5个时间单位基本上代表了逻辑处理时间。即是输入向量的加载至观察到模块在测试条件下输出之间的延迟时间。<br />
  这一模型中还有另外一个细微差别。Pal在初始化语句内被局部定义。为完成这一功能,初始化语句中的顺序过程(begin-end)必须标记。在这种情况下, ONLY_ONCE是顺序过程标记。如果在顺序过程内没有局部声明的变量,就不需要该标记。下面是测试模块产生的输出。<br />
<br />
PA, PB, PCi = 000 ::: PCo, PSum = 00<br />
PA, PB, PCi = 001 ::: PCo, PSum = 01<br />
PA, PB, PCi = 010 ::: PCo, PSum = 01<br />
PA, PB, PCi = 011 ::: PCo, PSum = 10<br />
PA, PB, PCi = 100 ::: PCo, PSum = 01<br />
PA, PB, PCi = 101 ::: PCo, PSum = 10<br />
PA, PB, PCi = 110 ::: PCo, PSum = 10<br />
PA, PB, PCi = 111 ::: PCo, PSum = 11<br />
<br />
  验证与非门交叉连接构成的RS_FF模块的测试模块如下例所示。<br />
<br />
`timescale 10ns/1ns<br />
module RS_FF (Q, Qbar, R, S);<br />
output Q, Qbar;<br />
input R, S;<br />
<br />
nand #1 (Q, R, Qbar);<br />
nand #1 (Qbar, S, Q,);<br />
//在门实例语句中,实例名称是可选的。<br />
endmodule<br />
<br />
module Test;<br />
reg TS, TR;<br />
wire TQ, TQb;<br />
<br />
//测试模块的实例语句:<br />
RS_FF NSTA (.Q(TQ), .S(TS), .R(TR), .Qbar(TQb)); <br />
//采用端口名相关联的连接方式。<br />
<br />
// 加载激励:<br />
initial<br />
begin:<br />
TR = 0;<br />
TS = 0;<br />
#5 TS = 1;<br />
#5 TS = 0;<br />
TR = 1;<br />
#5 TS = 1;<br />
TR = 0;<br />
#5 TS = 0;<br />
#5 TR = 1;<br />
end<br />
//输出显示:<br />
initial<br />
$monitor ("At time %t ," , $time,<br />
"TR = %b, TS=%b, TQ=%b, TQb= %b", TR, TS, TQ, TQb);<br />
endmodule<br />
<br />
  RS_FF模块描述了设计的结构。在门实例语句中使用门时延;例如,第一个实例语句中的门时延为1个时间单位。该门时延意味着如果R或Qbar假定在T时刻变化,Q将在T+1时刻获得计算结果值。<br />
  模块Test是一个测试模块。测试模块中的RS_FF用实例语句说明其端口用端口名关联方式连接。在这一模块中有两条初始化语句。第一个初始化语句只简单地产生TS和TR上的波形。这一初始化语句包含带有语句间时延的程序块过程赋值语句。<br />
  第二条初始化语句调用系统任务$monitor。这一系统任务调用的功能是只要参数表中指定的变量值发生变化就打印指定的字符串。下面是测试模块产生的输出。请注意`timescale指令在时延上的影响。<br />
<br />
At time 0, TR=0, TS=0, TQ=x, TQb= x<br />
At time 10, TR=0, TS=0, TQ=1, TQb= 1<br />
At time 50, TR=0, TS=1, TQ=1, TQb= 1<br />
At time 60, TR=0, TS=1, TQ=1, TQb= 0<br />
At time 100, TR=1, TS=0, TQ=1, TQb= 0<br />
At time 110, TR=1, TS=0, TQ=1, TQb= 1<br />
At time 120, TR=1, TS=0, TQ=0, TQb= 1<br />
At time 150, TR=0, TS=1, TQ=0, TQb= 1<br />
At time 160, TR=0, TS=1, TQ=1, TQb= 1<br />
At time 170, TR=0, TS=1, TQ=1, TQb= 0<br />
At time 200, TR=0, TS=0, TQ=1, TQb= 0<br />
At time 210, TR=0, TS=0, TQ=1, TQb= 1<br />
At time 250, TR=1, TS=0, TQ=1, TQb= 1<br />
At time 260, TR=1, TS=0, TQ=0, TQb= 1<br />
<br />
  后面的章节将更详细地讲述这些主题。<br />
<br />
习题<br />
<br />
1. 在数据流描述方式中使用什么语句描述一个设计?<br />
2. 使用`timescale 编译器指令的目的是什么?举出一个实例。<br />
3. 在过程赋值语句中可以定义哪两种时延?请举例详细说明。<br />
4. initial语句与always 语句的关键区别是什么?<br />
5. 为2.3节中描述的模块Decode2x4编写一个测试验证程序。<br />
6. 列出你在Verilog HDL模型中使用的两类赋值语句。<br />
7. 在顺序过程中何时需要定义标记?<br />
8. 找出下面连续赋值语句的错误。<br />
assign Reset = #2 ^ WriteBus;<br />
 <br />
<br />&nbsp;</td></tr><tr bgcolor="#ffffff"><td valign="top"><table border="0" cellspacing="0" cellpadding="0" align="left"><tr><td></td></tr></table> <table border="0" cellspacing="0" cellpadding="0" align="right"><tr><td> </td></tr></table></td></tr></table></td></tr></table><table width="760" cellspacing="0" cellpadding="0" align="center"><tr bgcolor="#ffffff"><td colspan="2"></td></tr><tr bgcolor="#ffffff"><td class="post" valign="top"> &nbsp;</td><td align="right">

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -