Skip to content

再谈模拟器

最近在项目中使用simpleSim进行了一个自研CPU的开发,在这个过程中,有一些想法值得记录下来,所以又写了这篇模拟器相关的文档

为什么使用simpleSim

选用simpleSim这个模拟器进行开发主要有如下几点原因:

  • 这次的CPU很多结构都是其他CPU所不具有的,并且是全新的自定义指令集架构,开发时间很紧迫,基于Gem5进行开发,工作量很大,一人月很难完成。
  • 在项目中既要开发功能模拟器又要开发性能分析模拟器。功能模拟器优先级很高,性能模拟器不是必选项。
  • 自身不熟悉systemc
  • simpleSim代码简单,指令集抽象简单,功能模拟和性能模拟可以共用同一套指令集,开发周期预估时间短

基于以上几点原因,选择了使用SimpleSim进行开发

开发过程

simpleSim框架的代码量较小,整个开发过程大概分为如下几步

    |-------------------------------|
    | 下载源码,编译测试,熟悉使用方式 |
    |-------------------------------|
                  |
                  V
    |-------------------------------|
    | 建立本地gitlab仓库,上传代码    |
    |-------------------------------|
                  |
                  V                                        |- 指令集管理子系统(指令编码,指令解码,指令抽象)
    |-------------------------------|     模拟器子系统      |- 内存管理子系统
    | 结合官方文档,熟悉模拟器基本结构 | ---------------------|- 运行调度管理子系统(loop还是事件调度,多线程还是单线程)
    |   sim-safe    sim-outorder    |                      |- register file管理子系统
    |-------------------------------|                      |- debug,trace子系统
                  |
                  V
    |--------------------------------|
    | 完成对sim-safe整个模拟过程的熟悉 |
    |--------------------------------|
                  |
                  V
    |-------------------------------|
    |       构建新的指令集           |
    |-------------------------------|
                  |
                  V                
    |-------------------------------|
    |      简单验证指令正确性        |
    |-------------------------------|
                  |
                  V                                         |- fetch,decode,dispatch,rename,execute,writeback,commit各个阶段的实现
    |-------------------------------|      cycle simulator  |- lsu的实现
    |      分析sim-outorder         | ----------------------|- fu的实现
    |-------------------------------|                       |- outoforder相关实现,RUU
                  |                                         |- 各阶段的反压实现
                  V                                         |- 性能统计功能的实现
    |--------------------------------|
    | 基于新指令集测试原有sim-outorder |
    |--------------------------------|
                 |
                 V
    |--------------------------------|
    | 逐步修改微架构,靠近设计方案     |
    |--------------------------------|

过程总结

对于熟悉一个新模拟器框架,目前觉得比较高效的方式就是:

  • 先搭建好环境,运行几个case,熟悉模拟器使用方法。
  • 然后结合相关文档,梳理每个目录大致在做什么事情。这时候可能暂时不能全部梳理清楚,不着急,尽可能的去熟悉
  • 搭建好调试环境,可以使用vscode嵌入的gdb,调试比较方便,这时候就需要大致清楚程序是多线程还是单线程
  • 逐步的熟悉整个框架的组织结构
  • 针对一个小点进行分析,比如功能模拟器就可以从指令解码执行过程入手进行分析,在分析小点的过程中逐步清晰整个框架的结构; 性能模拟器会相对复杂,可以从fetch阶段进行分析。
  • 由点成面,扩大理解范围
  • 大致清楚整体结构,研究清楚一个模块后再进行改动
  • 改动时尽量保持随时可运行,小步快跑,改动后立即验证,随时可release

结果

最终,功能模拟器成功在计划时间之前稳定,性能模拟器能够大致进行指令流分析,但没能对项目提供有用帮助。

功能模拟器实现总结

功能模拟器是成功的,在功能模拟器对接过程中收获了新的经验

功能模拟器一般是以.so的方式进行release的,这是因为UVM验证时,往往是把功能模拟器作为其一部分进行集成的,这时候,动态链接库(据说静态库在集成时会有很多坑?)就显得非常方便。 同时,做cosim验证时,一般需要实现step()接口。(有些验证会采用dump整个case log的方式,但这个不适合大型case的验证,比如一个case有几百万条指令)

功能模拟器的重点在于指令编解码及行为实现,指令实现又有几个重点:

  • 符号扩展问题,这个要保持思路清晰
  • 浮点处理问题,浮点的摄入精度(fenv.h)以及是否需要使用softfloat

功能模拟器当然是越快越好,因此好多功能模拟器实现一些cache来帮助快速执行,比如指令cache来缩短解码时间,tlb地址cache来缩短页表查找时间。当然还有一些其他优化手段,比如采取多线程

性能模拟器总结

性能模拟器从项目上来讲是失败的,因为没有给项目带来什么收益。我觉得主要有以下几个原因:

  • 开发功能模拟器占用了一部分时间
  • 性能模拟器和RTL是同时进行开发的,两者的周期基本保持一致,甚至性能模拟器的周期还更长一些。RTL进入验证阶段,性能模拟器还不能给出一些参考数据
  • 个人对于性能模拟器开发的规划存在问题,在修改某些结构时没能做到时刻保持代码可执行。最终收敛速度比较慢。
  • 代码思路上存在一些问题,当时想着微结构均是自己进行设计的,要想比较精确的模拟,就需要对一部分微结构进行建模,没法使用现有微结构。这种思路注定周期会比较长,收敛速度会比较慢。 如果能够直接在原先微结构基础上进行稍微变动,以达到基本拟合,那么实现周期应该会有所缩短,收敛速度也会快很多。

