推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

验证仿真提速系列--SystemVerilog编码层面提速的若干策略

   2023-08-06 网络整理佚名1550
核心提示:今天别的先不聊,就单从代码习惯出发聊聊编码层面提速的若干策略。值得一提的是,本文虽偏重定性分析和结论摆出,但是这些结论还是具有很不错的价值,例如对仿真速度的编码层面优化方法提供了一些思路和认知,对代码风格建立提供了一个新的观察视角,当你在代码提速优化“山穷水尽”之时,也许因为某条“柳暗花明”。从其他层面如何提高仿真速度?

来源 | 由 Jerry IC 验证(ID:)

|原作者| 杰瑞

专辑

介绍

随着设计复杂性和尺寸的增加,验证平台的复杂性也随之增加。 验证平台的仿真速度成为验证过程中的一个重要问题。

很多初学者可能没有深刻理解模拟速度的重要性。 你可以和Jerry想象这样一个场景:同样的情况,一个验证平台10分钟可以完成,另一个验证平台1分钟可以完成。 有什么不同?

也许有人会说:“仅仅相差9分钟,就是我喝一杯AD钙奶的时间了~”

这位哥们说得对,一个案例的差距确实很小,但是100个案例、1000个案例呢? 他们之间的速度关系不是9分钟的差距,而是10倍! 10倍是什么意思? 意思是前者返回1回合,后者返回10回合! 仅从这一点来看,他们的验证效率就已经相差很大了!

但不要害怕,这个问题其实可以通过多种方式来考虑和解决,例如来自:

模拟工具视角,

从回归策略的角度来看,

从代码风格的角度来看,

业务逻辑考虑等等。

在很多层面上,加速的手段不同,带来的好处也不同。

我们将在专辑《验证模拟提速系列》中一起讨论并解决这个问题(注:专辑发布顺序与模拟提速福利无关,完全取决于天气和心情!! !)

点赞看收藏转发,防止迷路,开车吧~

本文由“壹伙伴编辑”提供技术支持

今天我们不谈其他的事情,只从代码习惯的角度谈一些加快编码水平的策略。

本文的主要策略来自于Cliff和他的团队多年来得出的一些研究结论。 提出的策略主要是定性分析而不是定量分析,结论比详细的理论讨论更重要。 如果有兴趣,可以自己设计模拟实验进行进一步的定量分析,或者深入文献研究原理。

值得一提的是,虽然本文侧重于定性分析和结论,但这些结论仍然具有很大的价值。 例如,对仿真速度的编码层面优化方法提供了一些思路和认知,为代码风格的建立提供了基础。 换个新的观察角度,当你在代码速度优化上“到了山穷水尽”的时候,可能是因为某种“柳暗花明又一村”。

好了,废话不多说了,请提供干货:

1.频繁的函数/任务调用会增加开销

比如:通过遍历来计数(有内置函数),还不如单独的计数器! 下面的代码:

这样写比较慢:

像这样写会更快:

对于简单的调用,编译器可以内联函数/任务以避免堆栈帧操作,但是出于编译器性能考虑,复杂的调用通常不会内联,并且每个函数/任务都会将数据引用或数据的完整副本推送到调用堆栈,并处理任何指定的退货。 这会增加模拟时间。 如果函数/任务本身被回收的话,就会浪费更多的时间!

上面的计数器示例代码通过遍历的方式统计mad_q中的元素个数,每次都需要使用一次内部内置函数,会比独立计数器慢!

2.计算表达式和引用请“转义”循环

例2.1:循环条件中不包含计算,每个循环计算一次

这样写比较慢:

像这样写会更快:

例2.2:与循环因子无关的计算应在循环外计算

这样写比较慢:

像这样写会更快:

示例 2.3:引用不应与循环相关联

这样写比较慢:

像这样写会更快:

本示例中较慢的代码将 comms.proto.pkt... 等引用引入循环中。

在硬件世界中,可以预先计算分层引用,因为这些引用在运行时是静态的。 在 中,引用通常通过类实例层次结构和动态类型进行,所有这些都可以在模拟运行期间发生变化。 因此模拟器必须遍历所有引用才能获取数据,这显然会减慢速度。

3.更加注意条件的相关编码

例 3.1:简单的条件短路

if第一行用“或”连接的条件,当term1为1时,可以得出if条件整体满足,无需后续判断。

