irun MSIE tips


irun MSIE

前一阵子折腾MSIE的验证环境,现在环境比较稳定了,也是时候来总结一下了。

MSIE的全称是Multi-Snapshot Incremental Elaboration,是cadence的irun工具的一个feature。使用这个feature可以节约仿真的编译时间,其本质就是把一些子模块编译好,顶层编译的时候直接把编译好的子模块的database拿过来,节省了elaboration的时间。

举一个例子,比如下面的一个验证环境。

<img src="../../images/msie.png" class="img-thumbnail" width="60%" >

其中,相对稳定基本不变的部分是cpu,其他的比如dut和tb_top会经常改变。那么cpu部分就可以提前编译好,作为一个database,官方叫法是primary snapshot。"挖掉"cpu的dut也可以作为一个primary snapshot。

具体的irun命令是:

cpu_compile:
	irun $(MSIE_PRIM_OPT) -top cpu -name cpu -f cpu.f -nclibdirname $(WORKDIR)/cpu -l cpu_compile.log -incrpath tb_top.u_dut.u_cpu

dut_compile:
	irun $(MSIE_PRIM_OPT) -top dut -name dut -f dut_no_cpu.f -nclibdirname $(WORKDIR)/dut_no_cpu -l dut_compile.log -incrbind cpu -incrpath tb_top.u_dut

tb_compile:
	irun $(MSIE_INCR_OPT) -top tb_top -f tb_top_no_dut.f  -l tb_compile.log -primname dut@$(WORKDIR)/dut_no_cpu -primname cpu@$(WORKDIR)/cpu

如果是后仿,还要做sdf反标,就要在compile每一个primary snapshot的时候把sdf文件吃进去,option是-sdf_cmd_file xx.cmd。sdf的拆分需要根据partition情况使用工具ncsdfc来完成。

Perl tips series 3


perl OOP 浅尝辄止二

perl中,继承类的实现,如下。

package CD::PIANO;
use base qw(CD);

sub init {
    my $self = shift;
    $self->SUPER::init('piano song');
    $self->{player} = shift;
    return $self;
}

sub play {
    my $self = shift;
    print "Welcome to a wonderful world!\n";
    print "Played by $self->{player}\n";
}

1;
  • 关键字base,来传达继承于哪个类;
  • 这个package的名字叫"CD::PIANO",既反映了继承关系,也同时反映了文件存储的位置,放在了目录CD下面,和目录CD并列的是CD.pm;
  • 关键字SUPER,来调用父类的函数;
  • 函数print_me的实现在其父类CD中;
  • 和其他语言不同,perl的继承类可以没有自己的new函数,把其父类的new函数继承来就可以了;
use FindBin;
use lib "$FindBin::Bin";
use CD::PIANO;

my $piano = CD::PIANO->new;

$piano->init('Someone');

$piano->play;

$piano->print_me;

运行结果如下:

Welcome to a wonderful world!
Played by Someone
music = piano song

Perl tips series 2


perl OOP 浅尝辄止一

在perl中,至少是perl5中,class是通过package来实现的,如下。

package CD;
use strict;
sub new {
    my $class = shift;
    my $self = {};
    bless $self, $class;
}
sub init {
    my $self = shift;
    $self->{music} = shift;
    return $self;
}

sub print_me {
    my $self = shift;
    print "music = $self->{music}\n";
} 
1;

有几个注意的点:

  • bless是把hash ref和class关联起来,每一个objet其实就是一个hash ref;
  • 注意$class和$self不要混淆了,$class就是new的时候传进来的类名字,$self是hash ref;
  • package 最后的返回1,必不可少;

我们来使用这个类,如下:

use FindBin;
use lib "$FindBin::Bin";
use CD;

my $cd = CD->new;

$cd->init('ABC');

$cd->print_me;
  • 为了让perl解释器能够找到我们的类,通过FindBin来指出路径;
  • 构建一个类的对象时,千万不能写成CD::new,要定位好久才发现,CD::new其实是选择了namespace的CD这个package里的new,并不是执行一个class的new函数;

