SV中的OOP

摘要:
1) 本地/受保护的Property(变量)和Method(函数/任务)的封装可以用Property和Method来描述。Local表示成员仅对此类的对象可见。

OOP:Object-Oriented Programming,有两点个人认为适合验证环境的搭建:1)Property(变量)和Method(function/task)的封装,其实是BFM模型更方便的应

         用。2)继承(Inheritance)与合成(Composition)非常适合一个架构的搭建。

在SV中,类可以定义在program, module, package中,但是一般一个类或几个相关的类会单独写在一个文件中。最终在program中调用。而且在SV中,所有成员

         默认都是public类型的,除非显示的用local/protected来声明。实际上,SV class中的变量应该尽可能的用public并且rand,来增加可控性。

local/protected都可以针对property和method来说明,local表示member只对该class的对象可见,extend出的subclass也是不可见的。

                                                                                     但是该class中non-local的method调用了local的method或者property,

                                                                                                                           inherit之后的method也是可以正常执行的。

                                                                         protected表示member对该class和它的subclass都是可以见的。 对外部的大环境不可见。

基类的指针可以直接指向扩展类的对象,但是只能调用扩展类中基类的那一部分,

基类的指针也可以直接转换为扩展类的指针,通过$cast函数,此时的基类指针指向的已经是扩展类对象,通过cast,转变为扩展类指针,可以调用扩展类中自有的元素。 

SV默认会为每个类创建一个new函数,并初始化对象(二值变量为0,四值变量为X)。用户一般自定义new函数赋初值,分配空间可以交给SV来做。

显式的释放一个句柄指向的对象的空间 tr=null。

在SV中可以用关键字(Static)创建静态变量,可以让类有一个类级别的全局变量。一般子初始化时即赋值,还可以建一个静态的方法,来单独的操作静态变量。静态变

            量可以通过类名::变量名的方式来引用。类中的其他变量都默认是automatic的修饰符,而且可以通过this.变量名,来直接调用类一级别的变量。在类的外

            部定义方法时,可以用关键字extern来在类中声明,后在类外通过类名::方法名的方式来定义。

当类作为一个function的传递参数时,此时不加ref传递的是一个句柄的复制量。在下边的例子中,tr只有一个t的句柄的复制量,所以是Null,之后tr的改变和t没有直

            接关系。

            Transaction   t;                                         function void create(Transaction tr);

            initial  begin                                                                     tr = new ();

                          creat(t);                                                            tr.addr = 42;

                          $display(t.addr);                         endfunction 

                      end

一个对象的复制,可以通过简单的new函数来实现,也可以通过一种自定义的copy函数来实现,这种实现方式在类包含中,很适用。

new函数,当要复制的对象中还包含另一个类的对象时,会直接复制另一个对象的所有东西,包括id,句柄。

            Transaction  src, dst;                            function    Transaction copy

             initial  begin                                                                 copy = new();

                           src = new;                                                     copy.addr = addr;

                           dst = new src;                                                copy.stats = stats.copy();

                       end                                          endfunction

copy函数,在每一个类中都定义,在使用时,递归调用。这是id号都会刷新,而且因为对象都是new来构造出来的,所以更符合设计意图。

        

在一个类中例化另一个类(Composition),这时对方法的调用:ststs.startT.function,为了确保在编译时,不会因为编译顺序而造成某个类还不能被识别,可以在文

           件开头加 typedef class Statistics, 来先声明类Statistics。而且此时外层类的new函数中,必须包含内层类的new函数。这种composition的结构非常适合

           去构建一个大的验证框架,因为它可以将各个部分联系起来。

