GEM5算是体系结构层面上仿真绕不开的项目了。GEM5作为一个由开源社区维护的仿真器项目,虽然其支持的功能完整性和实效性都可能略显吃力,但是依然是一个很棒的仿真项目,支持的体系结构从X86、ARM到MIPS、RISCV。最近正在准备配合其他人做一些和Cache相关的实验,需要在原有的GEM5基础上加一些辅助功能,以此文记录一下探索的过程。
SCons
GEM5使用SCons作为管理和编译框架,不同于cmake项目,SCons在编译过程的自动化程度更高。对于GEM5来说,有许多头文件是由SCons在编译阶段生成的,且编译时会根据配置的configuration选择对应的组件参与编译。这样对于项目的拓展确实很不错,但是对于传统的依赖cmake编译命令来索引源码和浏览源码的工具很不友好,比如Sourcetrail。而且,使用SCons对编译的依赖要求很高,至少需要python环境和一些比较重的库。虽然SCons在4.0版本之后也添加了导出成compilation_command.json
的Builder方法,但是对于GEM5来说,并不好使,依然受制于配置指定模组和编译预处理过程生成一些头文件这两点。
另外,由于组件的源码是通过配置选择并加入编译索引路径的,在看源码的时候,会有一定的不舒适。比如某个类内成员声明为某个基类的指针,在调用virtual方法的时候,并不能找到对应方法正确的override,因为具体的派生类实例化完完全全是由SCons控制,Sourcetrail无法知道,而就算有compilation_command.json
也只是有某个配置的源码,不能看到全局。
Cache
GEM5在不同组件之间的通讯采用 request/response pkg 的机制,BaseCache 部分主要实现了Cache与其他对象(如Process、Memory、其他Cache)之间pkg的交互和各类请求处理。同时,可以在BaseCache内注册各种event并进行schedule,以实现各种拓展的功能。BaseCache还负责维护和更新Statistics。其中比较关键的成员变量为:tags
、mshrQueue
、writeBuffer
。
Cache实现中对行为的仿真控制主要在tags。GEM5使用类似TagsStore的类,来管理所有和Cache命中、替换策略等用户侧相关的逻辑。全相连绑定LRU替换,派生成FALRU
类,直接继承于BaseTags
;组相连则为BaseSetAssoc
类;此外还有类似SectorTags
这种。在BaseTags
中的关键对象为indexingPolicy
,其类型为BaseIndexingPolicy
的指针,具体编译实例化的时候,为配置选择的对应派生类。在indexingPolicy
指向的对象中,保存着sets
,为ReplaceableEntry
的指针二位数组,管理每个组(set)中的不同路(way)。
在BaseSetAssoc
中,关键的成员为blks
和replacementPolicy
。前者为本TagsStore管理的Cache Block的地址,为CacheBlk
,继承于TaggedEntry
,其继承于ReplaceableEntry
。在ReplaceableEntry
中定义的replacementData
为替换策略主要使用的数据对象,用std::shared_ptr
包了一层,必须由替换策略来进行实例化,且对于不同的替换策略实现,对replacementData
的实现内容也不同。这样一个设计,对于替换策略的保护和拓展很友好,但是不方便从Cache这一个层面直接对替换策略进行监控和修改,需要额外实现全套的替换策略类内方法。
在CacheBlk
中实现了关于Cache Block状态的方法,包括访存一致性、预取、安全空间相关。其中关键的成员变量为data
,按字节单位存放对应Cache Block中的数据,描述是为了 "easy access"不过我还没发现CacheBlk里面的数据还在哪有存放 →_→ 。
MMU
待续
目前可以公布的情报:当前版本的GEM5中ARM的Process实现没有对应的TLB(未完成),对应的地址转换由MMU实现。