人生状态机 · 2022年03月21日

架构模型的激励源

处理器架构模型用来在设计初期进行需求分析和设计方案评估,是架构设计阶段必须的工具。架构模型的开发也同样是一项严格的软件开发工作,也需要有严谨的架构设计和方案选择。这一次就探讨一下如果选择处理器架构模型的激励源。架构模型根据激励源模拟处理器执行测试程序的行为。首先需要说明的是,本文探讨的处理器不仅指CPU,还包括GPU以及当前非常活跃的AI等领域专用处理器。架构模型的激励源主要可以分为可执行文件(binary)激励和记录文件(trace)激励两种情况。

可执行文件激励

处理器架构模型可以直接以可执行文件为激励。这里的可执行文件,不仅指CPU的ISA可执行文件,还包括GPU的PTX等格式文件以及其他处理器的可执行文件。可执行文件激励是目前最主流的激励方式,比如CPU的架构模型Gem5和GPU的架构模型GPGPU-Sim等。

图1. 可执行文件激励的示例

图1给出了一个CPU处理器模型的例子。这个模型按照时钟驱动的方式模拟CPU流水线完整过程(取指、译码、执行和回写)。模型维护了CPU的指令指针(PC)。每一个周期中,CPU模型都会根据PC通过可执行文件接口获得指令字节。假设这个CPU只有4级流水线,那么处理器模型的伪代码如下。

Instr* instr_exec;      // 寄存器(执行级和回写级之间)。
Instr* instr_decode;    // 寄存器(译码级与执行级之间)。
uint32_t instr_data;    // 指令字节。
uint32_t PC;            // 指令指针。

// 以时钟驱动仿真。
for (;;) 
{
    // 回写级。
    write_back(instr);
    // 执行级。
    instr_exec = execution(instr_decode);
    // 译码级,将指令字节译码为控制信号。
    instr_decode = decode(instr_data);
    // 取值级,从PC地址取32比特的指令字节。
    instr_data = fetch(PC);
    // 如果有跳转指令,则从跳转方向取指令;否则PC加16。
    PC = update(PC);
}

可执行文件接口充当了程序loader的角色。可执行文件接口需要解析可执行文件的格式(比如linux下ELF格式),然后从ELF文件中解析出程序的代码段和数据段,并且将代码段和数据段映射到指令的寻址空间中。比如,linux上的程序入口是0x400000。架构模型看到的是已经被放到地址空间的可执行代码段。在fetch函数中,从内存中复制出PC指向的字节;在decode函数中,解码指令,得到指令的控制信号和操作数;在execution函数中,执行指令的功能,得到执行结果;在write\_back函数中,将执行结果回写到寄存器或内存。

由可执行文件驱动的架构模型必须模拟指令的译码和执行,也就是说这样的架构模型必须首先是一个功能模型。指令的执行流完全是由架构模型来控制的,比如条件跳转的方向。如果模型没有执行能力,也就没有办法确定程序跳转与否。此外,架构模型需要指令编码中包含的各种信息(指令类型、操作数等)来控制处理器的行为,这些信息必须通过译码得到。

以可执行文件为激励还要求处理器有比较稳定的ISA。在这种情况下,可以实现测试程序与架构设计的解耦。不同架构可以使用相同的测试程序进行性能测试,从而也方便进行性能比较。这种方式在CPU领域应用的非常好,因为不论是Intel、ARM还是RISC,其指令都具有向后兼容的稳定性。测试程序通过指令集的编译器就可以得到在某个ISA上运行的可执行文件。比如SPEC测试程序就可以在不同的ISA上运行,是检验CPU性能的基础测试程序。在GPU领域,架构仿真则以兼容CUDA和OpenCL为主要方向(比如GPGPU-Sim)。

如果不能满足行为模型和稳定ISA这两点,采用可执行文件驱动架构模型就是一种压力比较大的选择。首先,开发功能模型需要消耗较大的人力和时间成本。如果测试程序中存在OS层面的系统调用,那么还需要在仿真器中接管这些系统调用。比如对于多核的测试程序,需要架构模型能够接管fork等系统调用(在以后的文章中展开)。其次,如果没有稳定的ISA,那么在修改微架构的同时也需要修改测试程序。这就需要模型开发工程师兼顾硬件团队、应用团队、乃至编译器团队的角色。

记录文件激励

