interface中的clocking


对上篇的博客做个补充!


1. 一些概念

  • interfacemodule有类似的地方,都可以定义端口port、定义functiontask、使用initialalways
  • interface用于连接硬件(DUT)和软件(验证环境),还可以简化模块之间的连接,为模块端口提供了标准化的封装方式;
  • interface中声明clocking时钟块,可以避免delta cycle问题,通过波形上的可见延迟帮助理解仿真时序;
  • modport只是对interface中已经声明的信号再次声明方向;
  • module中可以例化module,也可以例化interfaceinterface中可以例化interface,不可以例化module

2. 举个例子接口怎么用

重新写一下之前的例子,以前记录的,好多细节不太清楚,重新复习,还是一位加法器


  • modport用于对接口中的信号分组,并指定方向,后续模块使用接口可以直接例化modprt;
  • modprot的参数列表可以是port,也可以是clocking;
  • 对于激励产生部分simulus,验证环境要输出a、b、cin作为dut的输入;
  • 对于加法器adder,她要接收验证环境的输出作为自己的输入;
  • 对于监测器monitor,她会接收所有的端口作为自己的输入;
  • 对于一个接口中的同一个信号,比如a,她是dut的输入,又是环境的输出,这就需要用modport添加方向限制;也就是说,信号的方向对于不同的作用对象可以是不一样的

代码如下:

2.1. 声明接口adder_interface

interface adder_interface(input bit clk);
  logic     a;      // declare port signal
  logic     b;
  logic     cin;
  logic     cout;
  logic     sum;

  clocking cp @(posedge clk);  // declare which signals are triggered at the rising edge of the clk
    output  a, b, cin;
  endclocking

  clocking cn @(negedge clk);  // declare which signals are triggered at the falling edge og the clk
    input   a, b, cin, cout, sum;
  endclocking

  modport simulus (clocking cp);
  modport adder   (input a, b, cin, output cout, sum);
  modport monitor (clocking cn);

endinterface
  • 在clocking cn中,利用时钟clk的下降沿触发采集输出事件,其实也是一种延迟;

2.2. 激励的产生

module simulus(adder_interface.simulus port);
  always @(port.cp) begin
    port.cp.a   <= $random() % 2;
    port.cp.b   <= $random() % 2;
    port.cp.cin <= $random() % 2;
  end
endmodule

2.3. 加法器

module adder(adder_interface.adder port);
  assign {port.cout, port.sum} = port.a + port.b + port.cin;
endmodule

3.4. 监测器

module monitor(adder_interface.monitor mon);
  always @(mon.cn) begin
    $display("%0t: %d + %d + %d = %d %d", $time, mon.cn.a, mon.cn.b, mon.cn.cin, mon.cn.cout, mon.cn.sum);
  end
endmodule

3.5. 顶层文件

`timescale 1ns/1ps;
module top();
  bit clk = 0;
  always #10 clk = ~clk;

  adder_interface adder_vif(clk);  // 在test中例化接口

  simulus sim(adder_vif.simulus);
  adder   add(adder_vif.adder);
  monitor mon(adder_vif.monitor);

endmodule

3. 分析仿真结果

在这里插入图片描述
上述代码直接仿真,可以看到监测器实在时钟下降沿触发打印的,因为打印发生在always @(mon.cn)


在实际电路中,组合逻辑都会有延迟,但是我们的仿真器不能够直接在波形上看到这个延迟时间,所有的信号都会在@(posedge clk)的时间变化,真是电路信号的变化都会在@(posedge clk)的前一点或者后一点;
在这里插入图片描述
vld是我们在仿真波形上看到的,信号的变化时间都是一样的,都在时钟上升沿;
vld_acutal是实际电路的变化;
vld_delay是我们可以clocking中指定偏移时间的结果,这样就可以直接在波形上看到这个偏差;

4. 在clocking中设置偏移时间

  • clocking内的输入信号是对接口信号的采样,也就是dut的输出信号;
  • clocking内的输出信号是对接口信号进行驱动,也就是dut的输入信号;
  • 在clocking中用default可以设置输入输出的偏移时间;
    clocking my_if();
    	default input #1 output #2;
    endclocking
    
  • clocking cp里面使用defalut仿真时报错:注意想要在clocking中添加延时,必须要指定时间单位和时间精度,否则仿真报错;
    在这里插入图片描述
    其实我在top最上面已经加了timescales 1ns/1ps,但还是报错说需要指定时间单位和精度,这是因为timescales的作用范围没有用到interface中(因为我的各个module都是在独立的.sv中),在top中将interface.sv导入进来就行;
  clocking cp @(posedge clk);  // declare which signals are triggered at the rising edge of the clk
  	default ouput #2;
    output  a, b, cin;
  endclocking
`timescale 1ns/1ns;

`include "adder_interface.sv"

module top();
  // timeunit        1ns;
  // timeprecision   1ns;
  
  bit clk = 0;
  always #10 clk = ~clk;

  adder_interface adder_vif(clk);

  simulus sim(adder_vif.simulus);
  adder   add(adder_vif.adder);
  monitor mon(adder_vif.monitor);

endmodule

在这里插入图片描述

  • 对于dut来说,a、b、cin作为激励的输入来得晚,那自己的计算输出cin、cout就算的晚;也就是我延后了激励的输出,延后了simulus的输出

  • 上面的例子相当于有三个模块,激励发生部分、加法器、监测器三个模块,三个模块共用一个接口的实例adder_vif,我在工作中暂时没有遇到过这种共用的用法,基本都是一个模块用一个接口;

  • 关于clocking我在工作中暂时也没有用过,跑后仿的时候不知道需不需要查看detal cycle;

  • 上面加法器的举例,不是很好,所有的模块相当于都是硬件环境,没有做到验证环境(软件环境)和硬件环境的分离

  • 假设我想在验证环境中让dut打印时间后延,我可以在interface中的clocking中指定,也可以在验证环境中的task中手动添加延迟,规定在时钟上升沿后1ns就打印结果;

    while(1) begin
      @(posedge adder_vif.clk);
      #1ns;
      $display("%0t: %d + %d + %d = %d %d", $time, adder_vif.a, adder_vif.b, adder_vif.cin, adder_vif.cout, adder_vif.sum);
    end
    
  • 假如我的加法器有一个工作使能信号en,只有在en拉高的情况下,加法器才会计算,并输出结果,下面有两种写法。由于en存在连续两拍都拉高的情况,所以不能用边沿检测的方法监测en,只能用电平检测;

  • 写法1:实际波形会在en拉高的下一拍,因为对输入数据的采样会以时钟上升沿前的为准;对于en信号的检查发生在一个时钟周期的结束,这有可能不是我们想要的;

    while(1) begin
      @(posedge adder_vif.clk);
      if(adder_vif.en == 1) 
        $display("%0t: %d + %d + %d = %d %d", $time, adder_vif.a, adder_vif.b, adder_vif.cin, adder_vif.cout, adder_vif.sum);
    end
    

    在这里插入图片描述

  • 写法2:手动添加延迟,我在每一个时钟周期内去检查,这样就可以保证实时,当拍打印当拍的结果;

    while(1) begin
      @(posedge adder_vif.clk);
      #1ps;  // 手动添加延时
      if(adder_vif.en == 1) 
        $display("%0t: %d + %d + %d = %d %d", $time, adder_vif.a, adder_vif.b, adder_vif.cin, adder_vif.cout, adder_vif.sum);
    end
    

    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值