一、过程语句
1、module/endmodule,interface/endinterface可以被视为硬件世界,program/endprogram,class/endclass可以被视为软件世界。
2、always过程块只可以在module或者interface(不常用)中使用,initial过程块可以在module、interface(不常用)和program中使用。过程块的书写方式,最好使用begin..end将其作用域‘包’住。
二、函数function
- 可以在参数列表中指定输入参数(input)、输出参数(output)、输入输出参数(inout)或者引用参数(ref)。
- 声明参数时,可以给入默认数值,参数默认参数类型是logic,默认参数方向是input。
- 可以返回数值或者不返回数值(void),可以通过三种方式返回值。例如:
- 函数不写返回值类型,默认是void,如 function double(ref int a);
1、值传递
module test;
function int double(input int a);
return 2*a;
endfunction
initial begin
$display("double of %0d is %0d", 10, double(10));
end
endmodule
打印结果:
double of 10 is 20
2、参数变量传递(地址传递)
module test;
function void double(input int a,output int b);//不返回值
b=2*a;
endfunction
initial begin
int x;
double(10,x);
$display("double of %0d is %0d", 10, x);
end
endmodule
打印结果:
double of 10 is 20
3、引用传递(地址传递)
module test;
function void double(ref int a);//不返回值,采用ref传递,a默认输入方向是input
a=2*a;
endfunction
initial begin
int x=10;
$display("old x is %0d", x);
double(x);
$display("double of x is %0d", x);
end
endmodule
打印结果:
old x is 10
double of 10 is 20
2、output参数说明:
- 函数调用之后,新建一份 b,函数 f 中处理的是 b 这份空间
- 函数返回之后,会将 x指向b 空间,而原来的 x 指向的空间会被释放掉,所以 x 的值都被修改了
3、ref类型说明:
- 在使用ref 时,有时候为了保护数据对象只被读取不被写入,可以通过const的方式来限定ref 声明的参数,如:const ref int i;
ref 只能用于动态方法中: function/task automatic , 不加动态关键字 automatic 编译错误,但
只调用了一次带ref参数的相同的function/task时,不会报错
,因为在调用时不会因为多处调用导致ref类型形参变量冲突。举例:
-
program test10; int sig; task automatic pass_bu_ref(ref int i) ;//automatic $display("pass_by_ref : i is %0d",i); endtask task pass_by_ref(ref int i) ; //static #5 $display("pass_by_ref : i is %0d",i); endtask initial begin sig = 0; pass_bu_ref(sig); #2 sig =1; pass_by_ref(sig); end endprogram 打印结果: pass_bu_ref : i is 0 pass_by_ref : i is 1
函数返回数组:有三种方式返回数组:typedef形式、ref传递、output传递
typedef int fixed_array[5];//给含有5个int类型元素的数组新定义类型
fixed_array f5;
program test;
function fixed_array init(int start);
foreach(init[i])
init[i] = init[i] + start;
endfunction
initial begin
//fixed_array f5; //作用域不同
foreach(f5[i])
$display("f5[%0d]=%0d",i,f5[i]); //结果全为0
f5 = init(5);
foreach(f5[i])
$display("f5[%0d]=%0d",i,f5[i]); //结果全为5
end
endprogram
program test;
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”,i,fa[i]);
end
endprogram
说明:
- 调用typedef给数组新定义类型,typedef int fixed_array[5];表示类型int[5]取别名为fixed_array,定义fixed_array a1,a2等价于int a1[5],a2[5]。
- function采用数组类型,是通过直接修改函数init地址上的数据的值,再通过函数地址传回给实参,不返回return,形参或者函数局部变量需要返回时,才用return返回。
- ref、output返回数组与前面返回值类似。
引用说明:
引用就是对变量取别名,引用的本质在程序内部实现是一个指针常量,指针常量是指针指向不可改
举例说明指针:指针里面存放的是地址A,A地址中存放一个数据5,指针本身也要存放在内存中,所以也自身有个地址S,指针常量是指自己存放的这个地址A不能改变,但A地址上的数据5是可以改变的;常量指针是指自己存放的这个地址A可以改变(指针本身是一个变量),但A地址上的数据5是不可以通过这个指针去可以改变的
//-------常量指针------- //
const int *p1 = &a; a = 200; //OK,仍然可以通过原来的声明修改值,
// *p1 = 13; //Error,*p1是const int的,不可修改,即常量指针不可修改其指向地址
p1 = &b; //OK,指针还可以指向别处,因为指针只是个变量,可以随意指向;
//-------指针常量-------//
int* const p2 = &a; a = 200; //OK,仍然可以通过原来的声明修改值,初始化的就给它分配地址,可以的(相当于分配初值);
*p2 = 900; //OK,指针是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化
//p2 = &b; //Error,因为p2是const 指针,因此不能改变p2指向的内容
//-------指向常量的常量指针-------//
const int* const p3 = &a; //*p3 = 1;
//Error //p3 = &b; //Error
a = 10; //OK,仍然可以通过原来的声明修改值
//发现是引用,转换为 int* const ref = &a;
void func(int& ref){
ref = 100; // ref是引用,转换为*ref = 100
}
int main(){
int a = 10;
//自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改
int& ref = a;
ref = 20; //内部发现ref是引用,自动帮我们转换为: *ref = 20;
cout << "a:" << a << endl;
cout << "ref:" << ref << endl;
func(a);
return 0;
}
三、任务task
- task操作与上述function操作类似
- function可以通过return返回结果;而task无法通过return返回结果,因此只能通过output、inout或者ref的参数来返回
- task内可以置入耗时语句,而function则不能。常见的耗时语句包括@event、 wait event、# delay等
- function和task均可调用function;而task只可被task调用,而不可被function调用(如果task里有耗时语句)
- function如果不用return返回值则要声明void或默认void,如果用return返回值则需要声明返回什么类型的值;而task加return并不用声明返回什么类型的值(return;),而是起返回调用task的位置,因为在调用function、task时都会有一个入口地址和程序正常执行中调用时的地址。
automatic | static |
---|---|
设置位置:关键字task/function与task/function名之间 | 默认static,但是这个关键字不要显式出现; |
task/function中定义的局部变量是动态分布的,即对于不同的调用分配的存储空间不同,所以 task/function被多次调用时互相之间不会影响 | task/function中定义的局部变量是静态分配的,即对于不同的调用分配的存储空间相同,所以task/function被多次调用时,相互之间会相互影响 |
task/function中定义的变量在task/function执行完后就立刻释放掉 | task/function中定义的变量一直保持,直到整个仿真 过程结束; |
task/function不同的调用中,其中的变量都会分配新的内存空间 | task/function不同的调用中,其中的变量共享共同的 内存空间 |
四、wait和@的区别
- @是边沿敏感触发,而wait是电平敏感触发
program automatic test_event();
logidclk,a,b, c;
initial begin
clk =1'b0;
#3 clk = 1 'b1;
end
initial fork
begin
#5 ;
wait(clK)
$dispay("i have reached clk") ;
end
begin
@(clk)
$display ( "reached posedgg of clk" );
end
join
endprogram
打印结果:
reached posedge of clk
i have reached clk