SV子程序,接口,驱动和采样
2026/7/17 5:09:08 网站建设 项目流程

SV子程序

automatic生命周期,一般要在function后面加automatic

ref传递意味着子程序直接操作外部变量的内存地址(类似于 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 根线,并且在例化testDUT时各自连一遍(重复工作两次)。一旦芯片接口增减一个信号,tb_toptestDUT全部都要修改。

接口连接

引入interface后,接口成了一个独立的“物理实体”。把所有信号打包定义在interface内部。在tb_top中只需要例化一个arb_if。然后例化test和DUT时,直接把这一个arb_if塞给他们,即时接口内部增加10个信号,只用interface改变。

$display("@%0t: Drove req=01 (via interface)", $time); repeat(2) @(posedge arbif.clk); @(posedge arbif.clk) arbif.request <= 2'b11; $display("@%0t: Drove req=11 (via interface)", $time); repeat(2) @(posedge arbif.clk); $display("@%0t: Test Finished", $time); $finish; end endmodule

接口连接很常用,先写(内部信号clk,rst);再写外部信号(设计代码输入输出的信号),modport定义一下测试端和设计端看到的信号方向。设计端的方向与设计代码input output一致,测试端输入clk rst信号,其他设计端的输入是测试端的输出,设计端的输出是测试端的输入。定义完interface,end 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 clk=0; forever #5 clk = ~clk; end initial begin rst=1; #2; rst=0; 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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询