阻塞赋值操作符 = 阻塞赋值是指当前的赋值语句阻断了其后的语句,也就是说后面的语句必须等到当前的赋值语句执行完毕才能执行。阻塞赋值可以看成是一步完成的,即计算等号右边的值并同时赋给左边变量。 例如:采用阻塞赋值实现四选一多路选择器。 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块描述组合逻辑时,应采用阻塞赋值语句。例如,用阻塞赋值实现四输入与或非门。 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
|
|
来自: 丁毛毛qxevv5ba > 《FPGA基础》