同理,在if的第二行中,如果使用“and”链接的条件,当term1为0时,无需后续判断,就可以得出if条件整体不成立。

所以这样写条件会更快,例如:

if(最高频率的条件||次高频率的条件||最低频率的条件),先写最高频率。

例3.2:如果满足条件后可以进行计算,不要急于在前面计算。

比如下面的例子,数据的计算是调用函数(),但是这个值只有在If(live==TRUE)条件成立后才使用! 如果不满足条件,那就没有用了。 如果没有用就没有价值了吗? 资源自然就被浪费了! (前面讲循环的时候说过,要提前计算好,看到这里的条件,可能要多思考一下,事实证明,提前计算好一切是不够的,哈哈)

例3.3:在UVM平台中使用()函数作为条件进行优化。

如果打印详细级别设置为 或以上,以下示例将触发消息打印。

例3.4:这里是UVM平台中使用条件的另一种情况,即在传输端口时,使用端口的size()作为条件,以减少不必要的数据包数量。

4.连接处逻辑的语义显式声明了连线,可以将其折叠到同一个对象中以加速模拟(RTL或TB)

这样写比较慢:

像这样写会更快:

中的逻辑类型,可以有wire存储或者var变量存储,如果没有显式声明,存储类型由模拟器根据上下文决定。

不要小看这种类型,它对于模拟来说是非常不同的。 如果是wire类型的话,模拟器可以折叠成同一个物体,以获得更高的模拟速度,但是变量不行!

因为逻辑类型的语义在除了 input 和 inout 之外的所有情况下都默认为变量存储! 因此,您的代码有时可能会正确模拟,但不知何故它比预期慢!

正如上面示例中的 A2.y、A2.X1.y 和 A2.X1.T1.y 不同,粗体线声明允许它们折叠成单个对象。 (当然,在上面的示例中,输入本身默认为连线类型,不需要显式声明,但所有显式声明都更清晰,这种代码风格更好)

5.“向量”上的直接运算比位运算更快

这样写比较慢:

像这样写会更快:

例如上例中32bit的a_t和c_t可以看作是由32个1bit变量组成的“向量”。 对该“向量”的直接操作将比对其进行 32 个 1 位循环操作更快。

顺便说一句,上面的反例除了按位操作之外,效率较低的示例还使用了一条语句,该语句创建了静态层次结构。 对于这种跨层次的问题,模拟器会对其进行优化,但对于复杂的问题,往往无法得到很好的优化,会成为隐藏的性能问题。

6. 尝试使用ref并传递不太复杂的数据结构

ref会直接对目标方法的内存进行操作,这样可以节省资源,特别是对于很多复杂的数据结构比如有上百个字段的结构,或者有上百个元素的队列,动态数组,联合数组等。其实很多时候函数只需要有权读取大数据对象,而根本不需要写入它。

7.动态数据结构,不要滥用,使用前要三思而后行

队列、动态数组、关联数组等“动态数据结构”是性能问题的常见来源,不应被滥用。 大多数具有这些类型的语言通常都会这样做。

因此,尽可能使用静态数组而不是动态数组。 即使数组长度有很小的变化,最好指定一个稍大的静态数组,以免产生动态数组的开销(内存占用和垃圾收集时间)。 比如可能有2--10个int类型的元素,直接定义使用“int A[10];”,或者更大一点的“int A[12];” 来存储元素,而不是直接定义和使用动态数组“int A[];” 动态分配空间。

另外,动态数组和队列都有各自适合的场景。 它们可以互相完成功能,但不要随意混合,否则性能会很差。 动态数组最适合查找、随机插入/删除操作,队列最适合自动调整向前和向后操作的大小,模拟器具有不同的内部表示来优化各自的操作,因此请尝试使它们适合其“帖子”。

8. 可以使用单个对象,不要使用更多new

较慢的书写方式:

更快的书写方式:

低效的内存可能会导致严重的缓存未命中、堆管理开销和垃圾收集开销,这些开销可能很难通过分析发现,所以要养成良好的代码习惯,比如尽可能减少新的不必要的对象,并且在需要的时候也尽可能少地使用。没有必要深复制动态对象。

9. 可以考虑静态类而不是动态类

接下来,如果同一组类被重复分配和释放内存,模拟器会重复循环进行内存管理,减少模拟时间,并且如果是静态定义的类,模拟的整体内存使用情况会保持一致,所以执行速度会变小。 快的!

