|
Verilog中阻塞和非阻塞赋值原则
2017-02-01 模电数电
阻塞赋值
阻塞赋值操作符 =
阻塞赋值是指当前的赋值语句阻断了其后的语句,也就是说后面的语句必须等到当前的赋值语句执行完毕才能执行。阻塞赋值可以看成是一步完成的,即计算等号右边的值并同时赋给左边变量。
例如:采用阻塞赋值实现四选一多路选择器。
module mux4_to_1 (out, i0, i1, i2, i3, s0, s1);
output out;
input i0,i1,i2,i3,s0,s1;
reg out;
always @(s1 or s0 or i0 or i1 or i2 or i3)
begin
case ( {s1, s0} )
2'b00: out = i0;
2'b01: out = i1;
2'b10: out = i2;
2'b11: out = i3;
default: out = 1'bx;
endcase
end
endmodule
非阻塞赋值
非阻塞赋值操作符 <=
非阻塞赋值是指在过程块中,当前的赋值语句不会阻断其后的语句。非阻塞赋值操作只能用于对寄存器型变量进行赋值,因此只能用在"initial"块和"always"块等过程块中。非阻塞赋值不允许用于连续赋值。
例如:采用非阻塞赋值实现移位寄存器。
module mux4_to_1 (q3, d, clk);
output [7:0] q3;
input [7:0] d;
input clk;
reg [7:0] q3, q2, q1;
always @(posedge or clk)
begin
q1 <= d;
q2 <= q1;
q3 <= q2;
end
endmodule
简单理解就是,阻塞赋值是按需执行,非阻塞赋值是并行执行。
在编写Verilog代码时,要牢记:
时序电路建模时,用非阻塞赋值。
锁存器电路建模时,用非阻塞赋值。
用always块建立组合逻辑模型时,用阻塞赋值。
在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值。
在同一个always块中不要既用阻塞又用非阻塞赋值。
不要在一个以上的always块中为同一个变量赋值。
用$strobe系统任务来显示用非阻塞赋值的变量值。
在赋值时,不要使用#0延迟。
应用举例
用always块描述组合逻辑时,应采用阻塞赋值语句。例如,用阻塞赋值实现四输入与或非门。
module ex4_to_1 (y, a, b, c, d);
output y;
input a, b, c, d;
reg y, tmp1, tmp2;
always @(a or b or c or d)
begin
tmp1 = a & b;
tmp2 = c & d;
y = tmp1 | tmp2
end
endmodule
当把组合逻辑和时序逻辑写入到同一个always块中时,应遵从时序逻辑建模原则,使用非阻塞赋值。例如,实现一个具有异步复位功能的触发器,该触发器利用时钟上升沿触发,输出q等于输入a、b的异或。
module nbex2 (q, a, b, clk, rst_n);
output q;
input a, b, clk, rst_n;
reg q;
always @(posedge clk or negedge rst_n)
if ( !rst_n ) q <= 1'b0; //时序逻辑
else q <= a^b; //异或,组合逻辑
endmodule
也可以将组合和时序逻辑分写在两个always块中,不要在同一个always块中同时使用阻塞和非阻塞赋值。
module nbex2 (q, a, b, clk, rst_n);
output q;
input a, b, clk, rst_n;
reg q, y;
always @(a or b)
y = a^b;
always @(posedge clk or negedge rst_n)
if ( !rst_n ) q <= 1'b0;
else q <= y;
endmodule
在一个以上的always块中对同一个变量进行多次赋值可能会导致竞争冒险,即使采用非阻塞赋值也可能会产生竞争冒险。
用Verilog HDL描述加法器电路很简单,综合器可以根据用户的配置自动综合成相应的加法器,比如配置成超前进位的并行加法器。四位加法器的行为描述方式如下:
module add_4 (cout, sum, a, b, cin);
output cout;
output [3:0] sum;
input [3:0] a,b;
input cin;
assign {cout, sum} = a + b + cin;
endmodule
用Verilog HDL描述八位乘法器
module mult_8 (X, Y, Product);
input [7:0] X, Y;
output [15:0] Product;
assign Product = X * Y;
endmodule
您也可以自己具体描述乘法器的实现方式:
parameter size = 8, longsize = 16;
reg [size:1] opa, opb;
reg [longsize:1] result;
begin: mult
integer bindex;
result = 0;
for (bindex=1; bindex<=size; bindex=bindex+1)
if (opb[bindex])
result = result + (opa<<(bindex-1));
end
3-8译码器设计实例(无使能控制端)
module decoder (out, in);
output [7:0] out;
input [2:0] in;
assign out = 1'b1<<in;
endmodule
简单的比较器设计实例
module compare (equal, a, b);
parameter size = 1;
output equal;
input [size-1:0] a, b;
assign equal = (a==b)? 1 : 0;
endmodule
多路选择器设计实例
module mux2 (out, a, b, sel);
output out;
input a, b,sel;
reg out;
always @( a or b or sel )
begin
case (sel)
1'b0: out = a;
1'b1: out = b;
default: out = 'bx;
endcase
end
endmodule
奇偶校验位生成器设计实例
module parity (even_bit, odd_bit, input_bus);
output even_bit, odd_bit;
input [7:0] input_bus;
assign odd_bit = ^input_bus;
assign even_bit = ~odd_bit;
endmodule
三态输出驱动器设计实例
module trist1 (out, in, enable);
output out;
input in, enable;
assign out = enable? in : 'bz;
endmodule
三态双向驱动器设计实例
module bidir (tri_inout, out, in, enable, b);
inout tri_inout;
output out;
input in, enable, b;
assign tri_inout = enable? in : 'bz;
assign out = tri_inout ^ b;
endmodule
触发器设计实例
module dff (q, data, clk);
output q;
input data, clk;
reg q;
always @( posedge clk )
begin
q <= data;
end
endmodule
电平敏感型锁存器设计实例
module latch1 (q, data, clk);
output q;
input data, clk;
assign q = clk ? data : q;
endmodule
带置位和复位端的电平敏感型锁存器设计实例
module latch2 (q, data, clk, set, reset);
output q;
input data, clk, set, reset;
assign q = reset? 0 : (set? 1 : (clk ? data : q ));
endmodule
移位寄存器设计实例
module shifter ( din, clk, clr, dout );
input din, clk, clr;
output [7:0] dout;
reg [7:0] dout;
always @( posedge clk )
begin
if (clr) dout <= 8'b0;
else
begin
dout <= dout << 1;
dout[0] <= din;
end
end
endmodule
8位计数器设计实例
module counter1 ( out, cout, data, load, cin, clk );
output [7:0] out;
output cout;
input [7:0] data;
input load, cin, clk;
reg [7:0] out;
always @( posedge clk )
begin
if (load)
out <= data;
else
out <= out + cin;
end
assign cout = (&out) & cin;
endmodule
Verilog HDL 三段式状态机模版
在用Verilog编写状态机时,建议分为三个alwaysk块完成。
三段式描述风格虽然代码结构复杂了一些,但这样做容易发现问题和改正模块编写中出现的问题。
//第一个always块,描述在时钟驱动下次态迁移到现态。
always @ (posedge clk or negedge rst_n) //异步复位
if (!rst_n)
current_state<= IDLE;
else
current_state<= next_state; //注意,使用非阻塞赋值
//第二个always块,描述状态的转移关系
always @ (current_state) //电平触发,将现态作为敏感信号
begin
case (current_state)
S1: next_state = S2; //阻塞赋值
S2: next_state = S3; //阻塞赋值
……
default: next_state = ‘bx; //对多余状态的处理
endcase
end
//第三个always块,描述每个状态对应的输出
always @ (current_state)
begin
...
case (current_state)
S1: out1 = 1'b1; //对输出进行赋值
S2: out2 = 1'b1;
……
default: ... // 避免综合出锁存器。
endcase
end
这种风格的描述比较适合大型的状态机,查错和修改也比较容易,很多公司的综合器都建议使用这种风格来描述状态机,俗话说,学好数电,风光无限。 |
|