命令层监视器编写步骤

第一步:接口

第一步定义在主设备和从设备之间交换信息的物理接口信号。单个信息交换(读或写操作)称为一个事务。在APB总线上只有一个主设备,可以有多个从从设备。从设备根据不同的地址范围来区分。

信号是在接口中定义的。接口的名字以前缀"apb_"开始,以此来鉴别接口遵循APB协议。这个定义接口的文件被`ifndef/`define/`endif结构包含,这是一个C语言技巧,它允许文件被多次包含,避免了重定义的错误。

File: apb/apb_if.sv 
`ifndef APB_IF__SV 
`define APB_IF__SV 
interface apb_if; 
   ... 
endinterface: apb_if 
`endif 


VMM bookP89-P96规定了接口的定义方法和规则。

第二步:连接到DUT

这一步将接口连接到DUT,需要在顶层模块中例化接口和DUT。通过层次化引用接口中信号连接DUT。

File: Command_Monitor_Xactor/tb_top.sv 
module tb_top; 
   ... 
   apb_if apb0(...); 
   ... 
   master_ip dut_mst(..., 
                     .apb_addr   (apb0.paddr[7:0]  ), 

                      .apb_sel    (apb0.psel        ), 
                     .apb_enable (apb0.penable     ), 
                     .apb_write  (apb0.pwrite      ), 
                     .apb_rdata  (apb0.prdata[15:0]), 
                     .apb_wdata  (apb0.pwdata[15:0]), 
                     ...); 
   slave_ip dut_slv(..., 
                    .apb_addr   (apb0.paddr[7:0]  ), 
                    .apb_sel    (apb0.psel        ), 
                    .apb_enable (apb0.penable     ), 
                    .apb_write  (apb0.pwrite      ), 
                    .apb_rdata  (apb0.prdata[15:0]), 
                    .apb_wdata  (apb0.pwdata[15:0]), 
                    ...); 
endmodule: tb_top 


第三步:事务描述符

下一步就是定义APB事务描述符(必须用事务描述符对事务进行建模RULE4-54)。事务描述符是一个vmm_data类的扩展类,包含公共类属性。类中需要有一个静态vmm_log属性的实例,来发起关于事务描述符的信息。用vmm_data的构造函数来传递vmm_log。

`ifndef APB_RW__SV 
`define APB_RW__SV 
`include “vmm.sv” 
class apb_rw extends vmm_data; 
   static vmm_log log = new(“apb_rw”, “class”); 
   enum {READ, WRITE} kind; 
   bit   [31:0] addr; 
   logic [31:0] data; 
   function new(); 
      super.new(this.log); 
   endfunction: new 
   ... 
endclass: apb_rw 
... 
`endif 


“data”用一个属性表示,尽管APB总线有独立的读和写总线。因为APB不支持并行的读和写,所以在一个时间总是只有一个data值是有效的。依据kind的不同,data是不同的。在一个WRITE的事务中,它是要写的数据;在一个READ事务中,它是要读的值。data是logic类型,因为READ周期要可能会读到unknown值。

Although the transaction descriptor is not yet VMM-compliant, it is has the minimum
functionality to be used by a monitor. A transaction-level interface will be required to
transfer transaction descriptors to a transactor to be executed. This is done using the
`vmm_channel macro (Rule 4-56).

`ifndef APB_RW__SV 
`define APB_RW__SV 
`include “vmm.sv” 
class apb_rw extends vmm_data; 
   static vmm_log log = new(“apb_rw”, “class”); 
   enum {READ, WRITE} kind; 
   bit   [31:0] addr; 
  logic [31:0] data; 
   function new(); 

super.new(this.log); 
   endfunction: new 
   ... 
endclass: apb_rw 
`vmm_channel(apb_rw) 
... 
`endif 


Defines a channel class to transport instances of the specified class. The transported
class must be derived from the vmm_data class. This macro is typically invoked in
the same file where the specified class is defined and implemented.

This macro creates an external class declaration and no implementation. It is typically
invoked when the channel class must be visible to the compiler but the actual channel
class declaration is not yet available.

第四步:

这一步开始定义监视器。它是一个由vmm_xactor派生出来的类。这个事务处理器需要一个物理级接口来观测事务。用虚拟的端口来实现物理级接口,并将它作为本事务处理器构造函数的一个参数。(RULE4-108)

File: apb/apb_monitor.sv 
`ifndef APB_MONITOR__SV 
`define APB_MONITOR__SV 
`include “apb_if.sv” 
`include “apb_rw.sv” 
... 
class apb_monitor extends vmm_xactor; 
   virtual apb_if.passive sigs; 
   ... 
   function new(string                 name, 
                int unsigned           stream_id, 
                virtual apb_if.passive sigs, 
                ...); 
      super.new(“APB Monitor”, name, stream_id); 
     this.sigs = sigs; 
      ... 
   endfunction: new 

   ... 
endclass: apb_monitor 
`endif 


事务描述符内要放在main方法中从物理接口观察到的的数据。

virtual protected task main(); 
      super.main(); 
      forever begin 
         apb_rw tr; 
         ... 
         // Wait for a SETUP cycle  
         do @ (this.sigs.pck);  
         while (this.sigs.pck.psel !== 1'b1 ||  
                this.sigs.pck.penable !== 1'b0);  
         tr = new; 
         ... 
         tr.kind = (this.sigs.pck.pwrite) ? 
                      apb_rw::WRITE : apb_rw::READ;  
         tr.addr = this.sigs.pck.paddr;  
         @ (this.sigs.pck);  
         if (this.sigs.pck.penable !== 1'b1) begin  
            `vmm_error(this.log, "APB protocol violation: SETUP 
cycle not followed by ENABLE cycle"); 

       end  
         tr.data = (tr.kind == apb_rw::READ) ? 
                      this.sigs.pck.prdata :  
                      this.sigs.pck.pwdata;  
         ... 
   end 
   endtask: main 
endclass: apb_monitor 
`endif


一旦监视器检测到事务的开始,在事务被检测和整个事务被报告结束之前,不能停止它。否则,如果在事务流中间被终止,它将至多报告一个错误。因此在事务开始之前要调用vmm_xactor:: wait_if_stop方法。

virtual protected task main(); 
      super.main(); 
      forever begin 
         apb_rw tr; 
         this.wait_if_stopped(); 
         // Wait for a SETUP cycle  
         do @ (this.sigs.pck);  
         while (this.sigs.pck.psel !== 1'b1 ||  
                this.sigs.pck.penable !== 1'b0);  
         tr = new; 
         ... 
         tr.kind = (this.sigs.pck.pwrite) ? 
                      apb_rw::WRITE : apb_rw::READ;  


第五步:报告事务

监视器必须能告知测试平台:一个事务已经被检测到。简单的显示观测到的事务没有什么用处,因为这每次还需要人工检查。被观测到事务通过在vmm_xactor::notify属性中的通知来报告。

The observed transaction, being derived from vmm_data, is attached to a
newly defined “OBSERVED” notification and can be recovered by all interested parties
waiting on the notification.

`ifndef APB_MONITOR__SV 
`define APB_MONITOR__SV 
`include “apb_if.sv” 
`include “apb_rw.sv” 
... 
class apb_monitor extends vmm_xactor; 
   virtual apb_if.master sigs; 
   ... 
   typedef enum {OBSERVED} notifications_e; 
   ... 
   function new(string                 name, 
                int unsigned           stream_id, 
                virtual apb_if.passive sigs, 
                ...); 
      super.new(“APB Master”, name, stream_id); 
      this.sigs = sigs; 
      ... 
      this.notify.configure(OBSERVED); 
   endfunction: new 
   ... 

   virtual protected task main(); 
      super.main(); 
      forever begin 
         apb_rw tr; 
         this.wait_if_stopped(); 
         // Wait for a SETUP cycle  
         do @ (this.sigs.pck);  
         while (this.sigs.pck.psel !== 1'b1 ||  
                this.sigs.pck.penable !== 1'b0);  
         tr = new; 
         ... 
         tr.kind = (this.sigs.pck.pwrite) ? 
                      apb_rw::WRITE : apb_rw::READ;  
         tr.addr = this.sigs.pck.paddr;  
         @ (this.sigs.pck);  
         if (this.sigs.pck.penable !== 1'b1) begin  
            `vmm_error(this.log, "APB protocol violation: SETUP 
cycle not followed by ENABLE cycle");  
         end  
         tr.data = (tr.kind == apb_rw::READ) ? 
                      this.sigs.pck.prdata :  
                      this.sigs.pck.pwdata;  
         ... 
         this.notify.indicate(OBSERVED, tr); 
         ... 
   end 
   endtask: main 
endclass: apb_monitor 
`endif 


第六步:第一个测试

尽管现在监视器还不是完全的遵循VMM,但它现在能用来监视APB总线的活动。它在顶层环境的vmm_env基类的扩展类中例化。监视器在vmm_env::start方法的扩展方法中构建,在vmm_env::start方法的扩展方法中启动。在vmm_env::build方法中引用包含APB物理信号的接口是通过层次化引用来实现的。

`ifndef TB_ENV__SV 
`define TB_ENV__SV 
`include “vmm.sv” 
`include “apb_monitor.sv” 

class tb_env extends vmm_env; 
   ... 
   apb_monitor mon; 
   virtual function void build(); 
      super.build(); 
      ... 
     this.mon = new(“inst”, 0, tb_top.apb0); 
   endfunction: build 
   virtual task start(); 
      super.start(); 
      ... 
      this.mon.start_xactor(); 
      ... 
   endtask: start 
   ... 
endclass: tb_env 
`endif 


监视器能够用来报告检测到的事务,也能够用来决定何时结束测试(通过足够的APB事务被检测到)。

`ifndef TB_ENV__SV 
`define TB_ENV__SV 
`include “vmm.sv” 
`include “apb_monitor.sv” 
class tb_env extends vmm_env; 
   ... 
   apb_monitor mon; 
   int stop_after = 10; 
   virtual function void build(); 
      super.build(); 
      ... 
      this.mon = new(“0”, 0, tb_top.apb0); 
   endfunction: build 
   virtual task start(); 
      super.start(); 
      ... 
      this.mon.start_xactor(); 
      ... 
      fork 
         ... 
         forever begin 
            apb_rw tr; 

            this.mon.notify.wait_for(apb_monitor::OBSERVED); 
            this.stop_after--; 
            if (this.stop_after <= 0) -> this.end_test; 
            $cast(tr, 
                  this.mon.notify.status(apb_monitor::OBSERVED)); 
            tr.display("Notified: "); 
         end 
      join_none 
   endtask: start 
   virtual task wait_for_end(); 
      super.wait_for_end(); 
      @ (this.end_test); 
   endtask: wait_for_end 
   ... 
endclass: tb_env 
`endif 


第七步:标准方法

Rule 4-76  All classes derived from the  vmm_data class shall provide
implementations for the  psdisplay(),  is_valid(),
allocate(), copy() and compare() virtual methods.

virtual function string psdisplay(string prefix = “”);
    $sformat(psdisplay, “%sAPB %s @ 0x%h = 0x%h”, prefix,
             this.kind.name(), this.addr, this.data);
endfunction: psdisplay

Three other standard methods, vmm_data:byte_size(), vmm_data::byte_pack() and
vmm_data::byte_unpack() should also be overloaded for packet-oriented
transactions, where the content of the transaction is transmitted over a serial
interface (Recommendation 4-77).

Recommendation 4-77  All  classes derived from the  vmm_data class should
provide implementations for the  byte_size(),
byte_pack() and byte_unpack() virtual methods.

如果数据需要通过物理接口发送或者在不同的仿真环境(eg .from SV ro systemc)之间传输,则必须实现Recommendation 4-77  的方法。byte_pack方法的具体实现手段应该基于判别属性的值。

第八步:更多关于事务的

vmm_notify通知服务接口发出发出一个随机值给线程或事务处理器,但它一次仅能存储一个事务。

When the transactor is reset, the output channel must be flushed. This is
accomplished in the extension of the vmm_xactor::reset_xactor() method

virtual function void reset_xactor(reset_e rst_typ = SOFT_RST);
      super.reset_xactor(rst_typ);
      this.out_chan.flush();
   endfunction: reset_xactor

对较高层的功能,了解由监视器报告的事务是何时开始和结束的是十分重要的。这些事物的结束点被记录在事务描述符本身(通过vmm_data::STARTED和vmm_data::ENDED通知)。 (Rule 4-142).

virtual protected task main(); 
   super.main(); 
   forever begin 
      apb_rw tr; 
      this.wait_if_stopped(); 
      // Wait for a SETUP cycle  
      do @ (this.sigs.pck);  
      while (this.sigs.pck.psel !== 1'b1 ||  
             this.sigs.pck.penable !== 1'b0);  
      tr = new; 
      tr.notify.indicate(vmm_data::STARTED);  
      tr.kind = (this.sigs.pck.pwrite) ? 
                   apb_rw::WRITE : apb_rw::READ;  
      tr.addr = this.sigs.pck.paddr;  

         @ (this.sigs.pck);  
         if (this.sigs.pck.penable !== 1'b1) begin  
            `vmm_error(this.log, "APB protocol violation: SETUP 
cycle not followed by ENABLE cycle");  
         end  
         tr.data = (tr.kind == apb_rw::READ) ? 
                      this.sigs.pck.prdata :  
                      this.sigs.pck.pwdata;  
         tr.notify.indicate(vmm_data::ENDED);  
         ... 
         this.notify.indicate(OBSERVED, tr); 
         this.out_chan.sneak(tr); 
   end 
   endtask: main 
endclass: apb_monitor 
`endif 


第九步:调试信息

为了能完全的重用,应该理解监视器做什么和不用查看源代码就能调试它的操作。调试信息应该加到合适的点来显示监视器将做什么,正在做什么,或已经做了什么。调试信息应该插入,可以使用`vmm_trace(),`vmm_debug(),`vmm_verbose().

virtual protected task main(); 
      super.main(); 
      forever begin 
         apb_rw tr; 
         this.wait_if_stopped(); 

`vmm_trace(log, "Waiting for start of transaction..."); 

        // Wait for a SETUP cycle  
        do @ (this.sigs.pck);  
        while (this.sigs.pck.psel !== 1'b1 ||  
               this.sigs.pck.penable !== 1'b0);  
        tr = new;

     tr.notify.indicate(vmm_data::STARTED);  
         tr.kind = (this.sigs.pck.pwrite) ? 
                      apb_rw::WRITE : apb_rw::READ;  
         tr.addr = this.sigs.pck.paddr;  
         @ (this.sigs.pck);  
         if (this.sigs.pck.penable !== 1'b1) begin  
            `vmm_error(this.log, "APB protocol violation: SETUP 
cycle not followed by ENABLE cycle");  
         end  

         tr.data = (tr.kind == apb_rw::READ) ? 
                      this.sigs.pck.prdata :  
                      this.sigs.pck.pwdata;  
         tr.notify.indicate(vmm_data::ENDED);  
         ... 
         `vmm_trace(log, {"Observed transaction.../n", 
                          tr.psdisplay("   ")}); 
         this.notify.indicate(OBSERVED, tr); 
         this.out_chan.sneak(tr); 
   end 
   endtask: main 
endclass: apb_monitor 
`endif 


File: Command_Monitor_Xactor/Makefile
% vcs –sverilog –ntb_opts vmm +vmm_log_default=trace ...

第十步:扩展点

回调方法是另一种机制来报告观测到事务。一个事务被检测到后应该使用回调方法。这个回调方法应该能把观测到的事务记录在功能覆盖模型中或者能和期望的响应比较。

回调方法首先应在由基类vmm_xactor_callbacks的扩展类的回调面板中定义为virtual void function。应该使用`vmm_callback宏来调用已注册的回调方法。

File: apb/apb_monitor.sv 
`ifndef APB_MONITOR__SV 
`define APB_MONITOR__SV 
`include “apb_if.sv” 
`include “apb_rw.sv” 
typedef class apb_monitor; 
class apb_monitor_cbs extends vmm_xactor_callbacks; 
   virtual function void post_cycle(apb_monitor xactor, 
                                    apb_rw      cycle); 
   endfunction: post_cycle 
endclass: apb_monitor_cbs 

……………….    

  tr.data = (tr.kind == apb_rw::READ) ? 
                      this.sigs.pck.prdata :  
                      this.sigs.pck.pwdata;  
         tr.notify.indicate(vmm_data::ENDED);  
         `vmm_callback(apb_monitor_cbs, post_cycle(this, tr));  
         `vmm_trace(log, {"Observed transaction.../n",  
                          tr.psdisplay("   ")});  
         this.notify.indicate(OBSERVED, tr);  
         this.out_chan.sneak(tr); 
   end 
   endtask: main 
endclass: apb_monitor 


待续::

第十一步:顶层文件

To help users include all necessary files without having to know the detailed
filenames and file structure of the transactor, interface and transaction descriptor, it
is a good idea to create a top-level file that will automatically include all source files
that make up the verification IP for a protocol.

File: apb/apb.sv 
`ifndef APB__SV 
`define APB__SV 
`include “vmm.sv” 
`include “apb_if.sv” 
`include “apb_rw.sv” 
`include “apb_monitor.sv” 
`endif 


You have now completed the creation of a VMM-compliant command-layer monitor
transactor!
Upon reading this primer, you probably realized that there is much code that is
similar across different monitors. Wouldn’t be nice if you could simply cut-and-paste
from an existing VMM-compliant monitor and only modify what is unique or different
for your protocol? That can easily be done using the “vmmgen” tool provided with

VCS 2006.06-6. Based on a few simple question and answers, it will create a
template for various components of a VMM-compliant monitor.
Command
% rvmgen –l sv

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值