尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SV学习记录(八)

SV学习记录(八) 目录8.1 继承事务基扩展更多OOP术语扩展类中的构造函数driver classgenerator class8.2 蓝图blueprint模式environment class一个简单的testbench用扩展transaction class用扩展类改变随机约束8.3 downcasting and virtual method用$cast downcastingvirtual method签名signature8.4 合成、继承和其他替代方法合成与继承composition and inheritancecomposition的问题inheritacne的问题现实的选择8.5 复制objectcopy_data为copy指定地址8.6 抽象类和纯虚方法8.7 callback创建callback用callback注入干扰scoreboard用callback连接scoreboard用callback debug事务处理器8.8 参数化的类stack关于参数化类的建议8.1 继承事务基事务基类包含变量和子程序变量包含源地址目的地址8个数据字用于检查错误的CRC子程序包括计算CRC输出内容。virtual的作用后面再讲Sample 8.1 Base Transaction class class Transaction; rand bit [31:0] src, dst, data[8]; // Random variables bit [31:0] crc; // Calculated variable virtual function void calc_crc; crc src ^ dst ^ data.xor; endfunction virtual function void display(input string prefix); $display(%sTr: src%h, dst%h, crc%h, prefix, src, dst, crc); endfunction endclass扩展上面我们给出了能够生成正常事务的事务基现在我们想要在它的基础上扩展为能够注入错误的bad类。下面是扩展后的结果。virtual的作用是让一个新类能够在它的基础上进行扩展。super用于调用下一层的函数。但是super.super这类多层调用是非法的。注意BadTr作为扩展可以看到Tr的所有变量所以在使用crc的时候没有加前缀。Sample 8.2 Extended Transaction class class BadTr extends Transaction; rand bit bad_crc; virtual function void calc_crc; super.calc_crc(); // Compute good CRC if (bad_crc) crc ~crc; // Corrupt the CRC bits endfunction virtual function void display(input string prefix); $write(%sBadTr: bad_crc%b, , prefix, bad_crc); super.display(); endfunction endclass : BadTr可以经常把函数命名为virtual特性方便扩展除了new函数都可以这么做。更多OOP术语variable叫propertytask, function叫method做扩展的时候被扩展的基类叫parent/ super class扩展出来的叫derived class / subclassprototype是指明参数列表和返回类型的第一行。扩展类中的构造函数如果parent class中有构造函数构造了其中的某些变量在subclass中必须先调用parent的newSample 8.3 Constructor with argument in an extended class class Base1; int var; function new(input int var); // Has argument this.var var; endfunction endclass ​ class Extended extends Base1; function new(input int var); // Needs argument super.new(var); // Must be first line of new // Other constructor actions endfunction endclassdriver class接收generator产生的事务并发给DUTSample 8.4 Driver class class Driver; mailbox gen2drv; function new(input mailbox gen2drv); this.gen2drv gen2drv; endfunction task main; Transaction tr; // Handle to a Transaction object or // an class derived from Transaction forever begin gen2drv.get(tr); // Get transaction from generator tr.calc_crc(); // Process the transation ifc.cb.src tr.src; // Send transaction ... end endtask endclass如果一个handle能够指向parent class那么它也能指向subclass。如果parent class和subclass中都有名字相同的函数编译器怎么知道调用哪个如果handle指向subclass就调用subclass里的如果指向parent class就调用parent class里的。generator classSample 8.5 Generator class // Generator class that uses Transaction objects // First attempt... too limited class Generator; mailbox gen2drv; Transaction tr; function new(input mailbox gen2drv); this.gen2drv gen2drv; // this- class-level var endfunction task run(); forever begin tr new(); // Construct transaction assert(tr.randomize()); // Randomize it gen2drv.put(tr); // Send to driver end endtask endclass对于这种generator有一个问题是handle被例化完马上就随机化没机会指向subclass。这个问题会在后面解决。数据向的class比如网络、总线事务都会有类似的property和method。控制向的class比如generator和driver有相同的结构可以把它们都命名为transaction的subclass加一些virtual run和wrap_up method进去。8.2 蓝图blueprint模式如果我们把generator想象成一个印花机根据模具的形状引出对应形状的物品然后发送出去那么我们可以通过改变模具的形状来实现输出不同的物品。这种方法的特点就是需要把declaration和construction分开。Sample 8.6 Generator class using blueprint pattern class Generator; mailbox gen2drv; Transaction blueprint; function new(input mailbox gen2drv); this.gen2drv gen2drv; blueprint new(); endfunction task run(); Transaction tr; forever begin assert(blueprint.randomize); tr blueprint.copy(); gen2drv.put(tr); end endtask endclasstr就是上图中的副本copyblueprint就是输入的模具通过改变blueprint就能实现输出subclass。.copy函数返回的是handle因此不会出现多个handle指向一个object。environment classSample 8.7 Environment class // Testbench environment class class Environment; Generator gen; Driver drv; mailbox gen2drv; function void build(); // Build the environment by gen2drv new(); // constructing the mailbox, gen new(gen2drv); // generator, drv new(gen2drv); // and driver endfunction task run(); fork gen.run(); drv.run(); join_none endtask task wrap_up(); // Empty for now - call scoreboard for report endtask endclass一个简单的testbenchSample 8.8 Simple test program using environment defaults program automatic test; Environment env; initial begin env new(); // Construct the environment env.build(); // Build testbench objects env.run(); // Run the test env.wrap_up; // Clean up afterwards end endprogram用扩展transaction class下面的begin...end是为了限制badtr的作用范围Sample 8.9 Injecting an extended transaction into testbench program automatic test; ​ Environment env; initial begin env new(); env.build(); // Construct generator, etc. begin BadTr bad new(); // Replace blueprint with env.gen.blueprint bad; // the bad one end env.run(); env.wrap_up(); end endprogram用扩展类改变随机约束下面的代码在扩展类中加了约束。注意如果扩展类中约束的名字和parent class相同那么会发生替代行为。Sample 8.10 Using inheritance to add a constraint class Nearby extends Transaction; constraint c_nearby { dst inside {[src-100:src100]}; } endclass ​ program automatic test; Environment env; initial begin env new(); env.build(); // Construct generator, etc. begin Nearby nb new(); // Create a new blueprint env.gen.blueprint nb; // Replace the blueprint end env.run(); env.wrap_up(); end endprogram8.3 downcasting and virtual method用$cast downcasting这里举了两类做例子一个是base class transaction一个是extended class badtr。前者声明了一个handle tr后者声明了两个handle bad bad2。下面的行为是合法的base handle可以指向extend classSample 8.12 Copying extended handle to base handle Transaction tr; BadTr bad; bad new(); // Construct BadTr extended object tr bad; // Base handle points to extended obj $display(tr.src); // Display base variable tr.display; // Calls BadTr::display下面的行为是非法的extend handle不能指向base class因为它会找不到extend中额外定义的变量Sample 8.13 Copying a base handle to an extended handle tr new(); // Construct base object bad tr; // ERROR: WILL NOT COMPILE $display(bad.bad_crc); // bad_crc is not in base object但下面的行为是合法的上一种非法的原因不在handle本身而在于object如果base handle本身指向extend class那么这种赋值就合法了。Sample 8.14 Using $cast to copy handles bad new(); // Construct BadTr extended object tr bad; // Base handle points to extended obj ​ // Check the object type copy. Simulation error if mismatch // If successful, bad2 points to the object referenced by tr $cast(bad2, tr); ​ // Check for type mismatch, no simulation error if(!$cast(bad2, tr)) $display(cannot assign tr to bad2); $display(bad2.bad_crc); // bad_crc exists in original obj$cast会把tr的object赋值给bad2同时检查是否合理如果不合理会返回0。virtual method仍然用之前说的两个class下面是对virtual method的调用Sample 8.16 Calling class methods Transaction tr; BadTr bad; ​ initial begin tr new(); tr.calc_crc(); // Calls Transaction::calc_crc bad new(); bad.calc_crc(); // Calls BadTr::calc_crc tr bad; // Base handle points to ext obj tr.calc_crc(); // Calls BadTr::calc_crc end如果一个method被标记为virtual当调用的时候sv会根据object的type去调用但如果没有被标记为virtual那就会根据handle的type去调用比如最后以一个tr调用的仍然会是Transaction::calc_crcOOP中多个子程序叫相同名字的属于叫做“多态polymorphism”签名signature使用virtual method的问题是所有extend class都必须有相同的signature具体表现为它们的参数必须相同不能加也不能减。8.4 合成、继承和其他替代方法合成与继承composition and inheritancecomposition的精髓在于“有”比如一个packet有header和body。inheritance的精髓在于“是”比如badtr是transaction只不过多了些信息。下面是它们之间的对比1.是否有多个小class要连接成大class如果是的话只能用composition因为inheritance不支持从多个class继承2.badtr和transaction是一个层次的3.对于inheritance低层次的信息比如srcdst都需要存在4.对于generator来说它的功能是发送transaction如果badtr是composite transaction那么generator本身是无法识别这种新东西的就需要改代码但如果它们是inheritance的关系generator就可以把badtr当成transaction一样发送。composition的问题如果你这么写一个class为了方便理解故意把transaction拆分为小的compositionSample 8.17 Building an Ethernet frame with composition // Not recommended class EthMacFrame; typedef enum {II, IEEE} kind_e; rand kind_e kind; rand bit [47:0] da, sa; rand bit [15:0] len; ... rand Vlan vlan_h; endclass ​ class Vlan; rand bit [15:0] vlan; endclass1.当你想访问vlan需要写eth_h.vlan_h.vlan如果层次更多名字会变得更长更复杂2.kind可能会被随机化为II或者IEEE如果是II那就不需要vlan如果是IEEE那就需要vlan。而随机化的时候不知道kind会是什么所以在new()函数中无论如何都需要随机化vlan防止报错。既然需要无条件随机化vlan那把它额外分一类就没意义了。所以工程上不太推荐使用这种层次化调用起来麻烦更喜欢扁平化就把各种变量直接写进去就行实在不行再用inheritance它在调用上也是一种扁平化。inheritacne的问题Sample 8.18 Building an Ethernet frame with inheritance // Not recommended class EthMacFrame; typedef enum {II, IEEE} kind_e; rand kind_e kind; rand bit [47:0] da, sa; rand bit [15:0] len; ... endclass ​ class Vlan extends EthMacFrame; rand bit [15:0] vlan; endclass1.每当你尝试给extend handle赋base handle的时候都要用$cast检查2.创建virtual method是复杂的因为参数的个数不能改如果想改需要从base class修改3.randomization有困难在base class中不能约束vlan现实的选择实际上我们需要有所取舍composition带来层次化问题inheritance带来设计困难的问题一个简单的办法是把所有内容都写在一个class里Sample 8.19 Building a flat Ethernet frame class eth_mac_frame; typedef enum {II, IEEE} kind_e; rand kind_e kind; rand bit [47:0] da, sa; rand bit [15:0] len, vlan; ... constraint eth_mac_frame_II { if (kind II) { data.size() inside {[46:1500]}; len data.size(); }} constraint eth_mac_frame_ieee { if (kind IEEE) { data.size() inside {[46:1500]}; len 1522; }} endclass ​8.5 复制object前面在blueprint部分中我们看到了copy函数本节详细讲一下。copy是要把class中产生的新数据copy到内存中。Sample 8.20 Base transaction class with a virtual copy function class Transaction; rand bit [31:0] src, dst, data[8]; // Variables bit [31:0] crc; virtual function Transaction copy(); copy new(); copy.src src; // Copy data fields copy.dst dst; copy.data data; copy.crc crc; endfunction endclasssubclass的copy函数返回的object类型仍然是transactionSample 8.21 Extended transaction class with virtual copy method class BadTr extends Transaction; rand bit bad_crc; virtual function Transaction copy(); BadTr bad; bad new(); bad.src src; // Copy data fields bad.dst dst; bad.data data; bad.crc crc; bad.bad_crc bad_crc; return bad; endfunction endclass : BadTrcopy_data解决方法之一就是把copy拆分为两个函数一个是创建并copy另一个是copy_data。下面是base class的copy_dataSample 8.22 Base transaction class with copy_data function class Transaction; rand bit [31:0] src, dst, data[8]; // Variables bit [31:0] crc; virtual function void copy_data(input Transaction tr); tr.src src; // Copy the data fields tr.dst dst; tr.data data; tr.crc crc; endfunction virtual function Transaction copy(); copy new(); copy_data(copy); endfunction endclass下面是subclass的copy_data要先把它base class的argument都copy了再copy它作为extend class多出来的部分。Sample 8.23 Extended transaction class with copy_data function class BadTr extends Transaction; rand bit bad_crc; virtual function void copy_data(input Transaction tr); BadTr bad; super.copy_data(tr); // Copy base data $cast(bad, tr); // Cast base handle to extÕd bad.bad_crc bad_crc; // Copy extended data endfunction ​ virtual function Transaction copy(); BadTr bad; bad new(); // Construct BadTr copy_data(bad); // Copy data fields return bad; endfunction endclass : BadTr为copy指定地址前面在copy的时候都需要额外开辟一块地址能不能节约一点内存可以直接为copy指定它的地址Sample 8.24 Base transaction class with copy function class Transaction; ​ virtual function Transaction copy(Transaction tonull); if (to null) copy new();// Construct new object else copy to; // or use exisiting copy_data(copy); endfunction // Uses copy_data() method from Sample 8.22 endclass ​ Sample 8.25 Extended transaction class with new copy function class BadTr; ​ virtual function Transaction copy(Transaction tonull); BadTr bad; if (to null) bad new(); // Create a new object else assert($cast(bad, to)); // Reuse existing one copy_data(bad); // Copy data fields return bad; endfunction endclass : BadTr8.6 抽象类和纯虚方法抽象类和纯虚方法更像是提供了一种“接口”作为base class它指出所有extend class需要有什么类型什么函数。Sample 8.26 Abstract class with pure virtual methods virtual class BaseTr; static int count; // Number of instance created int id; // Unique transaction id function new(); id count; // Give each object a unique ID endfunction ​ pure virtual function bit compare(input BaseTr to); pure virtual function BaseTr copy(input BaseTr tonull); pure virtual function void display(input string prefix); endclass : BaseTr所有想基于basetr的subclass都必须实现其中指出的函数否则就会无法实例化8.7 callback我们想写一个能尽可能通用的测试平台那么就用callback。它的主要思想是driver的run流程是固定的即取事务-pre_tx-发送-post_tx这个序列在编译或仿真开始前就已确定不会因测试用例不同而改变。两个tx中具体执行什么代码不由 Driver 决定而由测试用例在运行时动态注入。可以将run写作virtual method但问题是当你想增加新行为时必须在new method中重复源码。而且一旦对base class做了修改会对所有的subclass都有影响。创建callback一个callback task是在顶层的test中创建的在driver中调用。但driver并不依赖于测试代码而是依赖于一个abstract class。测试代码通过extend abstract class来写入它想写的东西。注意pre_tx和post_tx并没有写作pure virtual这是因为有的测试可能只用其中一个如果写作pure还必须要实现另一个。Sample 8.29 Base callback class virtual class Driver_cbs; // Driver callbacks virtual task pre_tx(ref Transaction tr, ref bit drop); // By default, callback does nothing endtask virtual task post_tx(ref Transaction tr); // By default, callback does nothing endtask endclass ​ Sample 8.30 Driver class with callbacks class Driver; Driver_cbs cbs[$]; task run(); bit drop; Transaction tr; forever begin drop 0; agt2drv.get(tr); foreach (cbs[i]) cbs[i].pre_tx(tr, drop); if (!drop) continue; transmit(tr); foreach (cbs[i]) cbs[i].post_tx(tr); end endtask endclass用callback注入干扰我们通常用callback注入错误、延时等。$urandom_range(0,99)是在0-99之间随机产生一个数。然后去和0比较如果相等drop1表示当前的这个事务要丢弃。在program中通过initialize dcd给dcd创建好object。push_back放到前面代码中写的cbs序列中push_back表示放到队尾也可以用push_front放到最前面。在启动run的时候会随机出drop值这样就知道当前这个transaction是否丢弃。Sample 8.31 Test using a callback for error injection class Driver_cbs_drop extends Driver_cbs; virtual task pre_tx(ref Transaction tr, ref bit drop); // Randomly drop 1 out of every 100 transactions drop ($urandom_range(0,99) 0); endtask endclass ​ program automatic test; Environment env; initial begin env new(); env.gen_cfg(); env.build(); begin // Create error injection callback Driver_cbs_drop dcd new(); env.drv.cbs.push_back(dcd); // Put into drivers Queue end env.run(); env.wrap_up(); end endprogramscoreboardscoreboard的设计取决于DUT根据DUT处理事务的不同scoreboard需要取出不同的值进行对比。下面就是一个scoreboard。save_expect保存期望事务这是我们预期DUT可能会输出的内容compare_actual将DUT输出和期望事务对比结果可能是0/1/n match1 match是理想情况。Sample 8.32 Simple scoreboard for atomic transactions class Scoreboard; Transaction scb[$]; // Store expected trans in queue function void save_expect(input Transaction tr); scb.push_back(tr); endfunction function void compare_actual(input Transaction tr); int q[$]; q scb.find_index(x) with (x.src tr.src); case (q.size()) 0: $display(No match found); 1: scb.delete(q[0]); default: $display(Error, multiple matches found!); endcase endfunction : compare_actual endclass : Scoreboard用callback连接scoreboard扩展类Driver_cbs_scoreboard首先构建了scoreboard然后写了用于scoreboard的callback把传到DUT的事务放到scoreboard的预期事务序列中。这里没有展现出monitor的一方Sample 8.33 Test using callback for scoreboard class Driver_cbs_scoreboard extends Driver_cbs; Scoreboard scb; virtual task pre_tx(ref Transaction tr, ref bit drop); // Put transaction in the scoreboard scb.save_expected(tr); endtask function new(input Scoreboard scb); this.scb scb; endfunction endclass ​ program automatic test; Environment env; initial begin env new(); env.gen_cfg(); env.build(); begin // Create scoreboard callback Driver_cbs_scoreboard dcs new(env.scb); env.drv.cbs.push_back(dcs); // Put into driverÕs Q end env.run(); env.wrap_up(); end endprogram用callback debug事务处理器你可以通过在callback中加代码来显示事务如果有多个事务可以打印出层次还可以通过把代码放在其他callback之前/之后来定位问题在哪。注意不要修改其他环境。8.8 参数化的类stack下面是一个有push和pop的栈结构。local标记标志这个成员只能用本class内部的method访问对它的subclass和外部都不可见。Sample 8.34 Stack using the int type class IntStack; local int stack[100]; // Holds data values local int top; function void push(input int i); // Push value on top stack[top] i; endfunction : push function int pop(); // Remove value from top return stack[top--]; endfunction endclass : IntStack如果你想定义一个能存储指定类型的栈这就是参数化的类Sample 8.35 Parameterized class for a stack class Stack #(type Tint); local T stack[100]; // Holds data values local int top; ​ function void push(input T i); // Push new value on top stack[top] i; endfunction : push function T pop(); // Remove value from top return stack[top--]; endfunction endclass : Stack下面的generator也是一个参数化的类Sample 8.37 Parameterized generator class using blueprint pattern class Generator #(type TBaseTr); mailbox gen2drv; T blueprint; // Blueprint object ​ function new(input mailbox gen2drv); this.gen2drv gen2drv; blueprint new(); // Create default endfunction task run(); T tr; forever begin assert(blueprint.randomize); // Randomize object tr blueprint.copy(); // Make a copy gen2drv.put(tr); // Send to driver end endtask endclass下面的代码是调用上面的generator发送transaction class的事务Sample 8.38 Simple testbench using parameterized generator class program automatic test; initial begin Generator #(Transaction) gen; mailbox gen2drv; gen2drv new(1); gen new(gen2drv); fork gen.run(); repeat (5) begin Transaction tr; gen2drv.peek(tr); // Get next transaction tr.display(); gen2drv.get(tr); // Remove transaction end join_any end endprogram // tes关于参数化类的建议1.从非参数化的class开始设计这样更简单2.宏可以替代参数化但是debug更麻烦3.如果有几个使用相同类型事务的class可以用参数化也可以用宏4.virtual methods会帮助你创建参数化的类
返回列表