verilog初识-initial、always、task、function
1.initial
一个程序中可以有多个initial语句块,多个initial说明语句并行执行,一个initial语句块仅执行一次。
initial多用于对变量的初始化。
如:(1)用initial块对存储器变量初始化
inital
begin
areg = 0; //初始化寄存器变量areg
for(index = 0; index < size; index = index + 1)
meory[index] = 0; //初始化memory变量
end
(2)用initial语句生成激励波形
initial
begin
inputs = b'0000; //初始时刻inputs为0
#10 inputs = b'1010; //在第10个单位时间后改变inputs的值
#10 inputs = b'0101; //在第20个单位时间后改变inputs的值
end
2.always
一个程序中可以有多个always语句块,每个always说明语句在仿真的开始同时执行;always语句不断重复地执行,直到仿真结束。always语句后紧跟的过程块是否运行,要看它的触发条件是否满足,一旦触发条件满足,则运行过程块,直至仿真过程结束。
其声明结构如下:
always (时序控制) 语句
always如果没有时序控制,会使仿真器产生死锁。如:
always areg = ~areg;
这条语句会生成一个0延迟的无限循环跳变过程,会导致仿真器发生死锁。
正确的使用方法如:
always #period areg = ~arge;
这条语句生产了一个周期位2*period的无限延续的波形信号,常用这种方法来描述时钟信号。
如:
reg [7:0] counter;
reg tick;
always @(posedge areg)
begin
tick = ~tick; //在每一个arge信号上升沿到来时,tick翻转,同时counter计数加1
counter = counter + 1;
end
always块的事件控制:
- OR事件控制(其中“or”也可以使用“,”进行替换)
- 使用电平触发的always语句块:
如:描述一个异步复位的的触发器
always @(reset or clock or d) //当reset、clock、d中任意一个信号位高电平时执行always语句块
//等价于 always @(reset, clock, d)
begin
if(reset) q = 1’b0;//复位时,q值初始化为0
else if(clock)
q = d; //当clock信号为高时,锁存输入信号d
end - 使用沿触发的always语句块
如:
always @(posedge clk, negedge reset)//此处,代替or
begin
if(!reset) q <= 1’b0;
else q <= d; //在每个clk的上升沿到来时,锁存输入信号d
end - @*和@(*),表示对其后面语句块中所有输入变量的变化都是敏感的
如:
always @(*)等价于always @( a or b or c or d or e or f)
begin
out1 = a?b:c;
out2 = d?e:f;
end
- 使用电平触发的always语句块:
- 电平敏感时序控制
使用关键字wait来表示等待电平敏感的条件为真。
如:
always
wait(count_en) #20 count=count+1; //当count_en为1时,延迟20个单位时间,count加1,否则count值保持不变
3.task
task的特点:
- 任务可以定义自己的仿真时间;
- 任务能启动其他任务和函数;
- 任务可以没有或有多个任务类型的变量;
- 任务不需要返回值。
task说明语句的语法结构为:
task 任务名;
端口及数据类型声明语句;
语句1;
语句2;
…
语句n;
endtask
任务的调用结构如下:
任务名(端口1,端口2,…,端口n);
如:
(1)任务定义:
task my_task;
input a,b;
inout c;
output d,e;
…
c = f1;
d = f2;
e = f3;
endtask
任务调用:my_task(v,w,x,y,z);含义为:任务启动时,将v、w、x变量赋值给a、b、c,任务结束时,将c、d、e的值赋给x、y、z。
如:描述红黄绿交通灯行为:
module traffic_lights; //定义模块名为traffic_lights
reg clock,red,amber,green; //定义变量
parameter on = 1,off = 0, red_tics = 350, amber_tics = 30, green_tics = 200; //定义参数
initial red = off;//初始化变量
initial amber = off;
initial green = off;
always
begin
red = on;
light(red,red_tics);//调用任务light
green = on;
light(green,green_tics);
amber = on;
light(amber,amber_tics);
end
task light;
output color;
input [31:0] tics;
begin
repeat(tics);
always @(posedge clock)//在clock上升沿到来时,灭灯
color = off;
end
endtask
always #100 clock = ~clock;//产生时钟脉冲
endmodule
4.function
function的特点:
- 函数只能与主模块共用同一个仿真时间单位;
- 函数不能启动任务;
- 函数至少要有一个输入变量;
- 函数的目的是返回一个用于表达式的值。
函数的语法结构为:
function (返回值的类型或范围) 函数名;
端口说明语句;
变量类型说明语句;
begin
语句块
…
end
endfunction
tips:
“返回值的类型或范围”为可选项,默认时返回值为一位寄存器类型数据。
如:
function [7:0] getbyte;
input [15:0] address;
begin
…
getbyte = result_expression;//将结果赋予函数的返回字节
end
endfunction
函数的调用:
函数名(表达式,…,表达式)
如:
word = control?{getbyte(msbyte),getbyte(lsbyte)}:0;//control为1时,将getbyte函数的高字节与低字节拼接后赋给word,否则对word清0
如:阶乘函数的定义和调用
module tryfact;
function [31:0] factorial;
input [3:0] operand;
reg [3:0] index;
begin
factorial = 1;//0和1的阶乘均为1
for(index=2;index<=operand;index=index+1)
factorial = index*factorial;//n的阶乘=n*(n-1)*(n-2)*...*1
end
endfunction
reg [31:0] result;
reg [3:0] n;
initial
begin
result =1;
for(n=2;n<=9;n=n+1)
begin
$display("partial result n=%d result =%d",n,result);
result = n*factorial(n-1);//调用函数factorial
end
$display("finalresult=%d",result);
end
endmodule
- 递归函数(自动函数)
verilog中的函数不能够进行递归调用,因为设计模块中若某函数在两个不同的地方被同时并发调用,由于这两个调用同时对同一块地址空间进行操作,那么计算结果将是不确定的。
verilog中函数声明时使用关键字automatic,将函数变为自动或可递归的,即仿真器为每一次函数调用动态地分配新的地址空间,每一个函数调用对各自的地址空间进行操作。因此,自动函数中声明的局部变量不能通过层次名进行访问。而自动函数本身可以通过层次名进行调用。
如:
module top;
...
function automatic integer factorial;
input [31:0] oper;
interger i;
begin
if(oper >= 2) factorial=factorial(oper-1)*oper;
else factorial = 1;
end
endfunction
integer result;
intial
begin
result = factorial(4);
$display("factorial of 4 is %d",result);
end
endmodule