(四)SV过程语句、子程序

一、过程语句

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时都会有一个入口地址和程序正常执行中调用时的地址。
automaticstatic
设置位置:关键字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

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值