基于MIPS指令集单周期CPU设计与实现——硬布线式
- 指令分析
指令分析是第一步:MIPS指令集一共有三种指令。
R型(寄存器型):由Op(6位,下同)操作码,Rs(5),Rt(5),Rd(5),shamt(5)移位位,func(6)功能码组成
I型(立即数型):由Op(6),Rs(5),Rt(5),imm16(16)16位立即数组成
J型(跳转型):由Op(6),imm26(26)26位立即数组成
下面是我选的一部分指令的分析
助记符 |
指令格式 |
示例 |
示例含义 |
操作及解释 |
||||||||||||||||
R-型 |
op |
rs |
rt |
rd |
shamt |
func |
|
操作 |
解释 |
|||||||||||
Addu |
000000 |
rs |
rt |
rd |
0 |
100001 |
addu $1,$2,$3 |
$1=$2+$3 |
(rd)<-(rs)+(rt);rs=$2,rt=$3,rd=$1 |
|||||||||||
Subu |
000000 |
rs |
rt |
rd |
0 |
100011 |
subu $1,$2,$3 |
$1=$2-$3 |
(rd)<-(rs)-(rt);rs=$2,rt=$3,rd=$1 |
|||||||||||
or |
000000 |
rs |
rt |
rd |
0 |
100101 |
or $1,$2,$4 |
$1=$2|$3 |
(rd)<-(rs)|(rt);rs=$2,rt=$3,rd=$1 |
|||||||||||
sltu |
000000 |
rs |
rt |
rd |
0 |
101011 |
sltu $1,$2,$3 |
If($2<$3) $1=1else$1=0 |
if(rs<rt)rd=1else rd=0;rs=$2,rt=$3,rd=$1 |
|||||||||||
syscall |
000000 |
rs |
rt |
rd |
0 |
|
|
|
|
|||||||||||
I-型 |
Op |
rs |
rt |
immediate |
示例 |
示例含义 |
操作及解释 |
|||||||||||||
Addiu |
001001 |
Rs |
Rt |
immediate |
Addiu $1,$2,10 |
$1=$2+10 |
(rt)<-(rs)+(sign-extend)immediate,rt=$1,rs=$2 |
|||||||||||||
Andi |
001100 |
Rs |
Rt |
immediate |
Andi $1,$2,10 |
$1=$2&10 |
(rt)<-(rs)&(zero-extend)immediate,rt=$1,rs=$2 |
|||||||||||||
Lw |
100011 |
Rs |
Rt |
immediate |
Lw $1,10($2) |
$1=Memory[$2+10] |
(rt)<-Memory(rs)+(sign_extend)offset),rt=$1,rs=$2 |
|||||||||||||
Sw |
101011 |
Rs |
Rt |
immediate |
Sw $1,10($2) |
MeMORY[$2+10]=$1 |
Memory[(rs)+(sign_extend)offset]<-(rt),rt=$1,rs=$2 |
|||||||||||||
Beq |
000100 |
Rs |
Re |
immediate |
Beq $1,$2,40 |
If($1=$2) goto PC+4+40 |
If((rt)=(rs))then (PC)<-(PC)+((sign_extend)offset<<2),rs=$1,rt=$2 |
|||||||||||||
J-型 |
op |
address |
示例 |
示例含义 |
操作及解释 |
|||||||||||||||
Jal |
000011 |
address |
Jal 10000 |
$31=PC+4 goto 10000 |
($31)<-(PC)+4;(PC)<-((zero_extend)address<<2),address=10000/4 |
2.基本数据通路设计
- 注意:此处NPC和PC合二为一成为PC
运行流程:pc开始->指令存储器->取指令->控制器->分析指令->得到操作的控制信号->发送到各个器件
取完指令之后->IR送寄存器组->选择通路->送到ALU进行计算->进行后续操作。
1.时序模块设计与仿真
(1)原理图
(2)功能
- 输入引脚
- H_RUN输入引脚,L_STEP输入引脚,时序控制信号。
H_RUN |
L_STEP |
功能 |
X |
0 |
时序电路不输出节拍信号 |
0 |
低变高 |
时序电路仅输出一组节拍信号,供单步调试用。 |
1 |
1 |
时序电路输出连续节拍信号 |
- CLK输入引脚,主时钟信号。
- RST1输入引脚,主复位控制信号,低电平复位。
- 输出引脚
共4个节拍信号T1、T2、T3、T4。
(3)仿真图
- 寄存器堆模块
(1)原理图
(2)功能
- 输入引脚
clkread是寄存器堆的读时钟,clkwr是寄存器堆的写时钟,Reset是寄存器堆的复位信号,Regwr是寄存器堆的读写控制信号,高电平写,低电平读,RA[4..0]寄存器堆端口的输入端,RB[4..0]寄存器堆端口的输入端,RW[4..0]是寄存器堆的输出端,BusW[31..0]是寄存器堆的输入端,readnum[4..0]是寄存器堆的输入端,j是jal型指令的控制信号。
- 输出引脚
BusA[31..0]和BusB[31..0]是寄存器堆的通道输出端,readdata[31..0]是读取寄存器内容的输出端。
-
仿真
- 数据存储器ROM和程序存储器RAM模块
(1)原理图
ROM
RAM
(2)功能
- 输入引脚
address是RAM存储器的地址输入端口,clock是RAM存取器的读写时钟控制信号。
data[31..0]是RAM存储器的数据输入端口,wren是读写控制信号,高电平写,低电平读,address[7..0]是RAM存储器的地址输入端口,clock是RAM存储器的读写时钟控制信号。
- 输出引脚
q[31..0]是RAM和ROM存储器的数据输出端口。
(3)仿真图
ROM
RAM
3.ALU运算部件
(1)原理图
(2)功能
- 输入引脚
A[31..0]和B[31..0]是运算器的输入端,ALUop[2..0]是运算器的控制信号
- 输出引脚
ALUout[31..0]是运算器输出结果,zero是做减法时,结果为零时发出的信号
(3)仿真
- PC程序计数器
(1)原理图
(2)功能
- 输入引脚
reset是重置运算器的控制信号,enable是运算器的使能信号,clk是时钟脉冲信号,PCop[1..0]是控制控制PC进行不同操作的信号,imm26[25..0]是传输的立即数,zero是ALU做减法时零标志位,beq是进行beq操作时发出的信号
- 输出引脚
PCout[31..0]输出下条指令的地址,PC4用于Jal操作时,下条指令地址的输出。
(3)仿真
- MUX多路选择器
(1)原理图
(2)功能
- 输入引脚
A0[31..0]、A1[31..0]、A2[31..0]、A3[31..0]是四个数据输入端,S是控制选择的控制信号。
- 输出引脚
Y[31..0]是数据输出端。
(3)仿真
- EXT拓展电路
(1)原理图
(2)功能
- 输入引脚
imm16[15..0]是16立即数数据输入输入端,Extop是控制ext拓展器进行符号拓展或者零拓展的控制信号。
- 输出引脚
Extout[31..0]是数据输出端,是拓展后输出的数值。
(3)仿真
- 控制器的设计
(1)原理图
- 功能
- 输入引脚
op[5..0]和func[5..0]是数据输入端,决定控制信号。
- 输出引脚
ALUop[2..0]、PCop[1..0]、MUX_alub、MUX_busw[1..0]、MUX_rw[1..0]、extop、Regwr、DMwr、Beq、halt、j是数据输出端,分别控制ALU的操作,控制PC做不同的指令跳转,控制进入alu的线路,busw的输出端,控制不同信号的读写功能,拓展
仿真
- 完整数据通路设计
- 各模块的连接分析
指令 | PC | Im | RF | EXT | ALU | DM | ||||||
输入引脚 | PCin | Imm26 | Imaddr | RA | RB | RW | BusW | Imm16 | BusA | BusB | Dmaddr | Dmin |
subu | PC.pcout | PC.pcout | IR[25:21] | IR[20:16] | IR[15:11] | ALU.aluout | RF.BusA | RF.BusB | ||||
addiu | PC.pcout | PC.pcout | IR[25:21] | IR[20:16] | ALU.aluout | IR[15:0] | RF.BusA | EXT.extout | ||||
beq | PC.pcout | IR[25:0] | PC.pcout | IR[25:21] | IR[20:16] | RF.BusA | RF.BusB | |||||
lw | PC.pcout | PC.pcout | IR[25:21] | IR[20:16] | DM.dmout | IR[15:0] | RF.BusA | EXT.extout | ALU.aluout | |||
sw | PC.pcout | PC.pcout | IR[25:21] | IR[20:16] | IR[15:0] | RF.BusA | EXT.extout | ALU.aluout | RF.BusB | |||
jal | PC.pcout | IR[25:0] | PC.pcout | 0x1F | PC.pc4 | |||||||
合并 | PC.pcout | IR[25:0] | PC.pcout | IR[25:21] | IR[20:16] | 0:IR[15:11] 1:IR[20:16] 2:OX1F | 0:ALU.aluout 1:DM.dmout 2:PC.pc4 | IR[15:0] | RF.BusA | 0:RF.BusB 1:EXT.extout | ALU.aluout | RF.BusB |
- 控制信号分析
Regwr | DMwr | Beq | halt | EXtop | MUX_alub | ALU_op | J | PCop | MUX_busw | MUX_RW | |
addu | 1 | 0 | 0 | 0 | 0 | 0 | 000 | 0 | 10 | 00 | 00 |
subu | 1 | 0 | 0 | 0 | 0 | 0 | 001 | 0 | 10 | 00 | 00 |
sltu | 1 | 0 | 0 | 0 | 0 | 0 | 100 | 0 | 10 | 00 | 00 |
or | 1 | 0 | 0 | 0 | 0 | 0 | 010 | 0 | 10 | 00 | 00 |
syscall | 0 | 0 | 0 | 1 | 0 | 0 | 000 | 0 | 10 | 00 | 00 |
addiu | 1 | 0 | 0 | 0 | 1 | 1 | 000 | 0 | 10 | 00 | 01 |
lw | 1 | 0 | 0 | 0 | 1 | 1 | 000 | 0 | 10 | 01 | 01 |
sw | 0 | 1 | 0 | 0 | 1 | 1 | 000 | 0 | 10 | 01 | 01 |
beq | 0 | 0 | 1 | 0 | 1 | 1 | 001 | 0 | 00 | 01 | 01 |
andi | 1 | 0 | 0 | 0 | 1 | 1 | 011 | 0 | 10 | 00 | 01 |
jal | 1 | 0 | 0 | 0 | 0 | 0 | 000 | 1 | 01 | 10 | 10 |
addu+subu+sltu+or+addiu+lw+andi+jal | sw | beq | halt | addiu+lw+sw+beq+andi | addiu+lw+sw+bqe+andi | 000:addu+syscall+addiu+lw+sw+andi+jal,001:subu+beq,010:or,011:andi,100:sltu | jal | 10:addu+subu+sltu+or+syscall+addiu+lw+sw+andi,00:beq,01:jal | 00:addu+subu+sltu+or+syscal+addiu,01:lw+sw+beq,10:jal | 00:addu+subu+sltu+or+syscall+sw,01addiu+sw+lw+andi+beq,10:jal |
1、测试程序
为了验证CPU的正确性,编写了表6.1测试程序,其中的寄存器编码、立即数、地址等指令格式中的信息必须具体化。
表6.1 测试程序
序号 |
汇编语言 |
操作码 |
地址码 |
16进制机器编码 |
功能 |
|
Op |
function |
|||||
1 |
addu $s0,$zero,$zero |
000000 |
100001 |
0 |
00008021 |
$s0=$zero+$zero |
2 |
addiu $s1,$zero,5 |
001001 |
|
4 |
24110005 |
$s1=$zero+5 |
3 |
subu $s2,$s1,$s0 |
000000 |
100001 |
8 |
02309023 |
$s2=$s1-$s0 |
4 |
or $s3,$s1,$s2 |
000000 |
100101 |
12 |
02329825 |
$s3=$s1|$s2 |
5 |
lw $s4,4($s0) |
100011 |
|
16 |
8e140004 |
$s4=memory[$s0+4] |
6 |
sltu $s5,$s1,$s2 |
000000 |
101011 |
18 |
0232a82b |
If($s1<$s2)$s5=1else$s5=0 |
7 |
sw $s6,8($s0) |
101011 |
|
24 |
ae160008 |
Memory[($s0+8)/4]=$s6 |
8 |
andi $s7,$s0,5 |
001100 |
|
28 |
32170005 |
$s7=$s0&5 |
9 |
beq $s1,$s2,4 |
000100 |
|
32 |
12320004 |
If($s1=$s2)pc=pc+4+16else pc=pc+4 |
10 |
Jal 9 |
000011 |
|
40 |
0c000009 |
Pc=36 |
11 |
syscall |
000000 |
001100 |
36 |
0000000c |
Halt |
2、仿真图
测试程序的仿真结果如图6.1所示。
图6.1 程序运行仿真图
从仿真图可见,程序运行完全正确,执行停机指令HALT时,指令不再跳转。
alut.v
module alut(A,B,ALUop,ALUout,zero);
input [31:0] A,B;
input [2:0] ALUop;
output [31:0] ALUout;
output zero;
reg [31:0] res;
assign zero=(res==0)?1:0;
assign ALUout=res;
always @*
begin
case(ALUop)
3'b000:res<=A+B;
3'b001:res<=A-B;
3'b010:res<=A|B;
3'b011:res<=A&B;
3'b100:res<=A<B?1:0;
endcase
end
Endmodule
controll.v
module Controll(op,func,ALUop,PCop,MUX_alub,MUX_busw,MUX_rw,extop,Regwr,DMwr,Beq,halt,j);
input [5:0] op;
input [5:0] func;
output Regwr,DMwr,Beq,halt,extop,MUX_alub,j;
output [1:0] PCop,MUX_busw,MUX_rw;
output [2:0] ALUop;
//R
wire addu = (op == 6'b000000 & func == 6'b100001)?1:0;
wire subu = (op == 6'b000000 & func == 6'b100011)?1:0;
wire Or = (op == 6'b000000 & func == 6'b100101)?1:0;
wire sltu = (op == 6'b000000 & func == 6'b101011)?1:0;
wire syscall= (op == 6'b000000 & func == 6'b001100)?1:0;
//I
wire addiu= (op == 6'b001001)?1:0;
wire andi = (op == 6'b001100)?1:0;
wire lw = (op == 6'b100011)?1:0;
wire sw = (op == 6'b101011)?1:0;
wire beq = (op == 6'b000100)?1:0;
//J
wire jal = (op == 6'b000011)?1:0;
assign Regwr=addu | subu | Or | sltu | addiu | lw | andi | jal;
assign DMwr=sw;
assign Beq=beq;
assign halt=syscall;
assign extop=addiu | lw | sw | andi;
assign MUX_alub=addiu | lw | sw | andi;
assign j=jal;
assign ALUop[2]=sltu;
assign ALUop[1]=Or | andi;
assign ALUop[0]=subu | andi | beq;
assign PCop[1]=addu | subu | Or | sltu | syscall | addiu | andi | lw | sw;
assign PCop[0]=jal;
assign MUX_busw[1]=beq | jal ;
assign MUX_busw[0]=lw | sw;
assign MUX_rw[1]=jal;
assign MUX_rw[0]=addiu | andi | lw | sw | beq ;
endmodule
EXT.v
module EXT(imm16,Extop,Extout);
input [15:0] imm16;
input Extop;
output reg [31:0] Extout;
always @(imm16 or Extop)
if(Extop)
if(~imm16[15])
begin
Extout<={{16{1'b0}},imm16[15:0]};
end
else
begin
Extout<={{16{imm16[15]}},imm16[15:0]};
end
endmodule
MUX2.v
module MUX2(A0,A1,S,Y);
input [31:0]A0,A1;
input S;
output [31:0]Y;
function [31:0]select;
input [31:0]A0,A1;
input S;
case(S)
1'b0:select=A0;
1'b1:select=A1;
endcase
endfunction
assign Y = select(A0,A1,S);
Endmodule
MUX4_r.v
module MUX4_r (A0, A1, A2, A3, S, Y);
input [4:0] A0, A1, A3;
input [1:0] A2;
input [1:0] S;
output [4:0] Y;
function [31:0] select;
input [4:0] A0, A1, A3;
input [1:0] A2;
input [1:0] S;
case(S)
2'b00: select = A0;
2'b01: select = A1;
2'b10: select = A2;
2'b11: select = A3;
endcase
endfunction
assign Y = select (A0, A1, A2, A3, S);
endmodule
MUX4.v
module MUX4 (A0, A1, A2, A3, S, Y);
input [31:0] A0, A1, A2, A3;
input [1:0] S;
output [31:0] Y;
function [31:0] select;
input [31:0] A0, A1, A2, A3;
input [1:0] S;
case(S)
2'b00: select = A0;
2'b01: select = A1;
2'b10: select = A2;
2'b11: select = A3;
endcase
endfunction
assign Y = select (A0, A1, A2, A3, S);
endmodule
pcall.v
module pcall(reset,enable,clk,PCop,imm26,zero,beq,PCout,PC4);
input enable,reset,clk,zero,beq;
input [1:0] PCop;
input [25:0] imm26;
output reg [31:0] PC4;
output reg[31:0] PCout;
initial
begin
PCout<=32'h00000000;
end
always @(posedge clk or posedge reset )
begin
if (reset)
begin
PCout<=32'h00000000;
end
else
if (enable)
if(PCop==2'b00&&beq&&zero)
begin
PCout<=PCout+32'h00000004+{{16{imm26[15]}},imm26[15:0],2'b00};
end
else if(PCop==2'b01)
begin
PC4<=PCout+32'h00000004;
PCout<={PCout[31:28],imm26,2'b00};
end
else if(PCop==2'b10)
begin
PCout<=PCout+32'h00000004;
end
else
begin
PCout<=PCout+32'h00000004;
end
end
endmodule
RegisterFile.v
module RegisterFile(clkread,clkwr,Reset,RegWr,RA,RB,RW,BusW,BusA,BusB,readnum,readdata,j);
input clkread,clkwr,Reset,RegWr,j;
input [4:0] RA,RB,RW,readnum;
input [31:0] BusW;
output reg [31:0] BusA,BusB;
output [31:0] readdata;
reg [31:0] regfile[31:0];
integer i;
initial
begin
regfile[0]=32'h00000000;
end
assign readdata=regfile[readnum];
always @(posedge clkwr or posedge Reset)
begin
if (Reset)
begin
for (i=1;i<=31;i=i+1)
regfile[i]<=0;
end
else
begin
if (RegWr)
if(j)
begin
regfile[31]<=BusW;
end
else
begin
regfile[RW]<=BusW;
end
end
end
always @(posedge clkread)
begin
BusA<=regfile[RA];
BusB<=regfile[RB];
end
endmodule