本文介绍了system verilog中静态数组、动态数组、队列作为函数参数传递的规则,以及input、output、ref关键字的规则。
先说明,sv中的静态数组、动态数组、队列都是用一块内存存放,而他们的名字作为该内存的地址,这点应该和c一致,但sv里面没有指针的概念。
传递这种大片内存的值一般只有两种规则
- 地址传递,函数内部修改可以改变函数调用的值。
- 值传递,将整片空间复制一份,函数内部修改不会改变函数调用的值。
但是system verilog里面关键字有三个,input、output、ref。
具体哪个对应哪个,我们来试一下。
静态数组作为参数
以静态数组作为模板来试。
1 | function void run(); |
加input 的情况
1 | function void f(input int a[2]); |
输出结果是:1 2
具体过程如下:
- 函数调用之前,内存里只有r数组的空间
- 函数调用之后,会将r拷贝一份到a,函数里面用的都是a这份空间
- 函数调用之后,赋值后,修改的是a这份空间的值
- 函数返回之后,a空间会被释放掉,所以r的值并没有修改
因此,input是标准的值传递。
加output 的情况
1 | function void f(output int a[2]); |
输出结果是:3 0
具体过程如下:
- 函数调用之前,内存里只有r数组的空间
- 函数调用之后,会新建一份a数组,注意这里并不会把r的值传进来,函数里面用的都是a这份空间
- 函数调用之后,赋值后,修改的是a这份空间的值
- 函数返回之后,会将r指向a空间,而原来r指向的空间会被释放掉,所以r的值都被修改了
因此,加output,采用的是地址传递,确切的说是反向的地址传递,数组共有两份,函数内部看到的是新建的一份,并传递给函数调用处,因此,函数调用前看到的是第一份,函数调用后看到的是新建的那份。
加ref 的情况
1 | function void f(ref int a[2]); |
输出结果是:3 2
具体过程如下:
- 函数调用之前,内存里只有r数组的空间
- 函数调用之后,会将a指向r所在的空间,函数里面用的都是这份空间
- 函数调用之后,赋值后,修改的是这份空间的值,所以r和a都修改了
- 函数返回之后,所以r的值只有部分修改
因此,加ref,采用的是标准的地址传递,只有一份数组,函数内部和调用处都是访问该空间。
对比来看,input是将函数外面的值传递到函数里面,调用结束之后,函数里面的值就被丢弃了;out是将函数里面的值传递到函数外面来,调用结束之后,函数外面的值就丢了;ref是函数内部和外部看到的是同一份值,哪里改都会改。
另外,可以看出,采用ref关键字是占用内存最小的,因为只有一份数组,这样能提高效率。
动态数组或队列或结构体作为参数
如果传递的是动态数组或者队列或者是结构体,其结果是一模一样的,不另外作说明。
类作为参数
但是!!如果传递的是类,则会有点不一样。
1 | class c; |
结果是:
- input:
3 2
- output:报null point错误
- ref:
3 2
可以看出,如果是传递类的话,使用input和ref是一样的,都是地址传递,而output和数组的结果是一样的。