MIPS单周期微处理器的verilog代码实现

单周期CPU功能

利用 Verilog 语言,基于 Xilinx FPGA nexys4 实验平台,设计一个能够执行以下MIPS指令集的单周期类MIPS处理器,要求完成所有支持指令的功能仿真,验证指令执行的正确性
支持基本的算术逻辑运算如 add,sub,and,or,slt 指令
支持基本的内存操作如 lw,sw 指令
支持基本的程序控制如 beq,j 指令

思路

按照逻辑框图实现每个组件的功能,再实现控制器,最后将控制器与功能组件连接,输入reset和clk信号

源代码文件

寄存器 PC.v

1
2
3
4
5
6
7
8
9
10
11
12
13
module PC(
input [31:0] D,
input Clk,
input Reset,
output reg [31:0] Q
);
always @(posedge Clk or posedge Reset) begin
if(Reset)
Q =32'h0000000;
else if(Clk && ~Reset)
Q = D;
end
endmodule

指令存储器 InstrROM.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
module InstrROM(
input [4:0] Addr,
input Clk,
output reg [31:0] Instr
);
//32个32位寄存器
reg [31:0] regs[0:31];

//读取addr处的指令instr
always @(posedge Clk) begin
if(Addr>=0)
Instr = regs[Addr];
end
//原异步读取指令代码
// assign instr = regs[addr];

////单独仿真时初始化存储模块
// initial

// begin:loop
// integer i;
// for(i=0;i<32;i=i+1)
// regs[i] = 4*i;
// end

//读取从mars导出的汇编机器码 总体仿真时再添加
initial
begin
regs[0] = 32'h00432020;
regs[1] = 32'h8c440004;
regs[2] = 32'hac450008;
regs[3] = 32'h00831022;
regs[4] = 32'h00831025;
regs[5] = 32'h00831024;
regs[6] = 32'h0083102a;
regs[7] = 32'h10630001;
regs[8] = 32'h8c620000;
regs[9] = 32'h10640001;
regs[10] = 32'hac620000;
regs[11] = 32'h08000000;

end
endmodule

控制器 Controller.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module Controller(
input [5:0] OpCode,
input [5:0] Funct,
output J,
output B,
output RegDst,
output RegWr,
output ALUSrc,
output MemWr,
output Mem2Reg,
output [3:0] ALUCtr
);

wire [1:0] ALUOp;

//引用MainCtr和ALUCtr实现译码
MainCtr U0(OpCode, J, B, RegDst, RegWr, ALUSrc, MemWr, Mem2Reg, ALUOp);
ALUCtr U1(ALUOp,Funct,ALUCtr);
endmodule

主控制器 Mainctr.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
module MainCtr (
input [5:0] OpCode,// 对应Instr的26~31位
output J,
output B,
output RegDst,
output RegWr,
output ALUSrc,
output MemWr,
output Mem2Reg,
output [1:0] ALUOp
);
reg [8:0] outputtemp;

// 信息解码
assign RegDst = outputtemp[8];
assign ALUSrc = outputtemp[7];
assign Mem2Reg = outputtemp[6];
assign RegWr = outputtemp[5];
assign MemWr = outputtemp[4];
assign B = outputtemp[3];
assign J = outputtemp[2];
assign ALUOp = outputtemp[1:0];

// 根据传入的OpCode判断需要输出的各个信号,并进行编码
always @(OpCode) begin
case (OpCode)
6'b000010: outputtemp = 9'bxxx0001xx;// j
6'b000000: outputtemp = 9'b100100010;// R
6'b100011: outputtemp = 9'b011100000;// lw
6'b101011: outputtemp = 9'bx1x010000;// sw
6'b000100: outputtemp = 9'bx0x001001;// beq
6'b001100: outputtemp = 9'b010100011;// andi
default: outputtemp = 9'b000000000;// 默认全为0
endcase
end
endmodule

