再谈模拟器¶
最近在项目中使用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,我会非常乐意。