Perl tips series 1


print中内嵌函数

有的时候,我们想要print一些信息,但是又要对信息处理后打印,我们不得不分成两部分来做。

my @array = qw(abc de fgh);

my $tmp = join ':', @array;

print "$tmp\n";

如果特别想省去$tmp这个临时变量,可以像下面的方式来写。

@array = qw(abc de fgh);
print "@{[join ':',@array]}";

@{[code]}这种写法是把code的执行结果先转成array reference,然后在变成array,很巧妙的写法!

按照文件的创建时间排序

这个需求的出发场景是,比如一个工具生成autohref.txt文件,等再run一次,工具生成autohref1.txt,以此类推。 如果我想对最新的autohref*.txt文件进行处理,一般的方法是找匹配/autohref(\d+)\.txt/的文件,同时$1是最大的。 但是,如果用户删除了其中一个autohref4.txt,那么下次run这个工具,就有可能破坏了这个前提。所以,去找最新的这个文件是比较保险的。

my @files = glob './autohref*.txt';

my @files_in_order = sort {(stat $b)[10] <=> (stat $a)[10]} @files;

my $latest_file = shift @files_in_order;

函数stat的用法,来这里看

Perl tips series 0


删除文件中重复的行

使用一行命令就能实现,连编辑器都不用打开。

perl -ne 'print unless $a{$_}++' file.txt

上一行命令中,-n表示对于每一行执行命令,-e后跟具体的命令。 $_存的是每一行的内容,hash变量%a里存的是每一个行出现的次数,因为是先判断后++,类似于c语言中的i++。所以,只会打印第一次出现的行内容。 unless可以看作是if的反过来。

正则表达式debugger

无意中发现一个特别棒的perl module,用来debug正则表达式,那就是Regexp::Debugger。

perl -E "use Regexp::Debugger; 'abbc' =~ /(a|b)b+c/x"

shell里运行上面的命令,可以看到下面的画面,真是赞啊!

<img src="/../images/reg.png" class="img-thumbnail" width="60%" >

写perl的注释和说明


perl里的注释

为了让代码更具有可读性,注释必不可少。

my $h; # $h is hash reference 

在perl里面有更漂亮的注释或者文档写法,就是POD格式,一些主流的编辑器都能识别这个格式。比如vim,emacs,sublime。

<img src="../../images/pod_emacs.png" class="img-thumbnail" width="60%">

在perl程序中,以“=”开头,一些关键字比如head1,head2然后紧跟着一个空行,都被认为是pod格式的说明注释,可以看到"my $err = '';"这一段也被注释掉了,因为perl默认会去找关键字“=cut”作为pod注释的结束标志。

我们在shell里运行这个文件,比如我们把它命名为pod.pl。

perldoc pod.pl

我们能得到我们的注释和说明如下:

<img src="../../images/pod_sh.png" class="img-thumbnail" width="60%" </img>

另外使用其他工具,比如pod2text,pod2html,pod2latex等,可以把我们的注释转成多种格式。

<img src="../../images/pod_trans.png" class="img-thumbnail" width="60%" </img>

perl中的glob函数


glob帮助我们快速操作文件

当我们想对某目录下的文件做操作的时候,使用glob函数是非常方便的。 比如我们想把当前路径的test目录下的所有以.txt结尾的文件列出来:

@files = glob './test/*.txt';
print @files;

或者我们可以这样隐式的写法:

while (<'./test/*.txt'>) {
    print "$_\n";
}

再也不要自己去开发轮子,处理满足某一个条件的多个文件了,Yeah!

OOP in Systemverilog


system verilog中的面向对象

下面的代码中,bad_tr继承tr,所以bad_tr具有了成员变量num和good的同时,也有自己的变量bad。

class tr;
   bit num;
   bit good;

   function new();
      num = 0;
      good = 0;
   endfunction // new

endclass // tr

class bad_tr extends tr;
   bit bad;

   function new();
      super.new();
      bad = 1;
   endfunction // new

