d643189658 发表于 2017-8-11 16:18:15

Verilog中阻塞和非阻塞赋值原则

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 q3;
    input d;
    input clk;
    reg 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 clkornegedge 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 @(aorb)
      y = a^b;
    always @(posedge clkornegedge 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 sum;
    input a,b;
    input cin;

    assign {cout, sum} = a + b + cin;
endmodule


用Verilog HDL描述八位乘法器
module mult_8 (X, Y, Product);
    input X, Y;
    output Product;

    assign Product = X * Y;
endmodule

您也可以自己具体描述乘法器的实现方式:
    parameter size = 8, longsize = 16;
    reg opa, opb;
    reg result;

    begin: mult
         integer bindex;
         result = 0;
         for (bindex=1; bindex<=size; bindex=bindex+1)
             if (opb)
               result = result + (opa<<(bindex-1));
    end


3-8译码器设计实例(无使能控制端)
module decoder (out, in);
    output out;
    input in;

    assign out = 1'b1<<in;
endmodule


简单的比较器设计实例
module compare (equal, a, b);
    parameter size = 1;
    outputequal;
    input a, b;

    assign equal = (a==b)? 1 : 0;
endmodule


多路选择器设计实例
module mux2 (out, a, b, sel);
    outputout;
    inputa, 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);
    outputeven_bit, odd_bit;
    input input_bus;

    assign odd_bit = ^input_bus;
    assign even_bit = ~odd_bit;
endmodule


三态输出驱动器设计实例
module trist1 (out, in, enable);
    outputout;
    input in, enable;

    assign out = enable? in : 'bz;
endmodule

三态双向驱动器设计实例
module bidir (tri_inout, out, in, enable, b);
    inouttri_inout;
    outputout;
    input in, enable, b;

    assign tri_inout = enable? in : 'bz;
    assign out = tri_inout ^ b;
endmodule


触发器设计实例
module dff (q, data, clk);
    outputq;
    inputdata, clk;
    reg q;

    always @( posedge clk )
         begin
             q <= data;
          end   
endmodule

电平敏感型锁存器设计实例
module latch1 (q, data, clk);
    outputq;
    inputdata, clk;
    assign q = clk ? data : q;
endmodule

带置位和复位端的电平敏感型锁存器设计实例
module latch2 (q, data, clk, set, reset);
    outputq;
    inputdata, clk, set, reset;
    assign q = reset? 0 : (set? 1 : (clk ? data : q ));
endmodule


移位寄存器设计实例
module shifter ( din, clk, clr, dout );
    inputdin, clk, clr;
    output dout;
    reg dout;

    always @( posedge clk )
         begin
             if (clr) dout <= 8'b0;
             else
               begin
                     dout <= dout << 1;
                     dout <= din;
               end
          end   
endmodule


8位计数器设计实例
module counter1 ( out, cout, data, load, cin, clk );
    output out;
    output cout;
    input data;
    input load, cin, clk;
            reg 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

这种风格的描述比较适合大型的状态机,查错和修改也比较容易,很多公司的综合器都建议使用这种风格来描述状态机,俗话说,学好数电,风光无限。

chen 发表于 2017-8-11 21:10:08

:handshake

d643189658 发表于 2017-8-11 22:32:08

chen 发表于 2017-8-11 21:10


{:3_52:} {:3_52:} {:3_52:}
页: [1]
查看完整版本: Verilog中阻塞和非阻塞赋值原则