UVM设计模式 ( 五 ) 迭代器模式、Python/SV中的迭代器、uvm_callback_iter、scoreboard中的迭代器

行为型设计模式数量较多,上一篇介绍了模板模式和策略模式,下面对迭代模式进行介绍,挖掘其在UVM中的应用。

迭代器模式

Iterator Design Pattern: 对容器 (聚合类,集合数据等) 的遍历操作从容器中拆分出来,放到迭代器中,实现迭代操作的解耦。

大部分编程语言都提供了多种遍历集合的方式,比如for循环,foreach循环等。对于简单的遍历,可以利用语言提供的迭代方式完成遍历操作,对于复杂条件的集合遍历,比如支持广度优先和深度优先遍历的树结构,用户需要自己创建迭代器。

迭代器模式的结构分为迭代器接口和迭代器实现类,容器接口和容器实现类。接口类是为了面向接口编程。迭代器中一般包含next()函数用于返回容器中的元素,布尔型变量hasNext判断是否结束循环。

Python中的迭代器

Systemverilog对聚合类型数据(Aggregate data types)的操作并没有把迭代器 “暴露” 出来,先从Python开始了解。Python允许用户自定义类对迭代的支持。

引用🔗Python Documentation

迭代器是一个表示数据流的对象;这个对象每次只返回一个元素。Python 迭代器必须支持 __next__() 方法;这个方法不接受参数,并总是返回数据流中的下一个元素。如果数据流中没有元素,__next__() 会抛出 StopIteration 异常。

内置的 iter() 函数接受任意对象并试图返回一个迭代器来输出对象的内容或元素,并会在对象不支持迭代的时候抛出 TypeError 异常。Python 有几种内置数据类型支持迭代,最常见的就是列表和字典。如果一个对象能生成迭代器,那么它就会被称作 iterable

>>> L = [1, 2, 3]
>>> it = iter(L)
>>> it  
<...iterator object at ...>
>>> it.__next__()  # same as next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

Python 有不少要求使用可迭代的对象的地方,其中最重要的就是 for 表达式。在表达式 for X in Y,Y 要么自身是一个迭代器,要么能够由 iter() 创建一个迭代器。以下两种表达是等价的:

for i in iter(obj):
    print(i)

for i in obj:
    print(i)

生成器表达式和列表推导式

迭代器的输出有两个很常见的使用方式,1) 对每一个元素执行操作,2) 选择一个符合条件的元素子集。比如,给定一个字符串列表,你可能想去掉每个字符串尾部的空白字符,或是选出所有包含给定子串的字符串。

列表推导式和生成器表达时(简写:"listcomps" 和 "genexps")让这些操作更加简明,这个形式借鉴自函数式程序语言 Haskell(Haskell Language)。你可以用以下代码去掉一个字符串流中的所有空白字符:

line_list = ['  line 1\n', 'line 2  \n', ...]

# Generator expression -- returns iterator
stripped_iter = (line.strip() for line in line_list)

# List comprehension -- returns list
stripped_list = [line.strip() for line in line_list]

通过列表推导式,你会获得一个 Python 列表;stripped_list 就是一个包含所有结果行的列表,并不是迭代器。 生成器表达式会返回一个迭代器,它在必要的时候计算结果,避免一次性生成所有的值。 这意味着,如果迭代器返回一个无限数据流或者大量的数据,列表推导式就不太好用了。 这种情况下生成器表达式会更受青睐。

生成器表达式两边使用圆括号 (" ( ) ") ,而列表推导式则使用方括号 (" [ ] ")。

SV数组内建方法

和数组定位相关的,SV提供了6种方式:

initial begin

int A[] = '{1,3,5,2,4,6};
int q[$];

q = A.find_index with (item > 3);
foreach(q[i])
    $display("q[%0d]:%0d",i,q[i]); # q[0]:2 q[1]:4 q[2]:5

end

initial begin

int A[] = '{1,3,5,2,4,6};
int q[$];

q = A.find with (item == item.index);
foreach(q[i])
    $display("q[%0d]:%0d",i,q[i]); # q[0]:4
end

如果没有在方法中声明迭代器参数(iterator argument), 则使用默认值 item。item是数组中依次被迭代器遍历的元素。如果指明了参数,则 with(expression)中应该使用指明的参数,如 q = A.find_index(x)with ( x > 3); with中的relational operators (<, >, ==) 返回的是布尔值(0,1)。

int A[] = '{1,3,5,2,4,6};
int total;
total = A.sum with ( int'(item > 4) ); # total = 0 对比较后返回的布尔值求和,布尔值位宽为1
total = A.sum with ( int'(item > 4) ); # total = 2 强制类型转化成32位宽

注意,这里的with使用的是 “ (  ) ”, 而调用 randomize 使用的 “  {  } ”。

uvm_callback_iter

uvm_callback_iter是UVM提供专门用于对容器m_pool  m_tw_cb_q 进行遍历的迭代器类。 

1. uvm_callback_iter参数化的类,需要迭代出 T = my_driver, CB = A 类型的 callback。这里对容器m_pool迭代,m_pool 联合数组, key = uvm_object, value = uvm_queue#(uvm_callback)

2. iter.first() 调用uvm_callbacks的静态函数get_first(), get_first()第一个参数是迭代器的“游标”,ref类型。此处为0。

3. m_get_q根据 KEY: object类型 在  m_pool 中找到对应的 VALUE: uvm_queue#(uvm_callback)。没有找到则 q.size = 0, get_first() return null

4. for循环遍历q, 当 $cast(cb, q.get(itr)) == 1时,说明q中的callback类型是A类型,然会这个A类型的callback。(找到一个A类型的callback, for循环便会退出,游标 itr 为 ref类型,所以uvm_callback_iter中的 m_i 会及时更新)

5. iter.next() 调用get_next()接着对q遍历,此时从上次的游标开始,直到遍历完q,return null, 到此结束。

迭代器的用法固定,一直调用next(), 直到遍历完退出:

如何实现是关键,如何维护游标,如何判断返回元素满足条件,这些需要根据实际业务具体实现。

迭代器在scoreboard中的应用

scoreboard一边接收dut transaction放入容器actual_array, 一边接收golden transaction放入容器golden_array。如果是保序的,则可以直接按先后顺序对比,如果不是保序的,则需要按照相应规则遍历容器筛选出transaction。

如果规则是Packet不是保序的,但是addr保持不变,则可以通过SV内建方法 find_index() with (item.addr == addr)在容器中筛选出待比较的Packet。find_index() with (expression) 中内建了迭代器,根据所给的expression条件“吐出”符合的元素。

对于比较复杂的情况,如果没有合适的SV内建方法,可以根据业务写一些function()来完成,如果容器的筛选规则经常变化或者需要写一个适用多场景的scoreboard VIP,建议创建迭代器的类,专门负责此事,便于扩展。

🔗2018 DVCon US : UVM Verification Environment Based on Software Design Patterns​​​​​​​ 中将迭代器模式应用到scoreboard中。

常见的scoreboard的构建方式:🔗OVM/UVM Scoreboards - Fundamental Architectures

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Holden_Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值