路科验证的个人空间 https://blog.eetop.cn/1561828 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

UVM世界观之六:核心基类(下)

已有 6391 次阅读| 2017-6-12 00:10 |个人分类:验证系统思想|系统分类:芯片设计



拷贝
在之前的例码中,读者初步认识到一旦声明了域的自动化,那么自动拷贝时可以省去不少麻烦。在这里,我们额外需要讲解的是,如果域的成员类型是对象,那么在自动拷贝时,是否会对该对象的内容也全部拷贝下来呢?通过上面的数据操作方法默认类型可以看到,当拷贝对象时,默认进行的是深拷贝,即会执行copy()和do_copy()。读者可以首先看看下面这个例子:

module object_copy2;
import uvm_pkg::*;
`include "uvm_macros.svh"

typedef enum {RED, WHITE, BLACK} color_t;

class ball extends uvm_object;
int diameter = 10;
color_t color = RED;
`uvm_object_utils_begin(ball)
`uvm_field_int(diameter, UVM_DEFAULT)
`uvm_field_enum(color_t, color, UVM_NOCOPY)
`uvm_object_utils_end
function new(string name="ball");
super.new(name);
endfunction

function void do_copy(uvm_object rhs);
ball b;
$cast(b, rhs);
$display("ball::do_copy entered..");
if(b.diameter <= 20) begin
diameter = 20;
end
endfunction
endclass

class box extends uvm_object;
int volume = 120;
color_t color = WHITE;
string name = "box";
ball b;
`uvm_object_utils_begin(box)
`uvm_field_int(volume, UVM_ALL_ON)
`uvm_field_enum(color_t, color, UVM_ALL_ON)
`uvm_field_string(name, UVM_ALL_ON)
`uvm_field_object(b, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name="box");
super.new(name);
this.name = name;
b = new();
endfunction
endclass

box b1, b2;
initial begin
b1 = new("box1");
b1.volume = 80;
b1.color = BLACK;
b1.b.color = WHITE;
b2 = new();
b2.copy(b1);
b2.name = "box2";
$display("%s", b1.sprint());
$display("%s", b2.sprint());
end
endmodule

输出结果:
ball::do_copy entered..
-----------------------------------
Name Type Size Value
-----------------------------------
box1 box - @336
volume integral 32 'h50
color color_t 32 BLACK
name string 4 box1
b ball - @337
diameter integral 32 'ha
color color_t 32 WHITE
-----------------------------------

-----------------------------------
Name Type Size Value
-----------------------------------
box box - @338
volume integral 32 'h50
color color_t 32 BLACK
name string 4 box2
b ball - @340
diameter integral 32 'h14
color color_t 32 RED
-----------------------------------

这段例码新添加了一个类ball,并且在box中例化了一个对象。而在拷贝过程中,box的其它成员都正常拷贝了,但对于box::b的拷贝则通过了ball的深拷贝方式进行。即先执行自动拷贝copy(),来拷贝允许拷贝的域,由于ball::color不允许拷贝,所以只拷贝了ball::diameter。接下来,再执行do_copy()函数,这个函数是需要用户定义的回调函数(callback function),即在copy()执行完后会执行do_copy()。如果用户没有定义该函数,那么则不会执行额外的数据操作。从ball::do_copy()函数可以看到的是,如果被拷贝对象的diameter小于20,那么则将自身的diameter设置为20。因此,最后对象b2.b的成员与b1.b的成员数值不同。

在介绍完copy()方法之后,接下来的三个方法compare()、print()和pack(),与copy()有相似的地方,也有不同的地方。相似的地方在于,这些方法也有各自的回调函数,供用户在默认的自动数据操作无法满足需要时,额外实现并覆盖之前的操作。不同的地方在于,这些方法需要额外用来对数据操作做配置的对象。因此,接下来主要介绍的方法有:
  • function bit compare (uvm_object rhs, uvm_comparer comparer=null)
  • function void print (uvm_printer printer=null)
  • function int pack (ref bit bitstream[], input uvm_packer packer=null)

比较
对于比较方法,默认情况下,如果不对比较的情况作出额外地配置,用户可以在调用compare()方法时,省略第二项参数,即采用默认的比较配置。

