类和对象、包

面向对象编程的三要素:封装(encapsulation)、继承(inheritance)、多态(polymorphism)

类与结构体异同

  • 类的定义的核心是属性声明(property declaration)和方法定义(method definition),所以类是数据和方法的自洽体(self-compatible),即可以保存数据和处理数据,与结构体的重要区别
  • 两者都可以定义数据成员
  • 类变量在声明后,需要构造(construction)才会构建对象(object)实体,而struct在变量声明时已经开辟内存

类与模块的异同

  • 从数据和方法定义看,都可以作为封闭容器来定义和存储
  • 例化,模块必须在仿真一开始就确定是否应该被例化,可通过generate来实现设计结构的变化;对于类而言,可以在仿真的任何时候被构造(开辟内存)创建新的对象。硬件部分必须在仿真一开始就确定下来,即module和其内部过程块、变量都应该是静态的(static);而软件部分,即类的部分可以在仿真任何阶段声明并动态创建出新的对象
  • 封装性,模块变量和方法对外部公共(public)开放,而类根据需要确定访问的权限是默认的公共类型或者保护类型(protected)还是私有类型(local)。
  • 继承性,模块没有继承性可言,无法在原有基础进行新的拓展,唯一可支持的方式恐怕是简单的拷贝和在拷贝的module上修改。而继承性是类的一大特点。

class不能出现initial和always(行为级建模基本语句),变量默认为var,不能定义为硬件里的reg或wire;module里再initial和always里(过程块)调用方法;class通过方法嵌套方法

handle句柄(指针):用来指向对象的指针

类和对象、包

  • 任何类都需要new函数,创建对象会开辟新的内存空间,用来存放新的成员变量和方法。构建函数new()是系统预定义函数,不需要指定返回值,函数会隐式地返回例化后的对象指针,会自动创建空的new函数。
  • new函数三个部分:系统会通过new函数开辟空间;进入new函数进行初始化,返回句柄。
  • 子类定义new函数时,应该首先调用父类的new函数,若父类new函数没有参数,子类也可以省略该调用,而系统编译时会自动添加super.new()

类和对象、包

 例化新对象后,句柄指向最后一个对象

注意:对象在域中会根据其为static、automatic进行销毁,如句柄声明其为动态,过程块外对象就会销毁(某个句柄多次使用时注意受其生命周期的影响)

类和对象、包

与硬件域如module,interface不同的是,class中声明的变量默认类型为动态变量,即其生命周期在仿真开始后的某时间点开始到某时间点结束。class中的方法默认也是动态方法。若使用static声明其为静态,则其生命周期开始于编译阶段,贯穿于整个仿真(静态指编译的时候就有位置,可以不通过对象引用) 类中声明了变量,可以直接引用变量class::var,或者通过例化对象引用object.var

静态方法内可以声明并使用动态变量,但不能使用类的动态成员变量。因为在调用静态方法时,可能并没有创建具体的对象,也因此没有为动态成员变量开辟空间,因此在静态方法中使用类的动态成员变量是禁止,可能会找出内存泄漏,但是静态方法可以使用类的静态变量,因为静态方法同静态变量一样在编译阶段就已经分配好了内存空间。

封装:将属性和方法封装在内部,通过local和protected设置外部访问权限

继承:继承包括了继承父类的成员变量和成员方法

比如local:外部句柄无法调用使用了local声明的变量,会报错,而通过方法调用则不会报错

ck.get_clock();  //调用内部方法,可返回变量
ck.clock;        //报错

this:表明所调用的成员是当前类的成员,而非同名的局部变量或者形式参数

super:继承父类

virtual function void calc_crc;
   super.calc_cac();          //注意super的使用方式

对象创建初始化顺序

  • 子类实例对象在初始化时首先调用父类的构造函数(new函数)
  • 当父类构造函数完成时,会将子类实例对象中各个成员变量按照定义时显示的默认值初始化,如果没有默认值则不被初始化
  • 当成员变量默认值赋予后(声明的同时赋值),才会最后进入用户定义的new函数中执行剩余的初始化代码

成员覆盖

  • 子类句柄指向子类对象(可以访问全局变量),优先访问子类对象;子类句柄赋值给父类句柄后,父类句柄指向之类对象,但只能访问属于其类型的变量和方法
  • Sv里用$cast(子类,父类)对父类句柄转为子类句柄作检查  
  • 默认情况下,没有super或者this来指示作用域,则按照由近到远的原则来引用变量

句柄的使用

  • 句柄可以作为形式参数通过方法来完成对象指针的传递,从外部传入方法内部
  • 句柄可以在方法内部首先完成修改,再由外部完成使用
  • 程序执行时,可以在任何时刻为句柄创建新的对象,并将新的指针赋值给句柄

类和对象、包

 最终报错

task generate trans();
  Transaction t;               //声明句柄
  Transaction fifo[$];        //声明储放句柄的队列,存放的是句柄,不是对象
  t=new();                    //创建对象,   注意,整个代码块只有一个对象
  for (int i=0;i<3;i++) begin
    t.addr=i<<2;
    fifo.push_back(t);          //为什么说有三个句柄?若要存放三个对象,将其new放在for循环里即可
  end                             因为t.addr改变,所以才会说三个句柄  
  t.fifo.pop_front();          //t.addr结果
endtask                        //储存的是句柄,但三个句柄指向一个对象,三个值都为t,结果由最后一次 
                                 赋值决定

包的使用

将不同模块的类定义归整到不同package。通过package实现在多个module、interface和program之*享parameter、data、type、task、function、class。意义在于将软件(类、类型、方法等)封装在不同的命名空间中,以此来与全局命名空间进行隔离。package里无法定义module、interface、program等硬件相关部分,可以定义类、静态方法和静态变量。类封装在包中,不应该在其他地方编译。没有介绍软件之前,硬件(module、interface、program)都会编译到库中。

'include "stimulator.sv"  include属于纯文本替换;注意编译顺序来放置’include

 `include将文件中所有文本原样插入包含的文件中。这是一个预处理语句,`include在import之前执行。他的主要作用就是在package中平铺其他文件,从而在编译时能够将多个文件中定义的类置于这个包中,形成一种逻辑上的包含关系。import不会复制文本内容。但是import可package中内容引入import语句所在的作用域,以帮助编译器能够识别被引用的类,并共享数据。
library是编译的产物,既可以容纳硬件类型,也可以容纳软件类型。从worklibrary里能找到各个package,无法直接找到package中的模块;而mcdt则可以直接从library中索引得到,不用import mcdt。

package是将命名空间分隔开来,使用不同package中的同名类,只需注明使用哪个package。但内部定义的类也应该尽可能独一无二。如加上前缀。

如: regs_pkg::monitor mon1=new();   arb_pkg::monitor mon2=new();   (域的索引)

上一篇:package.json——cli创建的uni-app


下一篇:yarn的安装和使用