Logisim实现单周期CPU
需求分析
指令一:add
1.从GRF中读取rs,rt
- 操作:(rs)->RegRead1, (rt)->RegRead2
2.rs与rt通过ALU相加
- 控制:ALUSrc=0, ALUOp=000
- 操作:RegRead1->ALUIn1, RegRead2->ALUIn2, ALUIn1+ALUIn2->ALURes
3. 将结果写入rd
- 控制:RegDst=1, RegWrite=1
- 操作:rd->RegAddr, ALURes->RegData
指令二:sub
1.从GRF中读取rs,rt
- 操作:(rs)->RegRead1, (rt)->RegRead2
2.rs与rt通过ALU相减
- 控制:ALUSrc=0, ALUOp=001
- 操作:RegRead1->ALUIn1, RegRead2->ALUIn2, ALUIn1-ALUIn2->ALURes
3. 将结果写入rd
- 控制:RegDst=1, RegWrite=1
- 操作:rd->RegAddr, ALURes->RegData
指令三:ori
1.从GRF中读取rs,rt
- 操作:(rs)->RegRead1,(rt)->RegRead2
2.imm进行无符号拓展
- 控制:ExtControl=0
- 操作:{16{0},imm}->extend
3.rs和imm通过ALU取或
- 控制:ALUSrc=1, ALUOp=010
- 操作:RegRead1->ALUIn1, extend->ALUIn2, ALUIn1|ALUIn2->ALURes
4.将结果写入rt
- 控制:RegDst=0, RegWrite=1
- 操作:rd->RegAddr, ALURes->RegData
指令四:lw
1.从GRF中读取base,rt
- 操作:(base)->RegRead1, (rt)->RegRead2
2.offest进行有符号拓展
- 控制:ExtControl=1
- 操作:{16{offset[15]},offset}->extend
3.base和offest通过ALU相加
- 控制:ALUSrc=1, ALUOp=000
- 操作:RegRead1->ALUIn1, extend->ALUIn2, ALUIn1+ALUIn2->ALURes
4.将主存结果写入rt
- 控制:MemToReg=1, MemWrite=0, RegDst=0, RegWrite=1
- 操作:ALURes->MemAddr, rt->RegAddr, MemRead->RegData
指令五:sw
1.从GRF中读取base,rt
- 操作:(base)->RegRead1, (rt)->RegRead2
2.offest进行有符号拓展
- 控制:ExtControl=1
- 操作:{16{offset[15]},offset}->extend
3.base和offest通过ALU相加
- 控制:ALUSrc=1, ALUOp=000
- 操作:RegRead1->ALUIn1, extend->ALUIn2, ALUIn1+ALUIn2->ALURes
4.将rt写入主存
- 控制:MemWrite=0, MemWrite=1
- 操作:ALURes->MemAddr, RegRead2->MemData
指令六:beq
1.从GRF中读取rs,rt
- 操作:(rs)->RegRead1,(rt)->RegRead2
2.offest进行有符号拓展
- 控制:ExtControl=1
- 操作:{16{offset[15]},offset}->extend
3.rs和rt通过ALU比较
- 控制:ALUSrc=0, ALUOp=100
- 操作:RegRead1->ALUIn1, RegRead2->ALUIn2, ALUIn1==ALUIn2->ALURes
4.修改pc值跳转
- 操作:pc+4+extend«2->pc
指令七:lui
1.imm进行有符号拓展
- 控制:ExtControl=1
- 操作:{16{imm[15]},imm}->extend
2.imm通过ALU加载到高位
- 控制:ALUSrc=1, ALUOp=011
- 操作:extend->ALUIn2, ALUIn2«16->ALURes
3.将结果写入rt
- 控制:RegDst=0, RegWrite=1
- 操作:rt->RegAddr, ALURes->RegData
控制设计
控制信号含义:
参数 | 含义 |
---|---|
RegWrite | 决定是否要写入寄存器文件,0 为否,1 为是 |
RegDst | 决定存放被写入寄存器的地址为哪几位,0 为 I 型指令,1 为 R 型 |
ALUSrc | 决定 ALU 的第二个数据是寄存器还是立即数的值,0 为寄存器,1 为立即数 |
Branch | 决定当前是否为分支指令,0 为否,1 为是 |
MemWrite | 决定是否要写入数据存储器,0 为否,1 为是 |
MemtoReg | 决定写入寄存器的是 ALU 计算结果还是主存读取结果,0 为计算结果,1 为读取结果 |
ALUOp | 决定 ALU 进行的运算,0 为加,1 为减,2 为或,3 为加载到高位,4 为判断是否相等 |
ExtControl | 决定扩展是符号扩展还是 0 扩展,1 为符号拓展,0 为 0 拓展 |
指令对应的控制信号:
指令 | RegWrite | RegDst | ALUSrc | Branch | MemWrite | MemtoReg | ALUOp | ExtControl |
---|---|---|---|---|---|---|---|---|
add | 1 | 1 | 0 | 0 | 0 | 0 | 000 | 1 |
sub | 1 | 1 | 0 | 0 | 0 | 0 | 001 | 1 |
ori | 1 | 0 | 1 | 0 | 0 | 0 | 010 | 0 |
lw | 1 | 0 | 1 | 0 | 0 | 1 | 000 | 1 |
sw | 0 | 0 | 1 | 0 | 1 | 0 | 000 | 1 |
beq | 0 | 0 | 0 | 1 | 0 | 0 | 100 | 1 |
lui | 1 | 0 | 1 | 0 | 0 | 0 | 011 | 1 |
模块设计
模块一:GRF
-
寄存器堆
-
寄存器选择
模块二:ALU
- ALUOp 决定 ALU 进行的运算,0 为加,1 为减,2 为或,3 为加载到高位,4 为判断是否相等
模块三:Splitter
- 从高到低按照op,rs,rt,rd,ra,rb分线,各个部分宽度分别为6,5,5,5,5,6bit
- 将rd,ra,rb拼接起来作为immediate/offset
模块四:IFU
- 每个时钟周期上升沿将pcNext赋值给pc
- 将pc的值减去初值0x00003000,作为ROM读取的地址addr
- 每个时钟周期上升沿从ROM读取指令instr
- pc若为0则将pc置为初始值0x00003000,地址置为0
模块五:与逻辑
- 结合MIPS指令集,对op和rb进行判断,确定指令类型
模块六:或逻辑
- 结合指令对应的控制信号,输入为指令类型,输出为对应的控制信号
模块七:DM
- 所需要的存储空间为3072*32位,考虑采用三片1024*32位的RAM进行存储
- 32bit=4byte,所以最低两位是字内的字节地址,无意义
- 每片RAM的片内地址需要10位,因此2-11位作为片内地址
- 共有三片RAM,需要两位片选地址,12-13位作为片选地址
模块八:Control
- 综合模块五与模块六的设计,输入为指令的op和rb,输出为对应的控制信号
模块九:main
(一)直接应用前八个模块
- 结合前几个模块的功能,对每一个模块的输入输出端口添加对应含义的tunnel
(二)直接翻译八个控制信号含义
- 按照控制信号含义,使用MUX对各自的功能进行直接翻译
(三)模块与模块建立联系
-
1.只有lw和sw指令能够对主存进行操作,它们访问的主存地址来自ALU计算结果,因此将ALURes与MemAddr直接相连
-
2.只有sw指令能够进行写主存操作,它写入的内容是(rt),也就是RegRead2,因此将RegRead2与MemData直接相连
-
3.结合需求分析,ALU的第一个输入一定来自(rs/base),也就是RegRead1 , 因此将ALUIn1与RegRead2直接相连
思考题
- 1.上面我们介绍了通过 FSM 理解单周期 CPU 的基本方法。请大家指出单周期 CPU 所用到的模块中,哪些发挥状态存储功能,哪些发挥状态转移功能。
状态存储 :GRF、DM 状态转移 :IFU、ALU、Controller
- 2.现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用 Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。
合理 (1)IM 只需被读取,ROM 只有读取功能,所以采用 ROM;如果采用寄存器或 RAM,它们的造价高于 ROM,而 IM 又不需要被写入,过于浪费 (2)DM 既要进行读取,又要进行写入,RAM具有读写功能,所以采用RAM;如果采用寄存器,DM 需要较大空间,寄存器造价高,空间需求大,过于浪费;如果采用ROM,不能进行写入 (3)GRF 既要进行读取,又要进行写入,寄存器具有读写功能,所以采用寄存器;如果采用 ROM,不能进行写入;如果采用 RAM,GRF 与 ALU 直接连接,需要高速地读写,RAM 的存取速度慢于寄存器
3.在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和 > 设计的思路。
没有
4.事实上,实现 nop 空指令,我们并不需要将它加入控制信号真值表,为什么?
该指令不需要输出任何控制信号 ,将所有控制信号置低电平 ,即可确保CPU的各个部件不受任何影响 ,所以并不需要将它加入控制信号真值表
5.阅读 Pre 的 “MIPS 指令集及汇编语言” 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。
该测试样例的强度较高。对于各个需要实现的指令,都进行了测试,先从每一条基本指令进行验证,之后再在其基础上对指令进行进一步更复杂的验证,对程序的验证有一定的准确性
- 测试样例所对应的汇编代码:
ori $28, $0, 0 ori $29, $0, 0 ori $1, $0, 13398 add $1, $1, $1 lw $1, 4($0) sw $1, 4($0) lui $2, 30840 sub $3, $2, $1 lui $5, 4660 ori $4, $0, 5 nop sw $5, -1($4) lw $3, -1($4) beq $3, $5, 1 beq $0, $0, 13 ori $7, $3, 1028 beq $7, $3, 11 nop lui $8, 30583 ori $8, $8, -1 sub $0, $0, $8 ori $0, $0, 4352 add $10, $7, $6 ori $8, $0, 0 ori $9, $0, 1 ori $10, $0, 1 add $8, $8, $10 beq $8, $9, -2 beq $0, $0, -1
- 附:自动将七条指令的机器码转化成MIPS汇编语言的python代码 ```python def hex_to_binary(hex_string): return bin(int(hex_string, 16))[2:].zfill(32) def binary_to_decimal_complement(binary_string): return int(binary_string, 2) - (1 « len(binary_string)) if binary_string[0] == ‘1’ else int(binary_string, 2) def binary_to_decimal(binary_string): return int(binary_string, 2) instructions = { “000000”: { “100000”: “add ${rd}, ${rs}, ${rt}\n”, “100010”: “sub ${rd}, ${rs}, ${rt}\n”, “000000”: “nop\n” }, “001101”: “ori ${rt}, ${rs}, {imm}\n”, “100011”: “lw ${rt}, {imm}(${rs})\n”, “101011”: “sw ${rt}, {imm}(${rs})\n”, “000100”: “beq ${rs}, ${rt}, {imm}\n”, “001111”: “lui ${rt}, {imm}\n” } with open(‘in.txt’, ‘r’) as infile, open(‘out.txt’, ‘w’) as outfile: for line in infile: hex_input = line.strip() binary_output = hex_to_binary(hex_input) op = binary_output[:6] rs = binary_output[6:11] rt = binary_output[11:16] rd = binary_output[16:21] imm = binary_output[16:32] if op in instructions: if op == “000000”: rb = binary_output[26:32] outfile.write( instructions[op].get(rb, “error”).format(rd=binary_to_decimal(rd), rs=binary_to_decimal(rs), rt=binary_to_decimal(rt))) else: outfile.write(instructions[op].format(rt=binary_to_decimal(rt), rs=binary_to_decimal(rs), imm=binary_to_decimal_complement(imm))) else: outfile.write(“error\n”)
```