nju-pa摸鱼记4-指令的生命周期
一、前言
在之前的3篇专栏中,主要探讨了NEMU中一些巧妙的宏定义,以及关于计算模型的思考。从本篇开始,将专注于“模拟器如何模拟真实计算机”这一话题。计算机最基础、最核心的功能是执行指令,因此执行指令也是NEMU模拟器最基本的功能。
二、计算机中指令的生命周期
对于精简指令集系统,五级流水线是一种经典的CPU核结构,它将一条指令的处理过程分为5个阶段:
- 取指:维护PC寄存器,发起访存请求从内存中取出指令; 
- 译码:翻译取出的指令,确定指令的操作方法和操作对象; 
- 执行:通过运算部件(如ALU,乘法器等)对操作对象进行算 
- 访存:若为访存类指令,则在这个阶段进行访存; 
- 写回:将指令的运算、访存等结果写回目的寄存器。 
这五个阶段轮流往复地运行,计算机便能自动地运行下去。
三、NEMU中指令的生命周期
在真实的CPU中,为了提升效率,在取指完成后PC跳转到紧接着当前指令的下一条指令继续取指,如果遇到跳转指令,可以冲刷流水线或者使用分支预测等技术增大取指的正确率。而在模拟器中则没有对于性能的要求,完全可以等到指令执行结束再更新PC,然后开始取下一条指令,在NEMU中,指令执行阶段的划分为:
- 取指:通过PC值访问“内存”,取出指令; 
- 译码:分析指令,确定操作方法和操作数; 
- 执行:通过对应的处理函数对操作数进行处理,同时将结果写回“寄存器”; 
- 更新PC:根据指令的执行情况更新PC,此PC一定是正确的PC。 
下面是一条语句在NEMU中的执行过程:
- NEMU调用定义在 - src/cpu/cpu-exec.c中的- cpu_exec()函数,该函数将反复进行取指、译码、执行、更新PC这个过程,直到遇到停机、断点或是什么别的情况。
- cpu_exec()函数的核心是一个死循环,其中包括了- fetch_decode_exec_updatepc()函数,该函数的定义如下:
| 1 |  | 
它将取指译码、执行和更新PC解构,分别对应函数中的三条语句。
- 首先调用fetch_decode()函数,该函数的核心功能可以简化如下:
| 1 |  | 
在fetch_decode()函数中,通过isa_fetch_decode()函数得到指令所对应的序号,该序号和这条指令对应处理函数在列表中的下标相同,然后为函数指针s->EHelper赋值为对应的处理函数。
- 进入isa_fetch_decode()函数,取指和译码进一步被解构,instr_fetch()函数负责与内存交互取指令;table_main()函数是译码函数,将取回的函数和模式串一一比对,若匹配成功则返回该指令对应的序号,若失败则返回一个无效指令序号,这将导致NEMU产生运行异常报给用户。同时在译码时,也将获取该指令的所有操作数,包括立即数和寄存器,它们都将被存在Decode结构中。
| 1 |  | 
- 现在返回到fetch_decode_exec_updatepc()函数中,它的第二条语句调用了s->EHelper()函数执行该指令,这些执行函数被定义在src/isa/$ISA/instr下的若干.h文件中,比如说lui指令的处理函数:
| 1 |  | 
它是由更加细化的rtl级函数rtl_li所完成的,所有指令的终点都是若干rtl函数,也就是将指令拆解成“微指令”。
- 执行完s->EHelper()后,语句
| 1 |  | 
将完成更新PC的过程。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!