第三章:过程语句和子程序
实际在写设计或验证时,代码基本卸载任务或函数里面,本章主要是介绍SV像C语言的改进–处理参数案方面。
3.1 过程语句
- 在for循环中定义循环变量,作用范围仅限于循环内部,避免一些代码漏洞。
- 自动递增/减符
- 在begin/fork语句中可使用标识符,在其相应的end或join语句中放相同的标号。
示例:
initial begin:example
integer array[10],sum,j;
// 在for语句中声明i
for(int i=0;i<10;i++ )
array[i]=i;
// 把数组的元素相加
sum = array[9];
j=8;
do
sum+=array[j]; // while循环
while(j--);
$display("Sum=%4d",sum); // %4d-指定宽度
end: example
// 循环中还可增加 continue和break功能。
initial begin
bit[127:0] cmd;
int file,c;
file = $fopen("commands.txt",'r');
while(!$feof(file)) begin
c = $fscanf(file,"%s",cmd);
case(cmd)
"": continue; //空行,终止本次循环
"done":break;// 终止并跳出循环
....
endcase
end
$fclose(file);
end
3.2 任务、函数以及void函数
在Verilog中,任何和函数之间有明显的区别,最大区别就是任务可以消耗时间,而函数不能带有如#100 的延时语句或诸如@(posedge clock)、wait(ready)的阻塞语句,也不能调用任务。函数必须要有返回值且必须被使用(基本用到赋值语句中)。
在SV中,若是有不消耗时间的任务,应定义成void()函数,此时能被函数和任务所调用。
示例:用于调试的void函数
function void print_state(...);
$display("@%0t:state=%s",$time,cur_state.name());
endfunction
// 有些仿真器,允许在不适用void的情况下,忽略返回值。
3.3 任务和函数概述
稍微改进:不带参数的子程序在定义或被调用时不需要带空括号。
3.3.1 在子程序中去掉begin…end
在SV中是可选的,在Verilog中对多行的子程序是必须要带的。
// 没带 begin...end
task multiple_lines;
$display("First line");
$display("Second line");
endtask:multiple_lines
3.4 子程序参数
SV中改进 使参数的声明变得更加方便,同时扩展参数的传递方式。
对传递参数处理,Verilog是直接进行复制,SV中参数传递方式–ref 是引用而不是复制。
// 在多线程之间使用ref
task bus_read(input logic[31:0] addr,
ref logic [31:0] data);
// 请求总线并驱动地址
bus.request =1'b1;
@(posedge bus.grant) bus.addr = addr;
// 等待来自存储器的数据
@(posedge bus.enable) data = bus.data;
// 释放总线并等待许可
bus.request = 1'b0;
@(posedge bus.grant);
endtask
logic[31:0] addr,data;
initial
fork
bus_read(addr,data);
thread2:begin
@data; // 在数据变化时触发
$display("Read %h from bus",data);
end
join
3.4.4 参数的缺省值
在SV中为参数制定一个缺省值,在调用时不指明参数,则使用缺省值。
function void print_checksum(ref bit[31:0] a[],
input bit[31:0] low=0,
input int high =-1);
bit[31:0] checksum =0;
if(high==-1 || high>=a.size())
high = a.size()-1;
for(int i=low;i<high;i++)
checksum+=a[i];
$display ("The array checksum is %0d",checksum);
endfunction
// 调用 使用参数缺省值
print_checksum(a); // a[0:size-1]中所有元素的校验和
print_checksum(a,2,4); // a[2:4]中所有元素的校验和
print_checksum(a,1); // 从1开始
print _checksum(a, ,2); //a[0:2]中所有元素的校验和
print_checksum(); // 编译错误:a没有缺省值
// 使用-1(或其他任何越界值)作为你缺省值,来判断调用时有没有给指定值
3.4.5 采用名字进行参数传递
在SV语言中,任何或函数的参数 有时被称为端口"port",就跟模块的接口一样。
// 采用名字进行参数传递
task many(input int a=1,b=2,c=3,d=4);
$display("%0d %0d %0d %0d",a,b,c,d);
endtask
initial begin
many(6,7,8,9); //a,b,c,d
many(); // 使用缺省值
many(.c(5)); // 只指定c
many(,6,.d(8)); // 混合方式
end
3.4.6 明确指明所有参数方向
task sticky(ref int array[50],
input int a,b); // 明确指定方向
3.5 子程序的返回
在Verilog中是执行完子程序的最后一条语句,程序就会返回到子程序的代码上。此外,函数会返回一个值,该值被赋给与函数同名的变量。在SV中增加return语句,遇到return就会返回。
3.5.1 从函数中返回一个数组
在Verilog中只能返回一个简单值,例如比特,整数或是向量。
在SV中,函数采用多种方式来返回一个数组。
//第一种:定义一个数组类型,然后在函数的声明中使用该类型
typedef int fixed_array5[5]; // 新定义一个数组类型
fixed_array5 f5;
function fixed_array5 init(int start)
foreach(init[i])
init[i] = i+start;
endfuncton
initial begin
f5 = init(5);
foreach(f5[[i]])
$display("f5[%0d] =%0d", i,f5[i]);
end
// 上面函数存在的问题时:创建一个数组,数组值被赋值,浪费空间
// 第二种:使用引用
function void init(ref int f[5], input int start);
foreach(f[i])
f[i] = i+start;
endfunction
int fa[5];
initial begin
init(fa,5);
foreach(fa[i])
$display("fa[%0d] = %0d",fa[i]);
end
// 第三种:将数组封装到哦一个类中,然后返回对象的句柄
3.6 局部数据存储
Verilog硬件语言对数据都是静态分配的,尤其是子程序和局部变量都是放在固定位置的。而C预压等式基于堆栈区来存储的。
在Verilog最新版中,可以指定任务、函数和模块使用自动存储–堆栈区,迫使仿真器使用堆栈区。在SV中你,模块module和program块使用关键字 automatic 来使用堆栈区。
示例:一个用于监测数据何时被写入存储器的任务
program aumomatic test;
task wait_for_mem(input [31:0] addr, expect_data, output success);
while(bus.addr!=addr)
@(bus.addr);
success = (bus.data ==expect_data);
endtask
.......
endprogram
3.6.2 有变量的初始化
示例:监测总线五个周期以后,创建一个局部变量并把当前地址总线的值作为初值赋给它
program automatic initialization;
task check_bus;
repeat(5) @(posedge clock);
if(bus_cmd ='READ) begin
//何时对local_addr赋初值
logic[7:0] local_addr =addr<<2;
$display("Local Addr=%h",local_addr);
end
endtask
endprogram
3.7 时间值
SV中新结构帮助在系统指明时间值
3.7.1 时间单位和精度
语句:`timescale 时间单元/时间精度
3.7.2 时间参数
使用$timeformat()和%t指定符进行格式化后的时延
3.7.3 时间和变量
这个具体遇到会更懂,现在看了迷迷糊糊的。
- $time:返回值是一个根据所在模块的时间精度要求进行舍入的整数,不带小数部分。
- $realtime 返回值是一个带小数部分的完整实数。
3.8 总结
SV中的程序化结构即任何和函数的新特点,使得更接近C++的特性,便于编写测试平台。
和C++相比,SV拥有自己的特殊结构,时序控制,简单的线程控制和四态逻辑。