GEM5 算是体系结构层面上仿真绕不开的项目了。GEM5 作为一个由开源社区维护的仿真器项目,虽然其支持的功能完整性和实效性都可能略显吃力,但是依然是一个很棒的仿真项目,支持的体系结构从 X86、ARM 到 MIPS、RISCV。最近正在准备配合其他人做一些和 Cache 相关的实验,需要在原有的 GEM5 基础上加一些辅助功能,以此文记录一下探索的过程。

SCons

  GEM5 使用 SCons 作为管理和编译框架,不同于 cmake 项目,SCons 在编译过程的自动化程度更高。对于 GEM5 来说,有许多头文件是由 SCons 在编译阶段生成的,且编译时会根据配置的 con­fig­u­ra­tion 选择对应的组件参与编译。这样对于项目的拓展确实很不错,但是对于传统的依赖 cmake 编译命令来索引源码和浏览源码的工具很不友好,比如 Source­trail。而且,使用 SCons 对编译的依赖要求很高,至少需要 python 环境和一些比较重的库。虽然 SCons 在 4.0 版本之后也添加了导出成 compilation_command.json 的 Builder 方法,但是对于 GEM5 来说,并不好使,依然受制于配置指定模组和编译预处理过程生成一些头文件这两点。

  另外,由于组件的源码是通过配置选择并加入编译索引路径的,在看源码的时候,会有一定的不舒适。比如某个类内成员声明为某个基类的指针,在调用 vir­tual 方法的时候,并不能找到对应方法正确的 over­ride,因为具体的派生类实例化完完全全是由 SCons 控制,Source­trail 无法知道,而就算有 compilation_command.json 也只是有某个配置的源码,不能看到全局。

Cache

  GEM5 在不同组件之间的通讯采用 re­quest/​re­sponse pkg 的机制,Base­Cache 部分主要实现了 Cache 与其他对象(如 Process、Mem­ory、其他 Cache)之间 pkg 的交互和各类请求处理。同时,可以在 Base­Cache 内注册各种 event 并进行 sched­ule,以实现各种拓展的功能。Base­Cache 还负责维护和更新 Sta­tis­tics。其中比较关键的成员变量为:tagsmshrQueuewriteBuffer

BaseCache.png
BaseCache.png

  Cache 实现中对行为的仿真控制主要在 tags。GEM5 使用类似 Tags­Store 的类,来管理所有和 Cache 命中、替换策略等用户侧相关的逻辑。全相连绑定 LRU 替换,派生成 FALRU 类,直接继承于 BaseTags;组相连则为 BaseSetAssoc 类;此外还有类似 SectorTags 这种。在 BaseTags 中的关键对象为 indexingPolicy,其类型为 BaseIndexingPolicy 的指针,具体编译实例化的时候,为配置选择的对应派生类。在 indexingPolicy 指向的对象中,保存着 sets,为 ReplaceableEntry 的指针二位数组,管理每个组 (set) 中的不同路 (way)。

  在 BaseSetAssoc 中,关键的成员为 blksreplacementPolicy。前者为本 Tags­Store 管理的 Cache Block 的地址,为类型。后者为替换策略实现对象,包括 LRU、Random、LFU 等等派生类实现。Cache Block 对应的派生类为 CacheBlk,继承于 TaggedEntry,其继承于 ReplaceableEntry。在 ReplaceableEntry 中定义的 replacementData 为替换策略主要使用的数据对象,用 std::shared_ptr 包了一层,必须由替换策略来进行实例化,且对于不同的替换策略实现,对 replacementData 的实现内容也不同。这样一个设计,对于替换策略的保护和拓展很友好,但是不方便从 Cache 这一个层面直接对替换策略进行监控和修改,需要额外实现全套的替换策略类内方法。

  在 CacheBlk 中实现了关于 Cache Block 状态的方法,包括访存一致性、预取、安全空间相关。其中关键的成员变量为 data,按字节单位存放对应 Cache Block 中的数据,描述是为了 "easy ac­cess" 不过我还没发现 CacheBlk 里面的数据还在哪有存放 →_→

MMU

待续

目前可以公布的情报:当前版本的 GEM5 中 ARM 的 Process 实现没有对应的 TLB(未完成),对应的地址转换由 MMU 实现。