“ 本文主要分享了在Verilog设计过程中一些经验与知识点,主要包块语句、阻塞赋值和非阻塞赋值以及结构说明语句(initial, always, task, function)。”
01
—
块语句
顺序块 begin…end
块内的语句是按照顺序执行的;
块内的每条语句延时控制都是相对于上条语句结束的时刻;
仿真时,执行到最后一条语句该语句块执行结束。
并行块 fork…end
块内的语句是按照独立的同时开始执行的;
块内的每条语句延时控制都是相对于程序进入该语句块的时刻而言;
仿真时,所需最长时间的语句执行结束后,该语句块执行结束。
例:
仿真结果如下:reg [7:0] l1,l2;
reg [7:0] k1,k2;
initial
begin
l1 = 0; l2 = 0;
#15 l1 = 2;
#10 l2 = 8;
end
initial
fork
k1 = 0; k2 = 0;
#15 k1 = 2;
#10 k2 = 8;
join
从仿真结果可以看出:在顺序块中,15ns的时候,l1被赋值为8’h2,在25ns的时候,l2被赋值为8’h8;而在并行块中,10ns的时候,k2被赋值为8’h8,在15ns的时候,k1被赋值为8’h2。可以很容易明白顺序块和并行块的特性。
02
—
阻塞赋值和非阻塞赋值
阻塞赋值(Blocking)
非阻塞赋值(Non_Blocking)
例1:组合逻辑中的阻塞与非阻塞
阻塞代码如下:仿真结果如下:always@(a,b,c,d)
begin
i1 = a & b;
i2 = c & d;
i3 = i1 & i2;
end
非阻塞代码如下:
always@(a,b,c,d)
begin
i1 <= a & b;
i2 <= c & d;
i3 <= i1 & i2;
end
仿真结果如下:
可以看出i1和i2在阻塞和非阻塞中结果相同,但是i3的结果却不同。这是因为在阻塞赋值中,i3的赋值使用的是i1和i2更新后的值,而非阻塞赋值中i3的赋值则使用的是i1和i2更新前的值。要想解决这一问题,则需要将“always@(a,b,c,d)”改成“always@(a,b,c,d,i1,i2)”代码如下:
仿真结果如下:always@(a,b,c,d,i1,i2)
begin
a & b; =
i2 <= c & d;
i3 <= i1 & i2;
end
综上,组合逻辑中更适合用阻塞赋值语句。
例2:时序逻辑中的阻塞和非阻塞。
以反馈振荡器的代码为例。非阻塞赋值代码:
always@(posedge clk,posedge rst)
begin
if(rst) a1 <=0;
else a1 <=a2;
end
always@(posedge clk,posedge rst)
begin
if(rst) a2 <=1;
else a2 <=a1;
end
阻塞赋值代码:
always@(posedge clk,posedge rst)
begin
if(rst) b1 = 0;
else b1 = b2;
end
always@(posedge clk,posedge rst)
begin
if(rst) b2 = 1;
else b2 = b1;
end
仿真结果如下:
可以看出阻塞赋值语句并不能达到我们想要的效果;而且综合后阻塞赋值语句中,无法确定哪个always块中的时钟沿先到达,哪个always块中的时钟后到达,所以存在一个冒险竞争的问题。综上,时序逻辑中更适合用非阻塞赋值语句。
03
—
结构说明语句(initial, always, task, function)
语句initial
一般initial语句用于测试文件的编写;但是随着编译器的进步,现在也可以综合,常用于一些变量的初始化。无论是用在什么场景,initial语句只执行一次。initial begin
// Add code here
end
语句always
例1:生成仿真时的信号波形
always可以用于仿真时的波形生成:always #5 clk = ~clk;这个例子就形成了一个周期为10ns(时间单位ns根据`timescale确定)的方波,常用来描述时钟信号(如果将#5去掉,那么会生成一个延迟为0的无限循环跳变过程,会发生仿真锁死,这是不推荐的)。仿真结果如下:
例2:
实现锁存器和触发器
多个敏感事件可以用“or”或者“,”区分(rst为复位信号,可以是posedge也可以是negedge)。always@(posedge clk or posedge rst)
begin
if(rst) cnt <= 0;
else cnt <= cnt + 1;
end
或
always@(posedge clk , posedge rst)
begin
//add codes
end
例3:实现组合逻辑
利用always实现组合逻辑时,要将所有的信号放进敏感列表,而时序逻辑中则不需要。上面的代码表示,a、b、c中任意电平发生变化,begin…end语句就会被触发。仿真结果如下所示:always@(a or b or c)
begin
x = x+1;
end
如上所示,因为敏感列表比较长,容易写错,所以Verilog又提供了两个特殊的符号:@*和@(*)。简化代码如下:always@(a or b or c or d or e)
begin
out = a + b + c + d + e;
end
仿真结果如下图所示:always@(*)
begin
a + b + c + d + e; =
end
注意:always模块内被赋值的每一个信号都必须定义为reg型 。
语句task
task <任务名>;
<端口及数据类型声明语句>;
<语句1>;
…
<语句n>;
endtask
示例代码如下:
仿真结果如下图所示:reg [7:0] j,k,i,x;
clk or posedge rst)
begin
if(rst)
begin
i <= 0;
j <= 0;
k <= 0;
x <= 0;
task1(i,j,k);
end
else
begin
i <= i + 1;
x <= i + 10;
task1(i,j,k);
end
end
task task1;
input [7:0] i;
output [7:0] j1;
output [7:0] k1;
begin
j1 = i + 10;
k1 = i + 11;
end
endtask
语句function
语句function的定义:
function<返回值的类型或范围>(函数名)
<端口说明语句>
<变量类型说明语句>
begin
…
end
endfunction
示意代码如下:
reg [7:0] i,j;
reg [8:0] sum_data;
clk or posedge rst)
begin
if(rst)
begin
i <= 100;
j <= 31;
sum_data <= sum(i,j);
end
else
begin
i <= i + 1;
j <= j + 2;
sum_data <= sum(i,j);
end
end
function [8:0] sum;
input [7:0] i1;
input [7:0] j1;
begin
sum = i1 + j1;
end
endfunction
仿真结果如下图所示:
注意:initial、always、task和function都是可以综合的。
审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !