
SV子程序automatic生命周期一般要在function后面加automaticref传递意味着子程序直接操作外部变量的内存地址类似于 C 语言的指针。为了防止“指针”指向的非静态变量在中途被销毁导致悬空SV 规定带有ref参数的子程序其自身必须被声明为automatic动态生命周期。ref int f[5]和int fa[5] 表示一个包含五个int元素的数组调用init(fa,5),仿真器会在内存里开辟一块全新的临时空间f.如果不写ref的话(默认input),他就会把fa里面五个数字一个一个复制到f里,占内存,,且在这个项目里改的是f[i],改的是替身. 当写了ref:f变成了fa,它直接指向了fa在堆栈中的真实内存地址。此时仿真器根本没有为f分配独立的数组存储空间。在函数内部只要你对f[i]进行操作底层直接修改的就是外部fa[i]那块内存。input传递相当于你把这个 Excel 文件复制了一份发给函数。函数在副本上修改了数据原文件毫无影响。ref传递相当于你把这个 Excel 文件的“共享编辑链接”发给了函数。函数f和创建者fa面对的是同一个文件函数动了第几行原文件立刻同步改变。结果:问题1在芯片验证中如果我们希望用ref来提高仿真速度不想复制大数组但是我们又非常担心写函数的工程师不小心在函数里面把我们的原数据给改了比如只需要读数据不需要写数据。在 SystemVerilog 中有什么修饰词可以和ref配合使用做到“既用引用传递提高速度又防止函数内部修改原数组”吗使用const ref类型声明一个子程序为function automatic void my_func(const ref int f[5]);函数内部就不允许再对f[i]进行任何赋值操作了因为它是只读的。所以原来的“初始化写数据”函数在语义上必须转变为“打印、检查或比对读数据”函数。function automatic void display_packet(const ref int f[5]); $display([TB_INFO] --- display_packet started ---); foreach(f[i]) begin $display(fa[%0d] %0d, i, f[i]); end endfunction function automatic void init(ref int f[5], input int start); foreach(f[i])begin f[i] i start; end endfunction initial begin // 1. 先用普通的 ref 写入数据 init(fa, 5); // 2. 调用安全的 const ref 函数打印 display_packet(fa); $finish; end接口设计代码传统端口需要把test模块和arb_port设计文件 DUT的所有引脚一个一个用wire或者reg/logic连起来。如果 DUT 有 1000 个引脚你的tb_top里需要声明 1000 根线并且在例化test和DUT时各自连一遍重复工作两次。一旦芯片接口增减一个信号tb_top、test、DUT全部都要修改。接口连接引入interface后接口成了一个独立的“物理实体”。把所有信号打包定义在interface内部。在tb_top中只需要例化一个arb_if。然后例化test和DUT时直接把这一个arb_if塞给他们即时接口内部增加10个信号只用interface改变。$display(%0t: Drove req01 (via interface), $time); repeat(2) (posedge arbif.clk); (posedge arbif.clk) arbif.request 2b11; $display(%0t: Drove req11 (via interface), $time); repeat(2) (posedge arbif.clk); $display(%0t: Test Finished, $time); $finish; end endmodule接口连接很常用先写内部信号clkrst再写外部信号设计代码输入输出的信号modport定义一下测试端和设计端看到的信号方向。设计端的方向与设计代码input output一致测试端输入clk rst信号其他设计端的输入是测试端的输出设计端的输出是测试端的输入。定义完interfaceend interface后要写顶层的testbench代码和测试模块定义代码。VCS仿真结果clocking block可读可写clocking block中 input是可读的output是可写的。因为它的输出端 表示测试平台在指定时钟沿去激励所以是可写的。input 表示TB在指定时钟沿去采样。interface arb_if(input logic clk,input logic rst); logic [1:0] request; logic [1:0] grant; clocking cb (posedge clk); output request; input grant; endclocking modport TEST( clocking cb, input rst ); modport DUT( input clk,rst,request, output grant ); endinterface //测试程序 program test(arb_if.TEST arbif); initial begin $monitor(%0d ns cb.grant %0d,$time,arbif.cb.grant); #50; $display(%0d ns:finish,$time); end endprogram //硬件设计 module arb(arb_if.DUT arbif); initial begin arbif.grant 1; #12 arbif.grant 2; #18 arbif.grant 3; end endmodule //仿真顶层 module tb_top; logic clk; logic rst; initial begin clk0; forever #5 clk ~clk; end initial begin rst1; #2; rst0; end //实例化接口 arb_if my_if(.clk(clk), .rst(rst)); //实例化设计和测试代码 arb u_dut (.arbif (my_if.DUT)); test u_test(.arbif (my_if.TEST)); //波形 fadb函数 endmodule