性能模拟器的统计很重要,simpleSim本身实现了计数器框架,能够统计一些想要的数据。另外,我探索出一套性能模拟器指令效率分析方案,这种方式对于分析性能有比较直观的帮助。

基于模拟器的处理器性能分析方案

方案分为两部分,一部分是指令流,一部分是性能统计

指令流分析方案

指令流分析方案的核心就是跟踪每一条指令在各个模块和stage中流动的情况,将其图表化,从而直观的提供给设计者进行分析

  |---------------------------|
  |      cycle simulator      |
  |---------------------------|
                |
                |
                V                                       |- when instruction fetched from memory
  |---------------------------|  based on cycle         |- when rename the instruction
  |     instruction log       | ------------------------|- when dispatch the instruction
  |---------------------------|                         |- when instruction oprands ready
                |  a perfect designed log format        |- when instruction be executed
                |  make analyze script easy             |- when instruction be commited
                |                                       |- ....... any action you want to analyze
                |
                |                    |---------------------------------|
                | <------------------|   a log analyze python script   |
                |                    |                                 |
                V                    | the script convert log to json  |
  |-----------------------------|    |---------------------------------|
  |   instruction_actions.json  |    example:                     
  |-----------------------------|      @0: FETCH: fetch0 inst imov_c(seq:1) core 0: 0x0000000000000000 imov_c r0,c0
                |                                                    |
                |                                                    V    will be convert to
                V                        |--------------------------------------------------------------------------------|
  |-----------------------------|        |   {                                                                            |
  |  show in chrome://tracing   |        |      "args":{                                                                  |
  |-----------------------------|        |        "info":"0x0000000000000000 (0x00000000c000001b) imov_c r0,c0"           |
        https://docs.google.com/         |      },                                                                        |
        document/d/1CvAClvFfyA5R         |      "cat":"imov_c(seq:1)",                                                    |
        -PhYUmn5OOQtYMH4h6I0n            |      "name":"imov_c(seq:1)",                                                   |
        SsKchNAySU/preview               |      "ph":"B",                                                                 |
                                         |      "pid":"fetch",                                                            |
                                         |      "tid":"fetch0",                                                           |
                                         |      "ts":0                                                                    |
                                         |    },                                                                          |
                                         |    {                                                                           |
                                         |      "args":{                                                                  |
                                         |        "info":"0x0000000000000000 (0x00000000c000001b) imov_c r0,c0"           |
                                         |        },                                                                      |
                                         |      "cat":"imov_c(seq:1)",                                                    |
                                         |      "name":"imov_c(seq:1)",                                                   |
                                         |      "ph":"E",                                                                 |
                                         |      "pid":"fetch",                                                            |
                                         |      "tid":"fetch0",                                                           |
                                         |      "ts":1                                                                    |
                                         |    },                                                                          |
                                         |--------------------------------------------------------------------------------|

性能统计方案

指令流分析只需要运行一轮case就进行分析即可,但性能分析就需要有多轮执行机制

 |-------------|   change   |-----------------------------|    can config simulator params  |- fetch width
 |             | ---------> |    simulator config file    | --------------------------------|- buffer size
 |             |            |-----------------------------|                                 |- commit width
 |             |                          |                                                 |- l1 icache size
 |             |                          | simulator load config file                      |- ......any params you want to test
 |             |                          |
 |             |                          V
 |   python    |    run     |-----------------------------|
 |   script    | ---------- |      cycle simulator        |
 |             |            |-----------------------------|
 |   control   |                          |
 |     run     |                          |
 |      N      |                          V
 |    times    |     get    |-----------------------------|     example    |- IPC
 |             | <--------- |      statistics result      | ---------------|- CPI
 |             |            |-----------------------------|                |- branch predict miss rate
 |             |                                                           |- buffer use rate
 |             |
 |             |
 |-------------|
        |
        |    after N times     |---------------------------------|
        |--------------------> |  python pandas show the result  |
                               |---------------------------------|

最终总结

最终cycle级模拟器还是失败了,有客观原因,也有主观原因。对个人来讲,收获是明确了最终的两个方案。同时也有一个疑问: 当模拟器和RTL同时进行开发时(我想这种情况对于国内目前的很多IC初创公司来讲很常见,甚至模拟器开发要落后于RTL开发),cycle级 模拟器到底能在项目中起到怎样的作用?因为从开发周期来讲,很可能开发模拟器的时间和写RTL的时间是差不多的。(尤其是有一些现有 模拟器框架都没有实现的微架构出现时)。如果时钟精确模拟器开发周期和RTL开发周期相近,那么我认为时钟精确模拟器开发就没有意义。

用软件实现硬件,如果不能体现出时间优势,那么一切就没有了意义。这不是说仅仅simpleSim或者Gem5没有意义,systemc也同样没有意义。 当然,如果能够有机会构建systemc,我会非常乐意。