在实际开发流程中,模型不一定要像图1那样描述一个完整处理器,而是只描述其中一部分系统或者一个特性。例如,为了研究分支预测算法的性能,并不一定要模拟这个处理器,只需要模拟出分支预测的算法和分支预测表的训练算法即可,如图2所示。分支预测模型里面只实现了分支预测算法和分支训练算法。分支预测算法以指令地址作为索引访问分支预测表,得到预测的跳转方向和目标地址。分支训练算法结合预测的跳转方向和目标地址,以及真实的跳转方向和目标地址,对分支预测表进行更新。这样的模型规模有限,运行速度和开发速度都比较快,得到结果可信度也比较高,适合于相对于独立的算法问题。

图2. 记录文件激励的示例

这样的模型不具备指令执行能力,无法以可执行文件作为激励源。同时,模型又需要指令的动态信息(比如跳转方向和目标地址),不能仅仅通过分析可执行文件得到。所以,必须通过某种方式将模型需要的信息存在一个记录文件中,然后再用记录文件去驱动模型。如图2所示,分支预测算法的模型从记录文件获得激励。记录文件按照真实的指令序列记录了分支指令的静态和动态信息,其中的一个激励源包括指令地址、跳转方向、目标地址等信息。

记录文件的格式常常是模型自定义的,千差万别。下面是示意的例子。其中第一列表示指令地址(PC);第二列表示指令的跳转方向,T表示跳转发生(Taken),NT表示跳转不发生(Not-Taken);第三列表示跳转的目标地址。记录文件中罗列了所有执行的跳转指令,包括同一条指令的多次执行。比如地址0x40058位置的这条跳转指令。这条指令是循环的跳转指令,循环执行了4次。前3次循环都没有结束,跳转发生;第4次循环结束,跳转不发生。

// PC     T/NT   Target
0x40008    T     0x40030
0x40058    T     0x40040
0x40058    T     0x40040
0x40058    T     0x40040
0x40058    NT    0x4005c
...

记录文件的来源有很多种,比如电路仿真、功能模型、JIT引擎执行或者硬件监控器。

  • 功能模型:在功能模型中实现记录文件输出的功能。
  • 电路仿真:在RTL代码中实现记录文件输出的功能(比如verilog的\$fwrite和\$display等系统调用)。
  • 硬件监控器:在特定硬件功能的帮助下,通过硬件指令信息保存到文件中。这种方式需要特定的硬件设备来帮助,也可以利用FPGA或Zebu等emulator平台。不论是那种方式,使用和开发门槛都比较高。
  • JIT引擎:利用JIT引擎执行测试程序,在指令前后的回调函数中实现输出记录文件的功能。下一节会详细介绍这种方式。

在工业环境中,利用功能模型和JIT引擎生成记录文件的方法,可以利用的资源比较丰富,使用较多。

记录文件也可以根据需求人为制造,比如进行片上互联网络测试的各种标准负载。这些标准负载并不来自于真实的测试程序,而是通过对负载进行抽象,得到了只需要少数参数就能够描述和确定的标准负载。例如,uniform负载表示所有的节点按照平均分布向其他节点发送数据包,发送的速度通过数据包注入率(PIR)确定。描述这样负载的记录文件可以直接通过简单的程序生成。

以记录文件作为激励,隐含的要求是架构设计对于激励没有影响。也就是说,架构的演进不会影响激励的数量、顺序或时序等特性。在实际应用中,这一点并不容易满足。比如片上互联网络的测试中,如果使用记录文件,那么不论网络性能如何变化,记录文件注入的网络负载都是一样的。但是,实际上网络负载会随着网络延迟的缩短而提高,随着网络延迟的减少而降低。记录文件的仿真器忽略了这层关系,使得模型只能用来评估网络的极限性能,而不能评估测试程序的性能。

由于无法完全满足激励与架构设计的解耦合,以记录文件作为激励一般都会引入系统误差。在使用模型之前需要详细分析系统误差的来源,以及引入的误差是否会对模型的结果产生趋势性的影响,判断模型方案设计是否可行。再以分支预测为例。如果分支训练算法需要考虑所有执行了的指令,那么投机路径上的指令也会对分支预测表产生更新,从而影响分支预测正确率。但是,由功能模型或JIT引擎提供的记录文件中都只有正确路径上的指令,并没有提供投机路上的指令。经过实际测试,忽略投机路径上指令,在预测正确率上引起的误差<5%,而且不会引起趋势性错误。所以,以记录文件作为分支预测模型的激励是完全可行的。

以记录文件作为激励避免了开发行为模型的时间和人力成本,可以快速开发和迭代模型。而且完整的架构模型和局部的架构模型都可以利用记录文件作为激励,进一步缩短了模型开发的周期。

