关于类里的一些事项

 类型转换

  • 类型转换可以分为静态转换和动态转换。
  • 静态转换即需要在转换的表达式前加上单引号即可(int'(4.0)),该方式并不会对转换值做检查。如果发生转换失败,我们也无从得知。
  • 动态转换即需要使用系统函数$cast(tgt, src)做转换。
  • 静态转换和动态转换均需要操作符号或者系统函数介入,统称为显式转换。
  • 不需要进行转换的一些操作,我们称之为隐式转换。例如赋值语句右侧是4位的矢量,而左侧是5位的矢量,隐式转换会先做位宽扩展((隐式),然后再做赋值。

动态转换

  • 当我们使用类的时候,类句柄的向下转换,即从父类句柄转换为子类句柄时,需要使用$cast()函数进行转换,否则会出现编译错误,这一步也是编译器的保护措施,防止用户出现错误的赋值。
  • 如果将子类句柄赋值给父类句柄时,编译器则认为赋值是合法的,但分别利用子类句柄和父类句柄调相同对象的成员时,将可能有不同的表现。
  • 将一个父类句柄赋值给一个子类句柄并不总是非法的。但是SV编译器对这种直接赋值的做法是禁止的,也就是说无论父类句柄是否真正指向了一个子类对象,赋值给子类句柄时,编译((静态)都将出现错误。
  • 因此需要$cast(tgt, src)来实现句柄类型的动态转换。
  • $cast(tgt, src)会检查句柄所指向的对象类型,而不仅仅检查句柄本身。(比如父类句柄指向子类对象时,转换可以成功,注意tgt是子类句柄,此时可以访问子类)
  • —旦源对象跟目的句柄是同一类型,或者是目的句柄的扩展类,$cast()函数执行即会成功,返回1,否则返回0。

虚方法

  • 类的继承是从继承成员变量和成员方法两个方面。从例码中可以看到test_wr类和test_rd类分别继承了basic_test类的成员变量以及成员方法。(即使子类和父类有同名的变量不同值,但还是继承了,通过super可以查找父类的变量)(子类覆盖override的方法不会继承父类同名方法,只有通过super.method()的方式显式执行才会达到继承的效果)
  • 正是由于类的多态性,使得用户在设计和实现类时,不需要担心句柄指向的对象类型是父类还是子类,只要通过虚方法,就可以实现动态绑定(dynamic binding) ,或者在SV中称之为动态方法查找(dynamic method lookup) 。
  • 我们将已经在编译阶段就可以确定下来调用方法所处作用域的方式称之为静态绑定(static binding) ,而与之相对的是动态绑定。动态绑定指的是,在调用方法时,会在运行时来确定句柄指向对象的类型,再动态指向应该调用的方法。(看句柄指向的对象来调用方法)
  • 为了实现动态绑定,我们将basic_test::test定义为虚方法。
  • 由于声明了basic_test::test为虚方法,系统在执行t.test时,会检查t所指向对象的类型为test_wr类,进而调用test_wr: :test。于是,输出结果与调用wr.test—致。
  • 我们就可以通过虚方法的使用来实现类成员方法调用时的动态查找,用户无需担心使用的是父类句柄还是子类句柄,因为最终都会实现动态方法查找,执行正确的方法。
  • 在为父类定义方法时,如果该方法日后可能会被覆盖或者继承那么应该声明为虚方法
  • 虚方法如果要定义,应该尽量定义在底层父类中。这是因为如果virtual是声明在类继承关系的中间层类中,那么只有从该中间类到其子类的调用链中会遵循动态查找,而最底层类到该中间类的方法调用仍然会遵循静态查找。即使子类方法没有super继承,一样调用同名方法?
  • 虚方法通过virtual声明,只需要声明一次即可。当然再次声明来表明该方法的特性也是可以的。
  • 虚方法的继承也需要遵循相同的参数和返回类型,否则,子类定义的方法须归为同名不同参的其它方法。
  • 没有办法可以使父类句柄索引到子类对象里面的变量。(除了将其转为子类句柄)
//这里使用virtual,主要是希望通过句柄指向的对象来调用相应的函数
class basic_test;
    function new();
        ……
    endfunction
    task test();                                     //virtual task test();
        $display("basic_test::test");
    endtask
endclass
class test_wr extends basic_test; //子类会继承父类的方法变量,即使同名不同值,通过super可获得
    function new();
        ……
    endfunction
    task test();                                          
        super.test();
        $display("test_wr:test");
    endtask
endclass
basic_test t;
test_wr wr;
initial begin
    wr=new();       
    t=wr;
    wr.test();  //basic_test::test                //basic_test::test
                //test_wr::test                   //test_wr::test
    t.test();   //basic_test::test                //basic_test::test
end                                               //test_wr::test

对象拷贝

  • 声明变量和创建对象是两个过程,也可以一步完成。
Packet p1;
p1 = new ;   //new可以有参数,不能有返回值,不能带 virtual
  • 如果将p1赋值给另外一个变量p2,那么依然只有一个对象,只是指向这个对象的句柄有p1和p2。
  • 以下这种方式表示p1和p2代表两个不同的对象。在创建p2对象时,将从p1拷贝其成员变量例如integer、string和句柄等,该种拷贝方式称为浅拷贝(shallow copy)。
Packet p1;
Packet p2;
p1 = new;         //p1=new();
p2 = new pl;      //p2=new()p1;        //p1、p2指向各自对象,p1=p2则只有一个对象
//新创建一个对象,把原有对象里的变量拷贝到新对象
//句柄拷贝,p1和p2指向同一对象
  •  对于拷贝(copy) ,对象的拷贝要比其它SV的变量类型都让人"当心”。因为就SV普通的变量拷贝而言,只需要通过赋值操作符"="就足够了。
  • 而对象的拷贝则无法通过"="来实现,因为这一操作是句柄的赋值,而不是对象的拷贝。
  • 那么如果要拷贝对象,指的是首先创建一个新的对象(开辟新的空间),再将目标对象的成员变量值拷贝给新对象的成员,这就使得新对象与目标对象的成员变量数值保持一致,即完成了对象的拷贝(成员变量的拷贝)。

区别句柄拷贝与对象拷贝的区别

class basic_test;
    ……
    virtual function void copy_data(basic_test t);
      t.def=def;
      t.fin=fin;
    endfunction
    virtual function basic_test copy();
      basic_test t=new(0);
      copy_data(t);
      return t;
    endfunction
endclass

class test_wr extends basic_test;
    ……
    function void copy_data(basic_test t);
      test_wr h;
      super.copy_data(t);  //super,所以会调用父类方法,直接执行  父类句柄到父类的copy_data
      $cast(h,t);         //父类句柄指向子类对象,要添加这个是因为让h也指向子类对象吗?
      h.def=def;         //实现子类句柄只拷贝子类成员
    endfunction
    function basic_test copy();  //函数返回类型为basic_test
      test_wr t=new();
      copy_data(t);     //调用该函数会隐式地将子类句柄转为父类句柄,但依然指向子类对象
      return t;
    endfunction
endclass

module tb;
    ……
    test_wr wr;
    test_wr h;
    initial begin
      wr=new();
      $cast(h,wr.copy());  //h和wr指向不同对象
      h.def=300;
    end
endmodule
  • 将成员拷贝函数copy_data()和新对象生成函数copy()分为两个方法,这样使得子类继承和方法复用较为容易。
  • 为了保证父类和子类的成员均可以完成拷贝,将拷贝方法声明为虚方法,且遵循只拷贝该类的域成员的原则,父类的成员拷贝应由父类的拷贝方法完成。
  • 在实现copy_data()过程中应该注意句柄的类型转换,保证转换后的句柄可以访问类成员变量。

回调函数

  • 理想的验证环境是在被移植做水平复用或者垂直复用时,应当尽可能少地修改模块验证环境本身,只在外部做少量的配置,或者定制化修改就可以嵌入到新的环境中。
  • 要做到这一点,一方面我们可以通过顶层环境的配置对象自顶向下进行配置参数传递,另外一方面我们可以在测试程序不修改原始类的情况下注入新的代码。
  • 例如,当我们需要修改stimulator的行为时,有两种选择,一个是修改父类,但针对父类的会传播到其它子类;另外一个选择是,在父类定义方法时,预留回调函数入口,使得在继承的子类中填充回调函数,就可以完成对父类方法的修改。
virtual class Driver_cbs; //Driver回调虚类   不能例化,可以继承,虚类是提供模板
    virtual task pre_tx(ref Transaction tr,ref bit drop);//两个ref,怎么第一个就是输入第二输出
        ……
    endtask
    virtual task post_tx(ref Transaction tr);
        ……
    endtask
endclass

class Driver;
    Driver_cbs cbs[$];  //队列,里面放着句柄
    task run();
endclass

//回调函数主要有三步
//第一预留入口
task Driver::run;
    forever begin
        ……
        <pre_callback>
        transmit(tr);
        <post_callback>
        ……
    end
endtask
//第二步,定义回调类
class Driver_cbs_drop extends Driver_cbs;  //继承自虚类
    virtual task pre_tx(ref Trsansaction tr,ref bit drop);
        drop=($urandom(0,99)==0);
    endtask
endclass
//第三步,例化以及添加回调类的实例
program automatic test;
    ……
    begin
        Driver_cbs_drop dcd=new();
        env.drv.cbs.push_back(dcd);
    end
    env.run;
    ……
endprogram

参数化的类

  • 参数化的使用是为了提高代码的复用率。
  • 无论是设计还是验证,如果代码会被更多的人使用或者被更多的项目所采用,那么就需要考虑使用参数来提高复用率。
  • 参数的使用越合理,后期维护的成本就会相应降低。
  • 在硬件设计中,参数往往是整形,例如端口数目或者位宽。在验证环境中,参数的使用更加灵活,可以使用各种类型来做类定义时的参数
  • 在SV中,可以为类增加若干个数据类型参数,并在声明类句柄的时候指定类型
  • SV的类参数化近似于C++中的模板。
//该maibox只能用于操作整数类        类定义时添加参数
class mailbox;                         //class maibox #(type T=int);  //参数名为T,值为int
    local int queue[$];                // local T queue[$];
    task put(input int i);             //task put(input T i);
        queue.push_back(i);
    endtask
    task get(ref int o);               //task get(ref T o);
        wait(queue.size()>0);
        o=queue.pop_front();
    endtask
    task peek(ref int o);              //task peek(ref T o);
        wait(queue.size()>0); 
        o=queue[0];
    endtask
endclass



//maibox #(real) mb;   //创建一个存储real类型的maibox 
//mb=new();
  • 同一个参数化类,两个指定不同类型T的句柄可以使用$cast完成句柄的类型转换
  • 一个参数类在定义时可以指定多个参数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值