ALU控制器 ALUControl.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module ALUCtr (
input [1:0] ALUOp,
input [5:0] Funct,
output reg [3:0] ALUCtr
);
// 根据ALUOp和Funct的值拼接出ALU的控制信号
always @(ALUOp or Funct) begin
casex ({ALUOp, Funct})
8'b00xxxxxx: ALUCtr[3:0] = 4'b0010;// lw, sw
8'b01xxxxxx: ALUCtr[3:0] = 4'b0110;// beq
8'b10xx0000: ALUCtr[3:0] = 4'b0010;// add
8'b10xx0010: ALUCtr[3:0] = 4'b0110;// sub
// andi其实就是把一个寄存器的值用一个立即数代替,可以用同一套操作
8'b10xx0100: ALUCtr[3:0] = 4'b0000;// and
8'b11xxxxxx: ALUCtr[3:0] = 4'b0000;// andi
8'b10xx0101: ALUCtr[3:0] = 4'b0001;// or
8'b10xx1010: ALUCtr[3:0] = 4'b0111;// slt
default: ALUCtr[3:0] = 4'b0000;// initial
endcase
end
endmodule

多路复用器 Mux2_1.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//实例化两个不同位宽的模块 5bit和32bit
module Mux2_1_32bit(
input [31:0] In1,
input [31:0] In2,
input sel,
output reg [31:0] Out
);
always @(*) begin
if(sel == 0)
Out[31:0] = In1;
else if(sel == 1)
Out[31:0] = In2;
end
endmodule

module Mux2_1_5bit(
input [4:0] In1,
input [4:0] In2,
input sel,
output reg [4:0] Out
);
always @(*) begin
if(sel == 0)
Out[4:0] = In1[4:0];
else if(sel == 1)
Out[4:0] = In2[4:0];
end
endmodule

3选1多路复用器 Mux3_1.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module Mux3_1(
input [31:0] In1,
input [31:0] In2,
input [31:0] In3,
input [1:0] sel,
output [31:0] Out
);

wire [31:0] Out1;

//引用Mux2_1实现三路选择
Mux2_1_32bit U0(In1,In2,sel[0],Out1);
Mux2_1_32bit U1(Out1,In3,sel[1],Out);
endmodule

