Verilog HDL之于FPGA--阻塞与非阻塞赋值

Verilog HDL之于FPGA

阻塞与非阻塞赋值

Verilog HDL硬件描述语言:
Verilog HDL硬件描述语言脱胎于C语言,却与C语言执行的方式不同。
Verilog 有并行和顺序执行两种方式,
而C语言程序只能从main函数进入,然后开始顺序执行。
并行执行:
Verilog HDL在模块与模块之间是并行执行。

module test(clk,a,b,c);
    input clk;
    output a,b,c;

    reg a=0,b=0;
    /*第一个模块*/
    always @(posedge clk)
        begin
            a <= 1;
        end
    /*第二个模块*/
    always @(posedge clk)
        begin
            b <= 1;
        end
    /*第三个模块*/
    assign c = 1;

endmodule

注:两个模块之间不能同时对一个寄存器进行操作,比如:

always @(posedge clk)
        a <= 0;
    always @(posedge clk)
        a <= 1;
        ….

顺序执行:

Verilog HDL 在always块里面是顺序的;
但是这种顺序执行的结果也不一定跟C语言同样。
Verilog的阻塞与非阻塞问题,这是个难点。(笔者曾经在一段很长的时间内一直搞混,如果习惯用C语言的同学理解起来就会有一点点。。。那个,虽然笔者是先学verilog 后学C语言,但是开始时理解也很吃力。)

如果在块里面用“=”时也就是用阻塞赋值功能,这样代码执行就会以顺序执行方式执行,比如:

`timescale 1ns / 1ps
module adder(clk,out);
    input clk;
    output [3:0]out;

    reg [3:0]out = 4'b0; 
    always @(posedge clk)   
       begin
          out = out + 1'b1;
          out = out + 1'b1;
       end

endmodule

Verilog HDL之于FPGA--阻塞与非阻塞赋值_第1张图片

从图中可以看出,out的值一个周期后为2。
分析:
开始out = out + 1’b1; out 本身值加1然后赋给out本身,这时 out 的值已经变为1,
然后再一句 out = out + 1’b1;
也是同上面一样,out本身的值加1然后赋给out本身,
本来out的值变为1了,加上1就是2了,所以out的值在一个周期结束后更新为 2;
这跟C执行的是一样。

但是,如果我们用 “<=” 时也就是非阻塞赋值时情况就不同了,所有的值要等到一个时钟周期结束后值才会被改变。比如:

`timescale 1ns / 1ps
module adder(clk,out);
input clk;
output [3:0]out;

reg [3:0]out= 4’b0;
always @(posedge clk)
    begin
        out <= out + 1’b1;
        out <= out + 1’b1;
    end

endmodule

Verilog HDL之于FPGA--阻塞与非阻塞赋值_第2张图片

从图中可以看出,out的值一个周期后为1。
分析:
开始out <= out + 1’b1;
右边 out 本身值加1,右边值为1,
但这时右边的值还没赋给左边out
(out的值还是初始值0,这个跟阻塞赋值不同,值的改变在块结束后立刻更新),
然后再一句 out <= out + 1’b1;
(在块里面代码是顺序的,如果出现两句代码对一个reg的值进行改变,虽然代码都运行了,但最终的值还是最后运行的那一句代码的值,也就是说前面的代码都无效了);
但是这时由于上面out的值没有被赋值,
所以右边out的值,还是初始时的0,然后加1 ,右边的值还是1,
然后一个周期后,更新了寄存器的值于是左边out的值变为1了,于是out的值最终为1,后面几个周期同理累加1。
(这个好好理解,如果不理解,后面写多代码就会明白的,不过一般在always块里面都是用非阻塞赋值”<=”,阻塞赋值一般用在assign语句中)

测试台编写testbench文件编写

`include "adder.v"
module adder_test();
    reg clk;
    wire [3:0]out;

   /*实例化adder 模块,采用位置对应实例化*/
   adder u0(clk,out);

   /*每10个时钟单位clk翻转一次*/
   always #10 clk = ~clk;

   /*初始化*/
   initial begin   
      clk = 0;
   end

endmodule

注:笔者能力有限,如有不对请指出,谢谢!

你可能感兴趣的:(FPGA现场可编程门阵列)