本文将描述基于 MIPS-32 的简单功能型处理器的 Verilog 实现。
本文整理了所有需要实现的 M I P S − 32 MIPS-32 MIPS−32 指令,共计 45 45 45 条;在构建单周期 c p u cpu cpu 时,我采用了按指令类别依次实现的方式,将在最后介绍;本文旨在分享我在实验中的想法,作者也是初学者,代码可能存在未知问题,欢迎指正。仅供参考,请勿复用!
信号名 | I/O | 说明 |
---|---|---|
rst | Input | 与处理器工作时钟同步的高电平复位信号 |
clk | Input | 处理器工作时钟 |
PC[31:0] | Output | 程序计数器, 复位后初值为32’d0 |
Instruction[31:0] | Input | 从内存(Memory)中读取至处理器的指令 |
Address[31:0] | Output | 数据访存指令使用的内存地址 |
MemWrite | Output | 内存访问的写使能信号(高电平有效) |
Write_data[31:0] | Output | 内存写操作数据 |
Write_strb[3:0] | Output | 内存写操作字节有效信号(支持32/16/8-bit内存写)Write_strb[i] == 1表示Write_data[8 × (i + 1) - 1 : 8 × i ] 位会被写入内存的对应地址 |
MemRead | Output | 内存访问的读使能信号(高电平有效) |
Read_data[31:0] | Input | 从内存中读取的数据 |
根据指令手册 The MIPS32 Instruction Set (Volume II) 内的指令
需要实现 45 45 45 个指令:
a d d i u , a d d u , s u b u , a n d , a n d i , n o r , o r , o r i , x o r , x o r i , s l t , s l t i , s l t u , s l t i u , s l l , s l l v , s r a , s r a v , s r l , s r l v , b n e , b e q , b g e z , b g t z , b l e z , b l t z , j , j a l , j r , j a l r , l b , l h , l w , l b u , l h u , l w l , l w r , s b , s h , s w , s w l , s w r , m o v n , m o v z , l u i addiu, addu, subu, and, andi, nor, or, ori, xor, xori, slt, slti, sltu, sltiu, sll, sllv, sra, \newline srav, srl, srlv, bne, beq, bgez, bgtz, blez, bltz, j, jal, jr, jalr, lb, lh, lw, lbu, lhu, lwl, lwr,\newline sb, sh, sw, swl, swr, movn, movz, lui addiu,addu,subu,and,andi,nor,or,ori,xor,xori,slt,slti,sltu,sltiu,sll,sllv,sra,srav,srl,srlv,bne,beq,bgez,bgtz,blez,bltz,j,jal,jr,jalr,lb,lh,lw,lbu,lhu,lwl,lwr,sb,sh,sw,swl,swr,movn,movz,lui
这些指令可以按照其执行的功能进行如下分类:
类别 | 指令 | |
R-Type | 运算指令 | addu, subu, and, or, xor, nor, slt, sltu |
移位指令 | sll, sra, srl, sllv, srav, srlv | |
跳转指令 | jr, jalr | |
mov指令 | movz, movn | |
REGIMM | bltz, bgez | |
J-Type | j, jal | |
I-Type | 分支指令 | beq, bne, blez, bgtz |
计算指令 | addiu, lui, andi, ori, xori, slti, sltiu | |
内存读指令 | lb, lh, lw, lbu, lhu, lwl, lwr | |
内存写指令 | sb, sh, sw, swl, swr |
下面列出了所有需要实现的指令的 MIPS-32 \textbf{MIPS-32} MIPS-32 定义:
运算指令 | opcode(6-bit) | rs(5-bit) | rt(5-bit) | rd(5-bit) | shamt(5-bit) | func(6-bit) |
addu | 000000(SPECIAL) | rs | rt | rd | 00000 | 100001 |
subu | 100011 | |||||
and | 100100 | |||||
or | 100101 | |||||
xor | 100110 | |||||
nor | 100111 | |||||
slt | 101010 | |||||
sltu | 101011 | |||||
sll | 00000 | sa | 000000 | |||
sra | 000011 | |||||
srl | 000010 | |||||
sllv | rs | 00000 | 000100 | |||
srav | 000111 | |||||
srlv | 000110 | |||||
jr | 00000 | 00000 | 001000 | |||
jalr | rd | 001001 | ||||
movz | rt | 001010 | ||||
movn | 001011 |
R − T y p e : o p c o d e [ 5 : 0 ] = = 6 ’ b 000000 R-Type:opcode[5:0] == 6’b000000 R−Type:opcode[5:0]==6’b000000
• func[5] == 1’b1:运算指令(ALU控制信号)
• func[5:3] == 3’b000:移位指令(移位器控制信号)
• {func[5:3], func[1]} == 4’b0010:跳转指令(PC变化信号)
• {func[5:3], func[1]} == 4’b0011:mov指令
各指令功能如下:(为了方便描述,使用 [ x ] [x] [x] 代表地址为 x x x 的通用寄存器, { x } \{x\} {x} 表示其存储的值)
指令 | func | 功能 |
---|---|---|
ADDU | 100001 | r d ← r s + r t : rd ← rs + rt: rd←rs+rt: { r s } \{rs\} {rs} 与 { r t } \{rt\} {rt} 进行加法运算,结果保存到 [ r d ] [rd] [rd] (不进行溢出检查,总是将结果保存到目的寄存器) |
SUBU | 100011 | r d ← r s − r t : rd ← rs - rt: rd←rs−rt: { r s } \{rs\} {rs} 与 { r t } \{rt\} {rt} 进行减法运算,结果保存到 [ r d ] [rd] [rd] (不进行溢出检查,总是将结果保存到目的寄存器) |
AND | 100100 | r d ← r s A N D r t : rd ← rs\ AND\ rt: rd←rs AND rt: { r s } \{rs\} {rs} 与 { r t } \{rt\} {rt} 进行逻辑“与”运算,结果保存到 [ r d ] [rd] [rd] |
OR | 100101 | r d ← r s O R r t : rd ← rs\ OR\ rt: rd←rs OR rt: { r s } \{rs\} {rs} 与 { r t } \{rt\} {rt} 进行逻辑“或”运算,结果保存到 [ r d ] [rd] [rd] |
XOR | 100110 | r d ← r s X O R r t : rd ← rs\ XOR\ rt: rd←rs XOR rt: { r s } \{rs\} {rs} 与 { r t } \{rt\} {rt} 进行逻辑“异或”运算,结果保存到 [ r d ] [rd] [rd] |
NOR | 100111 | r d ← r s N O R r t : rd ← rs\ NOR\ rt: rd←rs NOR rt: 将 { r s } \{rs\} {rs} 与 { r t } \{rt\} {rt} 进行逻辑“或非”运算,结果保存到 [ r d ] [rd] [rd] |
SLT | 101010 | r d ← ( r s < r t ) : rd ←(rs { r s } \{rs\} {rs} 与 { r t } \{rt\} {rt} 进行有符号数比较,如果 { r s } \{rs\} {rs} 小于 { r t } \{rt\} {rt},那么将 1 1 1 保存到 [ r d ] [rd] [rd];否则将 0 0 0 保存到 [ r d ] [rd] [rd] |
SLTU | 101011 | r d ← ( r s < r t ) : rd ←(rs 将 { r s } \{rs\} {rs} 与 { r t } \{rt\} {rt} 进行无符号数比较,如果 { r s } \{rs\} {rs} 小于 { r t } \{rt\} {rt},那么将 1 1 1 保存到 [ r d ] [rd] [rd];否则将 0 0 0 保存到 [ r d ] [rd] [rd] |
SLL | 000000 | r d ← r t < < s a ( l o g i c ) : rd ← rt << sa (logic) : rd←rt<<sa(logic): 将 { r t } \{rt\} {rt} 左移 s a sa sa 位,空出位置用 0 0 0 填充,结果保存到 [ r d ] [rd] [rd] |
SRA | 000011 | r d ← r t > > s a ( a r i t h m e t i c ) : rd ← rt >> sa(arithmetic): rd←rt>>sa(arithmetic): 将 { r t } \{rt\} {rt} 向右移 s a sa sa 位,空出位置用 r t [ 31 ] rt[31] rt[31] 的值填充,结果保存到 [ r d ] [rd] [rd] |
SRL | 000010 | r d ← r t > > s a ( l o g i c ) : rd ← rt >> sa (logic): rd←rt>>sa(logic): 将 { r t } \{rt\} {rt} 向右移 s a sa sa 位,空出位置用 0 0 0 填充,结果保存到 [ r d ] [rd] [rd] |
SLLV | 000100 | r d ← r t < < r s [ 4 : 0 ] ( l o g i c ) : rd ← rt << rs[4:0] (logic): rd←rt<<rs[4:0](logic): 将 { r t } \{rt\} {rt} 向左移,空出位置用 0 0 0 填充,结果保存到 [ r d ] [rd] [rd];移位位数由 { r s } \{rs\} {rs} 的第 0 − 4 b i t 0-4\ bit 0−4 bit 确定 |
SRAV | 000111 | r d ← r t > > r s [ 4 : 0 ] ( a r i t h m e t i c ) : rd ← rt >> rs[4:0] (arithmetic): rd←rt>>rs[4:0](arithmetic): 将 { r t } \{rt\} {rt} 向右移,空出位置用 r t [ 31 ] rt[31] rt[31] 填充,结果保存到 [ r d ] [rd] [rd];移位位数由 { r s } \{rs\} {rs} 的第 0 − 4 b i t 0-4\ bit 0−4 bit 确定 |
SRLV | 000110 | r d ← r t > > r s [ 4 : 0 ] ( l o g i c ) : rd ← rt >> rs[4:0] (logic): rd←rt>>rs[4:0](logic): 将 { r t } \{rt\} {rt} 向右移,空出位置用 0 0 0 填充,结果保存到 [ r d ] [rd] [rd];移位位数由 { r s } \{rs\} {rs} 的第 0 − 4 b i t 0-4\ bit 0−4 bit 确定 |
JR | 001000 | p c ← r s : pc ← rs: pc←rs: 将 { r s } \{rs\} {rs} 赋给寄存器 P C PC PC,作为新的指令地址 |
JALR | 001001 | r d ← r e t u r n a d d r e s s , p e ← r s : rd ← return\ address,pe ← rs: rd←return address,pe←rs: 将 { r s } \{rs\} {rs} 赋给寄存器 P C PC PC,作为新的指令地址,同时将跳转指令后面第 2 2 2 条指令的地址作为返回地址保存到 [ r d ] [rd] [rd],如果没有在指令中指明 r d rd rd( r d rd rd 为零),那么默认将返回地址保存到寄存器 31 31 31 |
MOVZ | 001010 | i f r t = 0 t h e n r d ← r s : if\ rt = 0\ then\ rd ← rs: if rt=0 then rd←rs: 如果 { r t } \{rt\} {rt} 为零,将 { r s } \{rs\} {rs} 赋给 { r d } \{rd\} {rd};否则保持 { r d } \{rd\} {rd} 不变 |
MOVN | 001011 | i f r t ≠ 0 t h e n r d ← r s : if\ rt ≠ 0\ then\ rd ← rs: if rt=0 then rd←rs: 如果 { r t } \{rt\} {rt} 不为零,将 { r s } \{rs\} {rs} 赋给 { r d } \{rd\} {rd};否则保持 { r d } \{rd\} {rd} 不变 |
指令 | opcode(6-bit) | rs(5-bit) | REG(5-bit) | imm(16-bit) |
bltz | 000001(REGIMM) | rs | 00000 | offset |
bgez | 00001 |
R E G I M M : o p c o d e [ 5 : 0 ] = = 6 ’ b 000001 REGIMM:opcode[5:0] == 6’b000001 REGIMM:opcode[5:0]==6’b000001
各指令功能如下:
转移地址为 o f f s e t offset offset 左移2位,并符号扩展为 32 32 32 位后加上 ( P C + 4 ) (PC+4) (PC+4)的结果。
转移目标地址: { { 14 { o f f s e t [ 15 ] } } , ( o f f s e t < < 2 ) , ′ 0 0 ′ } + ( p c + 4 ) \{\{14\{offset[15]\}\},\ (offset<< 2),\ '00'\}+(pc+4) {{14{offset[15]}}, (offset<<2), ′00′}+(pc+4)
指令 | REG | 功能 |
---|---|---|
BLTZ | 00000 | i f r s < 0 t h e n b r a n c h : if\ rs<0\ then\ branch: if rs<0 then branch: 如果地址为 r s rs rs 的通用寄存器的值小于 0 0 0,那么发生转移 |
BGEZ | 00001 | i f r s ≥ 0 t h e n b r a n c h : if\ rs≥0\ then\ branch: if rs≥0 then branch: 如果地址为 r s rs rs 的通用寄存器的值大于等于 0 0 0,那么发生转移 |
指令 | opcode(6-bit) | instr_index(26-bit) |
j | 000010 | instr_index |
jal | 000011 |
J − T y p e : o p c o d e [ 5 : 1 ] = = 5 ’ b 00001 J-Type:opcode[5:1] == 5’b00001 J−Type:opcode[5:1]==5’b00001
各指令功能如下:
指令 | opcode | 功能 |
---|---|---|
J | 000010 | p c ← { ( p c + 4 ) [ 31 , 28 ] , i n s t r _ i n d e x , ‘ 00 ’ } : pc ← \{(pc+4)[31,28],\ instr\_index,\ ‘00’\}: pc←{(pc+4)[31,28], instr_index, ‘00’}: 转移到新指令地址,其中新指令地址的低 28 28 28 位是指令中的 i n s t r _ i n d e x instr\_index instr_index 字段左移两位的值,高 4 4 4 位是跳转指令后指令地址的高 4 4 4 位 |
JAL | 000011 | p c ← { ( p c + 4 ) [ 31 , 28 ] , i n s t r _ i n d e x , ‘ 00 ’ } : pc←\{(pc+4)[31,28],\ instr\_index,\ ‘00’\}: pc←{(pc+4)[31,28], instr_index, ‘00’}: 转移到新指令地址,其中新指令地址的低 28 28 28 位是指令中的 i n s t r _ i n d e x instr\_index instr_index 字段左移两位的值,高 4 4 4 位是跳转指令后指令地址的高 4 4 4 位,同时将跳转指令后面第 2 2 2 条指令的地址作为返回地址保存到寄存器 31 31 31 |
指令 | opcode(6-bit) | rs/base(5-bit) | rt(5-bit) | imm(16-bit) |
beq | 000100 | rs | rt | offset |
bne | 000101 | |||
blez | 000110 | 00000 | ||
bgtz | 000111 | |||
addiu | 001001 | rt | imm | |
lui | 001111 | |||
andi | 001100 | |||
ori | 001101 | |||
xori | 001110 | |||
slti | 001010 | |||
sltiu | 001011 | |||
lb | 100000 | base | offset | |
lh | 100001 | |||
lw | 100011 | |||
lbu | 100100 | |||
lhu | 100101 | |||
lwl | 100010 | |||
lwr | 100110 | |||
sb | 101000 | |||
sh | 101001 | |||
sw | 101011 | |||
swl | 101010 | |||
swr | 101110 |
I − T y p e : I-Type: I−Type:
• 分支指令:opcode[5:2] == 4’b0001
• 计算指令:opcode[5:3] == 3’b001
• 内存读指令:opcode[5] && ~opcode[3]
• 内存写指令:opcode[5] && opcode[3]
各指令功能如下:(为了方便描述,使用 [ x ] [x] [x] 代表地址为 x x x 的通用寄存器, { x } \{x\} {x} 表示其存储的值)
转移指令转移的新指令地址为 o f f s e t offset offset 左移 2 2 2 位后进行符号扩展并加上 P C + 4 PC+4 PC+4 后的结果。
转移目标地址: { { 14 { o f f s e t [ 15 ] } } , ( o f f s e t < < 2 ) , ′ 0 0 ′ } + ( p c + 4 ) \{\{14\{offset[15]\}\},\ (offset<< 2),\ '00'\}+(pc+4) {{14{offset[15]}}, (offset<<2), ′00′}+(pc+4)
访存指令加载的地址为符号扩展的 i m m i d i a t e + b a s e immidiate + base immidiate+base 地址指示的值。
指定的加载地址: { { 16 { i m m [ 15 ] } } , i m m } + { b a s e } \{\{16\{imm[15]\}\},\ imm\} + \{base\} {{16{imm[15]}}, imm}+{base}
指令 | opcode | 功能 |
---|---|---|
BEQ | 000100 | i f r s = r t t h e n b r a n c h : if\ rs=rt\ then\ branch: if rs=rt then branch: 如果 { r s } \{rs\} {rs} 与 { r t } \{rt\} {rt} 相等,则发生转移 |
BNE | 000101 | i f r s ≠ r t t h e n b r a n c h : if\ rs≠rt\ then\ branch: if rs=rt then branch: 如果 { r s } \{rs\} {rs} 不等于 { r t } \{rt\} {rt},则发生转移 |
BLEZ | 000110 | i f r s ≤ 0 t h e n b r a n c h : if\ rs≤0\ then\ branch: if rs≤0 then branch: 如果 { r s } \{rs\} {rs} 小于等于零,则发生转移 |
BGTZ | 000111 | i f r s > 0 t h e n b r a n c h : if\ rs>0\ then\ branch: if rs>0 then branch: 如果 { r s } \{rs\} {rs} 大于零,则发生转移 |
ADDIU | 001001 | r t ← r s + ( s i g n _ e x t e n d e d ) i m m e d i a t e : rt ← rs+(sign\_extended)\ immediate: rt←rs+(sign_extended) immediate: { r s } \{rs\} {rs} 与指令中立即数符号扩展为 32 − b i t 32-bit 32−bit 后的值进行加法运算,运算结果保存到 [ r t ] [rt] [rt] (不进行溢出检查,总是将结果保存到目的寄存器) |
LUI | 001111 | { r t ← i m m e d i a t e , 0 ′ b 16 } : \{rt ← immediate,\ 0'b16\}: {rt←immediate, 0′b16}: 将指令中的 16 b i t 16bit 16bit 立即数保存到 [ r t ] [rt] [rt] 的高16位, [ r t ] [rt] [rt] 的低 16 16 16 位用 0 0 0 填充 |
ANDI | 001100 | r t ← r s A N D { 1 6 ′ b 0 , i m m e d i a t e } : rt ← rs\ AND\ \{16'b0,\ immediate\}: rt←rs AND {16′b0, immediate}: { r s } \{rs\} {rs} 与指令中立即数零扩展为 32 − b i t 32-bit 32−bit 后的值进行逻辑“与”运算,运算结果保存到 [ r t ] [rt] [rt] |
ORI | 001101 | r t ← r s O R { 1 6 ′ b 0 , i m m e d i a t e } : rt ← rs\ OR\ \{16'b0,\ immediate\}: rt←rs OR {16′b0, immediate}: { r s } \{rs\} {rs} 与指令中立即数零扩展为 32 − b i t 32-bit 32−bit 后的值进行逻辑“或”运算,运算结果保存到 [ r t ] [rt] [rt] |
XORI | 001110 | r t ← r s X O R { 1 6 ′ b 0 , i m m e d i a t e } : rt ← rs\ XOR\ \{16'b0,\ immediate\}: rt←rs XOR {16′b0, immediate}: { r s } \{rs\} {rs} 与指令中立即数零扩展为 32 − b i t 32-bit 32−bit 后的值进行逻辑“异或”运算,运算结果保存到 [ r t ] [rt] [rt] |
SLTI | 001010 | r t ← ( r s < { 1 6 ′ b 0 , i m m e d i a t e ) } : rt ← (rs < \{16'b0,\ immediate)\}: rt←(rs<{16′b0, immediate)}: 将指令中立即数零扩展为 32 − b i t 32-bit 32−bit 后的值与 { r s } \{rs\} {rs} 进行有符号数比较,如果前者大于后者,那么将 1 1 1 保存到 [ r t ] [rt] [rt];否则将 0 0 0 保存到 [ r t ] [rt] [rt] |
SLTIU | 001011 | r t ← ( r s < { 1 6 ′ b 0 , i m m e d i a t e ) } : rt ← (rs < \{16'b0,\ immediate)\}: rt←(rs<{16′b0, immediate)}: 将指令中立即数零扩展为 32 − b i t 32-bit 32−bit 后的值与 { r s } \{rs\} {rs} 进行无符号数比较,如果前者大于后者,那么将 1 1 1 保存到 [ r t ] [rt] [rt];否则将 0 0 0 保存到 [ r t ] [rt] [rt] |
LB | 100000 | r t ← m e m o r y [ b a s e + o f f s e t ] : rt ← memory[base+offset]: rt←memory[base+offset]: 从内存中指定的加载地址处读取 8 − b i t 8-bit 8−bit,然后符号扩展至 32 32 32 位,保存到 [ r t ] [rt] [rt] |
LH | 100001 | r t ← m e m o r y [ b a s e + o f f s e t ] : rt ← memory[base+offset]: rt←memory[base+offset]: 从内存中指定的加载地址处读取 16 − b i t 16-bit 16−bit,然后符号扩展至 32 32 32 位,保存到 [ r t ] [rt] [rt] (有地址对齐要求,要求加载地址的最低位为 0 0 0) |
LW | 100011 | r t ← m e m o r y [ b a s e + o f f s e t ] : rt ← memory[base+offset]: rt←memory[base+offset]: 从内存中指定的加载地址处读取 32 − b i t 32-bit 32−bit,保存到 [ r t ] [rt] [rt] |
LBU | 100100 | r t ← m e m o r y [ b a s e + o f f s e t ] : rt ← memory[base+offset]: rt←memory[base+offset]: 从内存中指定的加载地址处读取 8 − b i t 8-bit 8−bit,然后零扩展至 32 32 32 位,保存到 [ r t ] [rt] [rt] |
LHU | 100101 | r t ← m e m o r y [ b a s e + o f f s e t ] : rt ← memory[base+offset]: rt←memory[base+offset]: 从内存中指定的加载地址处读取 16 − b i t 16-bit 16−bit,然后零扩展至 32 32 32 位,保存到 [ r t ] [rt] [rt] (有地址对齐要求,要求加载地址的最低位为 0 0 0) |
LWL | 100010 | r t ← r t M E R G E m e m o r y [ b a s e + o f f s e t ] : rt ← rt\ MERGE\ memory[base+offset]: rt←rt MERGE memory[base+offset]: 从内存中指定的加载地址处,找出该地址所在的存储字(将加载地址的后两位设为 0 0 0),从加载地址开始读取直到该存储字结束,将读取到的值保存到 { r t } \{rt\} {rt} 的首端, { r t } \{rt\} {rt} 的其余 b i t bit bit 保持不变(例如读取到 11111 11111 11111, { r t } \{rt\} {rt} 原来是 22222222 22222222 22222222,则操作后 { r t } \{rt\} {rt} 为 11111222 11111222 11111222) |
LWR | 100110 | r t ← r t M E R G E m e m o r y [ b a s e + o f f s e t ] : rt ← rt\ MERGE\ memory[base+offset]: rt←rt MERGE memory[base+offset]: 从内存中指定的加载地址处,找出该地址所在的存储字(将加载地址的后两位设为 0 0 0),从加载地址开始读取直到该存储字结束,将读取到的值零保存到 { r t } \{rt\} {rt} 的尾端, { r t } \{rt\} {rt} 的其余 b i t bit bit 保持不变(例如读取到 11111 11111 11111, { r t } \{rt\} {rt} 原来是 22222222 22222222 22222222,则操作后 { r t } \{rt\} {rt} 为 22211111 22211111 22211111) |
SB | 101000 | m e m o r y [ b a s e + o f f s e t ] ← r t : memory[base+offset] ← rt: memory[base+offset]←rt: 将 { r t } \{rt\} {rt} 的最低字节存储到内存中的指定地址 |
SH | 101001 | m e m o r y [ b a s e + o f f s e t ] ← r t : memory[base+offset] ← rt: memory[base+offset]←rt: 将 { r t } \{rt\} {rt} 的最低两个字节存储到内存中的指定地址(有地址对齐要求,要求计算出来的存储地址的最低位为 0 0 0) |
SW | 101011 | m e m o r y [ b a s e + o f f s e t ] ← r t : memory[base+offset] ← rt: memory[base+offset]←rt: 将 { r t } \{rt\} {rt} 存储到内存中的指定地址(有地址对齐要求,要求计算出来的存储地址的最低位为 0 0 0) |
SWL | 101010 | 类比指令LWL,非对齐存储指令,向左存储,将 { r t } \{rt\} {rt} 存储到内存中的指定地址 |
SWR | 101110 | 类比指令LWR,非对齐存储指令,向右存储,将 { r t } \{rt\} {rt} 存储到内存中的指定地址 |
指令类型 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
寄存器堆读 | ALU | 内存访问 | 跳转 | 寄存器堆写 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
raddr1 | raddr2 | A | B | ALUop | Address | MemRead | MemWrite | Write_Data | Write_strb | 跳转地址 | 地址更新条件 | wen | waddr | wdata | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
R-Type运算指令 | rs | rt | rdata1 | rdata2 | ALUop译码表 | - | - | - | - | - | - | 0 | 1 | rd | ALU_result | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
R-Type移位指令 | rs | rt | rdata2(shifter) | sa(shifter) | Shiftop译码表 | - | - | - | - | - | - | 0 | 1 | rd | shifter_result | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
R-Type跳转指令 | rs | 5'b0 | - | - | - | - | - | - | - | - | rdata1 | 1 | func[0] | rd/31 | PC+8 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
R-Type mov指令 | rs | rt | - | - | - | - | - | - | - | - | - | 0 | 1 | rd | rdata1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
REGIMM | rs | - | rdata1 | 32'b0 | SLT | - | - | - | - | - | {offset[15] (14个), (offset<< 2), '00'}+(pc+4) | REG[0] nor Zero | 0 | - | - | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
J-Type | - | - | - | - | - | - | - | - | - | - | {(pc+4)[31,28], target, ‘00’} | 1 | opcode[0] | 31 | PC+8 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
I-Type分支指令 | rs | rt/5'b0 | rdata1 | rdata2/32'b0 | ALUop译码表 | - | - | - | - | - | {offset[15] (14个), (offset<< 2), '00'}+(pc+4) | opcode[1] xor opcode[0] xor Zero | 0 | - | - | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
I-Type运算指令 | rs | - | rdata1 | immdiate | ALUop译码表 | - | - | - | - | - | - | 0 | 1 | rt | ALU_result | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
I-Type访存指令 | base | rt | rdata1 | immdiate | ALUop译码表 | ALU_result | opcode[5] & (~opcode[3]) | opcode[5] & opcode[3] | rdata2 | Write_strb译码表 | - | 0 | 0 | - | - |
指令 | opcode | func | ALUop | 具体实现 |
addu | 000000 | 100001 | 010 | ADD/SUB: func[3:2] == 2'b00; ALUop = {func[1], 2'b10 |
subu | 000000 | 100011 | 110 | |
and | 000000 | 100100 | 000 | 逻辑运算: func[3:2] == 2'b01; ALUop = {func[1], 1'b0, func[0]} |
or | 000000 | 100101 | 001 | |
xor | 000000 | 100110 | 100 | |
nor | 000000 | 100111 | 101 | |
slt | 000000 | 101010 | 111 | 比较运算: func[3:2] == 2'b10; ALUop = {~func[0], 2'b11} |
addi | 001000 | - | 010 | ADD: opcode[2:1] == 2'b00; ALUop = {opcode[1], 2'b10} |
addiu | 001001 | |||
andi | 001100 | 000 | 逻辑运算: opcode[2] == 1'b1 && opcode[1:0] != 2'b11; ALUop = {opcode[1], 1'b0, opcode[0]} | |
ori | 001101 | 001 | ||
xori | 001110 | 100 | ||
lui | 001111 | - | 非运算类指令,需要单独处理 | |
slti | 001010 | 111 | 比较运算: opcode[2:1] == 2'b01; ALUop = {~opcode[0], 2'b11} | |
sltiu | 001011 | 011 |
指令 | opcode | func | Shiftop | 具体实现 |
sll | 000000 | 000000 | 00 | func[5:3] == 3’b000; Shiftop = func[1:0] |
sra | 000000 | 000011 | 11 | |
srl | 000000 | 000010 | 10 | |
sllv | 000000 | 000100 | 00 | |
srav | 000000 | 000111 | 11 | |
srlv | 000000 | 000110 | 10 |
指令 | opcode | Write_strb | 具体实现 |
sb | 101000 | 0001 / 0010 / 0100 / 1000 | 4'b1000 >> (~ALU_result[1:0]) |
sh | 101001 | 0011 / 1100 | {{2{ALU_result[1]}}, {2{~ALU_result[1]}}} |
sw | 101011 | 1111 | 4'b1111 |
swl | 101010 | 0111 / 0011 / 0001 | {ALU_result[1]&ALU_result[0], ALU_result[1], ALU_result[1]|ALU_result[0], 1'b1} |
swr | 101110 | 1110 / 1100 / 1000 | {1'b1, (~ALU_result[1]) | (~ALU_result[0]), (~ALU_result[1]), (~ALU_result[1]) & (~ALU_result[0])} |
以下是ALU代码:
`timescale 10 ns / 1 ns
`define DATA_WIDTH 32
`define ope_AND 3'b000 //逻辑按位与
`define ope_OR 3'b001 //逻辑按位或
`define ope_XOR 3'b100 //逻辑按位异或
`define ope_NOR 3'b101 //逻辑按位或非
`define ope_ADD 3'b010 //算数加法
`define ope_SUB 3'b110 //算数减法
`define ope_SLT 3'b111 //有符号数整数比较
`define ope_SLTU 3'b011 //无符号数整数比较
module alu(
input [`DATA_WIDTH - 1:0] A,
input [`DATA_WIDTH - 1:0] B,
input [ 2:0] ALUop,
output Overflow,
output CarryOut,
output Zero,
output [`DATA_WIDTH - 1:0] Result
);
// TODO: Please add your logic design here
wire [`DATA_WIDTH - 1:0] ADDSUB_A_B;
wire CarryOut_origin;
assign Overflow = (ALUop === `ope_ADD) ? ((A[31]) && (B[31])) && (~ADDSUB_A_B[31]) || ((~A[31]) && (~B[31]) && (ADDSUB_A_B[31]))
: ((ALUop === `ope_SUB || ALUop === `ope_SLT) && (B === 32'h80000000)) ? ~A[31]
: ((ALUop === `ope_SUB || ALUop === `ope_SLT) && (B != 32'h80000000)) ? ((A[31]) && (~B[31])) && (~ADDSUB_A_B[31])
|| ((~A[31]) && (B[31]) && (ADDSUB_A_B[31]))
: 1'b0;//进行与、或操作时 Overflow 无定义
assign {CarryOut_origin, ADDSUB_A_B} = {1'b0, A} + {1'b0, (ALUop === `ope_SUB || ALUop === `ope_SLT) ? ~B : B} + {31'b0, ALUop[2]};
assign CarryOut = (ALUop === `ope_ADD) ? CarryOut_origin //加法运算的溢出位即为进位
: (ALUop === `ope_SUB && B != 32'b0) ? ~CarryOut_origin //减法运算如果 B 不是全 0 且没有借位,都会溢出;因此溢出位为 1 代表没有借位
: 0; //既不是加法又不是减法则赋一个无意义的值;或者进行减法且 B 为全 0 则赋为 0
assign Zero = (Result === 32'b0) ? 1'b1 : 1'b0;
assign Result = (ALUop === `ope_AND) ? (A & B)
: (ALUop === `ope_OR) ? (A | B)
: (ALUop === `ope_XOR) ? (A ^ B)
: (ALUop === `ope_NOR) ? (~(A | B))
: (ALUop === `ope_ADD || ALUop === `ope_SUB) ? ADDSUB_A_B
: (ALUop === `ope_SLT) ? ADDSUB_A_B[31] ^ Overflow
: (ALUop === `ope_SLTU) ? ((A < B) ? 1 : 0)
: 32'b0;
endmodule
以下是寄存器堆代码:
`timescale 10 ns / 1 ns
`define DATA_WIDTH 32
`define ADDR_WIDTH 5
module reg_file(
input clk,
input [`ADDR_WIDTH - 1:0] waddr,
input [`ADDR_WIDTH - 1:0] raddr1,
input [`ADDR_WIDTH - 1:0] raddr2,
input wen,
input [`DATA_WIDTH - 1:0] wdata,
output [`DATA_WIDTH - 1:0] rdata1,
output [`DATA_WIDTH - 1:0] rdata2
);
// TODO: Please add your logic design here
reg [`DATA_WIDTH - 1:0] rf[`DATA_WIDTH - 1:0];
always@(posedge clk)begin
if (waddr != 5'd0 && wen)
rf[waddr] <= wdata;
end
assign rdata1 = (raddr1 != 5'd0) ? rf[raddr1] : 32'd0;
assign rdata2 = (raddr2 != 5'd0) ? rf[raddr2] : 32'd0;
endmodule
以下是移位器代码:
`timescale 10 ns / 1 ns
`define DATA_WIDTH 32
module shifter (
input [`DATA_WIDTH - 1:0] A,
input [ 4:0] B,
input [ 1:0] Shiftop,
output [`DATA_WIDTH - 1:0] Result
);
// TODO: Please add your logic code here
wire [`DATA_WIDTH - 1:0] signed_shift;
assign signed_shift[31:0] = ({32{A[31]}} << (6'd32 - {1'b0, B[4:0]})) | (A >> B[4:0]);
assign Result = (Shiftop === 2'b00) ? A << B
: (Shiftop === 2'b11) ? signed_shift
: (Shiftop === 2'b10) ? A >> B
: 1'b0;
endmodule
以下是我完成的的最终代码,仅供参考:(代码已通过仿真测试)
`timescale 10ns / 1ns
`define DATA_WIDTH 32
`define ADDR_WIDTH 5
// OPCODE: 6-bit
`define SPECIAL 6'b000000
`define REGIMM 6'b000001
`define ADDIU 6'b001001
`define LUI 6'b001111
`define LB 6'b100000
`define LH 6'b100001
`define LBU 6'b100100
`define LHU 6'b100101
`define LWL 6'b100010
`define LWR 6'b100110
`define SB 6'b101000
`define SH 6'b101001
`define SW 6'b101011
`define SWL 6'b101010
`define SWR 6'b101110
// FUNC: 6-bit
`define JR 6'b001000
`define JALR 6'b001001
`define MOVZ 6'b001010
`define MOVN 6'b001011
module simple_cpu(
input clk,
input rst,
output reg [`DATA_WIDTH - 1:0] PC,
input [`DATA_WIDTH - 1:0] Instruction,
output [`DATA_WIDTH - 1:0] Address,
output MemWrite,
output [`DATA_WIDTH - 1:0] Write_data,
output [3:0] Write_strb,
input [`DATA_WIDTH - 1:0] Read_data,
output MemRead
);
// THESE THREE SIGNALS ARE USED IN OUR TESTBENCH
// PLEASE DO NOT MODIFY SIGNAL NAMES
// AND PLEASE USE THEM TO CONNECT PORTS
// OF YOUR INSTANTIATION OF THE REGISTER FILE MODULE
wire RF_wen;
wire [`ADDR_WIDTH - 1:0] RF_waddr;
wire [`DATA_WIDTH - 1:0] RF_wdata;
wire [`DATA_WIDTH - 1:0] RF_rdata1;
wire [`DATA_WIDTH - 1:0] RF_rdata2;
// TODO: PLEASE ADD YOUR CODE BELOW
wire [5:0] opcode;
wire [`ADDR_WIDTH - 1:0] rs;
wire [`ADDR_WIDTH - 1:0] rt;
wire [`ADDR_WIDTH - 1:0] rd;
wire [4:0] sa;
wire [5:0] func;
wire [`DATA_WIDTH - 1:0] zero_extend;
wire [`DATA_WIDTH - 1:0] signed_extend;
wire [`DATA_WIDTH - 1:0] shift_signed_extend;
wire [2:0] ALU_control;
wire [`DATA_WIDTH - 1:0] ALU_result;
wire [`DATA_WIDTH - 1:0] ALU_num1;
wire [`DATA_WIDTH - 1:0] ALU_num2;
wire Zero;
wire [4:0] Shift_num;
wire [1:0] Shift_op;
wire [`DATA_WIDTH - 1:0] Shift_result;
wire Jump;
wire [`DATA_WIDTH - 1:0] Jump_addr;
wire Branch;
wire [`DATA_WIDTH - 1:0] Branch_addr;
wire [`DATA_WIDTH - 1:0] load_data;
wire [7:0] byte_data;
wire [15:0] half_data;
wire [`DATA_WIDTH - 1:0] lwl_data;
wire [`DATA_WIDTH - 1:0] lwr_data;
wire [`DATA_WIDTH - 1:0] PC_4;
assign opcode = Instruction[31:26];
assign rs = Instruction[25:21];
assign rt = Instruction[20:16];
assign rd = Instruction[15:11];
assign sa = Instruction[10:6];
assign func = Instruction[5:0];
assign zero_extend = {16'b0, Instruction[15:0]};
assign signed_extend = Instruction[15] ? {{16{1'b1}}, Instruction[15:0]} : {{16{1'b0}}, Instruction[15:0]};
assign shift_signed_extend = Instruction[15] ? {{14{1'b1}}, Instruction[15:0], 2'b00} : {{14{1'b0}}, Instruction[15:0], 2'b00};
assign ALU_control = (opcode == `SPECIAL && func[3:2] == 2'b00) ? {func[1], 2'b10}//ADD/SUB: R-Type: 运算指令-ADDU/SUBU
: (opcode == `SPECIAL && func[3:2] == 2'b01) ? {func[1], 1'b0, func[0]}//AND/OR/XOR/NOR: R-Type: 运算指令-AND/OR/XOR/NOR
: (opcode == `SPECIAL && func[3:2] == 2'b10) ? {~func[0], 2'b11}//SLT/SLTU: R-Type: 运算指令-SLT
: (opcode == `REGIMM || opcode[5:1] == 5'b00011) ? 3'b111//SLT: REGIMM指令/I-Type: 分支指令-BLEZ/BGTZ
: (opcode[5:1] == 5'b00010) ? 3'b110//SUB: I-Type: 分支指令-BEQ/BNE
: (opcode[5:3] == 3'b001 && opcode[2:1] == 2'b00) ? {opcode[1], 2'b10}//ADD: I-Type: 计算指令-ADDI/ADDIU
: (opcode[5:3] == 3'b001 && opcode[2] == 1'b1 && opcode[1:0] != 2'b11) ? {opcode[1], 1'b0, opcode[0]}//AND/OR/XOR: I-Type: 计算指令-ANDI/ORI/XORI
: (opcode[5:3] == 3'b001 && opcode[2:1] == 2'b01) ? {~opcode[0], 2'b11}//SLT/SLTU: I-Type: 计算指令-SLTI/SLTIU
: (opcode[5]) ? 3'b010//ADD: I-Type: 访存指令
: 3'bXXX;//NOPE
assign ALU_num1 = (opcode[5:1] == 5'b00011) ? 0 : RF_rdata1;//I-Type: 分支指令-BLEZ/BGTZ : 其他指令
assign ALU_num2 = (opcode == `REGIMM) ? 32'b0//REGIMM指令
: (opcode[5:1] == 5'b00011) ? RF_rdata1//I-Type: 分支指令-BLEZ/BGTZ
: (opcode[5:3] == 3'b001 && opcode != `ADDIU) ? zero_extend//I-Type: 计算指令(除了ADDIU)
: (opcode[5] == 1 || opcode == `ADDIU) ? signed_extend//I-Type: 访存指令/计算指令-ADDIU
: RF_rdata2;//其他指令
assign Shift_num = (func[2] == 0) ? sa : RF_rdata1[4:0];
assign Shift_op = (opcode == `SPECIAL && func[5:3] == 3'b000) ? func[1:0] : 2'bXX;
assign Jump = ((opcode == `SPECIAL && {func[5:3], func[1]} == 4'b0010) || opcode[5:1] == 5'b00001) ? 1//R-Type: 跳转指令/J-Type指令
: 0;
assign Jump_addr = (opcode == `SPECIAL && {func[5:3], func[1]} == 4'b0010) ? {RF_rdata1}//R-Type: 跳转指令
: {PC_4[31:28], Instruction[25:0], 2'b00};//J-Type指令
assign Branch = ((opcode == `REGIMM && (rt[0]^ALU_result[0]))//REGIMM指令(由于其与分支指令所跳转的地址一致,姑且合并处理,实际上是跳转操作)
|| (opcode[5:2] == 4'b0001 && opcode[0]^Zero))//I-Type: 分支指令
? 1 : 0;
assign Branch_addr = shift_signed_extend + PC_4;
assign RF_wen = (opcode == `REGIMM || opcode[5:2] == 4'b0001 || (opcode[5] && opcode[3])) ? 0//REGIMM指令/I-Type: 分支指令/I-Type: 内存写指令
: (opcode == `SPECIAL && {func[5:3], func[1]} == 4'b0011) ? func[0]^(RF_rdata2 == 32'b0)//R-Type: mov指令
: (opcode == `SPECIAL && func == `JR) ? 0//R-Type: 跳转指令-JR
: (opcode[5:1] == 5'b00001) ? opcode[0]//J-Type指令
: 1;
assign RF_waddr = (opcode[5:3] == 3'b001 || opcode[5] & ((~opcode[3]))) ? rt//I-Type: 计算指令/I-Type: 内存读指令
: (opcode[5:1] == 5'b00001 || (opcode == `SPECIAL && func == `JALR && rd == 0)) ? 31//J-Type指令/R-Type: 跳转指令-JALR(rd未指定)
: rd;
assign RF_wdata = (opcode == `SPECIAL && ((func == `MOVZ && RF_rdata2 == 32'b0) || (func == `MOVN && RF_rdata2 != 32'b0))) ? RF_rdata1//R-Type: mov指令
: (opcode == `LUI) ? {Instruction[15:0], 16'b0}//I-Type: 计算指令-LUI
: ((opcode == `SPECIAL && func[5] == 1'b1) || (opcode[5:3] == 3'b001)) ? ALU_result//R-Type: 运算指令/I-Type: 计算指令
: (opcode == `SPECIAL && func[5:3] == 3'b000) ? Shift_result//R-Type: 移位指令
: ((opcode == `SPECIAL && {func[5:3], func[1]} == 4'b0010) || opcode[5:1] == 5'b00001) ? PC + 8//R-Type: 跳转指令/J-Type指令
: (opcode[5] && (~opcode[3])) ? load_data//I-Type: 内存读指令
: 32'bx;
assign MemRead = (opcode[5:3] == 3'b100) ? 1 : 0;//I-Type: 内存读指令
assign load_data = (opcode == `LB) ? (byte_data[7] ? {{24{1'b1}}, byte_data} : {{24{1'b0}}, byte_data})//LB
: (opcode == `LH) ? (half_data[15] ? {{16{1'b1}}, half_data} : {{16{1'b0}}, half_data})//LH
: (opcode == `LBU) ? {{24{1'b0}}, byte_data}//LBU
: (opcode == `LHU) ? {{16{1'b0}}, half_data}//LHU
: (opcode == `LWL) ? lwl_data//LWL
: (opcode == `LWR) ? lwr_data//LWR
: Read_data;//LW
assign byte_data = (ALU_result[1] & ALU_result[0]) ? Read_data[31:24]
: (ALU_result[1] & ~ALU_result[0]) ? Read_data[23:16]
: (~ALU_result[1] & ALU_result[0]) ? Read_data[15:8]
: Read_data[7:0];
assign half_data = (~ALU_result[1] & ~ALU_result[0]) ? Read_data[15:0] : Read_data[31:16];
assign lwl_data = (ALU_result[1] & ALU_result[0]) ? Read_data[31:0]
: (ALU_result[1] & ~ALU_result[0]) ? {Read_data[23:0], RF_rdata2[7:0]}
: (~ALU_result[1] & ALU_result[0]) ? {Read_data[15:0], RF_rdata2[15:0]}
: {Read_data[7:0], RF_rdata2[23:0]};
assign lwr_data = (ALU_result[1] & ALU_result[0]) ? {RF_rdata2[31:8], Read_data[31:24]}
: (ALU_result[1] & ~ALU_result[0]) ? {RF_rdata2[31:16],Read_data[31:16]}
: (~ALU_result[1] & ALU_result[0]) ? {RF_rdata2[31:24], Read_data[31:8]}
: Read_data[31:0];
assign Address = {ALU_result[31:2], 2'b00};
assign MemWrite = (opcode[5:3] == 3'b101) ? 1 : 0;//I-Type: 内存写指令
assign Write_data = (opcode == `SB) ? (Write_strb[3] ? {RF_rdata2[7:0], 24'b0}
: Write_strb[2] ? {8'b0, RF_rdata2[7:0], 16'b0}
: Write_strb[1] ? {16'b0, RF_rdata2[7:0], 8'b0}
: {24'b0, RF_rdata2[7:0]})//SB
: (opcode == `SH) ? ((Write_strb[3] && Write_strb[2]) ? {RF_rdata2[15:0], 16'b0}
: {16'b0, RF_rdata2[15:0]})//SH
: (opcode == `SWL) ? (Write_strb[3] ? RF_rdata2
: Write_strb[2] ? {8'b0, RF_rdata2[31:8]}
: Write_strb[1] ? {16'b0, RF_rdata2[31:16]}
: {24'b0, RF_rdata2[31:24]})
: (opcode == `SWR) ? (Write_strb[0] ? RF_rdata2
: Write_strb[1] ? {RF_rdata2[23:0], 8'b0}
: Write_strb[2] ? {RF_rdata2[15:0], 16'b0}
: {RF_rdata2[7:0], 24'b0})
: RF_rdata2;
assign Write_strb = (opcode[1:0] == 2'b00) ? (4'b1000 >> (~ALU_result[1:0]))//SB
: (opcode[1:0] == 2'b01) ? {{2{ALU_result[1]}}, {2{~ALU_result[1]}}}//SH
: (opcode[1:0] == 2'b11) ? 4'b1111//SW
: (opcode[2:0] == 3'b010) ? {ALU_result[1]&ALU_result[0], ALU_result[1], ALU_result[1]|ALU_result[0], 1'b1}//SWL
: {1'b1, (~ALU_result[1]) | (~ALU_result[0]), (~ALU_result[1]), (~ALU_result[1]) & (~ALU_result[0])};//SWR
reg_file reg_file_module(
.clk(clk),
.waddr(RF_waddr),
.raddr1(rs),
.raddr2(rt),
.wen(RF_wen),
.wdata(RF_wdata),
.rdata1(RF_rdata1),
.rdata2(RF_rdata2)
);
alu alu_module(
.A(ALU_num1),
.B(ALU_num2),
.ALUop(ALU_control),
.Result(ALU_result),
.Overflow(),
.CarryOut(),
.Zero(Zero)
);
shifter shifter_module(
.A(RF_rdata2),
.B(Shift_num),
.Shiftop(Shift_op),
.Result(Shift_result)
);
assign PC_4 = PC + 4;
always@(posedge clk) begin
if (rst) begin
PC <= 32'b0;
end
else begin
PC <= Jump ? Jump_addr : (Branch ? Branch_addr : PC_4);
end
end
endmodule
从寄存器堆读地址 r s rs rs、 r t rt rt 读出 R F _ r d a t a 1 RF\_rdata1 RF_rdata1、 R F _ r d a t a 2 RF\_rdata2 RF_rdata2,分别作为运算数 A L U _ n u m 1 ALU\_num1 ALU_num1 和 A L U _ n u m 2 ALU\_num2 ALU_num2 并通过 A L U _ c o n t r o l ALU\_control ALU_control 确定 A L U _ o p ALU\_op ALU_op (确定的方法见ALUop译码表),进行运算,之后把运算结果 R e s u l t Result Result 写入到 r d rd rd,寄存器堆写使能为 1 1 1,写地址为 r d rd rd, 写数据为 R e s u l t Result Result。
从寄存器堆读地址 r s rs rs、 r t rt rt 读出 R F _ r d a t a 1 RF\_rdata1 RF_rdata1、 R F _ r d a t a 2 RF\_rdata2 RF_rdata2,将 R F _ r d a t a 2 RF\_rdata2 RF_rdata2 作为被移位数,通过 S h i f t _ n u m Shift\_num Shift_num 选择 R F _ r d a t a 1 [ 4 : 0 ] RF\_rdata1[4:0] RF_rdata1[4:0] 或者 s a sa sa 作为移位位数,进行 S h i f t _ o p Shift\_op Shift_op 指示的运算(见 S h i f t o p Shiftop Shiftop 译码表),将结果赋给 S h i f t _ r e s u l t Shift\_result Shift_result。
从寄存器堆读地址 r s rs rs 读出 R F _ r d a t a 1 RF\_rdata1 RF_rdata1,并赋值给 P C PC PC。这样的赋值是通过控制信号 J u m p Jump Jump 实现的:如果识别到 R − T y p e R-Type R−Type 跳转指令,将 J u m p Jump Jump 赋为 1 1 1,并将 R F _ r d a t a 1 RF\_rdata1 RF_rdata1 赋给 J u m p _ a d d r Jump\_addr Jump_addr。对于指令 J A L R JALR JALR,还需要将 P C + 8 PC+8 PC+8 保存到 r d rd rd 指示的位置,此时需要使寄存器堆写使能为 1 1 1,写地址为 r d rd rd,写数据为 P C + 8 PC+8 PC+8,如果没有指明 r d rd rd,即 r d rd rd 为 0 0 0(这是因为我们不能向零号寄存器写入,故 r d rd rd 为 0 0 0 即为未指定),写地址为 31 31 31。
读取 R F _ r d a t a 2 RF\_rdata2 RF_rdata2,并进行判断,如果 o p c o d e [ 0 ] & & R F _ r d a t a 2 ! = 3 2 ′ b 0 opcode[0]\ \&\&\ RF\_rdata2\ != 32'b0 opcode[0] && RF_rdata2 !=32′b0 (MOVN) 或 ∼ o p c o d e [ 0 ] & & R F _ r d a t a 2 = = 3 2 ′ b 0 \sim opcode[0]\ \&\&\ RF\_rdata2\ == 32'b0 ∼opcode[0] && RF_rdata2 ==32′b0 (MOVZ),则将写使能置为 1 1 1,写地址为 r d rd rd,写数据为 R F r d a t a 1 RF_rdata1 RFrdata1。
为了将 R F _ r d a t a 1 RF\_rdata1 RF_rdata1 与 0 0 0 比较,使用 A L U ALU ALU 进行 S L T SLT SLT 运算,让 A L U _ c o n t r o l ALU\_control ALU_control 在读取到REGIMM 指令时给出 A L U _ o p ALU\_op ALU_op 为 S L T SLT SLT,使 A L U _ n u m 2 ALU\_num2 ALU_num2 为 0 0 0,进行运算,然后判断 R e s u l t Result Result 是否为 0 0 0,得出 B r a n c h Branch Branch 的值。 B r a n c h _ a d d r Branch\_addr Branch_addr 为指定值,由于它是两个信号的和,我将计算 B r a n c h _ a d d r Branch\_addr Branch_addr 的操作交给一个实例化的 A L U ALU ALU 模块,使用 A L U ALU ALU 的加法计算。
利用之前 R − T y p e R-Type R−Type 跳转指令已经实现的 J u m p Jump Jump 信号,如果读取到 J − T y p e J-Type J−Type 指令, J u m p Jump Jump 赋为 1 1 1,并使 J u m p _ a d d r Jump\_addr Jump_addr 为指定值。注意 JAL 指令还要将 P C + 8 PC+8 PC+8 存到 31 31 31 号寄存器,寄存器堆写使能为 1 1 1,写地址为 31 31 31,写数据为 P C + 8 PC+8 PC+8。
BEQ 与 BNE 指令需要比较 R F _ r d a t a 1 RF\_rdata1 RF_rdata1 与 R F _ r d a t a 2 RF\_rdata2 RF_rdata2,这可以通过 A L U ALU ALU 进行减法,判断 A L U ALU ALU 输出的 Z e r o Zero Zero 的值来实现,转移到指定地址,这个是简单的。需要注意的是,BLEZ 与 BGTZ 指令判断 r s ≤ 0 rs\leq0 rs≤0 与 r d > 0 rd > 0 rd>0,这与 A L U ALU ALU 的 S L T SLT SLT 判断的 < < < 与 ≥ \geq ≥ 不同,实现时需要将 R F _ r d a t a 1 RF\_rdata1 RF_rdata1 放在 A L U _ n u m 2 ALU\_num2 ALU_num2,将 A L U _ n u m 1 ALU\_num1 ALU_num1 置为 0 0 0,之后只需要判断 Z e r o Zero Zero 即可,这个判断与上面的可以合并为 o p c o d e [ 0 ] ˆ Z e r o opcode[0]\ \^{}\ Zero opcode[0] ˆ Zero。
除了 LUI 指令以外的指令都可以通过 A L U ALU ALU 实现,只需要计算一下 A L U _ n u m 2 ALU\_num2 ALU_num2 即可。LUI 指令要将寄存器堆写使能置为 1 1 1,并将 { i m m , 1 6 ′ b 0 } \{imm,\ 16'b0\} {imm, 16′b0} 写入 r t rt rt 即可。注意 ADDIU 为符号扩展,其它为零扩展。
由于这一部分确实不容易归纳,只能一个一个实现,认真阅读 M I P S MIPS MIPS 指令手册,理解每条指令要干什么,不是太难。
为了更好地规划硬件线路,必须要预先了解每一个指令要做的事,做好译码工作。代码是建立在已经构筑好的逻辑框图上的,通过将每一类代码的实现框图进行合并得出总的逻辑框图,之后再根据逻辑框图进行信号定义与线连接,将会高效很多。