verilogHDL.docx
《verilogHDL.docx》由会员分享,可在线阅读,更多相关《verilogHDL.docx(21页珍藏版)》请在冰豆网上搜索。
verilogHDL
VerilogHDL语言
2.2层次建模
【例2-3】实现一个1位全加器。
1位全加器的VerilogHDL实现代码如下:
/*以下为全加器顶层模块*/
modulef_adder(ain,bin,cin,cout,sum);
outputcout,sum;
inputain,bin,cin;
wireain,bin,cin,cout,sum;
wired,e,f;
h_adderu0(ain,bin,d,e);
h_adderu1(e,cin,f,sum);
or2au2(d,f,cout);
endmodule
/*以下为半加器模块*/
moduleh_adder(a,b,co,so);
outputco,so;
inputa,b;
wirea,b,co,so,bbar;
andand2(co,a,b);
notnot1(bbar,b);
xnorxnor2(so,a,bbar);
endmodule
/*以下为或门模块*/
moduleor2a(a,b,c);
outputc;
inputa,b;
wirea,b,c;
assignc=a|b;
endmodule
程序说明:
(1)语句assignc=a|b;中,“|”是按位或运算符,其功能是将a与b按位或的结果赋给信号c。
(2)语句wirea,b,c;中,wire是线网数据类型,表示a、b、c是线网,是硬件单元之间的连接。
(3)一个VerilogHDL模块内部的实现方式有多种。
本例中,在或门模块内部使用了数据流语句assign;在半加器模块内部调用了基本逻辑门原语;在全加器模块内部调用了半加器模块和或门模块。
(4)在全加器模块中有两处调用了半加器:
h_adderu0(ain,bin,d,e);和h_adderu1(e,cin,f,sum);。
每次调用均给出一个唯一的实例名,而且调用时端口列表名称不同。
在VerilogHDL设计中,在模块调用的时候,可以按顺序将模块定义的端口与外部环境中的信号连接起来,这种方法称为“按顺序连接”。
h_adderu0(ain,bin,d,e);调用将ain、bin、d、e分别与模块定义中的端口a、b、co、so连接;h_adderu1(e,cin,f,sum);调用将e、cin、f、sum分别与模块定义中的端口a、b、co、so连接。
【例2-4】二选一数据选择器示例。
modulemux21a(a,b,s,y);
inputwirea,b,s;
outputregy;
always@(a,b,s)
if(s==1)y=b;
elsey=a;
endmodule
程序说明:
(1)在模块中,输入端口只能为wire类型,输出端口可以为wire类型,也可以为reg类型。
通常情况下,如果输出端口在always语句中使用,则必须声明为reg类型。
(2) if(s==1)y=b;elsey=a;为条件语句结构,表达的意思是当s为1时y=b,否则y=a。
条件语句只能用在always语句中。
【例2-7】设计参数型N位加法器。
moduleadd_N(X,Y,sum,co);
parameterN=4;
input[N-1:
0]X,Y;
output[N-1:
0]sum;
outputco;
assign{co,sum}=X+Y;
endmodule
//16位加法器只需要调用参数型N位加法器即可
moduleadd_16(X,Y,s,c);
input[15:
0]X,Y;
output[15:
0]s;
outputc;
add_N#(16)add16(X,Y,s,c);
endmodule
//8位加法器只需要调用参数型N位加法器即可
moduleadd_8(X,Y,s,c);
input[7:
0]X,Y;
output[7:
0]s;
outputc;
add_Nadd8(X,Y,s,c);
defparamadd8.N=8;
endmodule
程序说明:
(1)在调用add_N模块时,可用#(16)使参数N的值变为16,从而使模块功能变为16位加法器,具体实现语句为add_N#(16)add16(X,Y,s,c);。
(2)也可以使用defparam来改变参数。
用后缀改变引用模块的参数要用被引用模块的实例名作为参数的前缀,如add8.N=8,从而使被引用模块的功能变为8位加法器。
具体实现语句为:
add_Nadd8(X,Y,s,c);defparamadd8.N=8;。
通过使用参数,用户可以更加灵活地对模块进行说明。
用户不但可以根据参数来定义模块,还可以方便地通过参数值重定义来改变模块的行为。
3.3数据流级建模
连续赋值语句:
以关键词assign开始的语句为连续赋值语句,连续赋值语句是Verilog数据流建模的基本语句,用于对线网进行赋值。
【例3-10】使用数据流建模,实现一位半加器。
moduleadder_half(ain,bin,sum,co);
inputain,bin;
outputsum,co;
assign{co,sum}=ain+bin;
endmodule
程序说明:
(1) assign{co,sum}=ain+bin;是一条连续赋值语句,它将ain和bin的和存放在{co,sum}中。
该语句中,“{}”为位拼接符,其功能是将co和sum拼接成一个两位的数。
(2)连续赋值语句的左边必须是一个标量或向量线网,或者是标量或向量线网的拼接,而不能是任何形式的寄存器。
例如下面的形式是非法的:
regsum;
assign{co,sum}=ain+bin;//非法,sum不能为寄存器类型
【例3-9】表示时序逻辑的UDP举例:
D触发器。
primitived_edge_ff(q,clk,data);
outputq;
inputclk,data;
regq;
table//clkdatstatenext
(01)0:
?
:
0;
(01)1:
?
:
1;
//时钟下降沿
(?
0)?
:
?
:
-;
//时钟稳定时忽略data的变化
?
(?
?
):
?
:
-;
endtable
endprimitive
程序说明:
(1)使用QuartusⅡ软件综合的结果如图3-9所示。
从综合结果可以看出,该设计实现了一个D触发器。
(2)程序中的“?
”表示逻辑值可以为0、1或x。
(3)端口列表中的第一个端口q为输出端口,其余为输入端口。
3.4行 为 级 建 模
结构化过程语句always:
always语句是行为建模的基本语句,每个always语句代表一个独立的执行过程,也称为进程。
与C语言不同,VerilogHDL的各个always进程是并发执行的,而不是顺序执行的。
过程赋值语句:
VerilogHDL包括两种类型的过程赋值语句:
阻塞赋值语句和非阻塞赋值语句。
【例3-15】使用always语句描述D触发器。
modulemydff(q,clk,d);
inputclk,d;
outputq;regq;
always@(posedgeclk)
q<=d;
endmodule
程序说明:
(1)本程序的功能是在时钟上升沿时刻,将数据d赋予触发器输出q,其功能同D触发器一样。
(2) always语句由于其不断重复执行的特性,只有和一定的时序控制结合在一起才有用。
always@(posedgeclk)语句表示只有在clk上升沿时才开始执行always语句块,否则不执行。
这种时序控制是always语句最常用的。
(3) always的时序控制除沿触发时序控制外,还可以是电平触发的,也可以是单个信号或多个信号,中间需要用关键字or或“,”连接,如:
always@(posedgeclockorposedgereset)//由两个沿触发的always块
begin
…
end
always@(aorborc)//由多个电平触发的always块
begin
…
end
沿触发的always块常用来描述时序逻辑,如果符合可综合风格要求,则可用综合工具将其自动转换为表示时序逻辑的寄存器组和门级逻辑;而电平触发的always块常用来描述一般组合逻辑和带锁存器的组合逻辑,如果符合可综合风格要求,则可转换为表示组合逻辑的门级逻辑或带锁存器的组合逻辑。
一个模块中可以有多个always块,它们都是并行运行的。
从综合结果来看,本例实现了一个上升沿触发的D触发器。
【例3-16】阻塞赋值方式描述的移位寄存器1。
moduleblock1(Q0,Q1,Q2,D,clk);
outputQ0,Q1,Q2;
inputclk,D;
regQ0,Q1,Q2;
always@(posedgeclk)
begin
Q2=Q1;//注意赋值语句的顺序
Q1=Q0;
Q0=D;
end
endmodule
综合结果如图3-13所示。
【例3-17】阻塞赋值方式描述的移位寄存器2。
moduleblock2(Q0,Q1,Q2,D,clk);
outputQ0,Q1,Q2;
inputclk,D;
regQ0,Q1,Q2;
always@(posedgeclk)
begin
Q1=Q0;//该句与下句的顺序与例3-16颠倒
Q2=Q1;
Q0=D;
end
endmodule
综合结果如图3-14所示。
【例3-18】阻塞赋值方式描述的移位寄存器3。
moduleblock3(Q0,Q1,Q2,D,clk);
outputQ0,Q1,Q2;
inputclk,D;
regQ0,Q1,Q2;
always@(posedgeclk)
begin
Q0=D;//3条赋值语句的顺序与例3-16完全颠倒
Q1=Q0;
Q2=Q1;
end
endmodule
综合结果如图3-15所示。
【例3-19】非阻塞赋值方式描述的移位寄存器1。
modulenon_block1(Q0,Q1,Q2,D,clk);
outputQ0,Q1,Q2;
inputclk,D;
regQ0,Q1,Q2;
always@(posedgeclk)
begin
Q1<=Q0;
Q2<=Q1;
Q0<=D;
end
Endmodule
【例3-20】非阻塞赋值方式描述的移位寄存器2。
modulenon_block2(Q0,Q1,Q2,D,clk);
outputQ0,Q1,Q2;
inputclk,D;
regQ0,Q1,Q2;
always@(posedgeclk)
begin
Q0<=D;//3条赋值语句的顺序与例3-19完全颠倒
Q2<=Q1;
Q1<=Q0;
end
endmodule
例3-16~例3-20的程序说明:
(1) 5个例题的设计目标均是实现3位移位寄存器,但从综合结果可以看出例3-17和例3-18没有实现设计目标。
(2)Q2=Q1;这种赋值方式称为阻塞赋值,Q2的值在赋值语句执行完成后立刻就改变,而且随后的语句必须在赋值语句执行完成后才能继续执行。
所以当例3-18中的三条语句Q0=D;Q1=Q0;Q2=Q1;执行完后,Q0、Q1、Q2的值都变化为D的值,也就是说,D的值同时赋给了Q0、Q1、Q2,参照其综合结果能更清晰地看到这一点。
例3-16和例3-17可通过同样的分析得出与综合结果一致的结论。
(3)Q2<=Q1;这种赋值方式称为非阻塞赋值,Q2的值在赋值语句执行完后并不会立刻就改变,而是等到整个always语句块结束后才完成赋值操作。
所以当例3-20中的三条语句Q0<=D;Q2<=Q1;Q1<=Q0;执行完后,Q0、Q1、Q2的值并没有立刻更新,而是保持了原来的值,直到always语句块结束后才同时进行赋值,因此Q0的值变为了D的值,Q2的值变为了原来Q1的值,Q1的值变为了原来Q0的值(而不是刚刚更新的Q0的值D),参照其综合结果能更清晰地看到这一点。
例3-19可通过同样的分析得出与综合结果一致的结论。
(4)例3-16~例3-18采用的是阻塞赋值方式,可以看出阻塞赋值语句在always块语句中的位置对其结果有影响;例3-19和例3-20采用的是非阻塞赋值方式,可以看出非阻塞赋值语句在always块语句中的位置对其结果没有影响。
因此,在使用赋值语句时要注意两者的区别与联系。
【例3-22】使用always语句描述具有同步复位和同步置位功能的D触发器。
modulemy_dff(q,clk,set,clr,d);
inputclk,d,set,clr;
outputq;regq;
always@(posedgeclk)
begin
if(set)
q<=1;
elseif(!
clr)
q<=0;
elseq<=d;
end
endmodule
程序说明:
(1)本程序使用了第3种if语句形式:
if…elseif…else的条件语句,在时钟上升沿时刻,首先判断置位信号set是否有效,若有效则将D触发器输出置1,否则判断复位信号clr是否有效,若有效则将D触发器输出置0,否则将数据d赋予D触发器输出。
(2) if(set)等同于if(set==1),elseif(!
clr)等同于if(clr!
=1),VerilogHDL允许采用这样的表达式简写方式。
(3) always@(posedgeclk)语句表示只有在clk上升沿时才开始执行always语句块,否则不执行。
所以,D触发器置位和复位为同步置位和同步复位。
1.加法器
例4-3实现了N位加法器。
【例4-3】参数型N位加法器。
moduleadd_N(X,Y,sum,co);
parameterN=8;
input[N-1:
0]X,Y;
output[N-1:
0]sum;
outputco;
assign{co,sum}=X+Y;
endmodule
程序说明:
(1)程序中,X和Y分别为加数和被加数,sum和co分别为本位和及进位。
(2)本例使用数据流建模实现,在综合时会自动映射为QuartusⅡ自带的加法器宏功能模块。
【例4-12】一位D触发器。
实现方式一:
modulemy_dff(clk,d,q);
inputclk;
inputd;
outputregq;
always@(posedgeclk)
q<=d;
endmodule
实现方式二:
调用QuartusⅡ软件中的dff模块实现D触发器。
modulemy_dff(clk,d,q);
inputclk;
inputd;
outputq;
dffmy_dff(d,clk,,,q);
endmodule
2.寄存器
【例4-13】参数型n位寄存器。
(1)一般寄存器。
moduleregx(clk,d,q);
parameterN=8;
inputclk;
input[N-1:
0]d;
outputreg[N-1:
0]q;
always@(posedgeclk)
q<=d;
endmodule
(2)同步复位、同步置数、异步使能的寄存器。
moduleregister_N_0(D,Q,data,en,load,reset,clk);
parameterN=8;
inputen,load,reset,clk;
input[N-1:
0]D,data;
outputreg[N-1:
0]Q;
reg[N-1:
0]temp;
always@(posedgeclk)//同步复位、同步置数
begin
if(reset)temp<=0;
elseif(load)temp<=data;
elsetemp<=D;
end
always@(temp,en)//异步使能
begin
if(en)Q<=temp;
elseQ<='bz;
end
endmodule
②同步复位、同步置数要求复位或置数必须在时钟的上升沿发生。
③事实上,复位、置数、使能三个信号可以任意选取同步、异步两种方式之一,因此三个信号可以任意组合成8种不同功能的电路,如异步复位、同步置数、异步使能电路等。
(3)异步复位、同步置数、异步使能的寄存器。
moduleregister_N(D,Q,data,en,load,reset,clk);
parameterN=8;
inputen,load,reset,clk;
input[N-1:
0]D,data;
outputreg[N-1:
0]Q;
reg[N-1:
0]temp;
always@(posedgeclkorposedgereset)//异步复位、同步置数
begin
if(reset)temp<=0;
elseif(load)temp<=data;
elsetemp<=D;
end
always@(temp,en)//异步使能
begin
if(en)Q<=temp;
elseQ<='bz;
end
endmodule
②异步复位、同步置数要求只要复位信号为有效电平就复位,而置数必须在时钟的上升沿发生。
3.移位寄存器
例4-14设计了一个n位移位寄存器,该寄存器具有左移、右移、置数功能。
【例4-14】参数型n位移位寄存器。
//8位CPU中常用的移位寄存器模块
moduleshift_N(clk,Ci,mode,D,Q,Co);
inputclk,Ci;//时钟和移位输入
input[2:
0]mode;//移位模式控制字
input[N-1:
0]D;//待加载移位的数据
output[N-1:
0]Q;//移位数据输出
outputregCo;//移位输出
reg[N-1:
0]temp;
parameterN=8;//8位移位寄存器
always@(posedgeclk)
begin
case(mode)
3'b000:
begintemp<=D;end//加载待移数
3'b001:
begintemp[0]<=Ci;
temp[N-1:
1]<=temp[N-2:
0];Co<=temp[N-1];//带进位循环左移
end
3'b010:
begintemp[0]<=temp[N-1];
temp[N-1:
1]<=temp[N-2:
0];//自循环左移
end
3'b011:
begintemp[N-1]<=temp[0];
temp[N-2:
0]<=temp[N-1:
1];//自循环右移
end
3'b100:
begintemp[N-1]<=Ci;
temp[N-2:
0]<=temp[N-1:
1];Co<=temp[0];//带进位循环右移
end
default:
temp<=temp;//保持
endcase
end
assignQ=temp;//移位后输出
endmodule
程序说明:
(1)移位寄存器可以有多种工作模式,这由移位模式控制字mode来控制。
(2)仿真波形如图4-14所示。
从图中可以看出,第1个时钟上升沿时,mode为3'b000,该模式为加载待置数,因此D的值被装载进Q。
第2个时钟上升沿时,mode为3'b001,该模式为带进位循环左移,因此D的值循环左移,同时D的最低位取此时刻Ci的值。
从仿真波形图中可以看出,本段代码实现了题目要求的功能。
【例4-16】十进制加法计数器,带有异步复位和同步时钟使能功能。
modulecnt10(clk,rst,en,Q,cout);
inputclk,rst,en;
outputreg[N-1:
0]Q;
outputcout;
parameterN=4;
always@(posedgerst,posedgeclk)
begin
if(rst)//计数器异步复位
Q<=0;//为了能生成诸如触发器一类的时序逻辑,建议使用非阻塞赋值
else
if(en)//计数器同步使能
if(Q==9)Q<=0;
elseQ<=Q+1;
end
assigncout=(Q==0)?
1'b1:
1'b0;//计数器计满溢出后cout输出1
endmodule
【例2-5】一个使用多种数据类型的程序片断。
integerM;
reg[3:
0]A;
reg[7:
0]B;
initial
begin
M=-1;//M为32位整数,采用补码形式存放,补码形式为32个1
A=M;//A为4位无符号数,截取M的低4位赋给A:
1111
B=A;//B为8位无符号数,将A零扩展后送给B:
00001111
B=A+14;//B为29:
11101
A=A+14;//A+14为29,截取低4位送给A:
1101
end
【例2-6】存储器建模。
modulerom(clk,rst,rd,data,addr);
inputclk,rst,rd;//rd为读使能信号
input[2:
0]addr;//建立的存储器有8个地址空间
outputreg[7:
0]data;//数据是8位的
reg[7:
0]memory[