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 协议 ,转载请注明出处!