左移运算器 LeftShift.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//将32位数据左移2位输出32位 n=32 m=32 x=2
module LeftShift_32_32_2(
input [31:0] In,
output reg [31:0] Out
);
always @(*) begin
Out = {In[29:0],2'b00};
end
endmodule

//n=26 m=28 x=2
module LeftShift_26_28_2(
input [25:0] In,
output reg [27:0] Out
);
always @(*) begin
Out = {In[25:0],2'b00};
end
endmodule

//n=26 m=29 x=3
module LeftShift_26_29_3(
input [25:0] In,
output reg [28:0] Out
);
always @(*) begin
Out = {In[25:0],3'b000};
end
endmodule

加法器 Adder.v

1
2
3
4
5
6
7
8
module Adder(
input [31:0] In1,
input [31:0] In2,
output reg [31:0] Out
);
always @(*)
Out = In1 + In2;
endmodule

寄存器文件 RegFile.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
module RegFile(
input [4:0] RsAddr,
input [4:0] RtAddr,
input [4:0] WrAddr,
input [31:0] DataIn,
input RegWr,
input Clk,
output [31:0] RsData,
output [31:0] RtData
);

//32个寄存器,每个寄存器位宽32位
reg[31:0] regs[0:31];

//异步输出
//若输入Rs或Rt地址,则输出相应的值,若地址为0则输出为0
assign RsData = (RsAddr == 5'b0) ? 32'b0 : regs[RsAddr];
assign RtData = (RtAddr == 5'b0) ? 32'b0 : regs[RtAddr];

//同步输入
//当写入信号RegWr使能时,将数据写入WriteAddr的寄存器
always @ (posedge Clk) begin
if(RegWr == 1) begin
regs[WrAddr] = DataIn;
end
end

//初始化其中的数据
initial
begin:loop
integer i;
for(i=0;i<32;i=i+1)
regs[i] = 4*i;
end
endmodule

符号扩展模块 SignedExtend.v

1
2
3
4
5
6
7
8
9
10
11
module SignedExtend(
input [15:0] In,
output reg [31:0] Out
);
always @(*) begin
if(In>=0)
Out[31:0] = {16'h0000,In};
else if(In<0)
Out[31:0] = {16'hffff,In};
end
endmodule

32位宽算术逻辑运算单元 ALU.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
module ALU(
//wire默认为无符号,要手动声明为有符号数
input signed [31:0] in1,
input signed [31:0] in2,
input [3:0] ALUCtr,
output reg [31:0] Res,
output reg Zero
);
always @(in1 or in2 or ALUCtr) begin
case(ALUCtr)
4'b0110://做减法
begin
Res = in1 - in2;
end
4'b0010://做加法
begin
Res = in1 + in2;
end
4'b0000://按位与运算
begin
Res = in1 & in2;
end
4'b0001://按位或运算
begin
Res = in1 | in2;
end
4'b0111://判断in1是否小于in2
begin
Res = (in1<in2)?1:0;
end
default:
begin
Res = 0;
end
endcase
Zero = (Res==0)?1:0;//当Res为0时Zero不为0,ppt中与视频中不同
end
endmodule

位拼接器 Concat.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module Concat_32(
input [3:0] In1,
input [27:0] In2,
output reg [31:0] Out
);
always@(*) begin
Out = {In1,In2};
end
endmodule

module Concat_2(
input In1,
input In2,
output reg [1:0] Out
);
always@(*) begin
Out = {In1,In2};
end
endmodule

数据存储器 DataRAM.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//使用寄存器实现数据存储器
module DataRAM(
input [4:0] Addr,
output [31:0] DataOut,
input [31:0] DataIn,
input MemWR,
input Clk
);
//32个32位寄存器
reg[31:0] regs[0:31];

//异步输出数据
assign DataOut = regs[Addr];

//同步输入数据
always @(posedge Clk) begin
if((Addr>=0) && MemWR)
regs[Addr] = DataIn;
end

//初始化其中的数据
initial
begin:loop
integer i;
for(i=0;i<32;i=i+1)
regs[i] = 4*i;
end
endmodule

整体实现简单指令集单周期MIPS微处理器 MIPSCPU.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
module MIPSCPU(
input Clk,
input Reset
);

//按照逻辑图从左往右的顺序一个个初始化部件
//每个部件的输出端口都建立一个wire

//程序计数器PC
wire [31:0] PC_D; //PC的输入D就是Mux3_1的输出
wire [31:0] PC_Q;
PC U0(PC_D,Clk,Reset,PC_Q);


//指令存储器
wire [31:0] Instr;
InstrROM U1(PC_Q[6:2],!Clk,Instr);

//主控制器与ALU控制器的结合
wire J;
wire B;
wire RegDst;
wire RegWr;
wire ALUSrc;
wire MemWr;
wire Mem2Reg;
wire [3:0] ALUCtr;
reg[5:0] OpCode;
always@(*)
OpCode = Instr[31:26];
Controller U2(OpCode,Instr[5:0],J,B,RegDst,RegWr,ALUSrc,MemWr,Mem2Reg,ALUCtr);

//RegFile下面那个 有符号数扩展
wire [31:0] SignedExtendOut;
SignedExtend U3(Instr[15:0],SignedExtendOut[31:0]);

//RegFile左边那个 5bit二选一
wire [4:0] Mux2_1_5bitOut;
Mux2_1_5bit U4(Instr[20:16],Instr[15:11],RegDst,Mux2_1_5bitOut);

//RegFile左边那个 向左移位
wire [27:0] LeftShift_26_28_2_Out;
LeftShift_26_28_2 U5(Instr[25:0],LeftShift_26_28_2_Out);

//PC输出到Adder
wire [31:0] AdderOut1;
Adder U6(32'h00000004,PC_Q,AdderOut1);

//RegFile上面那个 4+28拼接成32
wire [31:0] Concat_32_Out;
Concat_32 U7(AdderOut1[31:28],LeftShift_26_28_2_Out,Concat_32_Out);

//RegFile
wire [31:0] RegFile_DataIn; //RegFile_DataIn就是最右边的Mux2_1输出
wire [31:0] RegFile_RsData;
wire [31:0] RegFile_RtData;
RegFile U8(Instr[25:21],Instr[20:16],Mux2_1_5bitOut,RegFile_DataIn,RegWr,Clk,RegFile_RsData,RegFile_RtData);

//RegFile右边那个 向左移位
wire [31:0] LeftShift_32_32_2_Out;
LeftShift_32_32_2 U9(SignedExtendOut,LeftShift_32_32_2_Out);

//RegFile右边的Adder
wire [31:0] AdderOut2;
Adder U10(AdderOut1,LeftShift_32_32_2_Out,AdderOut2);

//RegFile右边的32bit二选一
wire [31:0] Mux2_1_32bitOut;
Mux2_1_32bit U11(RegFile_RtData,SignedExtendOut,ALUSrc,Mux2_1_32bitOut);

//ALU单元
wire [31:0] ALU_Res;
wire Zero;
ALU U12(RegFile_RsData,Mux2_1_32bitOut,ALUCtr,ALU_Res,Zero);

//Controller右边的Concat 1+1拼接成2bit
wire [1:0] Concat_2_Out;
Concat_2 U13(J,B&Zero,Concat_2_Out);

//Mux3_1
//PC_D就是这个Mux3_1的输出
Mux3_1 U14(AdderOut1,AdderOut2,Concat_32_Out,Concat_2_Out,PC_D);

//数据存储器
wire [31:0] DataRAM_DataOut;
DataRAM U15(ALU_Res[6:2],DataRAM_DataOut,RegFile_RtData,MemWr,Clk);

//最右边的32bit二选一
//wire [31:0] Mux2_1_32bitOut1; 这个Mux2_1输出到RegFile的DataIn
Mux2_1_32bit U16(ALU_Res,DataRAM_DataOut,Mem2Reg,RegFile_DataIn);
endmodule

仿真代码文件

寄存器 PCsim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
module PCsim(
output [31:0] Q
);
//PC中的输入信号在仿真激励文件中都定义为reg
reg [31:0] D;
reg Clk;
reg Reset;

//将目标仿真模块作为仿真文件的子模块
PC U0(D,Clk,Reset,Q);

//产生相对应的输入源
//时钟输入模板
parameter PERIOD = 10;//时钟周期10ns
always begin
Clk = 1'b0;
#(PERIOD/2) Clk = 1'b1;
#(PERIOD/2);
end

//仿真激励信号
initial
begin
Reset = 0;
D = 0;
#10
D = 1;
#10
D = 2;
#10
D = 3;
#10
D = 4;
#20
Reset = 1;
end
endmodule

指令存储器 InstrROMsim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
module InstrROMsim(
output [31:0] Instr
);
//InstrROM中的输入信号在仿真激励文件中都定义为reg
reg [4:0] Addr;
reg Clk;
//将目标仿真模块作为仿真文件的子模块
InstrROM U0(Addr,Clk,Instr);

//产生相对应的输入源
//时钟输入模板
parameter PERIOD = 10;//时钟周期10ns
always begin
Clk = 1'b0;
#(PERIOD/2) Clk = 1'b1;
#(PERIOD/2);
end

//仿真信号 按照实验要求上的来
initial
begin:loop
integer i;
//Addr从0遍历到31
Addr = 0;
for(i=1;i<31;i=i+1)begin
#10
Addr = i;
end
end
endmodule

控制器 Controllersim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
module Controllersim(
output J,
output B,
output RegDst,
output RegWr,
output ALUSrc,
output MemWr,
output Mem2Reg,
output [3:0] ALUCtr
);

//LeftShift中的输入信号在仿真激励文件中都定义为reg
reg [5:0] OpCode;
reg [5:0] Funct;

//将目标仿真模块作为仿真文件的子模块
Controller U0(OpCode, Funct, J, B, RegDst, RegWr, ALUSrc, MemWr, Mem2Reg, ALUCtr);

//仿真信号
initial
begin
OpCode = 6'h00;
Funct = 6'h20;
#10
Funct = 6'h22;
#10
Funct = 6'h24;
#10
Funct = 6'h25;
#10
Funct = 6'h2a;
#10

OpCode = 6'h23;
Funct = 6'h20;
#10
Funct = 6'h22;
#10
Funct = 6'h24;
#10
Funct = 6'h25;
#10
Funct = 6'h2a;
#10

OpCode = 6'h2b;
Funct = 6'h20;
#10
Funct = 6'h22;
#10
Funct = 6'h24;
#10
Funct = 6'h25;
#10
Funct = 6'h2a;
#10

OpCode = 6'h04;
Funct = 6'h20;
#10
Funct = 6'h22;
#10
Funct = 6'h24;
#10
Funct = 6'h25;
#10
Funct = 6'h2a;
#10

OpCode = 6'h02;
Funct = 6'h20;
#10
Funct = 6'h22;
#10
Funct = 6'h24;
#10
Funct = 6'h25;
#10
Funct = 6'h2a;
end

endmodule

主控制器 MainCtrsim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
module MainCtrsim(
output J,
output B,
output RegDst,
output RegWr,
output ALUSrc,
output MemWr,
output Mem2Reg,
output [1:0] ALUOp
);
//MainCtr中的输入信号在仿真激励文件中都定义为reg
reg [5:0] OpCode;
//将目标仿真模块作为仿真文件的子模块
MainCtr U0(OpCode, J, B, RegDst, RegWr, ALUSrc, MemWr, Mem2Reg, ALUOp);
//仿真激励信号
initial
begin
OpCode = 8'h00;
#10
OpCode = 8'h23;
#10
OpCode = 8'h2b;
#10
OpCode = 8'h04;
#10
OpCode = 8'h02;
end
endmodule

ALU控制器 ALUCtrsim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
module ALUCtrsim(
output [3:0] ALUCtr
);
//ALUCtr中的输入信号在仿真激励文件中都定义为reg
reg [1:0] ALUOp;
reg [5:0] Funct;

//将目标仿真模块作为仿真文件的子模块
ALUCtr U0(ALUOp,Funct,ALUCtr);

//仿真激励信号
initial
begin
ALUOp = 0;
Funct = 6'h20;
#10
Funct = 6'h22;
#10
Funct = 6'h24;
#10
Funct = 6'h25;
#10
Funct = 6'h2a;
#10
ALUOp = 1;
Funct = 6'h20;
#10
Funct = 6'h22;
#10
Funct = 6'h24;
#10
Funct = 6'h25;
#10
Funct = 6'h2a;
#10
ALUOp = 2;
Funct = 6'h20;
#10
Funct = 6'h22;
#10
Funct = 6'h24;
#10
Funct = 6'h25;
#10
Funct = 6'h2a;
end
endmodule

多路复用器 Mux2_1sim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
module Mux2_1sim(
output wire [4:0] Out0,
output wire [31:0] Out1
);

//Mux2_1中的输入信号在仿真激励文件中都定义为reg
reg [31:0] In1,In2;
reg sel;
reg [4:0] In1_5bit,In2_5bit;
always@(*)begin
In1_5bit = In1[4:0];
In2_5bit = In2[4:0];
end

//将目标仿真模块作为仿真文件的子模块
Mux2_1_5bit U0(In1_5bit,In2_5bit,sel,Out0);
Mux2_1_32bit U1(In1,In2,sel,Out1);

//仿真激励信号
initial
begin
In1 = 32'h12345678;
In2 = 32'h87654321;
sel = 0;
#10
sel = 1;
end
endmodule

3选1多路复用器 Mux3_1sim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
module Mux3_1sim(
output [31:0] Out
);

//Mux3_1中的输入信号在仿真激励文件中都定义为reg
reg [31:0] In1,In2,In3;
reg [1:0] sel;

//将目标仿真模块作为仿真文件的子模块
Mux3_1 U0(In1,In2,In3,sel,Out);

//仿真激励信号
initial
begin
In1 = 32'h12345678;
In2 = 32'h87654321;
In3 = 32'h33333333;
sel = 2'h0;
#10
sel = 2'h1;
#10
sel = 2'h2;
#10
sel = 2'h3;
end
endmodule

左移运算器 LeftShiftsim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
module LeftShiftsim(
output [31:0] Out1,
output [27:0] Out2,
output [28:0] Out3
);

//LeftShift中的输入信号在仿真激励文件中都定义为reg
reg [31:0] In;
reg [31:0] In1;
reg [25:0] In2;
reg [25:0] In3;

//从输入的In1和In2找出需要拼接的数据
always @(*)begin
In1 = In;
In2 = In[25:0];
In3 = In[25:0];
end

//将目标仿真模块作为仿真文件的子模块
LeftShift_32_32_2 U0(In1,Out1);
LeftShift_26_28_2 U1(In2,Out2);
LeftShift_26_29_3 U2(In3,Out3);

//仿真信号
initial
begin
In = 32'h22222222;
end
endmodule

加法器 Addersim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module Addersim(
output [31:0] Out
);
//Adder中的输入信号在仿真激励文件中都定义为reg
reg [31:0] In1,In2;

//将目标仿真模块作为仿真文件的子模块
Adder U0(In1,In2,Out);

//仿真激励信号
initial
begin
In1 = 32'h00000004;
In2 = 32'h00000008;
#10
In1 = 32'h44444444;
In2 = 32'h88888888;
end
endmodule

寄存器文件 RegFilesim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
module RegFilesim(
output [31:0] RsData,
output [31:0] RtData
);

//RegFile中的输入信号在仿真激励文件中都定义为reg
reg[4:0] RsAddr;
reg[4:0] RtAddr;
reg[4:0] WrAddr;
reg RegWr;
reg[31:0] DataIn;
reg Clk;

//产生相对应的输入源
//时钟输入模板
parameter PERIOD = 10;//时钟周期10ns
always begin
Clk = 1'b0;
#(PERIOD/2) Clk = 1'b1;
#(PERIOD/2);
end

//仿真信号
initial
begin:loop
integer i;
RsAddr = 0;
RtAddr = 0;
WrAddr = 0;
DataIn = 0;
RegWr = 1;
for(i=1;i<9;i=i+1) begin
#10
WrAddr = i;
DataIn = 4*i;
end
#10
WrAddr = 9;
DataIn = 36;
#10
RtAddr = 9;
RegWr = 0;
for(i=0;i<9;i=i+1)begin
#10
RsAddr = i+1;
RtAddr = 8-i;
end
end
//将目标仿真模块作为仿真文件的子模块
RegFile U0(RsAddr,RtAddr,WrAddr,DataIn,RegWr,Clk,RsData,RtData);
endmodule

符号扩展模块 SignedExtendsim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module SignedExtendsim(
output [31:0] Out
);
//SignedExtend中的输入信号在仿真激励文件中都定义为reg
reg [15:0] In;

//将目标仿真模块作为仿真文件的子模块
SignedExtend U0(In,Out);

//仿真激励信号
initial
begin
In = 16'h1234;
#10
In = 16'h8234;
end
endmodule

32位宽算术逻辑运算单元 ALUsim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
module ALUsim(
output [31:0] Res,
output Zero
);

//ALU中的输入信号在仿真激励文件中都定义为reg
reg [31:0] in1,in2;
reg [3:0] ALUCtr;

//将目标仿真模块作为仿真文件的子模块
ALU U0(in1,in2,ALUCtr,Res,Zero);

//仿真信号
initial
begin
in1 = 32'h00000004;
in2 = 32'h00000008;
ALUCtr = 0;
#10
ALUCtr = 1;
#10
ALUCtr = 2;
#10
ALUCtr = 6;
#10
ALUCtr = 7;
#10
in1 = 32'h44444444;
in2 = 32'h88888888;
ALUCtr = 0;
#10
ALUCtr = 1;
#10
ALUCtr = 2;
#10
ALUCtr = 6;
#10
ALUCtr = 7;
#10
in1 = 32'h99999999;
in2 = 32'h88888888;
ALUCtr = 0;
#10
ALUCtr = 1;
#10
ALUCtr = 2;
#10
ALUCtr = 6;
#10
ALUCtr = 7;
end
endmodule

位拼接器 Concatsim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
module Concatsim(
output [31:0] Out0,
output [1:0] Out1
);

//Concat中的输入信号在仿真激励文件中都定义为reg
reg [31:0] In1;
reg [31:0] In2;
reg [4:0] In11;
reg [27:0] In12;
reg In21;
reg In22;

//从输入的In1和In2找出需要拼接的数据
always @(*)begin
In11 = In1[31:28];
In12 = In2[27:0];
In21 = In1[0];
In22 = In2[0];
end

//将目标仿真模块作为仿真文件的子模块
Concat_32 U0(In11,In12,Out0);
Concat_2 U1(In21,In22,Out1);

//仿真信号
initial
begin
In1 = 32'h12345678;
In2 = 32'h87654321;
end
endmodule

数据存储器 DataRAMsim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
module DataRAMsim(
output [31:0] DataOut
);

//DataRAM中的输入信号在仿真激励文件中都定义为reg
reg [4:0] Addr;
reg [31:0] DataIn;
reg Clk;
reg MemWR;

//将目标仿真模块作为仿真文件的子模块
DataRAM U0(Addr,DataOut,DataIn,MemWR,Clk);

//产生相对应的输入源
//时钟输入模板
parameter PERIOD = 10;//时钟周期10ns
always begin
Clk = 1'b0;
#(PERIOD/2) Clk = 1'b1;
#(PERIOD/2);
end

//仿真信号 按照实验要求上的来
initial
begin
MemWR = 1;
Addr = 0;
DataIn = 0;
#10
Addr = 1;
DataIn = 4;
#10
Addr = 2;
DataIn = 8;
#10
Addr = 3;
DataIn = 12;
#10
Addr = 4;
DataIn = 16;
#10
Addr = 5;
DataIn = 20;
#10
Addr = 6;
DataIn = 24;
#10
Addr = 7;
DataIn = 28;
#10
Addr = 8;
DataIn = 32;
#10
Addr = 9;
DataIn = 36;
#10
MemWR = 0;
Addr = 0;
DataIn = 36;
#10
Addr = 1;
DataIn = 36;
#10
Addr = 2;
DataIn = 36;
#10
Addr = 3;
DataIn = 36;
#10
Addr = 4;
DataIn = 36;
#10
Addr = 5;
DataIn = 36;
#10
Addr = 6;
DataIn = 36;
#10
Addr = 7;
DataIn = 36;
#10
Addr = 8;
DataIn = 36;
#10
Addr = 9;
DataIn = 36;

end
endmodule

整体实现简单指令集单周期MIPS微处理器 MIPSCPUsim.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
module MIPSCPUsim(
);
//MIPSCPU中的输入信号在仿真激励文件中都定义为reg
reg Clk;
reg Reset;

//将目标仿真模块作为仿真文件的子模块
MIPSCPU U0(Clk,Reset);

//产生相对应的输入源
//时钟输入模板
parameter PERIOD = 10;//时钟周期10ns
always begin
Clk = 1'b0;
#(PERIOD/2) Clk = 1'b1;
#(PERIOD/2);
end

//仿真激励信号
initial
begin
Reset = 0;
#10
Reset = 1;
#10
Reset = 0;
end
endmodule

仿真过程

InstrROM 从地址 0 开始的连续 11 个存储单元存放机器指令

1
2
3
4
5
6
7
8
9
10
11
00432020
8c440004
ac420008
00831022
00831025
00831024
0083102a
10830001
08000000
8c620000
08000000

对应汇编指令如下:
1
2
3
4
5
6
7
8
9
10
11
12
main:
add $4,$2,$3 #0
lw $4,4($2) #1
sw $2,8($2) #2
sub $2,$4,$3 #3
or $2,$4,$3 #4
and $2,$4,$3 #5
slt $2,$4,$3 #6
beq $4,$3,exit #7
j main #8
exit: lw $2,0($3) #9
j main #10

前面 7 条指令都是顺序往下直行,到第七条指令时符合条件则跳转。$3 为复位时的初始值 0,$4 为从数据存储器装载的值,若数据存储器地址为 4 的存储字的初始值不为 0,则顺序执行所有指令之后在 0~8 号指令之间循环;

若数据存储器地址为 4 的存储字初始值为 0,则跳过第 8 条指令,进行到 9 号指令,再循环。

观察 PC 的变化可知,PC 先从 0,4,8,12,16,20,24,28,前 7 条指令顺序执行,数据存储器第 0 号地址的存储字初始值为 0,执行第 7 条指令的时候 Zero 的值为 1,说明两寄存器的值相等,符合跳转条件,故跳过了第 8 条指令,进入了第9 条指令,也就是地址变为 36。执行完 9,10 条指令后又返回原循环。

之后指令执行的过程中,由于 RegFile 以及 DataRAM 前 32 个存储单元存储的初始值为单元地址*4,都不为 0,所以程序在 0~7 条语句之间循环,PC 在 0,4,8,12,16,20,24,28 之间循环跳转。

第一条指令为 R 型指令,高 6 位为 000000,低 6 位功能码为 100000,译码译出的 ALUCtr 为 0010,即 2,执行加运算指令。

第二条指令为 I 型指令,高 6 位为 100011,译码译出的 ALUCtr 也为 0010,对应 lw 指令。低 16 位 Imm 域为常数地址 0x0004,符号扩展后也为 4,在 ALU中进行加运算,得到的地址在数据存储器中读到相应数据再写到寄存器文件中。

第三条指令为 I 型指令,高 6 位为 101011,译码译出的 ALUCtr 也为 0010,对应 sw 指令。 低 16 位 Imm 域为常数地址 0x0008,符号扩展后也为 8,在 ALU中进行加运算,得到地址,再在数据存储器把数据写到相应的地址中。

第 4,5,6 条指令也为 R 型指令,低 6 位功能码不同,分别执行 sub,or,and,slt不同功能。

第七条指令为 I 型指令,因为 zero 为 1,所以跳转到 exit 语句的位置。

第八条指令为 J 型指令,无条件跳转到 main 语句也就是第 0 条语句的位置,所以 PC 又恢复为 0。

第一条指令的 Rs 对应$2,Rt 对应$3,RsData=8=24,符合地址都是 2 的整数倍, RtData=0xc=12=34,相加后的结果为 0x14=20=8+12;

第二、三条指令的 Rs 都是$2,二的偏移地址为 4,三的偏移地址为 8,对应的SignExtended 分别为 4 和 8。

之后的 sub,or,and,slt 都为 R 型指令,由于$4 与$3 对应的值相等,都为 0xc,他们相减后为 0,相与、相或之后的结果相同,左移之后右边补 0,所以最终得到的是 0。

再执行 beq 指令,$4,$3 寄存器对应的值相等,Zero 输出的值为 1,跳转到exit 指令处,再往下执行一条 j main ,之后回到第 0 条指令。


MIPS单周期微处理器的verilog代码实现
http://tsailooy.top/2024/06/03/MIPS单周期微处理器/
作者
Tsailooy
发布于
2024年6月3日
更新于
2024年6月3日
许可协议