引言
在现代硬件设计中,仿真是验证和优化设计的重要环节。随着电路设计的复杂性不断增加,开发者需要有效的仿真工具来确保设计的正确性和性能。目前,硬件仿真主要有两种范式:Cycle-Based和Event-Driven。本文将对这两种仿真范式进行比较,分析它们的优缺点及适用场景,并介绍一个耳熟能详的开源仿真器Verilator和在CIRCT框架中实现的两款仿真器–Arcilator和llhd-sim,Verilator和Arcilator代表Cycle-Based模型,而llhd-sim代表Event-Driven模型。
Cycle-Based范式
Cycle-Based仿真是一种以时钟周期为单位进行仿真的方法,忽略了具体的时序细节,关注的是在每个时钟边沿的状态更新。在Cycle-Based仿真中,时钟信号是核心驱动因素,所有的计算和状态更新都发生在时钟信号的上升沿或下降沿,这使得仿真过程更加简化和高效。这种方法主要用于同步电路。
Cycle-Based仿真的核心思想:
-
简化计算:通过将时间划分为离散的时钟周期,允许在每个周期内对所有信号进行更新。这种方法显著减少了需要处理的时间点数量,从而提高了仿真的效率。相比于Event-Driven仿真,Cycle-Based仿真只在特定的时刻进行计算,而不是在每个可能的时间点;
-
二值逻辑:信号状态通常被简化为二值逻辑(0或1),这种简化使得仿真器能够高效地处理信号变化,尤其是在大规模设计中,减少了计算复杂性;
-
组合逻辑计算:在每个时钟边沿,仿真器会计算所有组合逻辑电路的输出。这意味着在一个时钟周期内,所有输入信号的变化都会影响输出,而不考虑输入信号在该周期内的具体变化顺序。这种机制确保了电路行为的一致性和可预测性。
Cycle-Based算法的实现细节:
以下是一个示例,展示如何在 C++ 中实现时钟信号的更新:
void update() {
clk = 0; // 设置时钟为低电平
eval(); // 评估当前状态
clk = 1; // 设置时钟为高电平
eval(); // 再次评估状态
}
-
时钟信号的表示:时钟信号通常被表示为一个布尔变量,如clk,并通过 C++ 代码在仿真过程中进行更新。时钟信号的变化(从 0 到 1,再从 1 到 0)会驱动电路状态的更新;
-
时钟域管理:Cycle-Based仿真器对不同的时钟域进行最低限度的智能决策。这意味着它只在指定的时钟域中寻找边沿,从而避免在不相关的信号上进行额外的时序检查。这种方法提高了仿真的效率,减少了不必要的计算;
-
时钟属性传播:Cycle-Based仿真器会自动将时钟的属性传播到所有依赖于clk的信号。这意味着与时钟信号相关联的触发器、寄存器和其他逻辑电路会被标记为在该时钟边沿触发,从而确保在仿真过程中正确处理这些信号;
-
时钟边沿检测:在仿真过程中,仿真器会监测指定时钟信号的边沿(上升沿或下降沿)。每当检测到时钟边沿时,所有与该时钟信号相关的触发器和逻辑模块的状态将被更新,接着调用eval()函数以评估当前电路状态;
-
周期性仿真:Cycle-Based仿真器实现周期性仿真,即每个时钟周期内所有相关信号的状态都会被更新。这种设计确保了电路在每个周期内达到稳态,并且所有输入对输出的影响都能在下一个周期内反映出来;
Cycle-Based仿真算法带来的优势:
-
适用于同步电路:Cycle-Based仿真特别适合同步电路设计,因为这些电路依赖于全局时钟信号,确保所有触发器和存储元件在同一时间点更新状态,从而保持电路的一致性和稳定性;
-
高效性:由于仅在时钟边沿进行计算,Cycle-Based仿真避免了在每个时间点进行复杂的信号更新。这种方法显著提高了仿真的速度,尤其是在处理大规模设计时,能够有效减少仿真时间;
-
内存使用率高:由于只在特定的时钟周期内更新信号状态,内存使用效率相对较高。与Event-Driven仿真相比,Cycle-Based仿真不需要维护复杂的事件队列,从而降低了内存消耗;
-
易于实现和维护:Cycle-Based仿真器的实现相对简单,因为不需要处理复杂的事件调度和时间管理,使得开发和维护变得更加容易。
Cycle-Based仿真的不足之处:
-
精度限制:由于只在时钟周期内更新信号状态,周期精确仿真可能无法捕捉到某些设计中的潜在问题,如瞬态故障或时序错误。这意味着对于一些需要严格时间约束的应用场景,可能不够准确;
-
不适合异步电路:这种方法主要用于同步逻辑,不适合处理异步电路的复杂性。对于异步电路,需要更复杂的事件驱动机制来捕捉其行为。
Cycle-Based仿真在数字电路设计和验证中有多种应用场景:
-
数字集成电路设计:广泛应用于ASIC和FPGA的功能验证,特别适合处理同步电路设计;
-
HDL测试平台:常用于Verilog和VHDL的测试环境中,帮助设计者快速编写和运行测试用例,以验证设计正确性;
-
快速原型开发:在FPGA原型开发中,Cycle-Based仿真可以加速设计迭代过程,使得设计者能够迅速验证设计修改的效果。
Cycle-Based仿真器通过以时钟周期为单位进行简化计算、使用二值逻辑和组合逻辑计算,实现了高效且易于维护的数字电路仿真。尽管存在一些局限性,但其优势使其成为现代数字电路设计中的重要工具。
基于Cycle-Based算法而设计的开源仿真器:
Verilator: Verilator的起源可以追溯到1994年,由 Digital Equipment Corporation (DEC) 的 Core Logic Group 开发。最初的目的是将 Verilog 代码转换为 C 代码,以便与基于 C 的 Alpha 处理器模型进行共同仿真。在1998年,DEC 发布了源代码,随后该项目被 Wilson Snyder 和其他开发者接手,并作为 Veripool 开源项目继续发展。2001年,Verilator 进行了全面重写,采用 C++ 语言进行开发,并增加了对 SystemC 模式的支持。随着时间的推移,Verilator在2022年的版本5中引入了符合 IEEE 标准的调度器和延迟语义,进一步增强了其功能。
Arcilator: Arcilator,最初名为 Circilator,是CIRCT项目中的关键组成部分。基于CIRCT项目的创立动机:旨在将开源的EDA工具的功能整合到同一框架中,因此该仿真器的最终目的是取代Verilator。Arcilator与Verilator最大的不同是前者将LLVM/MLIR中的软件开发方法应用于硬件设计中,即多层级中间表示的思想,其优势在于开发者可以在任何一层中间表示进行优化;而后者是直接将SystemVerilog代码转换为C++代码,没有中间表示的概念。 CIRCT项目于2021年在ASPLOS学术会议上由Chris Lattner分享主题为软硬件协同设计时代的编译器设计黄金时代一文中首次被介绍,此时Circilator就已被划分为未来将要支持的重要功能之一。经过两年的开发,于2023年LLVM峰会期间,SiFive公司分享了题为快速和周期精确的CIRCT硬件仿真器:Arcilator 的演讲。此次演讲中,重点对Rocket-small、Rocket-medium、Rocket-large以及BOOM-small和BOOM-large这两个RISC-V核进行单线程仿真,此时Arcilator尚不支持多线程仿真。数据显示,当对Rocket-small进行基准测试时,Arcilator的仿真速度比Verilator快4.3倍;即便是在对BOOM-large进行基准测试时,Arcilator仍然展现出优势,其速度是Verilator的1.9倍。此外,在仿真Rocket-small时,Arcilator生成的二进制文件大小比Verilator小4倍,而对应的BOOM-large也比Verilator小1.8倍。这些结果表明,Arcilator在性能和效率上均优于传统的仿真工具,为硬件设计提供了更为高效的解决方案。图1、图2是Arcilator与Verilator数据对比图:
图1:Arcilator与Verilator仿真性能对比
图2:Arcilator与Verilator二进制仿真文件大小对比
这种显著的性能提升不仅彰显了Arcilator作为硬件仿真工具的潜力,也验证了CIRCT采用多层级中间表示的可行性。 在2024年11月的金枪鱼之夜:OSPP 2024 项目说明会上,由清华大学TUNA成员分享的CIRCT编译器的电路划分及Arcilator仿真 并行 化一题,成功补全了Arcilator所缺失的多线程仿真特性。
Event-Driven范式
Event-Driven仿真是一种在数字电路设计和验证中广泛使用的仿真方法,特别适用于复杂的系统和异步电路。它通过处理事件的发生和信号的变化来模拟电路的行为。
Event-Driven仿真算法的核心思想:
-
事件的概念:在Event-Driven仿真中,任何信号状态的变化,例如从0变为1,都被视为一个事件。事件是仿真过程的基本单元,仿真器通过处理这些事件来模拟电路的行为。这种机制允许系统在发生特定事件时自动执行相应的操作;
-
事件存储:所有待处理的事件被存储在一个优先级队列中,按照事件发生的时间顺序排列。仿真器总是处理队列中即将发生的下一个事件,这样可以确保仿真时间的准确推进;
-
调度机制:在同一时刻可能会有多个事件需要处理,仿真器根据优先级调度这些事件,以确保正确执行。这种调度机制使得高优先级的事件能够及时响应,而不被低优先级事件阻塞;
-
时间推进:仿真时间通过处理事件来推进。在每个时刻,仿真器将当前时间更新为即将发生的下一个事件的时间,并执行该事件。这意味着在两个相邻事件之间,系统状态不会发生变化;
-
更新事件:当电路中的信号,如寄存器或线网发生变化时,会产生更新事件。这些更新会触发相关进程,使其激活并执行计算。更新事件通常由信号状态变化引发,并导致后续计算;
-
计算事件:由更新事件引发的计算操作通常涉及对电路逻辑的评估和输出信号的更新。更新和计算事件之间相互作用,推动仿真过程向前发展;
Event-Driven算法的实现细节:
-
划分时间槽:时间槽是指所有调度的事件在同一时刻下进行处理的一个区间,在这个时间槽内,系统会执行所有与该时刻相关的事件;
-
划分时间槽区域:时间槽的细分部分,用于组织和管理不同类型的事件。每个时间槽可以包含多个时间槽区域,这些区域根据事件的状态和处理优先级进行分类,如:
-
激活区域:当前正在处理的事件所在区域,在这个区域中的所有事件都会被立即执行,仅当前时间槽才能拥有的区域;
-
待激活区域:保存的是激活区域中的事件全部执行完后的事件,这些事件通常由延迟控制语句生成;
-
非阻塞赋值区域:由非阻塞赋值创建的区域,等待待激活区域中的事件执行完后再执行;
-
观察区域:用于评估property表达式,当这些表达式被触发时,相关代码将在此区域执行;
-
反映区域:用于处理响应性任务或事件,例如断言或其他需要实时响应的操作。
-
-
事件调度:根据事件类型,将其调度到相应的区域中;
-
处理激活区域:在当前时间槽内,执行顺序可能是任意的,但必须确保所有激活事件都被处理完毕;
-
处理待激活区域:将该区域中的所有事件提升到激活区域中进行执行;
-
检查其他区域:SystemVerilog IEEE标准一共划分了17个区域,如图3所示,需按照优先级进行其他区域的处理;
图3:事件调度区域
- 移动到下一个时间槽:一旦当前时间槽中的所有区域都被清空,系统将查找下一个非空时间槽,并将当前仿真时间移动到该时间槽。
Event-Driven仿真算法带来的优势:
-
高精度:Event-Driven仿真能够准确模拟电路中每个信号的变化,捕捉到细微的时序问题和逻辑错误,使其在精度需求高的验证中表现出色;
-
灵活性:Event-Driven仿真可以处理复杂的异步电路和多种输入条件,允许设计者灵活地定义事件和响应,以适应各种设计需求;
-
支持复杂交互:Event-Driven仿真器能够处理复杂模块间交互,适用于验证系统级芯片等复杂设计中的各个组件之间的交互。
Event-Driven仿真的不足之处:
-
速度慢:对于大型设计,Event-Driven仿真的速度可能较慢,因为它需要逐一处理每个事件并进行多次评估;
-
资源消耗高:维护事件队列需要更多内存,这在资源有限的环境中可能成为问题。
Event-Driven仿真的应用场景:
-
复杂数字电路设计:Event-Driven仿真适用于具有复杂时序关系和异步电路的设计,能够捕捉到细微的信号变化和逻辑错误;
-
实时系统仿真:在实时系统中,Event-Driven仿真可以用于评估系统响应时间和性能指标,以确保满足实时要求;
-
网络协议仿真:Event-Driven仿真适合用于网络协议的测试和验证,如车联网和通信网络中的路由算法等。
基于Event-Driven算法而设计的开源仿真器:
llhd-sim: llhd-sim仿真器是与LLHD项目紧密相关的工具,该项目首次在2020年的MLIR Open Design Meeting大会上被介绍,负责基于LLHD IR实现仿真功能。随着CIRCT项目的成立,LLHD的各个组件,包括LLHD方言、llhd-sim仿真器和Moore前端编译器,逐渐整合进了CIRCT项目中。然而,随着Arcilator仿真器的出现,llhd-sim逐渐被边缘化,并最终于2024年3月在CIRCT例会上正式退出了该项目。尽管llhd-sim不再被使用,但由于对SystemVerilog在CIRCT项目中的不断探索,LLHD方言仍然得以保留,并决定将其融入Arcilator中。这一决策反映了对现代硬件设计需求的适应性和灵活性。未来,Arcilator将成为Cycle-Based和Event-Driven的混合体,为硬件设计提供更强大的支持。
LLHD(Low-Level Hardware Description): LLHD 项目是在现代硬件设计的背景下发展而来的,旨在解决现代化HDL所面临的诸多挑战如:
-
复杂性:几乎所有的EDA工具都是针对SystemVerilog、VHDL这些语言来做分析的,但由于这些语言本身的复杂性,因此无法满足现代化快速迭代的数字电路设计需求;
-
工具兼容性:现有的开源工具往往无法有效配合使用,不同工具之间缺乏统一的标准和接口,导致开发者需要在多个工具之间进行切换。这种情况使得设计流程变得繁琐,降低了开发效率;
-
缺乏通用 IR:许多EDA工具都有自己的中间表示,这些 IR 通常是专有的且不兼容的。这样的局面导致了冗余和不必要的复杂性,使得整个设计流程变得更加低效;
为了解决这些问题以及受到LLVM项目成功经验的启发–LLVM 提供了一种灵活且高效的编译架构,使得不同语言能够通过统一的中间表示进行优化和生成目标代码,LLHD项目决定通过建立一个多层级中间表示来促进HDL与编译器之间的合作与创新。
总结
本文对Cycle-Based和Event-Driven两种硬件仿真范式进行了比较分析。Cycle-Based范式以其高效性和简单性适用于大规模同步逻辑设计,代表性的开源仿真器包括 Verilator 和 Arcilator。而Event-Driven范式则因其准确性和灵活性更适合复杂系统验证,以 llhd-sim 为代表。然而,随着技术的发展,Arcilator的出现使得llhd-sim逐渐被边缘化并最终退出CIRCT项目,但其核心理念和LLHD方言仍然在CIRCT项目中扮演着重要的角色,这一变化突显了行业对更高效、更灵活工具需求的不断演进。未来,在不断演进的硬件设计领域中,这两种范式将继续发挥重要作用,同时也促使新工具和新方法的探索,以满足日益增长的设计需求。
参考资料
[1] Arcilator: Fast and cycle-accurate hardware simulation in CIRCT
[2] EDN Access — 07.04.96 Digital logic simulation: event-driven, cycle-based, and home-brewe
[3] LLHD: A Multi-level Intermediate Representation for Hardware Description Languages
[4] LLHD: The Low Level Hardware Description Language
[5] Verilator online
[6] Verilog Scheduling and Event Queue
[7] 金枪鱼之夜:OSPP 2024 项目说明会