类的继承(Inheritance),扩展类拥有基类的一切变量与方法,在扩展类中调用基类的变量与函数,可以通过super.+变量/方法来调用。在扩展类的构造函数中,应该

           先对基类先进行new函数。在扩展类的对象中调用对象时,SV比较句柄的类型来决定调用的是基类function还是扩展类function。但是当function是virtual

           类型时,SV会根据对象的类型来决定调用基类function还是扩展类function。推荐需要扩展的基类中使用virtual function的形式,这样符合OOP的多

           态概念,也有利于向下转换。

           class  Transaction;                                                                     class extends Transaction;

                           rand bit [31:0]src,dst,data[8];                                               rand  bit  bad_crc;

                           bit [31:0]crc;                                                                        virtual  function  void  calc_crc();

                           virual function void cal_crc();                                                                super.calc_crc();

                                             crc = src^dst^data.xor;                                                      if(bad_crc) crc = ~crc;

                                    endfunction                                                                              endfunction

           endclass: Transaction                                                                 endclass:Badtr

                            Transaction  tr;

                            Badtr bad;

                            initial  begin

                                          tr = new ();

                                          tr.calc_crc ();                   //调用基类类型

                                          bad = new();

                                          bad.calc_crc ();                //调用扩展类类型

                                          tr = bad;                         //基类句柄可以直接指向扩展类对象

                                          tr.calc_crc ();                  //由对象类型来决定,所以调用BadTr.calc_crc

                                          $cast(bad , tr);               //将指向扩展类对象的基类句柄指向扩展类句柄。只有这种情况下,可以成功,$cast非零

                                          bad.calc_crc ();              //由对象类型决定,所以调用BadTr.cac_crc

                                      end

BluePrint pattern:其实是类的继承在可重用验证环境中的一种典型用法。以Transaction为例,定义一个Transaction的基类,然后再定义一个Transaction的扩展

                           类,然后在顶层Test中,指定Generator需要哪一个Transaction。其实和UVM中的sequencer调用sequence如出一辙。

               class Generator;                                                                         class Environment;

                          mailbox gen2drv;                                                                        Generator gen;

                          Transaction tr;                                                                            Driver drv;

                          function new (input mailbox gen2drv)                                           mailbox gen2drv;

                                                this.gen2drv = gen2drv;                                        function void build;      //new函数的构建,mailbox, drv, gen

                                                tr = new();                                                           task  run();

                          endfunction                                                                                 task  wrap_up() ; 

                          task run;                                                                          endclass

                                   Transaction tr1;

                                   forever begin

                                                  assert(tr.randomize() );

                                                  tr1 = tr.copy();       //copy一份到driver

                                                  gen2drv.put (tr1);

                                              end

                           endtask

               endclass

               program  automatic test;

                       Environment  env;

                           initial begin

                                       env = new();

                                       env.build();        //new函数

                                           begin
                                               BadTr bad = new();

                                               env.gen.tr = bad;           //通过类的顶层关系来调用,Transaction的替换

                                           end

                                       env.run();

                                       env.wrap_up();

                                    end

               endprogram

CallBack:在顶层中定义一个Task,在Driver中调用。便于直接添加task,并且因为是在顶层,所以更好针对项目来定制。

                class Driver;

                           Driver_cbs cbs[$];   //用于存放扩展类的句柄,这个扩展类可以使driver也可以是scoreboard,只要是需要回调的。

                           task  run();

                                     bit drop;

                                     Transaction tr;

                                     forever  begin

                                                     drop = 0;

                                                     agt2drvget(tr);

                                                     foreach (cbs[i])  cbs[i].pre_tx(tr, drop);        //前回调

                                                                   if(!drop)  continue;

                                                     

                                                              transmit (tr);      //最基本的driver就负责一种发送transaction

                                                    

                                                     foreach (cbs[i]) cbs[i].post_tx(tr);              //后回调

                                                 end

                           endtask

                  class  Driver_cbs_drop  extends  Driver_cbs;          

                                 scoreboard scb;     

                                 virtual task pre_tx (ref Transaction tr, ref bit drop)          //从一个纯虚类继承而来,之所以使用纯虚类,为了能够重载

                                    scb.save_expected (tr);                      //与scoreboard连接起来,

                                    drop = ($urandom_range (0,99) == 0);   //随机丢掉一个transaction                         

                  endclass

                  program autmatic test;                                                                              class Scoreboard;

                         Environment env;                                                                                        Transaction scb[$];

                         initial  begin                                                                                                function void save_expect (input Transaction tr)

                                       env= new();                                                                                 function void compare_actual( input Trans..  tr)

                                       env.build();                                                                          enclass   //一个简单的scoreboard模型

                                       begin

                                           Driver_cbs_drop  dcd = new();

                                           env.drv.cbs.push_back(dcd);         //在顶层加入一个task

                                       end

                                   end

                  endprogram

纯虚类和纯虚方法:只为继承的基类和方法。纯虚方法+pure声明,只需要定义一个prototype,并且只能在纯虚类中定义,必须继承才能使用。同样纯虚类,可以定

                          义非纯虚方法,但是还是必须继承才能例化。

                  virtual  class  BaseTr;

                                 static int count;

                                 int id;

                                 function new();

                                                id = count ++;

                                 endfunction

                                 pure virtual function bit compare(input BaseTr to);

                                 pure virtual function BaseTr copy(input BaseTr to = null)

                             endclass

还有一个问题:在基类中定义虚方法时,copy函数可以进一步优化。因为虚函数在基类和扩展类中,必须参数一致,返回类型一样,所以建议将copy_data单独分出

                    来。copy_data中传入指向扩展对象的指针,copy函数内进行new。copy的返回类型可以是基类指针,所以也可以是扩展类对象。

                    class  BadTr extends Transaction

                                 rand bit bad_crc;

                                 virtual function void copy_data(input Transaction tr);

                                               BadTr bad;

                                               super.copy_data(tr);          //这样返回的还是基类句柄,因为copy_data是virtual function

                                               $cast(bad,tr)

                                               bad.bad_crc = bad_crc;     //此处返回的应该是基类指针,也可以是扩展类对象

                                           endfunction

                                。。。。。。。

                                virtual  function void copy(input Transaction1 to == null);       //新的copy函数,更加健壮

                                               BadTr  bad;

                                               if(to == null)

                                                      bad = new();

                                               else  bad = to;

                                               copy_data(bad);          //扩展类对象。

                                               return bad;

                                           endfunction

基类句柄可以直接指向扩展类对象,此时基类的句柄可以赋值给扩展类的句柄$cast(base,extends),来访问扩展类中的变量。

如果基类句柄没有指向扩展类对象,那这个复制会报错。

免责声明:文章转载自《SV中的OOP》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇vue+大文件断点续传多视角学习的几篇文章整理下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

ADO.net数据访问方法

ADO.NET是一组用于和数据源进行交互的面向对象的类库。 核心组件有两个:   DataSet 是 ADO.NET 的非连接(断开)结构的核心组件。DataSet 的设计目的很明确:为了实现独立于任何数据源的数据访问。因此,ADO.NET结构可以用于多种不同的数据源,用于 XML 数据,或用于管理应用程序本地的数据。DataSet 包含一个或多个 Dat...

sqlserver 表循环-游标、表变量、临时表

SQL Server遍历表的几种方法 阅读目录 使用游标 使用表变量 使用临时表 在数据库开发过程中,我们经常会碰到要遍历数据表的情形,一提到遍历表,我们第一印象可能就想到使用游标,使用游标虽然直观易懂,但是它不符合面向集合操作的原则,而且性能也比面向集合低。当然,从面向集合操作的角度出发,也有两种方法可以进行遍历表的操作,总结起来,遍历表有下面...

Java——BufferedImage对象

BufferedImage对象中最重要的两个组件是Raster与ColorModel,分别用于存储图像的像素数据和颜色数据。 1、Raster对象的作用与像素存储 BufferedImage支持从Raster对象中获取任意位置(x,y)点的像素值p(x,y) image.getRaster().getDataElements(x,y,width,heigh...

Robot Framework自动化测试Selenium2Library库详细用法

一、浏览器驱动   通过不同的浏览器执行脚本。   Open Browser Htpp://www.xxx.com chrome   浏览器对应的关键字: firefox FireFox ff internetexplorer   Internet Explorer ie googlechrome   Google Chrome  ...

JavaScript学习笔记及知识点整理_1

一.js的基础部分 1.==和===的区别:==在判断是否相等的时候会进行类型转换,有时会得到非常奇怪的结果,因此一般情况下都是用===判断是否相等2.strict模式:在js中,如果一个变量没有用var进行声明,那么这个变量将会变成全局变量.采用strict模式声明后,如果该变量没有用var声明,那么将会报错!采用strict模式的方法是在js的第一行加...

[转]解决Xilinx Platform Studio无法打开 设置 环境变量

我安装好Xilinx ISE Design Suit 12.3后,ISE可以正常打开,但是Xilinx Platform Studio却无法打开,弹出的DOS窗口提示说——“Environment variable XILINX is not set - A compatible version of ISE tools must be installed...