本实验来自于谭志虎老师的《计算机组成原理实验指导与习题解析》5.3 节的全部内容,目的是向读者朋友提供一个实现参考,因为个人认为这节内容还是很不错的,实现了 MIPS 45条指令,已经绰绰有余了。
目录
1. 实验要求
实现 45 条指令的单周期 MIPS CPU。
在 24条指令的基础上随机增加表 5.13 中的扩展指令,应包括两种C类指令、一种 M 类指令、一条B类指令,简称 CCMB 系列指令。最终实现的MIPS处理器能正确运行标准测试程序 benchmark.asm,以及新增的扩展指令的测试程序。读者需要单独测新增试扩展指令的标准测试程序(详见实验资源包),并修改 syscall系统调用的功能。当系统调用号不等于34(十进制)时,一律是暂停当前程序的执行,而不是停机;系统调用号等于34(十进制)将数据输出到数码管;暂停后等待用户按下电路中的Go按钮才能继续运行后续测试程序。
2. 具体实现
使用 Logisim,采用单周期、硬布线控制器实现,下图是最终的 Logisim 电路图、真值表、内存控制器真值表。
由于是总体性,看不懂很正常,不懂可以大致看看,然后深入具体电路中会好懂一些。
![](https://img-blog.csdnimg.cn/direct/502497c181c34cf28b80856a30a71fee.png)
![](https://img-blog.csdnimg.cn/direct/59e800410ebb42f1a4b4159009e2448f.png)
![](https://img-blog.csdnimg.cn/direct/674000e600e64e3ab422ce9bff026341.png)
2.1 指令编码规律
注意:以下结论适合大部分指令,但不是绝对的,具体细节参考资源下载中的《MIPS32指令手册.pdf》
R型指令,rd 为目标寄存器编号,rs 第一源操作数、rt 第二源操作数
I型指令,rt 为目标寄存器编号,rs 第一源操作数、Imm(16) 为第二源操作数
R、I型指令,rs 第一源操作数
2.2 C 类指令实现
实现 C 类指令要注意的点,已经用红色加粗标识出来了。而且“硬布线控制器”的输出端,新增了 HiToReg、LoToReg、ResToLoHiWrite、LoExt、RegShamt 这几个输出控制信号。
2.2.1新增控制信号
1. HiToReg、LoToReg 控制信号
用于 mfhi 指令,将寄存器内容传送到 HI 寄存器中,同理 LoToReg 用于 mflo 指令,将寄存器内容传送到 LO 寄存器中。
2. ResToLoHiWrite
用于 mult、div 乘法,除法指令,将低32bit结果写入 Lo 寄存器,高32bit结果写入 Hi 寄存器。我觉得这个信号名称不太好,换成 LoHiWrite 比较好。
3. LoExt
用于 LUI 指令,将立即数写回寄存器时控制低位零扩展,形成32bit数据,写入 $rt 目标寄存器中。
lui $rt, imm
R[$rt] <- { (imm)[15:0], 16x0} 低位零扩展
4. RegShamt
用可变位移指令,注意,该类指令第一源操作数是 $rt,位移量由 $rs 提供,这3条指令的信号都是一样的,运算类型有 ALUOP 指定。
2.2.2 指令实现
- SLLV、SRLV、SRAV
需要注意第一操作数是 $rt、位移量是 $rs。
- SUBU、XOR
标准R型指令,和 add 的控制信号一样,注意:subu,这里的“u”是运算溢出后不触发中断,不是进行无符号减法,减法和加法本质上是不区分符号的。乘法、除法才需区分有符号和无符号。 - XORI
标准I型指令,和 andi 逻辑运算类指令信号一样。
- LUI
lui $rt, imm
R[$rt] <- { (imm)[15:0], 16x0} 低位零扩展
在我的实现中是:新增一个控制信号,用于控制低位零扩展,然后再将扩展后的32bit数据,写入到寄存器中。
- SLTIU
和 24 条基础指令总的 SLTI 的控制信号一样,除了 ALUOP 不一样,因为本指令是无符号比较,而 SLTI 是有符号比较
- MULTU、DIVU
multu $rs, $rt
{Hi, Lo} <- R[$rs] * R[$rt] 无符号乘法
divu $rs, $rt
Lo <- R[$rs] / R[$rt],Hi <- R[$rs] % R[$rt] 无符号除法
无符号乘、除。数据通路的控制有些特殊,只需将 ALU 的 Res1 写入 Lo,Res2 写入 Hi 即可,因此需要 RestoLoHiWrite=1。
- MFLO、MFHI
mflo $rd
R[$rd] <- Lo
mfhi $rd
R[$rd] <- Hi
将通用寄存器内容写入 Hi、Lo 寄存器,故 HiToReg、LoToReg 都为1。
2.3 B类指令实现
包括下面4条指令,和 beq、bne 类似,但又有不同。不同点:下面4条指令是和0左比较,而 beq、bne 是判断是否相等,而寄存器文件中 31号寄存器是常零值,就可以利用它来实现
信号控制信号
新增了 LEZ(小于或等于零则跳转)、GTZ(大于零则跳转)、LTZ(小于零则跳转)、GEZ(大于等于零则跳转)。
注意:BLTZ、BGEZ OpCode都为1(十进制),需要再根据指令字的 20:16 位来判断具体是那个,详细细节在资源文件的《MIPS32指令集完整.pdf》中。
在实现电路时需要再进行一次判断,判断当前指令是 BLTZ还是BGEZ。
地址位宽注意点
可以看到指令存储器地址输入端是32位的2-11位信号组成的10bit,作为地址输入信号,为什么是10bit,这好理解,因为我们的指令并没有那么多,重点是为什么是2:11位,为什么不是0:9组成的10位?因为 MIPS 32 每个指令固定占 4Byte,所以是4Byte对齐,也就是说低两位恒零,所以低2位就不需要了。
另外当前的指令存储器是按字(4Byte)寻址的,每个地址对应的存储单元占4Byte。
指令实现
blez $rs, imm if( R[$rs] <= 0 ) PC <- PC+4 + SignExt(imm)<<2 有符号比较
bgtz $rs, imm if( R[rs] > 0 ) PC <- PC+4 + SignExt(imm)<<2 有符号比较
bltz $rs, imm if( R[rs] < 0 ) PC <- PC+4 + SignExt(imm)<<2 有符号比较
bgez $rs, imm if( R[rs] >= 0 ) PC <- PC+4 + SignExt(imm)<<2 有符号比较
下图中的 Res1 隧道标签就是 ALU 运算的结果,我们封装的 ALU 有“有符号比较”运算,x<y ? 1:0,利用下面的逻辑表示,就可以判断出是哪条跳转指令是否满足。
2.4 M类指令实现
大坑
CS3410 MIPS RAM 有个很大的坑,输入数据总,即便是不写入存储器中,存在高阻态信号,也会导致写入出错。
要像这样才行,不写入的数据不能是高阻态。
地址位宽注意点 CS3410 MIPS RAM
数据存储器使用的是 CS3410 MIPS RAM,地址位宽10bit,数据位宽32bit,是按字(4Byte)寻址的,所以字节地址0x000、 0x001、0x002、0x003都对应字地址0x000;字节地址 0x004(0b100)对应字地址0x001,以此类推。
切记:CS3410 MIPS RAM 的地址输入端一定要是字地址(4Byte)寻址,即字节地址的低两位忽略。
若想读取1字节的字节地址 0x001(0b01),那么字地址等于0x000,Sel=0010。Sel就是选择4Byte中的那一个字节的。
若想读取2字节的字地址0x000,那么字地址等于0x000,Sel=0011。
新增信号
M类指令是访存类指令,原有的控制信号和 LW、SW 是一样的。新增了如下的控制信号(由内存控制器生成) 根据指令来生成访问内存位宽,来生成是操作连续的4Byte,还是连续2Byte,还是1Byte。
以及 MenOutSignExt,从内存中读取1Byte或2Byte数据传送到32Bit的寄存器中,必然要涉及到扩展,因为位宽不够。LB/LH进行符号扩展,LBU/LHU 进行零扩展。写入内存的数据不涉及位扩展。
指令实现
M类指令相对比 SW、lW 复杂了不少,主要是支持了字节、半字(2Byte)、字(4Byte)的数据写入和读出。
节选部分内存输入地址、输入数据,输出数据控制电路。
其中 8 输入扩展,输入1是8位数据,输入2是SignExt,是否进行符号扩展,输出是32位扩展后的数据。16位输入扩展同理。
MenByteSel
下图是内存控制器,输出的 MenByteSel 逻辑如下。
![](https://img-blog.csdnimg.cn/direct/b70167d47d944bc7ab1068b3a15fcb78.png)
上图的电路逻辑,用C语言描述,大概是:
if (1Byte) {
switch (Addr[1:0]) {
case 0b00:
MenByteSel = 0b0001;
break;
case 0b01:
MenByteSel = 0b0010;
break;
case 0b10:
MenByteSel = 0b0100;
break;
case 0b11:
MenByteSel = 0b1000;
break;
}
}
if (2Byte) {
switch (Addr[1:0]) {
case 0b00:
MenByteSel = 0b0011;
break;
case 0b01:
MenByteSel = 0b0110;
break;
case 0b10:
case 0b11:
MenByteSel = 0b1100;
break;
}
}
if (4Byte) {
MenByteSel = 0b1111
}
3. 测试说明
所有的测试程序都在资源文件下的“汇编工具及测试用例4.4”中,如果有想自定义测试的,可以手动写 mips 汇编,再使用 MARS 软件手动生成机器码。
注意:下面只是列出部分用于参考,具体请下载资源,自行查看。
测试程序的输出请看下图中的“CCMB输出汇总.docx”。
C类指令测试程序:
B类指令测试程序:
M类指令测试程序:
最终支持 45 条指令的 MIPS CPU,能够正确运行 CCMB、benchmark 这两个测试程序。
4. 资源下载(免费)
此压缩包包含了 Logisim电路、Excel 自动生成逻辑表达式(真正表输入输出均已正确填写)、以及详尽的测试用例。
5. 向我留言
如有任何疑问,可发表评论,我会及时处理!
若有讲解不清楚的地方,也欢迎留言,我都会及时处理!希望大家能够一起交流!