首先来看一个简单的例子:

module object_compare1;
import uvm_pkg::*;
`include "uvm_macros.svh"

typedef enum {RED, WHITE, BLACK} color_t;

class box extends uvm_object;
int volume = 120;
color_t color = WHITE;
string name = "box";
`uvm_object_utils_begin(box)
`uvm_field_int(volume, UVM_ALL_ON)
`uvm_field_enum(color_t, color, UVM_ALL_ON)
`uvm_field_string(name, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name="box");
super.new(name);
this.name = name;
endfunction
endclass

box b1, b2;
initial begin
b1 = new("box1");
b1.volume = 80;
b1.color = BLACK;
b2 = new("box2");
b2.volume = 90;
if(!b2.compare(b1)) begin
`uvm_info("COMPARE", "b2 comapred with b1 failure", UVM_LOW)
end
else begin
`uvm_info("COMPARE", "b2 comapred with b1 succes", UVM_LOW)
end
end
endmodule

输出结果:
UVM_INFO @ 0: reporter [MISCMP] Miscompare for box2.volume: lhs = 'h5a : rhs = 'h50
UVM_INFO @ 0: reporter [MISCMP] 1 Miscompare(s) for object box1@336 vs. box2@337
UVM_INFO @ 0: reporter [COMPARE] b2 comapred with b1 failure

在上面的两个对象的比较中,会将每一个自动化的域进行比较,所以在执行compare()函数时,内置的比较方法也会将比较错误输出。从上面的结果来看,比较发生了错误,返回了0值。那么,b1.color和b2.color虽然不相同,为什么没有比较错误的信息呢?原因在于,默认的比较器,即uvm_package::uvm_default_comparer最大输出的错误比较信息是1,也就是说当比较错误发生时,则不会进行后续的比较。

实际上,在uvm_object使用到的方法compare()、print()和pack(),如果没有数据操作配置对象作为参数时,即会使用在uvm_pkg中例化的全局成员。在这里,我们可以从下面的图中看到,在uvm_pkg中例化了不少对象,而在本节中我们会使用到的全局配置包括有:
  • uvm_default_comparer
  • uvm_default_printer
  • uvm_default_packer
    如果不想使用默认的比较配置,用户想自己对比较配置进行设定,可以考虑自行创建一个uvm_comparer对象,或者修改全局的uvm_comparer对象。下面的这段例码,采取了第一种方法:

    module object_compare2;
    import uvm_pkg::*;
    `include "uvm_macros.svh"

    typedef enum {RED, WHITE, BLACK} color_t;

    class box extends uvm_object;
    int volume = 120;
    color_t color = WHITE;
    string name = "box";
    `uvm_object_utils_begin(box)
    `uvm_field_int(volume, UVM_ALL_ON)
    `uvm_field_enum(color_t, color, UVM_ALL_ON)
    `uvm_field_string(name, UVM_ALL_ON)
    `uvm_object_utils_end
    function new(string name="box");
    super.new(name);
    this.name = name;
    endfunction
    endclass

    box b1, b2;
    uvm_comparer cmpr;
    initial begin
    b1 = new("box1");
    b1.volume = 80;
    b1.color = BLACK;
    b2 = new("box2");
    b2.volume = 90;
    cmpr = new();
    cmpr.show_max = 10;
    if(!b2.compare(b1, cmpr)) begin
    `uvm_info("COMPARE", "b2 comapred with b1 failure", UVM_LOW)
    end
    else begin
    `uvm_info("COMPARE", "b2 comapred with b1 succes", UVM_LOW)
    end
    end
    endmodule

    输出结果:
    UVM_INFO @ 0: reporter [MISCMP] Miscompare for box2.volume: lhs = 'h5a : rhs = 'h50
    UVM_INFO @ 0: reporter [MISCMP] Miscompare for box2.color: lhs = WHITE : rhs = BLACK
    UVM_INFO @ 0: reporter [MISCMP] Miscompare for box2.name: lhs = "box2" : rhs = "box1"
    UVM_INFO @ 0: reporter [MISCMP] 3 Miscompare(s) for object box1@336 vs. box2@337
    UVM_INFO @ 0: reporter [COMPARE] b2 comapred with b1 failure

    在这段例码中,额外创建了一个比较配置cmpr。这个对象是uvm_comparer类,而此类并不继承与任何其他的UVM类,只是单纯的一个用于存放比较配置信息的类。在设定了最大的比较错误次数之后,将b1与b2进行比较后的结果信息给得更加全面,这一次则将全部的比较错误信息都输出了。关于uvm_comparer的其它比较设置,用户可以阅读UVM类参考文档。


    打印
    打印方法是核心基类提供的另外一种便于开发和调试的功能。通过field automation,使得声明之后的各个成员域会在调用uvm_object::print()函数时自动打印出来。下面是一段例码:

    module object_print;
    import uvm_pkg::*;
    `include "uvm_macros.svh"

    typedef enum {RED, WHITE, BLACK} color_t;

    class box extends uvm_object;
    int volume = 120;
    color_t color = WHITE;
    string name = "box";
    `uvm_object_utils_begin(box)
    `uvm_field_int(volume, UVM_ALL_ON)
    `uvm_field_enum(color_t, color, UVM_ALL_ON)
    `uvm_field_string(name, UVM_ALL_ON)
    `uvm_object_utils_end
    function new(string name="box");
    super.new(name);
    this.name = name;
    endfunction
    endclass

    box b1;
    uvm_table_printer local_printer;
    initial begin
    b1 = new("box1");
    local_printer = new();

    $display("default table printer format");
    b1.print();

    $display("default line printer format");
    uvm_default_printer = uvm_default_line_printer;
    b1.print();

    $display("default tree printer format");
    uvm_default_printer = uvm_default_tree_printer;
    b1.print();

    $display("customized printer format");
    local_printer.knobs.full_name = 1;
    b1.print(local_printer);
    end
    endmodule

    打印结果:
    default table printer format
    -------------------------------
    Name Type Size Value
    -------------------------------
    box1 box - @336
    volume integral 32 'h78
    color color_t 32 WHITE
    name string 4 box1
    -------------------------------

    default line printer format
    box1: (box@336) { volume: 'h78 color: WHITE name: box1 }

    default tree printer format
    box1: (box@336) {
    volume: 'h78
    color: WHITE
    name: box1
    }
    customized printer format
    ------------------------------------
    Name Type Size Value
    ------------------------------------
    box1 box - @336
    box1.volume integral 32 'h78
    box1.color color_t 32 WHITE
    box1.name string 4 box1
    ------------------------------------

    从上面这段例码中,读者可以发现,只要被在field automation中声明过的域,在稍后的print()函数打印室,都将打印出它们的类型、大小和数值。如果用户不对打印的格式做出修改,那么在打印时,UVM会按照uvm_default_printer规定的格式来打印。在上面“比较”一节中,读者已经知道uvm_pkg中在仿真一开始的时候就会例化不少全局的对象,这其中就包括了uvm_default_printer和其它几个用于打印的对象,它们分别是:
    • uvm_default_tree_printer:可以将对象按照数状结构打印。
    • uvm_default_line_printer : 可以将对象打印到一行上面。
    • uvm_default_table_printer : 可以将对象按照表格的方式打印。
    • uvm_default_printer : UVM环境默认的打印设置,该句柄默认指向了uvm_default_table_printer。

    所以通过给全局打印机uvm_default_printer赋予不同的打印机句柄,就可以在调用任何uvm_object的print()方法时,得到不同的打印格式。如果用户需要自定义一些打印的属性,用户可以自己创建一个打印机,进而通过修改其属性uvm_printer::knobs中的成员,来输出自己的打印格式。每一台打印机中,都有自己的打印属性,用户可以通过查看UVM类的参考手册,查找关于详细的打印属性类uvm_printer_knobs

    除了简单的print()函数,用户还可以通过uvm_object::sprint()将对象的信息作为字符串返回,或者自定义do_print()函数来定制一些额外的打印输出。

    打包和解包
    最后,来看看另外一个核心功能,打包和解包。类似于之前的拷贝和打印,uvm_object也分别提供了通过field automation自动实现的打包和解包方法:
    function int pack (ref bit bitstream[], input uvm_packer packer=null);
    function int unpack (ref bit bitstream[], input uvm_packer packer=null);
    以及用户可以自定义的回调函数:
    virtual function void do_pack (uvm_packer packer);
    virtual function void do_unpack (uvm_packer packer);

    首先来看一段说明打包和解包的例码:
    module object_pack_unpack;
    import uvm_pkg::*;
    `include "uvm_macros.svh"

    typedef enum {RED, WHITE, BLACK} color_t;

    class box extends uvm_object;
    int volume = 120;
    int height = 20;
    color_t color = WHITE;
    `uvm_object_utils_begin(box)
    `uvm_field_int(volume, UVM_ALL_ON)
    `uvm_field_int(height, UVM_ALL_ON)
    `uvm_field_enum(color_t, color, UVM_ALL_ON)
    `uvm_object_utils_end
    function new(string name="box");
    super.new(name);
    endfunction
    endclass

    box b1, b2;
    bit packed_bits[];
    initial begin
    b1 = new("box1");
    b2 = new("box2");
    b1.volume = 100;
    b1.height = 40;
    b1.color = RED;

    b1.print();
    b1.pack(packed_bits);

    $display("packed bits stream size is %d \n", packed_bits.size());

    b2.unpack(packed_bits);
    b2.print();
    end
    endmodule

    输出结果:
    -------------------------------
    Name Type Size Value
    -------------------------------
    box1 box - @336
    volume integral 32 'h64
    height integral 32 'h28
    color color_t 32 RED
    -------------------------------

    packed bits stream size is 96

    -------------------------------
    Name Type Size Value
    -------------------------------
    box2 box - @337
    volume integral 32 'h64
    height integral 32 'h28
    color color_t 32 RED
    -------------------------------

    上面的例码中b1将自己内部已经声明过的域,首先进行打包,打包好的数据存入到一个比特数组中packed_bits,这个数组中存放着所有经过field aumation的域值,接下来b2又从packed_bits中解包,将数据存入到自己的各个域中。这么看起来,这个例子是完成一次对象数值的拷贝。如果是这样,那么为什么不适用uvm_object::copy()函数,而是费这么大周折,来将b1内的域值先打包,然后再通过b2解包完成数值的完整传递呢?

    实际上,打包和解包的方式并不是主要为软件对象之间的数值拷贝服务的,而是真正地在为从软件对象到硬件接口的赋值服务的。在硬件接口中,所有的接口都是按照一定的比特宽度来放置的,并不像软件对象内的各个域那样声明。因此,要完成一次从软件对象到硬件接口的赋值,一种方法就是利用uvm_object::pack()来实现。同样地,如果要完成对硬件信号的采样,也可以将排列好的硬件信号数值保存,继而通过uvm_object::unpack()来完成到软件对象的数值拷贝。

    pack()和unpack()函数的参数中,有一个是可以缺省的参数uvm_packer。如果用户不做特别指定,那么打包和解包的uvm_packer将会使用uvm_pkg中例化的全局对象uvm_default_packer。

    此外,用户如果需要自行打包,例如规定将b1.volume打包为什么长度的比特数组,来匹配硬件信号的位宽,则需要自己完成do_pack()的自定义函数。关于如何通过uvm_packer来完成自定义的打包方式和解包方式,用户可以阅读UVM类的参考手册,查看uvm_packer提供的公共方法。


    至此,我们已经将UVM和核心类uvm_object在注册时伴随的field automation以及因此带来的福利:拷贝、比较、打印和打包解包为读者们介绍完毕。有了这样完善的基础方法,接下来要进一步搭建房屋,为读者们介绍uvm_component时,我们将讨论UVM的phase机制,带领读者们思考,为什么需要phase机制,以及在实际使用过程中需要注意哪些地方。

    下一节,我们将为大家带来《phase机制》。

    谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力

点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 253

    粉丝
  • 25

    好友
  • 33

    获赞
  • 45

    评论
  • 访问数
关闭

站长推荐 上一条 /2 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-4-20 13:00 , Processed in 0.032739 second(s), 12 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部