沈阳响应式网站制作做二维码网站

当前位置: 首页 > news >正文

沈阳响应式网站制作,做二维码网站,王烨捷,哈尔滨建设发展集团有限责任公司文章目录 类型转换写在前面动态转换子类句柄赋值于父类句柄父类句柄转换为子类句柄 虚方法写在前面非虚函数的调用虚函数的调用虚方法的建议为什么使用虚方法 对象拷贝写在前面赋值和拷贝总结 回调函数写在前面实例完成回调函数功能需要三步#xff1a; 参数化类写在前面实现一… 文章目录 类型转换写在前面动态转换子类句柄赋值于父类句柄父类句柄转换为子类句柄 虚方法写在前面非虚函数的调用虚函数的调用虚方法的建议为什么使用虚方法 对象拷贝写在前面赋值和拷贝总结 回调函数写在前面实例完成回调函数功能需要三步 参数化类写在前面实现一个简化的mailbox 参考资料 类型转换 写在前面 类型转换可以分为 静态转换和动态转换 。静态转换即需要在转换的表达式前 加上单引号 即可该方式并不会对转换值做检查。如果发生转换失败我们也无从得知。动态转换即需要使用 系统函数\(cast(tgt src) 做转换。静态转换和动态转换均需要操作符号或者系统函数介入统称为 显式转换 。不需要显式操作的转换我们称之为 隐式转换 。例如赋值语句右侧是4位的矢量而左侧是5位的矢量隐式转换会先做位宽扩展(隐式)然后再做赋值。 动态转换 当我们使用类的时候 类句柄的向下转换 即从父类句柄转换为子类句柄时 需要 使用\)cast() 函数 进行转换 否则会出现编译错误这一步也是编译器的保护措施防止用户出现错误的赋值。如果将子类句柄赋值给父类句柄时编译器则认为赋值是合法的但分别 利用子类句柄和父类句柄调用相同对象的成员时将可能有不同的表现 。 子类句柄赋值于父类句柄 class Transaction;rand bit[31:0] src;function void display(input string prefix);\(display(%s Transaction: src%0d, prefix, src);endfunction endclassclass BadTr extends Transaction;bit bad_crc;function void display(input string prefix);\)display(%s BadTr: bad_crc%b, prefix, bad_crc);super.display(prefix);endfunction endclassTransaction tr; BadTr bad, bad2;Transaction tr; BadTr bad; badnew() ; //构建BadTr扩展对象 trbad; //基类句柄指向扩展对象 \(display(tr.src) ; //显示基类对象的变量成员 tr.display() ; //??tr.display() ; 调用的是父类的display。 父类句柄转换为子类句柄 trnew() ; //创建一个父类对象 badtr; //ERROR编译错误即便使用cast进行转换虽然解决编译错误但是返回值为0bad仍为空句柄。 \)display(bad.bad_crc) ; //bad_crc成员不在父类对象中将一个父类句柄赋值给一个子类句柄并不总是非法的。但是SV编译器对这种直接赋值的做法是禁止的也就是说无论父类句柄是否真正指向了一个子类对象赋值给子类句柄时编译(静态)都将出现错误。因此需要\(cast(tgt src) 来实现句柄类型的动态转换。\)cast(tgt src) 会检查句柄所指向的对象类型 而不仅仅检查句柄本身。一旦源对象跟目的句柄是同一类型或者是目的句柄的扩展类\(cast() 函数执行即会成功 返回1 否贝返回0。 badnew(); //创建Bad Tr子对象 trbad; //父类句柄指向子类对象 //动态类型转换 检查tr的源对象是否是bad 2类型或者其子类 //如果转换失败将报告错误信息 if(!\)cast(bad2, tr))\(display(cannot assign tr to bad2);\)display(bad2.bad_crc); //bad 2指向的对象包含band_src成员 bad2.display() ; //??bad2.display() ; 调用子类的方法。 虚方法 写在前面 class basic_test;int fin;int def 100;function new() ;\(display(basic_test::new) ;endfunctiontask test() ;\)display(basic_test::test) ;endtask endclassclass test_wr extends basic_test;int def 200;function new() ;super.new() ;\(display(test_wr::new) ;endfunctiontask test() ;super.test() ;\)display(test_wr::test) ;endtask endclass类的继承是从 继承成员变量和成员方法 两个方面。从例码中可以看到test_wr类和test_rd类分别继承了basic_test类的成员变量以及成员方法。除了介绍的类的封装和继承关于类的 多态性(polymorphism) 也是必须关注的。正是由于类的多态性使得用户在设计和实现类时 不需要担心句柄指向的对象类型是父类还是子类只要通过虚方法就可以实现动态绑定(dynamic binding) 或者在SV中称之为动态方法查找(dynamic method lookup) 。 非虚函数的调用 basic_test t; test_wr wr; initial beginwrnew() ;twr;\(display(wr test starts) ;wr.test() ;\)display(wr test ends) ;\(display(t test starts) ;t.test() ;\)display(t test ends) ; end//输出结果 // wr test starts // basic_test::test // test_wr::test // wr test ends // t test starts // basic_test::test // t test ends首先 在执行wr.test() 时 由于wr类型为test_wr 则索引到的test() 应该为test_wr类的方法test。同时 由于在test_wr::test中显式调用了super.test() 则会先执行basic_test::test 然后再执行test_wr::test中其余的代码。这里值得注意的是 默认情况下 子类覆盖(override) 的方法并不会继承父类同名的方法 而只有通过super.method()的方式显式执行才会达到继承父类方法的效果初学SV的用户容易在这里混淆方法覆盖和类继承的概念。当wr对象的句柄传递给t后 由于t本身是basic_test类 所以在执行t.test时 t只会搜寻basic_test::test方法。多态的问题是什么在句柄传递中子类句柄赋值给父类句柄调用同名函数时子类句柄调用的是子类函数父类句柄调用的是父类函数即便把子类句柄赋值给了父类句柄也无法通过父类句柄调用子类函数 虚函数的调用 我们将已经在编译阶段就可以确定下来调用方法所处作用域的方式称之为静态绑定(static binding) 而与之相对的是动态绑定。动态绑定指的是在调用方法时会 在运行时来确定句柄指向对象的类型再动态指向应该调用的方法 。为了实现动态绑定 我们将basic_test::test定义为虚方法。 class basic_test;…virtual task test(stm_ini ini) ;\(display(basic_test::test) ;endtask... endclass//wr test starts只做了这么一个改动以后我们重复运行之前的测试代码可以看到运行结果变为 // wr test starts // basic_test::test // test_wr::test // wr test ends // t test start.s // basic_test::test // test_wr::test // t test ends由于声明了basic_test::test为虚方法 系统在执行t.test时会检查t所指向对象的类型为test_wr类 进而调用test_wr::test。于是 输出结果与调用wr.test一致。我们就可以通过虚方法的使用来实现类成员方法调用时的动态查找用户无需担心使用的是父类句柄还是子类句柄因为最终都会实现动态方法查找执行正确的方法。 虚方法的建议 在为父类定义方法时如果该方法 日后可能会被覆盖或者继承 那么应该声明为虚方法。虚方法如果要定义应该 尽量定义在底层父类中 。这是因为如果virtual是声明在类继承关系的中间层类中 那么只有从该中间类到其子类的调用链中会遵循动态查找而最底层类到该中间类的方法调用仍然会遵循静态查找。虚方法 通过virtual声明 只需要声明一次即可 。例如上面代码中只需要将basic_test::test声明为virtual 而 其子类则无需再次声明 当然再次声明来表明该方法的特性也是可以的。虚方法的继承也需要 遵循相同的参数和返回类型 否则子类定义的方法须归为同名不同参的其它方法。 为什么使用虚方法 首先当 父类指针指向子类对象 如果不将test函数声明为虚函数最终调用的是父类test函数如果将test函数声明为虚函数那么调用的是子类test函数。为什么要用父类指针指向子类对象呢用子类指针指向子类对象不好吗 虽然这样说但是实际开发过程中不是这样的当我们使用一些类库、框架的时候这些类库、框架是事先就写好的。我们在使用的时候不能直接修改类库的源码我们只能派生类库中的类来覆盖一些成员函数以实现我们的功能但这些成员函数有的是由框架调用的。这种情况下用虚函数是很好的办法。 NND这不就是C吗 对象拷贝 写在前面 对于拷贝(copy) 对象的拷贝要比其它SV的变量类型都让人“当心”。因为就SV普通的变量拷贝而言只需要通过赋值操作符“”就足够了。而对象的拷贝则无法通过“”来实现因为这一操作是句柄的赋值而不是对象的拷贝。 test_wr h; initial beginwr new();h wr;\)display(wr.def%Od, wr.def);\(display(h.def%0d, h.def);h.def 300;\)display(wr.def%Od, wr.def);\(display(h.def%0d, h.def); end //输出结果 // wr.def 200 // h.def 200 // wr.def 300 // h.def 300在hwr之后由于是句柄的赋值所以h.def300的操作 实际上是对这两个句柄指向的共同对象做的成员变量赋值。所以从最终打印的结果可以看出wr.def与h.def的值相同。那么如果要拷贝对象指的是首先创建一个新的对象(开辟新的空间)再将目标对象的成员变量值拷贝给新对象的成员这就使得新对象与目标对象的成员变量数值保持一致即完成了对象的拷贝(成员变量的拷贝)。初学者需要区别句柄拷贝与对象拷贝的区别。 软件来说我们可能称呼的是值拷贝与引用拷贝 class basic_test; //父类...virtual function void copy_data(basic_test t) ;t.defdef;t.finfin;endfunctionvirtual function basic_test copy() ;basic_test tnew(0) ; //创建父类对象copy_data(t) ;return t;endfunction endclassclass test_wr extends basic_test; //子类...function void copy_data(basic_test t) ;test_wr h;super.copy_data(t) ;\)cast(h, t); //关键代码句柄转化h.defdef;endfunctionfunction basic_test copy() ;test_wr tnew() ; //创建子类对象copy_data(t) ;return t;endfunction endclassmodule tb;….test_wr wr; //子类test_wr h; //子类initial beginwrnew() ;\(cast(h, wr.copy()); //copy会返回父类句柄所以这里将父类句柄转化为子类句柄\)display(wr.def%0d, wr.def);\(display(h.def%0d, h.def);h.def300;\)display(wr.def%0d, wr.def);\(display(h.def%0d, h.def);end... endmodule//输出结果 // wr.def200 // h.def200 // wr.def200 // h.def300赋值和拷贝 声明变量和创建对象是两个过程也可以一步完成。 Packet pl; p1new;如果将p1赋值给另外一个变量p2那么依然只有一个对象只是指向这个对象的句柄有p1和p2. 以下这种方式表示p1和p2代表两个不同的对象。 在创建p2对象时 将从p 1拷贝其成员变量例如integer、string和句柄等 该种拷贝方式称为浅拷贝(shallowcopy) 。 Packet pl; Packet p2; p1new; p2new p1;总结 将成员拷贝函数copy_data() 和新对象生成函数copy() 分为两个方法这样使得子类继承和方法复用较为容易。为了保证父类和子类的成员均可以完成拷贝将拷贝方法声明为虚方法且遵循只拷贝该类的域成员的原则父类的成员拷贝应由父类的拷贝方法完成。在实现copy_data() 过程中应该注意句柄的类型转换 保证转换后的句柄可以访问类成员变量。 回调函数 写在前面 理想的验证环境是在被移植做水平复用或者垂直复用时应当尽可能少地修改模块验证环境本身只在外部做少量的配置或者定制化修改就可以嵌入到新的环境中。要做到这一点一方面我们可以通过顶层环境的配置对象自顶向下进行配置参数传递另外一方面我们可以在测试程序不修改原始类的情况下注入新的代码。例如 当我们需要修改stimulator的行为时 有两种选择 一个是修改父类但针对父类的会传播到其它子类另外一个选择是在父类定义方法时预留回调函数入口使得在继承的子类中填充回调函数就可以完成对父类方法的修改。 实例 示意 virtual class Driver_cbs; //Driver回调虚类virtual task pre_tx(ref Transaction tr, ref bit drop) ;//默认不做操作endtaskvirtual task post_tx(ref Transaction tr) ;//默认不做操作endtask endclass实例 class Driver;Driver_ebs cbs[\)];task run() ;bit drop;Transaction tr;forever begindrop0;agt2drv.get(tr) ;foreach(ebs[il) cbs[il.pre_tx(tr, drop) ;if(drop) continue;transmit(tr) ;foreach(cbs[il) cbs[i] .post_tx(tr) ;endendtask endclassclass Driver_cbs_drop extends Driver_abs;virtual task pre_tx(ref Transaction tr, ref bit drop) ;//1/100的传输事务丢弃概率drop(\(urandom range(0, 99) 0) ;endtask endclassprogram automatic test;Environment env;initial beginenvnew() ;env.gen_cfg() ;env.build() ;begin//创建回调对象井且植入driverDriver_abs_drop dcdnew() ;env.drv.cbs.push_back(dcd) ;endenv.run() ;env.wrap_up();end endprogram完成回调函数功能需要三步 预留回调函数入口定义回调的类和回调函数例化和添加回调的类 参数化类 写在前面 参数化的使用是为了提高代码的复用率。无论是设计还是验证如果代码会被更多的人使用或者被更多的项目所采用那么就需要考虑使用参数来提高复用率。参数的使用越合理后期维护的成本就会相应降低。在硬件设计中参数往往是整型例如端口数目或者位宽。在验证环境中参数的使用更加灵活可以使用各种类型来做类定义时的参数。在SV中可以为类增加若干个数据类型参数并在声明类句柄的时候指定类型。SV的类参数化近似于C中的模板。 实现一个简化的mailbox class mailbox;local int queue[\)] ;task put(input int i) ;queue.push_back(i) ;endtasktask get(ref int o) ;wait(queue.size() 0) ;oqueue·pop_front() ;endtasktask peek(ref int o) ;wait(queue.size() 0) ;oqueue[0] ;endtask endclass这个简化的mailbox的问题在于它只能用于操作整数类型。如果要存储real类型 或者某一个类的句柄那么就得复制该类然后将数据类型由int类型转换为real类型或者某一个类的类型。这样将导致类的快速增长而且是重复代码的大规模增长代码维护将变得冗长乏味而且还容易出错。 class mailbox #(type Tint) ;local T queue[$] ;task put(input T i) ;queue.push_back(i) ;endtasktask get(ref T o) ;wait(queue.size() 0) ;oqueue.pop_front() ;endtasktask peek(ref T o) ;wait(queue.size() 0) ;oqueue[Ol;endtask endclassinitial beginreal o;mailbox #(real) mb; //创建一个存储real类型的mailboxmbnew() ;for(inti0; i5; i)mb.put(i*2.0) ; //将real值存储入mbfor(inti0; i5; i)mb.get(o) ; //从mb取出real值 end在类定义时添加参数#(typeTint) 这表示后期类在声明变量时如果不指定参数类型 则默认采用int类型。将原代码int用参数T来代替。参数化的类将可以在后期例化时使用不同的参数以此来存储不同的数据类型。 参考资料 Wenhui’s Rotten PenSystemVerilogchipverify