PSS基础篇之十:继承

原创 路科验证 2022-01-20 12:34

PSS支持面向对象的继承(inheritance)和扩展(extension)概念,相比于SV/UVM,PSS多出来的扩展特性可以更好地实现PSS模型的复用和移植。对于继承而言,我们都很熟悉的是,子类可以继承父类的成员,并且在此基础上添加新的成员,或者使用同名的成员去掩盖父类的成员(父类成员仍然存在)。对于扩展,则在于我们可以在其它地方对原有的类型的定义添加新的成员(不需要添加新的类型定义)。适用于继承和扩展的类型除了像action/object/component这样与class相近的类型,还有struct类型,这些类型的继承和扩展的例子我们将在接下来的示例代码中看到。最后,我们还将介绍覆盖(override)的使用方法。


继承

相比于PSS 1.0a,PSS 2.0将继承这个与SV使用非常相似的面向对象特性单独整理出来,与扩展特性放置在独立的一章来讲述。对于这些可以继承的类型(action,component,struct,buffer,stream,state,resource),它们的子类在继承了父类的成员以后,如果新增的成员的父类成员同名,那么可以像SV一样,使用'super.name'的方法来访问父类成员变量,也可以在子类同名函数中,使用'super.name(args)'来继承父类的同名函数。


constraint继承

例如下面这个标准中的示例代码,提供了component以及action的继承方法。der_c继承了base_c,而der_c::der_a继承了base_c::base_a。这样的话,der_a继承的是base_a的所有成员和约束,并且在此基础上新添加了成员和约束。但同时需要注意,der_a::c这一约束与base_a::c同名,在遇到同名时,子类成员会将父类成员掩盖(mask),那么在约束求解时,base_a::c的约束将由于被掩盖的缘故,不会参与到der_a的约束求解过程中。

component base_c {  action base_a {    rand int i;    rand bit[31:0] b;    constraint {i < 10;}    constraint c {b > 7;}  }}component der_c : base_c {  action der_a : base_a {    rand int j;    constraint {j > 5 -> i < 5;}    constraint c {j < 10 -> b < 128;}  }}

这一点类似于SV在使用约束时需要注意的地方,尽管我们都知道子类可以继承父类的成员变量和成员方法,也知道可以使用'super'来完成访问,但如果我们在子类中使用了与父类同名的约束,那么在求解子类的随机约束时,父类的同名约束将会被掩盖(不参与求解)。

module tb;  class base_c;    rand int i;    constraint c {i inside {[10:12]};}    virtual function void print();    endfunction  endclass
class der_c extends base_c; rand int i; constraint c {i inside {[20:22]};} function void print(); $display("der_c::i = %0d, base_c::i = %0d", i, super.i); endfunction endclass
initial begin der_c inst = new(); base_c bch = inst; if(inst.randomize()) inst.print(); if(bch.randomize()) bch.print(); endendmodule


例如在上面这段SV示例代码中,子类与父类有同名的约束,那么在随机求解时从打印结果可以看到,子类的同名约束der_c::c在求解时掩盖了base_c::c。这也提醒我们如果不是必要的情况,子类应该避免与父类使用同名的成员变量和约束块

der_c::i = 20, base_c::i = 409176739der_c::i = 20, base_c::i = -1430829098


bind继承

如果父类中使用了bind语句,那么它的子类将会延续它对绑定路径的指示。相比于PSS 1.0a, PSS 2.0对bind语句的继承给出了明确说明。在下面的示例中,base_c2和der_c2都有各自的'bind cpu_p *'语句,按照PSS 2.0的标准,der_c2中的bind语句是冗余的,因为它会继承base_c2的bind语句,并且将cpu_p绑定到act1_a/act2_a/act3_a。只是在我们给出这些示例代码时,像DVT对PSS 2.0的支持还未发布,所以想在der_c2中给出冗余的绑定语句仍然是需要的(但这不意味着在其它支持PSS的工具中也需要这样的冗余绑定语句)。

