引言:本文继续介绍Verilog HDL基础知识,重点介绍赋值语句、阻塞与非阻塞、循环语句、同步与异步、函数与任务语法知识。
1. 赋值语句
在Verilog中,有两种进行赋值的方法,即连续赋值语句和过程赋值语句(块)。
1.1 连续赋值语句
连续赋值用于表示组合逻辑。左侧必须是Net数据类型即wire和tri类型。
连续赋值始终处于活动状态,这意味着,对右侧值的任何更改都会导致赋值左侧值被更新。
//连续赋值方法1
wire [15:0] adder_out = mult_out + out;
//连续赋值方法2
wire [15;0] adder_out;
assign adder_out = mult_out + out;
在仿真测试中,可以对连续赋值语句分配延迟,但在综合时,该延迟被忽略。
assign #5 adder_out = mult_out + out; //延迟5ns
1.2 过程赋值语句(块)
过程赋值语句包括initial和always两种。intial语句用于初始化仿真使用,always使用行为功能描述电路功能。
每个always和initial块都是并行运行的,但每个块内部是顺序执行的。
图1:过程赋值语句示例
2. 阻塞赋值和非阻塞赋值
always和initial过程块有种类型赋值即阻塞赋值(=)和非阻塞赋值(<=)。
过程赋值的左侧必须是变量数据类型,可以是reg、integer、real或time或realtime。赋值的右侧可以是任何有效的表达式或信号。
阻塞赋值和非阻塞赋值示例如下图所示。
图2:阻塞赋值和非阻塞赋值示例
如图2左侧阻塞赋值,两条语句同时执行,延迟#5时,a=b=2,再延迟#10时,c=a=2,即赋值语句立即执行,执行完毕后才执行下一条语句,左侧值在赋值语句执行完后立即改变。如图2右侧非阻塞赋值,延迟#5时,a=b=2,而再延迟#5时,c=a=1,即c值并未立即为a的新值。
图3展示了阻塞赋值和非阻塞赋值综合后的RTL电路举例。
图3:阻塞赋值和非阻塞赋值综合后的RTL电路举例
阻塞赋值和非阻塞赋值使用规则
组合电路一般使用阻塞赋值,时序电路使用非阻塞赋值,这可以避免混淆,提高代码可读性。
3. 两种类型RTL处理
组合逻辑处理:对所有输入列表信号敏感,用于组合逻辑。
图4:组合逻辑处理
时序逻辑处理:对时钟信号或者控制信号沿敏感(通过添加posedge或者negedge关键字),用于时序逻辑。
图5:时序逻辑处理
4. 过程块语句
initial和always过程块中有三种常用语句:if-esle、case和循环语句。
4.1 if-else语句
语句以关键字if开头,后跟一个条件,然后是条件为true时要执行的语句序列。else子句(如果存在)将在条件为false时执行。
图6:if-else示例
可以看到if语句合成为两个输入复用器。可以看到,多个if语句会导致多个复用,从而产生优先级。如果不需要在你的电路中进行优先级排序,case语句会更有效率。
4.2 case语句
在case语句中,所有条件都将根据表达式进行检查。这导致一个多路复用器具有多个输入。
图7:case语句示例
casez语句将表达式或条件中的所有'z'值视为不关心。
casex语句将所有“x”和“z”值视为不关心,而不是逻辑值。
图8:casez和casex语句示例
4.3 循环语句
(1)forever循环语句示例
initial begin
clk = 0;
forever #25 clk= ~clk;
end
以上示例为周期为#50的clk时钟,注意该语句不可综合,只能用于仿真。
(2)repeat循环语句
if(rotate == 1)
repeat(8) begin //循环8次
temp = data[15];
data = {data <<1,temp};
end
repeat语句可以设置循环的次数,该语句可以用于综合,要具有明确的循环表达式和循环条件。
(3)while循环语句
initial begin
cnt = 0;
while(cnt < 101) begin
$display("cnt = %d",cnt);
cnt = cnt + 1;
end
end
cnt循环计数100次,在cnt为101时退出循环,该语句可以用于综合,要具有明确的循环表达式和循环条件。
(4)for循环语句
integer i;
always @(inp,cnt) begin
result[7:4] = 0;
result[3:0] = inp;
if (cnt == 1) begin
for (i=4;i<=7; i=i+1) begin
result[i] = result[i-4];
end
end
end
以上for循环语句实现4bit左移位功能,for语句可以进行综合,实现硬件电路。
5. 同步逻辑和异步逻辑
图9:同步逻辑及异步逻辑
always敏度列表表达方式将导致控制信号是同步的或异步的。上图中左侧代码具有同步预置和触发器清除功能,该代码仅在时钟上升沿敏感,右侧代码敏感列表包含时钟和异步清除信号,如果aclr上升沿触发,就会引起q清零,不受时钟控制。
6. 函数(function)和任务(task)
Verilog HDL有两种子程序,函数和任务。它们都必须在模块中定义。函数根据其输入返回一个值。函数通常产生组合逻辑,并用于assign表达式中。
任务和函数对于模块中的重复代码非常有用。它们提高了代码的可读性。
图10:function示例
图11:task示例
function和task的异同点
相同点:
(1)任务和函数必须在模块内定义,其作用范围仅适用于该模块,可以在模块内多次调用;
(2)任务和函数中可以声明局部变量,如寄存器,时间,整数,实数和事件,但是不能声明线网类型的变量;
(3)任务和函数中只能使用行为级语句,但是不能包含always和initial块,设计者可以在always和initial块中调用任务和函数。
差异点:
(1)函数能调用另一个函数,但是不能调用任务,任务可以调用另一个任务,也可以调用函数;
(2)函数总是在仿真时刻0开始 ,任务可以在非零时刻开始执行;
(3)函数一定不能包含任何延迟,事件或者时序控制声明语句,任务可以包含延迟,事件或者时序控制声明语句;
(4)函数至少要有一个输入变量,也可以有多个输入变量,任务可以没有或者有多个输入,输出,输入输出变量;
(5)函数只能返回一个值,函数不能有输出或者双向变量,任务不返回任何值,或者返回多个输出或双向变量值。
延伸阅读:Verilog HDL基础知识(一)
欢迎关注FPGA技术实战公众号,喜欢就多多转发吧!