10.简单的异构数据结构可以使用结构体代替类

很多人常常有这样的想法:类是一种基于面向对象引入的更“高级”的封装方式,结构看似“低级”,其实不然! 单个类将需要堆管理,并且可能涉及垃圾收集,简单(结构)则不需要,因此速度更快。 简单的异构数据结构可以使用结构而不是类。

11、接口中的“重”功能放在接口中而不是类中

这样写比较慢:

像这样写会更快:

将接口密集型功能放入接口而不是类中也更有效。

首先,由于功能与界面本身相关,因此可重用性更好。

其次,对接口进行操作的类包含与该接口相关的基本操作,以便该接口的任何未来用户都可以复制这些基本代码,但无法通过该接口有效地引用它们。

12.减少动态任务或唤醒

模拟器是事件驱动的,在给定时间点运行的事件越多,运行速度就越慢。 进程中最常见的进程应该是带有敏感信号的块(例如clk),因为它太常见了,这个静态进程在所有仿真器中都被高度优化,但是动态任务或(例如DPI(或任何外部)函数) 、虚拟类任务/函数和虚拟接口任务/函数)可能会导致模拟器禁用优化! 在这种情况下,“坐着比躺着更糟糕”是少醒来最安全的方式。就像前面例子3.2中条件的处理一样,尽量减少它们的执行,如下

值得一提的是,除此之外,还有一种方法可以减少执行次数:使用iff,如下例

13.对于UVM平台中带有约束的随机,尝试分解或简化

这样写比较慢:

写成这样会快得多:

在上面的反例中,为循环中与其相邻的每个数组元素设置了约束。 假设有100个元素,相当于要同时解决100个约束。 以下代码使用,据统计,运行时性能可以提升1000倍!

14.断言序列和属性尽量避免使用局部变量

这样写比较慢:

像这样写会更快:

虽然可能需要局部变量来操作序列和属性内的数据,但它们会增加模拟期间的开销。 应尽可能避免局部变量。

15. 收集覆盖范围时,尽量减少抽样事件

这样写比较慢:

像这样写会更快:

上面第二段代码之所以比第一段更快,是因为使用相同事件的采样过程被组合在一起,更少的采样事件可以减少模拟时间。

因此此外,尝试使用特定的事件触发器而不是系统时钟等通用事件来采样覆盖率,以及覆盖率组共享通用表达式也可以减少仿真时间。

16.可以使用宏来加速循环计算

对于下面的循环代码,会在大量数据点处使用()函数,并且每次调用()都需要创建一个可能影响缓存命中的栈帧,模拟速度会很慢。 使用宏可以使模拟更快。 当然,过多使用宏会增加调试难度和内存消耗。

结语

正如上面所说:“文章发表顺序与加速效益无关”。 本文的提速手段,对于代码量较小的验证业务来说,说实话并不是最赚钱的提速手段,有些好处甚至难以察觉。 属于“勒紧裤腰带”的致富方式。 然而,“每一粒都是苦工”,多种物品搭配使用,积少成多。 当验证业务规模较大时(除了芯片规模外,还包括大量的仿真数据,比如大量数据、图片、视频的压测场景))就会获得不错的收益速度增益。

人生,时光如水,又到了说再见的时候。 篇幅有限,从代码角度提高仿真速度的技巧有很多。 欢迎大家研究、探讨、补充。

如何从其他方面提高仿真速度?

哪些加速方法好处更大?

如何监控我们的模拟速度?

我们回去慢慢聊吧~

祝越来越棒,大家加油!

涉及参考文献

1.“哎呀!为什么我的还是这样?” 克里夫,约翰·罗斯,亚当,.S. 2019.

2. “哎呀! 我的为何如此? 弗兰克·坎普夫 (Frank Kampf)、亚当 (Adam),DVCon 美国 2012 年。

3.IEEE Std 1800-2012,IEEE 的 -- 、 和 。 由 IEEE,3 Park,纽约,NY 10016-5997,美国。

4. 指南,14.2,2015 年,8 - “”,作者: , Inc., 2655 Seely , San Jose, CA 65134, USA。

5. “应用笔记:您的代码,第一部分”,作者:John Rose。 Tyler 的博文,2018 年 3 月 19 日。博客。

- 结束 -

历史推荐

同学们,为什么不留言呢? ! !

留言板

您订购的一切都在看,是对我们的鼓励

 
标签: 系统仿真
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON