VMM coding style -- recommended by synopsys AE

1. 定义一个系统级的config类, 如 syscfg.  在类中集中了所有的控制变量(总开关),这些变量决定了是否允许randomize,error injection, delay injection, 产生包的个数,等等

     这样可以有效的消除一些不必要的callback。一般callback只是用于将某段代码开放给后面的维护人员以便进行一些复杂的修改之用。

     Callback methods are a transactor mechanism that provides user access to data objects at various points in the normal 'flow' of the object. Callbacks are the primary means of gaining access to transaction objects for both scoreboarding and functional coverage.

 

2. main函数要尽量的简单清晰,尽量调用task,以便知道每一步做什么。 格式规定如下

 

virtual protected task main(); super.main // 不需要fork join_noe 包裹 fork // 并发多线程 while(1) begin taskA; //一定要有延迟或等待 end while(1) begin taskB; end while(1) begin taskC; end join endtask

 为何不一定要fork  ... join_none? 那是因为main 早被 fork --- join_none 包围起来了。 在 vmm_xactor 的new函数里面  fork    .....     main()    .....   join_none


3.  关于message的输出应该在一个transactor get 上一级传来的 transaction descriptor 并处理好之后 打印出有关的消息。打印时以块的形式打印

msg={msg, $psprintf("===============================/n")}; .... msg={msg, $psprintf("+ bala bala .................+/n",var...)}; .... msg={msg, $psprintf("===============================/n")}; .... `vmm_note(this.log, msg); // print as block   

 

 4.  何时用callback?

      有必要给其他人开放修改入口

      收集coverage 到scoreboard中以便统一维护

      transactor 到 transactor(包括scoreboard)优先考虑channel 而不是callback。 对于只传一次的用对象句柄。

 

5.  有的design是控制芯片,scoreboard不一定适用,可以灵活变通。直接使用monitor。

 

6.  一般我们强烈推荐使用一个interface,多个modport。直接把interface赋值给modport是允许的。通常推荐在tb_env中使用总的interface,在各个transactor的new函数的参数也是总的interface,而在transactor的成员声明中使用使用modport。如下:

class svidBFM extends vmm_xactor; virtual svidIntf.md_svid svid_intf; // svid bus virtual svidIntf.md_mon mon_intf; // check some flag extern function new( virtual svidIntf svidintf, svidTrans_channel bfm_chan = null); .... endclass function svidBFM::new( virtual svidIntf svidintf, svidTrans_channel bfm_chan = null); super.new("svidBFM","class"); // naming the svidBFM this.svid_intf=svidintf; // one interface multi-modport : bfm this.mon_intf=svidintf; // one interface multi-modport : monitor if(bfm_chan==null) bfm_chan = new("svidBFM bfm_chan", "class"); this.bfm_chan=bfm_chan; this.vclk_stop=1'b1; endfunction

 

 

7. 在RALF文件中推荐显示的声明register和field的地址,而不要寄希望与地址自动增加1. filed名在block中要具有唯一性(不同于register和任何其他field) RALF中system中byte声明的含义是整个系统占用的register和memory的space(以byte统计),这个意义不大,也可省略。RALF中 为了避免field和register同名, register的命名带下划线,而field的命名不带下划线,而用大小写比较好

 

 =============================================================================

1.  在verdi 中进行单步调试的时候 如果要波形同步必须在nwave中打开auto update. verdi的单步调试不同于dve,需要先把波形全部dump出来然后,load波形进行单步调试。verdi吃进vmm时,只需要指定+incdir+$VCS_HOME/ect/vmm verdi就会自动在VCS_HOME下搜寻vmm.sv

 

2.  command == SETPS   (即非枚举变量 == 枚举值)是可以的,要打印command,则需要

cmd_e cmd; string cmd_str; if(! $cast(cmd, command)) begin ; //然后用cmd.name打印。 cmd_str="unsupported"; end else begin cmd_str=cmd.name(); end  

3.  vmm_data的扩展类中如果需要引入了外部全局变量(REGCfg or SYSCfg),可以在此扩展类中用static定义REGCfg和SYSCfg的静态句柄。这样只需要在env gencfg时用 svidTrans::reg_cfg=reg_cfg;就完成了静态赋值。因为vmm_data以及它的扩展类的new函数中不允许带有参数,否则就不能使用vmm_atomic_generator了。

 

 

4. transactor中引入控制各个错误注入的子开关,这些开关是rand类型。而总开关在SYSCfg类中定义为非随机bit。在transactor中(一般是中间级XformXactor)定义总开关和子开关的行为,如下

  if (gbl_switch) begin default... if(sub_switch) begin end if(sub_switch) begin end ... end

 

 一般transaction中的constraint要越简单越好,完全可以不要任何constraint。 等待testcase中定义一个的transactor的扩展类,扩展类中指明了constraint。 这样非常便于维护。testcase中定义的transactor扩展类,通常针对不同的定制模式,采取以下两种方法:

         1) 增加关于data_id的 constraints

 

         2) 覆盖 post_randomize(),给出完全定制的 packet.

 

我们可以借鉴VIP中constraint的定义分类:

         1) Valid ranges: Valid ranges are what the model can handle, which may not be protocol compliant. The valid

                ranges are preset constraints that should not be disabled.

         2) Reasonable constraints: These constraints represent typical real world values for devices.

 

 

 

对于transaction的constraint,我们通常可以设置用户的testcase定义的constraint, user_define

 

再transaction类定义时,声明了空的constraint user_define; constraint cfg_mc_c::user_define { rw_delay_type == 1; }

 

4. 在XformXactor实现多个channel输入,一个channel输出的例子

   先到先传

task main(); super.main(); fork while(1) begin // channel 1 peek // deal with transaction // put into out channel // channel 1 get end while(1) begin // channel 2 peek // deal with transaction // put into out channel // channel 2 get end join endtask

 

  优先级仲裁

task main(); super.main(); fork while(1) begin // channel 1 peek // put into queue // channel 1 get end while(1) begin // channel 2 peek // put into queue // channel 2 get end while(1) begin // 查询 queue // 当queue中有值时,根据优先级送出 end join endtask

        

5. vmm_data 也可发出通知事件。xactor也可根据vmm_data的通知进行相应的操作。但是必须在vmm_data的object撤销之前。

 

 

6. 所有的xactor(包括BFM)推荐包含 xxxSYSCfg  sys_cfg,  xxxxREGCfg   reg_cfg,   ral_sys_xxxRegSys  ral_sys 这三个handler。应为写xactor时随时,可能使用这些handler。

 

7. 一般应该遵循以下原则,task中不包含fork_join, fork_join仅仅出现在main函数中。

 

8. 在@(posedge xxxx) 声明中,xxx 不要用clocking block中的同步信号 md_x.cb.xx, 而是应该使用异步信号。

 

9. 如果在transactor中要通过backdoor的方式大量使用design的register,不妨吧design中所有的register都列出类,并用一个task, 譬如peek_registers(),省得以后来回调用back door peek函数。

     

你可能感兴趣的:(SystemVerilog)