1. task
task就是任务,就是用于执行特定功能的可重用代码块,使用任务可以把大的程序模块分解成多个小的模块,可以使程序结构更清晰。
2. 使用task写tb文件
一次在写tb文件的时候,编写测试激励,需要重复去读写寄存器,并且读写使能、地址和数据是有时序要求的,那么这个读写过程就可以写成task重复调用。
在使用task的过程中,遇到一些问题,也会记录下来。
2.1 task 定义
先查找了task如何定义,网上查到如下:
task <任务名>; //注意无端口列表
端口及数据类型声明语句;
其它语句;
endtask
但是实际上我按照这样定义时,用modesim仿真会报错,根据提示,改成如下:
task <任务名> (
input 输入端口, //端口定义
output 输出端口
);
begin
end
endtask
这里的端口定义与使用module定义模块的方式是一样的,如task不需要端口则括号里面为空。
注意调用时端口的顺序要与定义的顺序一致。
2.2 task使用
了解了task的定义,现在开始着手编写里面功能。
我需要的task是去读写程序里的寄存器,有延迟要求,以写寄存器为例,需要控制三个信号:写使能Wren,写地址Addr,写数据Data,需要先给Addr和Data赋值,y延迟100ns之后,Wren拉高50ns。
我第一版是这样写的:
task Write_reg(
input [15:0] I_dat,
input [7:0] I_addr,
output O_Wren,
output [15:0] O_dat,
output [7:0] O_addr
);
begin
O_Wren = 0;
O_addr = I_addr;
O_dat = I_dat;
#(100);
O_Wren = 1;
O_addr = I_addr;
O_dat = I_dat;
#(50);
O_Wren = 0;
O_addr = I_addr;
O_dat = I_dat;
end
endtask
调用:
reg I_REG_WR_VLD; //Wren
reg [7:0] I_REG_WRRD_ADDR; //Addr
reg [15:0] I_REG_WR_DATA; //data
initial
begin
Write_reg(16'h000f,8'd1,I_REG_WR_VLD,I_REG_WR_DATA,I_REG_WRRD_ADDR);
end
task内的代码,单独放在initial里面仿真是没问题的,但是使用task的写法后,仿真发现DATA和Addr的数据变了,Wren没有拉高。
去查找了相关问题,原来是task定义的变量是局部变量,在task外是无法使用的,因此仿真只是显示了#(50)后面的运行结果。
如果想用task做出这部分时序,需要将要控制的信号定义为全局变量。
修改task如下:
reg I_REG_WR_VLD; //Wren
reg [7:0] I_REG_WRRD_ADDR; //Addr
reg [15:0] I_REG_WR_DATA; //data
//被控制信号定义为全局变量,全局变量可以在task中直接使用
task Write_reg(
input [15:0] I_dat,
input [7:0] I_addr
);
begin
I_REG_WR_VLD = 0;
I_REG_WRRD_ADDR = I_addr;
I_REG_WR_DATA = I_addr;
#(100);
I_REG_WR_VLD = 1;
I_REG_WRRD_ADDR = I_addr;
I_REG_WR_DATA = I_dat;
#(50);
I_REG_WR_VLD = 0;
I_REG_WRRD_ADDR = I_addr;
I_REG_WR_DATA = I_dat;
end
endtask
task调用
initial
begin
Write_reg(16'h0000,8'd6);
#(1000)
Write_reg(16'h0038,8'd6);
end
Modesim结果如图:
3. 使用task写可综合程序
task要成为可综合的,task本身必须只包含组合逻辑。如果task中包含时序逻辑,那么它就不能被综合。
在编写可以综合的task时,需要注意遵守特定的语法规则和限制,例如,task的第一行不能列出端口名称,任务中不能出现initial或always语句,且任务的输出只能为reg类型。
此外,确保task中的代码本身就是可以综合的也是非常重要的。