不过,这样的架构模型局限性也很大,需要特别考虑模型本身的系统误差。另外,获得记录文件有时也有一定的难度,需要对原始程序进行调整,甚至要依赖于JIT引擎等工具实现。

在线记录文件驱动仿真

记录文件不一定是实体存在。产生记录文件的程序和仿真程序可以同时运行,两个程序之间通过进程间通信的方式进行交互。获得记录文件的程序会实时将激励提供给仿真程序,仿真程序也会实时接收激励并驱动仿真运行。利用JIT引擎驱动仿真的典型代表是Sniper和ZSim等CPU仿真器。

图3. 在线记录文件记录仿真的示例

在线的记录文件可以由功能模型或者JIT引擎产生。图3展示了由JIT引擎驱动的分支预测仿真器。左边是JIT执行引擎的进程。通过JIT引擎,可以得到指令执行的序列,指令跳转的方向等信息。这些信息通过管道传递给分支预测算法。在操作系统中,管道是一种特殊的文件,所以这里也用了文件的图标来表示。

JIT执行引擎(比如Intel的PIN工具)模拟了一套处理器的执行环境,将原始的指令转换为在JIT引擎模拟的执行环境上操作,而不是在物理机的寄存器上操作。这样做的效果是,JIT执行引擎既可以利用物理机进行指令执行,又可以或者每一条指令执行前后的执行环境。同时,JIT引擎还提供对执行环境进行修改的能力。通过JIT执行引擎,我们可以获得指令执行的很多动态信息。

在线记录文件驱动模型和记录文件驱动模型并没有本质区别,只是提供了不同的软件架构,减少了记录文件对于存储空间的占用。对于软件生态比较完善的处理器,如果有较为成熟的JIT引擎和功能模型,推荐在线记录文件仿真的方式。减少人力需求,缩短开发和迭代周期是其主要的优势。

波形文件激励

最后,架构模型也可以以波形文件作为激励。可以区分为两种情况。第一种情况是对架构模型进行详细校准。首先,利用EDA工具仿真得到波形。然后从波形中提取出模块的输入和输出信号。输入信号用来驱动模型,输出信号用来与模型的输出进行比对。这种需求在实际应用中存在,但并不是必须的。因为架构模型并不要求与实际电路完全一致。

另一种情况是应用于软硬件联合仿真,即将某一个模块的RTL与全系统的模型组合在一起仿真。利用软件模型来产生的激励,验证硬件RTL设计的功能和性能是否达到要求。

图4. 波形文件激励仿真的示例

图4的上半部所示的是一个用可执行文件激励的架构模型。架构模型中包含一个设计关心的模块。为了验证开发出来的RTL设计能够满足功能和性能要求,可以用模块的RTL设计替换模型中的模块模型,如图4的下半部分所示。替换之后,模块RTL设计也需要与架构模型进行交互。架构模型给RTL设计提供输入激励,同时接收模块仿真的输出。架构模型和RTL设计共同模拟了系统架构。

RTL仿真的运行环境和架构模型的运行环境是不同的,需要专门的接口来实现仿真环境的交互,比如VPI或DPI接口。另一个需要关注的问题是,架构模型的激励和RTL设计的端口是不对应的。因此,需要开发转换器(上图中绿色的部分)来进行信号形式的转换。例如,将由使能、类型、地址和数据信号表示的一组访存请求转化为事务级模型的一个事件。

在这样的一个联合仿真系统中,架构模型的抽象度比较高,执行速度比较快。所以仿真的性能瓶颈在VCS一侧。一种提高硬件仿真速度的思路是利用可编程硬件FPGA。将RTL设计放到FPGA上执行,在FPGA和仿真计算机之间构建连接,实现联合仿真。这就是emulator环境Zebu的基本思想。随着emulator工具的发展和普及,架构模型与RTL的联合仿真会有更多的应用,也会成为设计流程中重要的一环。

总结

本文总结了处理器架构模型中,激励源的可选方案。在处理器的架构模型中,使用最多的还是以可执行文件作为激励的情况。不过,架构建模是一个非常个性化的工作,需要根据实际的目标和条件进行选择。充分利用已有的条件,实现快速开发和快速迭代,减少重复造轮子。

以上是本人在工作中的一些总结,偏颇和不全面的地方在所难免,还请指正。

END

知乎:https://zhuanlan.zhihu.com/p/345812628

推荐阅读

更多内容请关注其实我是老莫的网络书场专栏
推荐阅读
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息