endclass // bad_tr

下面的代码,在initial块中create了两个对象,分别是m_tr和m_bad_tr,并且m_tr指向了其子类对象m_bad_tr。当想做downcast时,也就是说比如让m_bad_tr1指向tr的子类时,要用$cast函数。

tr     m_tr;
bad_tr m_bad_tr;

bad_tr m_bad_tr1;

initial begin
   m_tr = new();
   m_bad_tr = new();
   m_tr = m_bad_tr;

   if ($cast(m_bad_tr1, m_tr))
     $display("Successful!");
   else
     $display("Can not assign!");

end

Why we use p_sequencer in UVM?


p_sequencer

经常会看到在sequence和virtual sequence中,使用了宏`uvm_declare_p_sequencer。那么,为什么我们要使用p_sequencer呢?

class base_vseq extends uvm_sequence;
  function new(string name="base_vseq");
    super.new(name);
  endfunction

  `uvm_object_utils(base_vseq)
  `uvm_declare_p_sequencer(uart_ctrl_virtual_sequencer)
 endclass

从继承的关系上,uvm_sequence_item->uvm_sequence_base->uvm_sequence,uvm_sequence_item中有一个成员变量m_sequencer(喂喂喂,怎么说m_sequencer了?嗯嗯,别急,往后看),当我们在test的run_phase中start一个sequence的时候,其实就是给m_sequencer赋值了,把我们环境的sequencer赋给m_sequencer。m_sequencer是不带parameter的。

class uvm_sequence_item extends uvm_transaction;

  local      int                m_sequence_id = -1;
  protected  bit                m_use_sequence_info;
  protected  int                m_depth = -1;

  protected  uvm_sequencer_base m_sequencer;

把宏`uvm_declare_p_sequencer展开看,就是把m_sequencer赋值给了p_sequencer,当然了,m_sequncer所指向的对象,一定要是p_sequencer或者p_sequencer的子类,否者cast函数会报错的。

`define uvm_declare_p_sequencer(SEQUENCER) \
  SEQUENCER p_sequencer;\
  virtual function void m_set_p_sequencer();\
    super.m_set_p_sequencer(); \
    if( !$cast(p_sequencer, m_sequencer)) \
	`uvm_fatal("DCLPSQ", \
	$sformatf("%m %s Error casting p_sequencer, please verify that this sequence/sequence item is intended to execute on this type of sequencer", get_full_name())) \
  endfunction 

那么,费了这么多事,要做什么呢?其实就是传递配置。因为sequence和component不一样,component是在build_phase环节就有了,而sequence是在run_phase阶段才会第一次出现,sequence想要去从环境上拿到配置信息,一定要通过它的m_sequencer,但是m_sequencer是不带parameter的,和我们start的时候一定不match,所以UVM就事先在定义sequence的时候,把p_sequencer这个带parameter的sequencer准备好,方便后面的使用。

virtual task body();
      response_queue_error_report_disabled = 1;
      start_addr = `TX_FIFO_REG;
      while (num_of_wr) begin
	`uvm_info(get_type_name(), $sformatf("Starting %0d Writes...", num_of_wr), UVM_LOW)
	for (int i = 0; i < num_of_wr; i++) begin
	  if (p_sequencer.tful) begin
	    num_of_wr -= i;
	    `uvm_info("UART_APB_SEQLIB", $sformatf("Breaking from apb_to_uart_wr since tfifo is not empty yet, pending num_of_wr = %d", num_of_wr), UVM_LOW)
	    #10000;
	    break;
	  end
	  `uvm_do_with(req, 
	    { req.addr == start_addr;
	      req.direction == APB_WRITE;
	      req.delay == del; } )
	  #200;
	  if (i == num_of_wr - 1)
	     num_of_wr = 0;
	end
      end
    endtask

上面这段代码就是sequence的片段,可以看到直接访问了p_sequencer的变量tful,如果写成m_sequencer.tful,是一定不成的。