resource cpu_core_s {}component base_c2 {  pool[4] cpu_core_s cpu_p;  bind cpu_p *; // bind to be inherited  action act1_a {    share cpu_core_s cpu_share;  }  action act2_a {    lock cpu_core_s cpu_lock;  }}component der_c2 : base_c2 {  bind cpu_p *; // redundant bind  action act3_a {    share cpu_core_s cpu_share;  }  action test {    activity {      parallel {        do act1_a;        do act2_a;        do act3_a;      };    };  };}

函数的多态

学习过SV的虚方法(多态)之后,再入手PSS的多态要轻松一些。与SV多态使用类似,PSS中函数的多态也是由于子类定义了与父类同名的函数,掩盖了父类的函数。在下面这个示例代码中,子类poly_der_c继承了父类poly_base_c,同时它定义的同名函数foo()掩盖了父类的foo()。在接下来的func_poly中,func_poly::test对它的子活动b_foo/d_foo做了约束(即将子活动与所在的子一级组件示例做了映射)。

component poly_base_c {  function void foo() {    print("base_c::foo");  }  action call_foo {    exec body {      comp.foo();    }  }}component poly_der_c : poly_base_c {  function void foo() {    print("der_c::foo");  }};component func_poly {  poly_base_c b;  poly_der_c d;  action test {    poly_base_c::call_foo b_foo, d_foo;    constraint {      b_foo.comp == this.comp.b;      d_foo.comp == this.comp.d;    }    activity {      b_foo;      d_foo;    }  }}


最后,func_poly::b和funct_poly::d都参与到了活动调度中(由于func_poly的约束),而这两个子一级组件实例在调用foo()函数时,分别调用的是poly_base_c::foo()和poly_der_c::foo()函数。可以预见到的是,按照顺序调度的关系,最后打印出以下消息内容。这里,由于DVT暂不支持真实的执行过程(包括函数调用、执行块调用等),它作为一款集成开发环境其作用主要在于梳理类、模块、包之间的关系,以及使用它们来随机调度安排测试场景(即产生多个随机调度可能)。

base_c::fooder_c::foo



域的指向和实例的映射

在下面这个示例中,我们使用了'component-type::action-type',通过域的指向来找到需要的动作,并且通过使用匿名的方式来执行子活动。

我们可以通过子类scope_spec_dma_der_c或者父类scope_spec_dma_base_c来指向action xfer_a。

我们可以通过子类scope_spec_dma_der_c来指向action mult_xfer_a,不过无法通过其父类来指向该action,因为它是在子类的域中定义的。

在调度子活动时,还可以像SV一样使用类似的内嵌约束来将某个子活动与某个组件实例完成映射。需要注意在下面示例中'this'所指向的域是scope_spec_dma_test_c::test_a,而'this.comp'则指向的是scope_spec_dma_test_c

component scope_spec_dma_base_c {  action xfer_a {    rand int i;  }}component scope_spec_dma_der_c : scope_spec_dma_base_c {  int j;  action mult_xfer_a {    activity {      repeat(3) {        do xfer_a; // dma_base_c::xfer_a      }    }  }}component scope_spec_dma_test_c {  scope_spec_dma_der_c dma1, dma2;  action test_a {    activity {      do scope_spec_dma_base_c::xfer_a;      do scope_spec_dma_der_c::xfer_a with {              comp == this.comp.dma1;        (this.comp.dma1.j < 8) -> i>4;      }      do scope_spec_dma_der_c::mult_xfer_a;    }  }}

以上的示例会产生多个随机遍历可能,下面是其中的一个。可以发现,前面2个xfer_a的子活动中,第2个子活动因为约束限定其需要与dma1映射。而在后面3个xfer_a的子活动(由scope_spec_dma_der_c::mult_xfer_a展开)中,由于没有映射限制,在repeat的控制块内是又一层的子活动,因此实际上在每一次repeat块遍历其子活动时,需要与dma1/dma2完成一次随机映射,于是生成了以下的一个遍历可能。



这一节我们结合PSS 2.0新添加的对继承内容的规范做了描述,到了下一节我们将继续对类型的扩展和覆盖做深入了解。




系列回顾:
PSS基础篇之一:数据类型
PSS基础篇之二:组件
PSS基础篇之三:动作
PSS基础篇之四:活动调度
PSS基础篇之五:活动控制
PSS基础篇之六:活动的扩展继承与引用
PSS基础篇之七:流对象
PSS基础篇之八:资源对象
PSS基础篇之九:池类型


往期精彩:
让诸位久等,V3课程这次真得来了
除了这门升级中的V2Pro课程,恐怕你找不到更好的学验证的途径了
在V2Pro春季班开班前,你是否还在疑虑这些问题?
相约今晚8点 2021 IC秋招实际情况与经验分享
路科发布| 稳中带涨!25w成芯片校招薪资平均底!2020应届秋招数据全面分析!
UVM RAL模型:用法和应用
如果你突然被裁员了,你的Plan B是什么?
[彩虹糖带你入门UVM]
理解UVM-1.2到IEEE1800.2的变化,掌握这3点就够


路科验证 专注于数字芯片验证的系统思想和前沿工程领域。路桑是Intel资深验证专家,主持验证架构规划和方法学研究,担任过亿门级通信芯片的验证经理角色。在工程领域之外,他在西安电子科技大学和西安交通大学客座讲授芯片验证课程。著有书籍《芯片验证漫游指南》。
评论 (0)
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