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
从图中可以看出,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
从图中可以看出,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
注:笔者能力有限,如有不对请指出,谢谢!