output Q0,Q1,Q2; input clk,D; reg Q0,Q1,Q2;
always @(posedge clk) begin Q1<=Q0; Q2<=Q1; Q0<=D; end
E ndmodule
【例3-20】 非阻塞赋值方式描述的移位寄存器2。 module non_block2(Q0,Q1,Q2,D,clk); output Q0,Q1,Q2; input clk,D; reg Q0,Q1,Q2;
always @(posedge clk) 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触发器。 module my_dff(q, clk, set, clr, d); input clk, d, set, clr; output q; reg q;
always @(posedge clk) begin if(set)
q<=1; else if (!clr) q<=0; else q<=d; end endmodule
程序说明:
(1) 本程序使用了第3种if语句形式:if?else if?else的条件语句,在时钟上升沿时刻,首先判断置位信号set是否有效,若有效则将D触发器输出置1,否则判断复位信号clr是否有效,若有效则将D触发器输出置0,否则将数据d赋予D触发器输出。 (2) if(set)等同于if(set==1),else if (!clr)等同于if(clr!=1),Verilog HDL允许采用这样的表达式简写方式。
(3) always @(posedge clk)语句表示只有在clk上升沿时才开始执行always语句块,否则不执行。所以,D触发器置位和复位为同步置位和同步复位。
1. 加法器
例4-3实现了N位加法器。
【例4-3】 参数型N位加法器。 module add_N( X, Y, sum, co); parameter N=8; input [N-1: 0] X, Y; output [N-1: 0] sum; output co;
assign { co, sum } = X + Y;
endmodule 程序说明:
(1) 程序中,X和Y分别为加数和被加数,sum和co分别为本位和及进位。
(2) 本例使用数据流建模实现,在综合时会自动映射为Quartus Ⅱ自带的加法器宏功能模块。
【例4-12】 一位D触发器。 实现方式一:
module my_dff(clk, d, q); input clk; input d;
output reg q;
always @(posedge clk) q <= d; endmodule
实现方式二:调用Quartus Ⅱ软件中的dff模块实现D触发器。 module my_dff(clk, d, q); input clk; input d; output q;
dff my_dff(d, clk, , , q); endmodule
2. 寄存器
【例4-13】 参数型n位寄存器。
(1) 一般寄存器。
module regx(clk, d, q); parameter N=8; input clk;
input[N-1:0] d;
output reg[N-1:0] q; always @(posedge clk) q <= d; endmodule
(2) 同步复位、同步置数、异步使能的寄存器。 module register_N_0(D, Q, data,en,load,reset,clk); parameter N=8;
input en,load,reset,clk; input [N-1: 0] D,data; output reg[N-1: 0] Q; reg[N-1: 0] temp;
always @(posedge clk) //同步复位、同步置数 begin
if(reset) temp<=0;
else if(load) temp<=data; else temp<=D;
end
always @(temp,en) //异步使能 begin
if(en) Q<=temp; else Q <= 'bz; end endmodule
② 同步复位、同步置数要求复位或置数必须在时钟的上升沿发生。
③ 事实上,复位、置数、使能三个信号可以任意选取同步、异步两种方式之一,因此三个信号可以任意组合成8种不同功能的电路,如异步复位、同步置数、异步使能电路等。
(3) 异步复位、同步置数、异步使能的寄存器。 module register_N(D, Q, data,en,load,reset,clk); parameter N=8;
input en,load,reset,clk; input [N-1: 0] D,data; output reg[N-1: 0] Q; reg[N-1: 0] temp;
always @(posedge clk or posedge reset) //异步复位、同步置数 begin
if(reset) temp<=0;
else if(load) temp<=data; else temp<=D; end
always @(temp,en) //异步使能 begin
if(en) Q<=temp; else Q <= 'bz; end endmodule
② 异步复位、同步置数要求只要复位信号为有效电平就复位,而置数必须在时钟的上升沿发生。
3. 移位寄存器
例4-14设计了一个n位移位寄存器,该寄存器具有左移、右移、置数功能。 【例4-14】 参数型n位移位寄存器。
//8位CPU中常用的移位寄存器模块 module shift_N(clk,Ci,mode,D,Q,Co); input clk,Ci; //时钟和移位输入 input[2:0] mode; //移位模式控制字 input[N-1:0] D; //待加载移位的数据 output[N-1:0] Q; //移位数据输出 output reg Co ; //移位输出 reg[N-1:0] temp;
parameter N=8; //8位移位寄存器 always @(posedge clk)
begin case(mode)
3'b000: begin temp <=D; end //加载待移数 3'b001: begin temp[0] <= Ci ; temp[N-1:1] <= temp[N-2:0]; Co<=temp[N-1]; //带进位循环左移 end 3'b010: begin temp[0] <= temp[N-1]; temp[N-1:1] <= temp[N-2:0]; //自循环左移 end
3'b011: begin temp[N-1] <= temp[0]; temp[N-2:0] <= temp[N-1:1]; //自循环右移 end 3'b100: begin temp[N-1] <= Ci ; temp[N-2:0] <= temp[N-1:1]; Co<=temp[0]; //带进位循环右移 end default: temp <= temp ; // 保持 endcase end
assign Q = temp; //移位后输出 endmodule 程序说明:
(1) 移位寄存器可以有多种工作模式,这由移位模式控制字mode来控制。
(2) 仿真波形如图4-14所示。从图中可以看出,第1个时钟上升沿时,mode为3'b000,该模式为加载待置数,因此D的值被装载进Q。第2个时钟上升沿时,mode为3'b001,该模式为带进位循环左移,因此D的值循环左移,同时D的最低位取此时刻Ci的值。从仿真波形图中可以看出,本段代码实现了题目要求的功能。
【例4-16】 十进制加法计数器,带有异步复位和同步时钟使能功能。 module cnt10(clk,rst,en,Q,cout); input clk,rst,en;
output reg[N-1:0] Q; output cout; parameter N=4;
always @(posedge rst, posedge clk) begin
if(rst) //计数器异步复位 Q <= 0; //为了能生成诸如触发器一类的时序逻辑,建议使用非阻塞赋值 else if(en) //计数器同步使能 if(Q==9) Q <= 0; else Q <= Q + 1; end
assign cout= (Q==0)? 1'b1:1'b0; //计数器计满溢出后cout输出1 endmodule