这里说的模拟器,是经过软件模拟CPU、外设、存储系统等硬件的行为,来虚拟出一台设备,并在其上运行操做系统及应用程序。好比 Android 模拟器。其实和日常用的虚拟机没太大区别,不过虚拟机一般是模拟 x86 架构,速度很快;而这里我打算模拟 MIPS 架构,与 x86 差别很大,速度慢。这也是 Android 模拟器慢的缘由。git
《计算机组成与设计(硬件/软件接口)》2.1-2.10节
CPU 经过执行指令来完成操做,所以咱们应首先看看指令长什么样子:github
R型指令,经常使用于计算架构
I型指令,经常使用于访存、分支函数
J型指令,经常使用于跳转
MIPS 采用了定长的指令结构,全部指令都是32位长,经过识别 op 就能肯定指令的类型(在咱们要实现的 MIPS 核心指令集上,R型的 opcode 都为0,J型的有两个 j(0x2)、jal(0x3)),进而就能肯定每一位的含义。
再看看都有哪些指令:spa
操做都比较简单,毕竟这是计算机中最底层的语言了。操作系统
为了执行指令,咱们还须要模拟寄存器和存储器。
MIPS 寄存器有32个:设计
int regs[32];
就能够模拟。另外还须要模拟 $pc 寄存器指示当前指令的地址。该寄存器不能被直接操做,因此没有列在上面。本来超快速的寄存器放到内存中模拟,速度必然被拖慢了。
存储器直接 malloc 申请一段空间就行了。
利用循环不断取指令,直接用 switch 语句判断 op 字段来译码,用相应的 C 语句模拟指令行为,咱们的 CPU 就运行起来了。code
是否是太简单了?
确实是,用软件模拟省略了大量的硬件实现细节,咱们作的只是描述 MIPS 指令的行为,并让编译器和汇编器帮咱们转化成 x86 指令执行,这其中必然引入了大量没必要要的操做。blog
《计算机组成与设计(硬件/软件接口)》4.1-4.4节
这里就是要模拟传说中的硬布线控制器。接口
重点是中间的主控单元,经过对 op 字段的分析,肯定每一个控制信号有效/无效(即高电位/低点位)。好比 RegWrite 有效,则寄存器堆就会把 WriteData 端口的值写入 Write Register 端口输入的寄存器号中, 无效则执行读取操做;RegDst 有效,则会将 rd 字段输入 Write Register 端口,不然将 rt 字段输入该端口。每一个部件都按照指定的方式工做,指令也就被执行了。
此外,每一个单元还有一个时钟信号输入(即矩形波),在时钟的上升沿/降低沿,单元的状态改变。PC 寄存器每一个周期都会自增,这样就能不断读入新的指令并执行。
通常来讲,这种逻辑设计应该使用 Verilog 语言描述。这里我用了一种比较奇葩的方法,使用 C 语言来描述。
每一个单元用一个函数模拟,参数就是输入端口上的值,返回值就是输出端口上的值,控制信号做为全局变量,函数调用时的实参就起到了多选器的做用。按上图设计就行了。
须要注意的是,C 语句有前后顺序:必须先执行指令存储器,再执行寄存器,等等。可是在硬件中指定好控制信号,在一个周期内就会达到想要的状态,并无人为指定前后顺序,有点像原子操做。用 C 模拟仍是有点不三不四的感受。
这里我模拟了 lw、 sw、 beq、 add、 sub、 and、 or、 slt、 j 指令。其实这一部分彻底没有必要,只会增长程序的复杂度。不过经过这样的练习,咱们对处理器的认识也会更加深入些。
具体过程看代码吧:https://github.com/hduhxc/echovm