P7 Design documents

P7 Design documents

Charles Lv7

Verilog流水线CPU设计文档

一、 CPU设计方案综述

(一) 总体设计概述

使用Verilog开发一个简单的流水线CPU,总体概述如下:

  1. 此CPU为32位CPU

  2. 此CPU为流水线设计

  3. 此CPU支持的指令集为:

    mips-c指令集所有指令

  4. add, sub不支持溢出

(二) 关键模块定义

img

主代码mips

Folding coding
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
92
93
94
95
96
97
98
99
100
101
102
`timescale 1ns / 1ps
`include "macro.v"
module mips(input clk,
input reset,
input interrupt,
input [31:0] i_inst_rdata,//IM_RD
input [31:0] m_data_rdata,//DM_RD

output [31:0] macroscopic_pc,//PC
output [31:0] i_inst_addr,//IM_Address
output [31:0] m_data_addr,//DM_Address
output [31:0] m_data_wdata,//DM_WD
output [3:0] m_data_byteen,//DM_en
output [31:0] m_int_addr,//Int_Address
output [3:0] m_int_byteen,//Int_en
output [31:0] m_inst_addr,//M_PC
output w_grf_we,//Grf_en
output [4:0] w_grf_addr,//Grf_Address
output [31:0] w_grf_wdata,//Grf_WD
output [31:0] w_inst_addr//W_PC
);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////cpu
wire inter_T0,inter_T1;
//Bridge
wire [31:0] WD_out,RD_out;
wire [31:0] Address_out;
wire [3:0] DM_en;
wire T0_WE,T1_WE;
//TC
wire [31:2] TC_Addr;
wire [31:0] T0_RD,T1_RD;
assign TC_Addr=Address_out[31:2];
//interrupt
//cpu
cpu cpu (
.clk(clk), //
.reset(reset),//
.interrupt(interrupt), //
.inter_T0(inter_T0), //
.inter_T1(inter_T1), //

.i_inst_rdata(i_inst_rdata),//
.Rdata(RD_out), //dm_read_data

.macroscopic_pc(macroscopic_pc),//
.i_inst_addr(i_inst_addr),//
.m_data_addr(m_data_addr), //
.m_data_wdata(m_data_wdata), //dm_write_data
.Byteen(m_data_byteen),//
.m_int_addr(m_int_addr),//Int_Address
.m_int_byteen(m_int_byteen),//Int_en
.m_inst_addr(m_inst_addr),
.w_grf_we(w_grf_we), //
.w_grf_addr(w_grf_addr), //
.w_grf_wdata(w_grf_wdata), //
.w_inst_addr(w_inst_addr)//
);

//Bridge
Bridge bridge (
.Address_in(m_int_addr),
.WD_in(m_data_wdata),
.byteen(m_data_byteen),
.DM_RD(m_data_rdata), //
.T0_RD(T0_RD),
.T1_RD(T1_RD),

.DM_WE(DM_en),
.T0_WE(T0_WE),
.T1_WE(T1_WE),
.Address_out(Address_out),
.WD_out(WD_out), //DM
.RD_out(RD_out)
);


////T0
TC T0 (
.clk(clk),
.reset(reset),
.Addr(TC_Addr),
.WE(T0_WE),
.Din(WD_out),

.Dout(T0_RD),
.IRQ(inter_T0)
);

////T1
TC T1 (
.clk(clk),
.reset(reset),
.Addr(TC_Addr),
.WE(T1_WE),
.Din(WD_out),

.Dout(T1_RD),
.IRQ(inter_T1)
);

endmodule

宏的定义

Folding coding
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
92
93
94
`timescale 1ns / 1ps
//alu
`define _ADD 12'b000000000001
`define _SUB 12'b000000000010
`define _AND 12'b000000000100
`define _OR 12'b000000001000
`define _XOR 12'b000000010000
`define _NOR 12'b000000100000
`define _SLL 12'b000001000000
`define _SRA 12'b000010000000
`define _SRL 12'b000100000000
`define _SLT 12'b001000000000
`define _SLTU 12'b010000000000
`define _ALUNew 12'b100000000000
//ifu
`define PC_Initial 32'h0000_3000
//ext
`define Zero_Ext 3'b001
`define Sign_Ext 3'b010
`define Lui_Ext 3'b100
//NPC
`define PC4_NPC 5'b00001
`define B_transfer_NPC 5'b00010
`define J_transfer_NPC 5'b00100
`define Jr_NPC 5'b01000
`define NEW_NPC 5'b10000
//Controller_for_Reg
`define ALUop_Initial 7'b0000001
`define AluSrc1_Initial 4'b0001
`define AluSrc2_Initial 4'b0001
`define WhichtoReg_Initial 8'b00000001
`define RegDst_Initial 4'b0001
`define DM_type_Initial 6'b000001
//DM
`define Word_DM 6'b000001
`define Half_DM 6'b000010
`define Byte_DM 6'b000100
`define Unsigned_Half_DM 6'b001000
`define Unsigned_Byte_DM 6'b010000
//B_transfer
`define nop_B_trans 4'b0000
`define beq_B_trans 4'b0001
`define bgez_B_trans 4'b0010
`define bgtz_B_trans 4'b0011
`define blez_B_trans 4'b0100
`define bltz_B_trans 4'b0101
`define bne_B_trans 4'b0110
//MD_Unit
`define nop_MDU 4'b0000
`define mult_MDU 4'b0001
`define multu_MDU 4'b0010
`define div_MDU 4'b0011
`define divu_MDU 4'b0100
`define mfhi_MDU 4'b0101
`define mflo_MDU 4'b0110
`define mthi_MDU 4'b0111
`define mtlo_MDU 4'b1000
//TC
`define IDLE 2'b00
`define LOAD 2'b01
`define CNT 2'b10
`define INT 2'b11
`define ctrl mem[0]
`define preset mem[1]
`define count mem[2]
//CP0
`define IM SR[15:10]
`define EXL SR[1]
`define IE SR[0]
`define BD Cause[31]
`define IP Cause[15:10]
`define ExcCode Cause[6:2]
`define SR_Address 5'd12
`define Cause_Address 5'd13
`define EPC_Address 5'd14
//Bridge
`define Data_Begin 32'h0000_0000
`define Data_End 32'h0000_2fff
`define Text_Begin 32'h0000_3000
`define Text_End 32'h0000_6fff
`define Error_Entry 32'h0000_4180
`define T0_Begin 32'h0000_7f00
`define T0_End 32'h0000_7f0b
`define T1_Begin 32'h0000_7f10
`define T1_End 32'h0000_7f1b
`define Echo_Begin 32'h0000_7f20
`define Echo_End 32'h0000_7f23
//Error_Stream
`define Error_Int 5'd0
`define Error_AdEL 5'd4
`define Error_AdES 5'd5
`define Error_Syscall 5'd8
`define Error_RI 5'd10
`define Error_Ov 5'd12

CPU模块

<一>.F级流水线

1. PC

(1) 端口说明

表1-IFU端口说明
序号 信号名 方向 描述
1 clk I 时钟信号
2 reset I 同步复位信号,将PC值置为0x0000_3000:
0:无效
1:复位
3 en I 使能信号,决定是否阻塞
4 NPC[31:0] I 下一周期PC的地址
6 PC[31:0] O 当前执行的PC

(2) 功能定义

表2-IFU功能定义
序号 功能 描述
1 复位 reset有效时,PC置为0x00003000
2 更新PC的值 将PC赋值为NPC

<二>.D级流水线

1. GRF

(1) 端口说明

表3-GRF端口说明
序号 信号名 方向 描述
1 clk I 时钟信号
2 reset I 同步复位信号,将32个寄存器中全部清零
1:清零
0:无效
3 WE I 写使能信号
1:可向GRF中写入数据
0:不能向GRF中写入数据
4 A1[4:0] I 5位地址输入信号,指定32个寄存器中的一个,将其中存储的数据读出到RD1
5 A2[4:0] I 5位地址输入信号,指定32个寄存器中的一个,将其中存储的数据读出到RD2
6 A3[4:0] I 5位地址输入信号,指定32个寄存器中的一个,作为RD的写入地址
7 WD[31:0] I 32位写入数据
8 WPC[31:0] I 当前写入GRF的PC
9 RD1[31:0] O 输出A1指定的寄存器的32位数据
10 RD2[31:0] O 输出A2指定的寄存器的32位数据

(2) 功能定义

表4-GRF功能定义
序号 功能 描述
1 同步复位 reset为1时,将所有寄存器清零
2 读数据 将A1和A2地址对应的寄存器的值分别通过RD1和RD2读出
3 写数据 当WE为1且时钟上升沿来临时,将WD写入到A3对应的寄存器内部
4 内部转发 当A1和A2之一与A3相等且写入信号为1时,用WD代替RD1或RD2的输出

2. EXT

(1) 端口说明

表9-EXT端口说明
序号 信号名 方向 描述
1 imm16[15:0] I 代扩展的16位信号
2 sign[2:0] I 无符号或符号扩展选择信号
3’b001:无符号扩展
3’b010:符号扩展
3’b100: 寄存到高位
3 imm32[31:0] O 扩展后的32位的信号

(2) 功能定义

表10-EXT功能定义
序号 功能 描述
1 无符号扩展 当sign为3’b001时,将imm16无符号扩展输出
2 符号扩展 当sign为3‘b010时,将imm16符号扩展输出
3 存储到高位 当sign为3’100时,将imm16存在高16位

3. Controller

(1) 端口说明

表11-Controller端口说明
序号 信号名 方向 描述
1 op[5:0] I instr[31:26]6位控制信号
2 func[5:0] I instr[5:0]6位控制信号
3 rt[4:0] I instr[20:16]5位控制信号
4 AluOp[11:0] O ALU的控制信号
5 WeGrf O GRF写使能信号
0:禁止写入
1:允许写入
6 WeDm O DM的写入信号
0:禁止写入
1:允许写入
7 branch[3:0] O PC转移位置选择信号
4’b0001:其他情况
4’b0010:beq
4’b0100:j||jal
4’b1000:jr;
8 AluSrc1[3:0] O 参与ALU运算的第一个数
4’b0001:RD1
4’b0010:RD2
9 AluSrc2[3:0] O 参与ALU运算的第二个数,来自GRF还是imm
4’b0001:RD2
4’b0010:imm32
4’b0100: offset
10 WhichtoReg[7:0] O 将何种数据写入GRF?
8’b0000_0001:ALU计算结果
8’b0000_0010:DM读出信号
8’b0000_0100:upperImm
8’b0000_1000: PC+4
11 RegDst[3:0] O 写入GRF的哪个寄存器?
4’b0001:rd
4’b0010:rt
4’b0100:31号寄存器
12 SignExt[2:0] O 拓展方式:
3’b001:0拓展
3’b010:符号拓展
3’b100:存储到高位
13 B_change[3:0] O B转移的类型
4’b0001:beq
4’b0010:slt
4’b0100:blez
14 DM_type[5:0] O 存取指令类型:
6’b000001:lw/sw
6’b000010:lh/sh
6’b000100:lb/sb
6’b001000:lhu
6’b010000:lbu
15 MDop[3:0] O 乘除指令选择信号:
4‘b0000:无操作
4’b0001:mult
4’b0010:multu
4’b0011:div
4’b0100:divu
4’b0101:mfhi
4’b0110:mflo
4’b0111:mthi
4’b1000:mtlo
16 D_Tuse_rs[1:0] O 指令的rs寄存器的值从第一次进入D级到被使用的周期数
17 D_Tuse_rt[1:0] O 指令的rt寄存器的值从第一次进入D级到被使用的周期数
18 D_Tnew[1:0] O 指令从进入D开始到产生运算结果需要的周期数

4.NPC

NPC(下一指令计算单元)

该模块根据当前pc(包括D级和F级)和其他控制信号(NPCOp,CMP输出信息),计算出下一指令所在的地址npc,传入IFU模块。

  • 端口定义
信号名 方向 位宽 描述
F_pc I 32 F级指令地址
D_pc I 32 D级指令地址
offset I 32 地址偏移量,用于计算B类指令所要跳转的地址
imm26 I 26 当前指令数据的前26位(0~25),用于计算jal和j指令所要跳转的地址
ra I 32 储存在寄存器($ra或是jalr指令中存储“PC+4”的寄存器)中的地址数据,用于实现jr和jalr指令
ALU_change I 1 B类指令判断结果 1:说明当前B类指令的判断结果为真 0:说明判断结果为假
branch I 4 NPC功能选择 0x000:顺序执行 0x001:B类指令跳转 0x010: jal/j跳转 0x011: jr/jalr跳转
npc O 32 输出下一指令地址
PC8 O 32 jr指令时存储PC+8的值

5.B_transfer(B类指令比较单元)

该单元根据输入的CMPOp信号对当前B指令的类型进行判断,进而对当前输入的数值进行相应比较,最后输出结果。

  • 端口定义
信号名 方向 位宽 描述
A I 32 输入B_transfer单元的第一个数据
B I 32 输入B_transfer单元的第二个数据
Type I 4 Type功能选择信号
0x0001:beq判断
0x0010:slt判断
0x0100:blez判断
ALU_change O 1 判断结果输出
1: 判断结果为真
0:判断结果为假

<三>.E级流水线

1. ALU

(1) 端口说明

表5-ALU端口说明
序号 信号名 方向 描述
1 A[31:0] I 参与运算的第一个数
2 B[31:0] I 参与运算的第二个数
3 ALUop[11:0] I 决定ALU做何种操作
12’b0000_0000_0001:无符号加
12’b0000_0000_0010:无符号减
12’b0000_0000_0100:与
12’b0000_0000_1000:或
12‘b0000_0001_0000:异或
12‘b0000_0010_0000:或非
12‘b0000_0100_0000:sll
12‘b0000_1000_0000:sra
12‘b0001_0000_0000:srl
12‘b0010_0000_0000:slt
12‘b0100_0000_0000:sltu
4 res O A与B做运算后的结果

(2) 功能定义

表6-ALU功能定义
序号 功能 描述
1 加运算 res = A + B
2 减运算 res = A - B
3 与运算 res = A & B
4 或运算 res = A | B
5 左移位运算 Res=A<<B

2.MD_Unit

  • 端口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 mips.v中的clk
I reset 1 同步复位信号 mips.v中的reset
I en 1 MD_Unit使能信号 判断是否为乘除计算
I MDop 4 指令选择信号 Controller
I A 32 第一个计算数 e_A
I B 32 第二个计算数 e_B
O HI 32 HI寄存器输出
O LO 32 LO寄存器输出
O out 32 取出HI或LO的值
O busy 1 是否在进行乘除计算

<四>.M级流水线

1. DM_In

  • 端口定义
方向 信号名 位宽 描述 输入来源
I low2 2 时钟信号 Address低两位
I WD 31 处理前写入结果 32位写入数据
I DM_type 6 决定存取指令类型 存取指令类型:
6’b000001:lw/sw
6’b000010:lh/sh
6’b000100:lb/sb
6’b001000:lhu
6’b010000:lbu
O data_byteen 4 字节使能信号
O DM_input 32 处理后的写入结果

2.DM_Out

  • 端口定义
方向 信号名 位宽 描述 输入来源
I low2 2 时钟信号 Address低两位
I DM_output 32 处理前读入信号 32位读入数据
I DM_type 6 决定存取指令类型 存取指令类型:
6’b000001:lw/sw
6’b000010:lh/sh
6’b000100:lb/sb
6’b001000:lhu
6’b010000:lbu
O RD 32 处理后的读入结果

3.CP0

  • 端口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 cpu主时钟
I reset 1 复位信号 cpu同步复位信号
I WE 1 写入使能 前一级相同信号输入
I Address 5 CP0 寄存器编号 前一级相同信号输入
I WD 32 CP0 寄存器的写入数据 前一级相同信号输入
I BD_in 1 分支延迟槽指令标志 前一级相同信号输入
I VPC 32 中断/异常时的 PC 前一级相同信号输入
I ExcCode_in 5 中断/异常的类型 前一级相同信号输入
I HWInt 6 6 个设备中断 前一级相同信号输入
I EXLClr 1 置 0 SR 的EXL 位 m_eret控制信号输入
O req 1 异常/中断请求
O EPC 32 处理后的读入结果
O D_out 32 CP0 寄存器的输出数据
Folding coding
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
`timescale 1ns / 1ps
`include "macro.v"
module CP0(input clk,
input reset,
input WE,
input [4:0] Address,
input [31:0] WD,
input BD_in,//branch delay
input [31:0] VPC,
input [4:0] ExcCode_in,
input [7:2] HWInt,
input EXLClr,

output req,
output reg [31:0] EPC,
output [31:0] D_out
);
reg [31:0] SR;
reg [31:0] Cause;
assign InterInt = (ExcCode_in!= `Error_None)&&(!`EXL);
assign ExterInt = (|(HWInt&`IM))&&`IE&&(!`EXL);
always @(posedge clk)begin
if (reset)begin
SR <= 0;
Cause <= 0;
EPC <= 0;
end
else begin
`IP <= HWInt;
if (EXLClr) begin
`EXL <= 0;
end
else if (req) begin
`BD <= BD_in;
EPC <= BD_in?(VPC-32'd4):VPC;
`ExcCode <= ExterInt?`Error_Int:ExcCode_in;
`EXL <= 1'b1;
// $display("BD: %h,EPC: %h,ExcCode: %h,EXL: %h",`BD,EPC,`ExcCode,`EXL);
end
else if (WE)begin
if (Address == `SR_Address)begin
SR <= WD;
//$display("SR: %h",WD);
end
else if (Address == `EPC_Address)begin
EPC <= WD;
// $display("EPC: %h",WD);
end
end
end
end
assign req =(ExterInt||InterInt);
assign D_out = (Address == `SR_Address)?SR://mfc0,mtc0
(Address == `Cause_Address)?Cause:
(Address == `EPC_Address)?EPC://eret
32'b0;
endmodule

<五>.各级流水线寄存器

1.IF_ID

D_Reg(IF/ID流水寄存器)

  • 端口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 mips.v中的clk
I reset 1 同步复位信号 mips.v中的reset
I en 1 D级寄存器使能信号 stall信号取反
I clr 1 D级寄存器清空信号 是否清空延迟槽决定
I req 1 D级寄存器异常/中断请求信号 CP0
I F_instr 32 F级instr输入 IFU_instr
I F_pc 32 F级pc输入 IFU_pc
I F_ExcCode 5 F级ExcCode输入 前一级相同信号
I F_BD 1 F级BD输入 前一级相同信号
O D_instr 32 D级instr输出
O D_pc 32 D级pc输出
O D_ExcCode 5 D级ExcCode输出
O D_BD 1 D级BD输出

2.ID_EX

E_Reg(ID/EX流水寄存器)

  • 端口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 mips.v中的clk
I reset 1 同步复位信号 mips.v中的reset
I clr 1 E级寄存器清空信号 HazardUnit中stall信号
I req 1
I D_RD1 32 D级GRF输出RD1 通过B_transfer_D1转发的数据
I D_RD2 32 D级GRF输出RD2 通过B_transfer_D2转发的数据
I D_instr_s 5 D级instr的shamt D_instr的s域数据
I D_A1 5 D级A1输入 D_instr的rs域数据
I D_A2 5 D级A2输入 D_instr的rt域数据
I D_A3 5 D级A3输入 通过MUX_A3选择出的数据
I D_imm32 32 D级imm32输入 通过EXT模块扩展出的数据
I D_PC 32 D级PC输入 前一级相同信号
I D_PC8 32 D级PC8输入 前一级相同信号
I Tnew_D 2 D级指令的Tnew输入 前一级相同信号
I D_Wegrf 1 D级控制信号输入 前一级相同信号
I D_WeDm 1 D级控制信号输入 前一级相同信号
I D_ALUop 7 D级控制信号输入 前一级相同信号
I D_AluSrc1 4 D级控制信号输入 前一级相同信号
I D_AluSrc2 4 D级控制信号输入 前一级相同信号
I D_WhichtoReg 8 D级控制信号输入 前一级相同信号
I D_RegDst 4 D级控制信号输入 前一级相同信号
I D_DM_type 6 D级控制信号输入 前一级相同信号
I D_ALU_change 1 D级ALU_change输入 前一级相同信号
I D_MDop 4 D级MDop输入 前一级相同信号
I D_branch 5 D级branch输入 前一级相同信号
I D_ExcCode 5 D级ExcCode输入 前一级相同信号
I D_BD 1 D级BD输入 前一级相同信号
I D_mfc0 1 D级mfc0输入 前一级相同信号
I D_mtc0 1 D级mtc0输入 前一级相同信号
I D_eret 1 D级eret输入 前一级相同信号
I D_syscall 1 D级syscall输入 前一级相同信号
O E_RD1 32 E级RD1输出
O E_RD2 32 E级RD2输出
O E_instr_s 5 移位指令的位移数
O E_A1 5 E级A1输出
O E_A2 5 E级A2输出
O E_A3 5 E级A3输出
O E_imm32 32 E级imm32输出
O E_PC 32 E级PC输出
O E_PC8 32 E级PC8输出
O E_Tnew 2 E级指令的Tnew输出
O E_Wegrf 1 E级控制信号输出
O E_WeDm 1 E级控制信号输出
O E_ALUop 12 E级控制信号输出
O E_AluSrc1 4 E级控制信号输出
O E_AluSrc2 4 E级控制信号输出
O E_WhichtoReg 11 E级控制信号输出
O E_RegDst 3 E级控制信号输出
O E_DM_type 6 E级控制信号输出
O E_ALU_change 1 E级ALU_change输出
O E_MDop 4 E级MDop输出
O E_branch 5 E级branch输出
O E_ExcCode 5 E级ExcCode输出
O E_BD 1 E级BD输出
O E_mfc0 1 E级mfc0输出
O E_mtc0 1 E级mtc0输出
O E_eret 1 E级eret输出
O E_syscall 1 E级syscall输出
  • 运算功能

    Tnew_E = (Tnew_D > 0) ? Tnew_D - 1: 0Tnew_E=(Tnew_D>0)?Tnew_D−1:0

3.EX_MEM

M_Reg(EX/MEM流水寄存器)

  • 端口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 mips.v中的clk
I reset 1 同步复位信号 mips.v中的reset
I E_A2 5 E级A2输入 ALU_out数据
I E_A3 5 E级A3输入(转发值) MUX_ALU选择出来的数据
I E_RD2 32 E级RD2输入 前一级相同信号
I E_ALUout 32 E级res输入 前一级相同信号
I E_PC 32 E级PC输入 前一级相同信号
I E_PC8 32 E级PC8输入 前一级相同信号
I E_Tnew 2 E级Tnew输入 前一级相同信号
I E_Wegrf 1 E级控制信号输入 前一级相同信号
I E_WeDm 1 E级控制信号输入 前一级相同信号
I E_WhichtoReg 8 E级控制信号输入 前一级相同信号
I E_RegDst 4 E级控制信号输入 前一级相同信号
I E_DM_type 6 E级控制信号输入 前一级相同信号
I E_ALU_change 1 E级ALU_change输入 前一级相同信号
I E_imm32 32 E级imm32输入 前一级相同信号
I E_branch 5 E级branch输入 前一级相同信号
I E_ExcCode 5 E级ExcCode输入 前一级相同信号
I E_BD 1 E级BD输入 前一级相同信号
I E_mfc0 1 E级mfc0输入 前一级相同信号
I E_mtc0 1 E级mtc0输入 前一级相同信号
I E_eret 1 E级eret输入 前一级相同信号
I E_syscall 1 E级syscall输入 前一级相同信号
O M_A2 5 M级A2输出
O M_A3 5 M级A3输出
O M_RD2 32 M级RD2输出
O M_ALUout 32 M级res输出
O M_PC 32 M级PC输出
O M_PC8 32 M级PC8输出
O M_Tnew 2 M级Tnew输出
O M_Wegrf 1 M级Tnew输出
O M_WeDm 1 M级控制信号输出
O M_WhichtoReg 8 M级控制信号输出
O M_RegDst 4 M级控制信号输出
O M_DM_type 6 M级控制信号输出
O M_ALU_change 1 M级ALU_change输出
O M_imm32 32 M级imm32输出
O M_branch 5 M级branch输出
O M_ExcCode 5 M级ExcCode输出
O M_BD 1 M级BD输出
O M_mfc0 1 M级mfc0输出
O M_mtc0 1 M级mtc0输出
O M_eret 1 M级eret输出
O M_syscall 1 M级syscall输出
  • 运算功能

    Tnew_M = (Tnew_E > 0) ? Tnew_E - 1: 0Tnew_M=(Tnew_E>0)?Tnew_E−1:0

4.MEM_WB

W_Reg(MEM/WB流水寄存器)

  • 接口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 mips.v中的clk
I reset 1 同步复位信号 mips.v中的reset
I M_A3 5 M级A3输入 前一级相同信号
I M_RD 32 M级RD输入 前一级相同信号
I M_PC 32 M级PC输入 前一级相同信号
I M_PC8 32 M级PC8输入 前一级相同信号
I M_Wegrf 1 M级控制信号输入 前一级相同信号
I M_WhichtoReg 1 M级控制信号输入 前一级相同信号
I M_RegDst 4 M级控制信号输入 前一级相同信号
I M_imm32 32 M级imm32输入 前一级相同信号
I M_ALU_change 1 M级ALU_change输入 前一级相同信号
I M_Tnew 2 M级Tnew输入 前一级相同信号
O W_A3 5 W级A3输出
O W_ALUout 32 W级res输出
O W_RD 32 W级RD输出
O W_PC 32 W级PC输出
O W_PC8 32 W级PC8输出
O W_Wegrf 1 W级控制信号输出
O W_WhichtoReg 8 W级控制信号输出
O W_RegDst 4 W级控制信号输出
O W_imm32 32 W级imm32输出
O W_ALU_change 1 W级ALU_change输出
O W_Tnew 2 W级Tnew输出

<六>.暂停、转发处理及相关多路选择器

(一).冲突综合单元(HazardUnit)

方向 信号名 位宽 描述
I D_A1 5 D级A1端输入
I D_A2 5 D级A2端输入
I E_A1 5 E级A1端输入
I E_A2 5 E级A2端输入
I M_A2 5 M级A2端输入
I E_A3 5 E级A3端输入
I M_A3 5 M级A3端输入
I W_A3 5 W级A3端输入
I D_Tuse_rs 2 D_Tuse_rs输入
I D_Tuse_rt 2 D_Tuse_rt输入
I E_Tnew 2 E级Tnew输入
I M_Tnew 2 M级Tnew输入
I W_Tnew 2 W级Tnew输入
I E_Wegrf 1 E级Wegrf输入
I M_Wegrf 1 M级Wegrf输入
I W_Wegrf 1 W级Wegrf输入
I E_WhichtoReg 8 E级WhichtoReg输入
I M_WhichtoReg 8 M级WhichtoReg输入
I start 1 乘除开始信号
I busy 1 乘除忙碌信号
I MDU_en 1 D级将进行乘除运算信号
I D_eret 1 D级eret输入
I E_mtc0 1 E级mtc0输入
I M_mtc0 1 M级mtc0输入
O SelB_D1 2 B_transfer的D1输入转发信号
O SelB_D2 2 B_transfer的D2输入转发信号
O SelALU_A 2 ALU输入A转发信号
O SelALU_B 2 ALU输入B转发信号
O SelDM 1 DM写入WD转发信号
O stall 1 冲突信号

(二).控制和冒险简述

  • 对于控制冒险,本实验要求大家实现比较过程前移至 D 级,并采用延迟槽

  • 对于数据冒险,两大策略及其应用:

    1
    假设当前我需要的数据,其实已经计算出来,只是还没有进入寄存器堆,那么我们可以用**转发**( Forwarding )来解决,即不引用寄存器堆的值,而是直接从后面的流水级的供给者把计算结果发送到前面流水级的需求者来引用。如果我们需要的数据还没有算出来。则我们就只能**暂停**( Stall ),让流水线停止工作,等到我们需要的数据计算完毕,再开始下面的工作。

(三).冒险处理

冒险处理我们均通过“A_T”法实现——

转发(forward)

当前面的指令要写寄存器但还未写入,而后面的指令需要用到没有被写入的值时,这时候会产生数据冒险,我们首先考虑进行转发。我们假设所有的数据冒险均可通过转发解决。也就是说,当某一指令前进到必须使用某一寄存器的值的流水阶段时,这个寄存器的值一定已经产生,并存储于后续某个流水线寄存器中

在这一阶段,我们不管需要的值有没由计算出,都要进行转发,即暴力转发。为实现这一机制,我们要清楚哪些模块需要转发后的数据(需求者)和保存着写入值的流水寄存器(供应者

  • 供应者及其产生的数据
流水级 产生数据 MUX名&选择信号名 MUX输出名
E E_imm32,
E_PC8
直接流水线传递 直接流水线传递
M M_ALUout,
M_PC8
直接流水线传递 直接流水线传递
W w_res,
w_RD,
w_imm32,
W_PC8
w_WhichtoReg WD

注:当M级指令为读hi和lo的指令时, M_AO中的结果是从上一周期在乘除槽中读取的hi或lo的值;如果是其他指令,M_AO是上一周期ALU的计算结果。

  • 需求者及其产生的数据
接收端口 选择数据 HMUX名&选择信号名 MUX输出名
B_transfer_D1 D_V1,
M_out,
E_out
SelB_D1 d_b_transfer1
B_transfer_D2 d_RD2,
m_res,
e_res
SelB_D2 d_b_transfer2
ALU_A e_RD1,
WD,
m_res
SelALU_A e_A
ALU_B e_RD2,
WD,
m_res
SelALU_B e_B
DM_WD m_RD2,
WD
SelDM M_WD_f
NPC_ra D_V1_f ,
E_PC8 ,
M_PC8
SelJr ra

从上表可以看出,W级中的数据没有转发到D级,原因是我们在GRF内实现了内部转发机制,将GRF输入端的数据(还未写入)及时反映到RD1或这RD2,判断条件为A3 == A2或者A3 == A1

此时为了生成HMUX的选择信号,我们需要向HCU(冒险控制器)输入”A”数据,然后进行选择信号的计算,执行转发的条件为——

  • 前位点的读取寄存器地址和某转发输入来源的写入寄存器地址相等且不为 0

  • 写使能信号有效

    转发的构造

暂停(stall)

接下来,我们来处理通过转发不能处理的数据冒险。在这种情况下,新的数据还未来得及产生。我们只能暂停流水线,等待新的数据产生。为了方便处理,我们仅仅为D级的指令进行暂停处理。

我们把Tuse和Tnew作为暂停的判断依据——

  • Tuse:指令进入 D 级后,其后的某个功能部件经过多少时钟周期就必须要使用寄存器值。对于有两个操作数的指令,其每个操作数的 Tuse 值可能不等(如 store 型指令 rs、rt 的 Tuse 分别为 1 和 2 )。
  • Tnew:位于 E 级及其后各级的指令,再经过多少周期就能够产生要写入寄存器的结果。在我们目前的 CPU 中,W 级的指令Tnew 恒为 0;对于同一条指令,Tnew@M = max(Tnew@E - 1, 0)、

在这一阶段,我们找到D级生成的Tuse_rs和Tuse_rt和在E,M,W级寄存器中流水的Tnew_D,Tnew_M,Tnew_W,如下表所示

  • Tuse表和计算表达式
指令类型 Tuse_rs Tuse_rt
calc_R 1 1
calc_I 1 X
shift X 1
shiftv 1 1
load 1 X
store 1 2
md 1 1
mt 1 X
mf X X
branch 0 0
j / jr X X
jal / jalr 0 X
lui X X
  • Tnew表和计算表达式
指令类型 Tnew_D Tnew_E Tnew_M Tnew_W
calc_R 2 1 0 0
calc_I 2 1 0 0
shift 2 1 0 0
shiftv 2 1 0 0
load 3 2 1 0
store X X X X
md X X X X
mt X X X X
mf 2 1 0 0
branch X X X X
jal / jalr 0 0 0 0
j / jr X X X X
lui 1 0 0 0

然后我们Tnew和Tuse传入HCU(冒险控制器中),然后进行stall信号的计算。如果满足以下条件则stall有效——

  • Tnew > Tuse

  • 前位点的读取寄存器地址和某转发输入来源的写入寄存器地址相等且不为 0

  • 写使能信号有效

  • 当E级延迟槽在进行运算(start | busy)时,D级为md、mt、mf指令

  • 阻塞的构造(D级)

Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
assign stall_rs = (D_A1!= 0)&&((D_Tuse_rs<E_Tnew)&&(D_A1 == E_A3)&&E_Wegrf||
(D_Tuse_rs<M_Tnew)&&(D_A1 == M_A3)&&M_Wegrf||
(D_Tuse_rs<W_Tnew)&&(D_A1 == W_A3)&&W_Wegrf);
//the pre is the condition
assign stall_rt = (D_A2!= 0)&&((D_Tuse_rt<E_Tnew)&&(D_A2 == E_A3)&&E_Wegrf||
(D_Tuse_rt<M_Tnew)&&(D_A2 == M_A3)&&M_Wegrf||
(D_Tuse_rt<W_Tnew)&&(D_A2 == W_A3)&&W_Wegrf);

assign stall_md=(start||busy)&&(MDU_en);

assign stall_eret=D_eret&((E_mtc0&(E_A3==5'd14))||(M_mtc0&&(M_A3==5'd14)));
assign stall = (stall_rs||stall_rt||stall_md||stall_eret)&&(~Is_nop);

真值表

端口 addu subu ori lw sw lui beq
op 000000 000000 001101 100011 101011 001111 000100
func 100001 100011
AluOp 0000001 0000010 0001000 0000000 0000000 0000000 0000000
WeGrf 1 1 1 1 0 1 0
WeDm 0 0 0 0 1 0 0
branch 0001 0001 0001 0001 0001 0001 0010
AluSrc1 0001 0001 0001 0001 0001 0001 0001
AluSrc2 0001 0001 0010 0010 0010 0001 0001
WhichtoReg 0001 0001 0001 0010 0001 0100 0001
RegDst 0001 0001 0010 0010 0010 0010 1010
SignExt 0 0 0 1 1 0 1
端口 andi jal j jr sll add sub
op 001100 000011 000010 000000 000000 000000 000000
func 001000 000000 100000 100010
AluOp 0000100 0000000 0000000 0000000 0010000 0000000 0000001
WeGrf 1 1 0 0 1 1 1
WeDm 0 0 0 0 0 0 0
branch 0001 0100 0100 1000 0001 0001 0001
AluSrc1 0001 0001 0001 0001 0010 0001 0001
AluSrc2 0010 0001 0001 0001 0100 0001 0001
WhichtoReg 0001 1000 0001 0001 0001 0001 0001
RegDst 0010 0100 0001 0001 0001 0001 0001
SignExt 1 0 0 0 0 0 0

桥和计数器

1.Bridge

  • 端口定义
方向 信号名 位宽 描述 输入来源
I Address_in 32 写入/读取的外设单元的地址 前一级相同信号
I WD_in 32 写入外设单元的数据 前一级相同信号
I byteen 4 写入外设单元的使能 前一级相同信号
I DM_RD 32 DM读取值的输入 前一级相同信号
I T0_RD 32 Timer1读取值的输入 前一级相同信号
I T1_RD 32 Timer0读取值的输入 前一级相同信号
O DM_WE 4 DM写入使能
O T0_WE 1 Timer1写入使能
O T1_WE 1 Timer2写入使能
O Address_out 32 写入/读取的外设单元的地址
O WD_out 32 写入外设单元的数据
O RD_out 32 外设单元的读取值输出
Folding coding
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
`timescale 1ns / 1ps
`include"macro.v"
module Bridge(input [31:0] Address_in,
input [31:0] WD_in,
input [3:0] byteen,
input [31:0] DM_RD,
input [31:0] T0_RD,
input [31:0] T1_RD,

output [3:0] DM_WE,
output T0_WE,
output T1_WE,
output [31:0] Address_out,
output [31:0] WD_out,
output [31:0] RD_out
);
assign DM_WE = (Address_in >= `Data_Begin&&Address_in <= `Data_End)?byteen:4'd0;
assign T0_WE = (Address_in >= `T0_Begin&&Address_in <= `T0_End)?&byteen:1'd0;
assign T1_WE = (Address_in >= `T1_Begin&&Address_in <= `T1_End)?&byteen:1'd0;
assign Address_out = Address_in;
assign WD_out = WD_in;
assign RD_out = (Address_in >= `Data_Begin&&Address_in <= `Data_End)?DM_RD:
(Address_in >= `T0_Begin&&Address_in <= `T0_End)?T0_RD:
(Address_in >= `T1_Begin&&Address_in <= `T1_End)?T1_RD:
32'd0;
endmodule

2.TC

  • 端口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 前一级相同信号
I reset 1 同步复位信号 前一级相同信号
I Addr 30 Timer写入地址 前一级相同信号
I WE 1 Timer写入使能 前一级相同信号
I Din 32 Timer写入数据 前一级相同信号
O D_out 32 Timer读取数据
O IRQ 1 中断请求
Folding coding
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
`timescale 1ns / 1ps
`include"macro.v"
module TC(input clk,
input reset,
input [31:2] Addr,
input WE,
input [31:0] Din,

output [31:0] Dout,
output IRQ
);

reg [1:0] state;
reg [31:0] mem [2:0];

reg _IRQ;
assign IRQ = `ctrl[3] & _IRQ;

assign Dout = mem[Addr[3:2]];

wire [31:0] load = Addr[3:2] == 0 ? {28'h0, Din[3:0]} : Din;

integer i;
always @(posedge clk) begin
if(reset) begin
state <= 0;
for(i = 0; i < 3; i = i+1) mem[i] <= 0;
_IRQ <= 0;
end
else if(WE) begin
// $display("%d@: *%h <= %h", $time, {Addr, 2'b00}, load);
mem[Addr[3:2]] <= load;
end
else begin
case(state)
`IDLE : if(`ctrl[0]) begin
state <= `LOAD;
_IRQ <= 1'b0;
end
`LOAD : begin
`count <= `preset;
state <= `CNT;
end
`CNT :
if(`ctrl[0]) begin
if(`count > 1) `count <= `count-1;
else begin
`count <= 0;
state <= `INT;
_IRQ <= 1'b1;
end
end
else state <= `IDLE;
default : begin
if(`ctrl[2:1] == 2'b00) `ctrl[0] <= 1'b0;
else _IRQ <= 1'b0;
state <= `IDLE;
end
endcase
end
end

endmodule

重要机制实现方法

CP0响应机制

CP0是检测异常和中断的重要部件,异常和中断通过以下接口传入——

  • 中断信息通过 HWInt[7:2] 接口进入,其中HWInt[2]连接Timer0的终端请求,HWInt[3]连接Timer1的终端请求,HWInt[4]连接外部的中断请求。
  • 异常信息通过ExcCode_inBD_in两个接口传入,ExcCode_in传入的是异常代码(ADEL,ADES,RI,OV),BD_in传入的是延迟槽指令标志(有效则表示当前指令为延迟槽指令)。

检测到中断或者异常时,CP0会进行判断并响应,决定是否将req(CP0向CPU发出的中断请求)置1。判断逻辑如下——

1
2
3
wire inter_req  = (|(HWInt & IM)) & IE & (!EXL);//中断有效判断
wire exc_req = (ExcCode_in != 5'd0) & (!EXL);//异常有效判断
assign req = inter_req | exc_req;//

当req有效,CP0还需要完成以下任务——

  • EXL置1

  • 将M级PC存入EPC(延迟槽指令中存入EPC-4)

  • 如果当前响应中断,ExcCode寄存器写入0;若响应异常,ExcCode寄存器写入外部传入的异常代码ExcCode_in

  • BD寄存器写入外部传入的BD_in信号

  • 此外,无论是否发出中断请求,在每一周期均需要将HWInt[6:2]写入Cause寄存器中的的IP域

    系统桥设计

    系统桥其实是充当一个“交换机”的角色,将CPU传来的地址写入相应的外设(DM、Timer0、Timer1),只需要组合逻辑便可实现。

    异常识别

    P7中我们考虑的异常情况有以下几种——

    • F级异常:
      • PC地址没有字对齐(AdEL)
      • PC地址超过0x3000 ~ 0x6ffc(AdEL)
    • D级异常:
      • 未知的指令码(RI)
    • E级异常:
      • addi、add、sub计算溢出(Ov)
      • load类指令计算地址时加法溢出(AdEL)
      • store类指令计算地址时加法溢出(AdES)
    • M级异常:
      • lw取数地址未 4 字节对齐(AdEL)
      • lh、lhu取数地址未与 2 字节对齐(AdEL)
      • lh、lhu、lb、lbu取 Timer 寄存器的值(AdEL)
      • load型指令取数地址超出 DM、Timer0、Timer1 的范围(AdEL)
      • sw存数地址未 4 字节对齐(AdES)
      • sh存数地址未 2 字节对齐(AdES)
      • sh、sb存 Timer 寄存器的值(AdES)
      • sw向计时器的 Count 寄存器存值(AdES)
      • store型指令存数地址超出 DM、Timer0、Timer1 的范围(AdES)

    针对以上列出的异常情况,我们在每一个流水级对异常进行检测。由于教程提出了以下要求——

    • 发生取指异常后视为 nop 直至提交到 CP0。
    • 发生 RI 异常后视为 nop 直至提交到 CP0。
    • loadstore 类算址溢出按照 AdELAdES 处理。

    因此不会出现一个指令在多级出现异常的情况。如果某个流水级出现了新的异常,我们将这个异常流水到下一级即可,而不是流水上一级传来的异常;如果这个流水级没有出现新的异常,则将上一级传来的异常继续流水给下一级即可。

二、 测试方案

(1) 测试代码:

注:本测试方案覆盖所有异常,并未测试中断

Folding coding
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
.text
mtc0 $0, $12
ori $at, $0, 0xfffc
#====OV=====
lui $t0, 0x7fff
lui $t1, 0xffff
add $t2, $t0, $t1
sub $t2, $t0, $t1
sub $t2, $t1, $t0
lui $t1, 0x7fff
add $t2, $t0, $t1
ori $t1, $t1, 0xffff
addi $t2, $t1, 0xfffffff0
addi $t1, $t1, 0x0010

#=====SYSCALL=====
syscall

#=====ADEL=====
lui $t1, 0x7fff
jal label1
add $ra, $ra, $t1
label1:
jr $ra
nop
jal label2
addi $ra, $ra, 1
label2:
jr $ra
nop
ori $t0, $0, 0x7f00
ori $t2, $0, 0x7f20
sw $t0, 0($0)
lw $t0, 0($0)
lw $t0, 1($0)
lw $t0, 2($0)
lh $t0, 3($0)
lh $t0, 0($t0)
lh $t0, 2($t0)
lb $t0, 0($t0)
lb $t0, 3($t0)
loop_timer1:
lw $t1, 0($t0)
addi $t0, $t0, 4
bne $t0, $t2, loop_timer1
nop
ori $t0, $0, 0x3000
lw $t0, 0($t0)
lui $t0, 0x7fff
ori $t0, $t0, 0xffff
lw $t0, 1($t0)
lw $t0, -4($0)

#=====ADES=====
sw $0, 1($0)
sw $0, 2($0)
sh $0, 3($0)
sw $0, 4($0)
sh $0, 6($0)
sb $0, 7($0)
ori $t0, $0, 0x7f00
sh $0, 0($t0)
sh $0, 2($t0)
sb $0, 0($t0)
sb $0, 3($t0)
ori $t1, $0, 0x7f30
loop_timer2:
sw $0, 0($t0)
addi $t0, $t0, 4
bne $t0, $t1, loop_timer2
nop
ori $t0, $0, 0x3000
sw $0, 0($t0)
lui $t0, 0x7fff
ori $t0, $t0, 0xffff
sw $0, 1($t0)
sw $0, -1($0)

#=====ALTOGETHER=====
lui $t0, 0x7fff
ori $t1, $t0, 0xffff
sw $0, 0($t0)
addi $t1, $t1, 1
syscall

sw $0, 0($t0)
addi $t1, $t1, 1
nop

sw $0, 0($t0)
addi $t1, $t1, 0
syscall

sw $0, 0($0)
addi $t1, $t1, 1
syscall

lui $t0, 0x8000
addi $t1, $t1, 1
beq $t0, $t1, end
nop

end:
beq $0, $0, end
nop

.ktext 0x4180
_main_handler:
mfc0 $k0, $13
mfc0 $k0, $14
and $k0, $k0, $at
addi $k0, $k0, 4
mtc0 $k0, $14
sb $0, 0x7f20($0)
eret

(2) 该CPU运行结果

Folding coding
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
42@00003004: $ 1 <= 0000fffc
46@00003008: $ 8 <= 7fff0000
50@0000300c: $ 9 <= ffff0000
54@00003010: $10 <= 7ffe0000
74@00004180: $26 <= 00000030
78@00004184: $26 <= 00003014
86@00004188: $26 <= 00003014
90@0000418c: $26 <= 00003018
110@00003018: $10 <= 80000000
114@0000301c: $ 9 <= 7fff0000
134@00004180: $26 <= 00000030
138@00004184: $26 <= 00003020
146@00004188: $26 <= 00003020
150@0000418c: $26 <= 00003024
170@00003024: $ 9 <= 7fffffff
174@00003028: $10 <= 7fffffef
194@00004180: $26 <= 00000030
198@00004184: $26 <= 0000302c
206@00004188: $26 <= 0000302c
210@0000418c: $26 <= 00003030
246@00004180: $26 <= 00000020
250@00004184: $26 <= 00003030
258@00004188: $26 <= 00003030
262@0000418c: $26 <= 00003034
282@00003034: $ 9 <= 7fff0000
286@00003038: $31 <= 00003040
290@0000303c: $31 <= 7fff3040
322@00004180: $26 <= 00000010
326@00004184: $26 <= 7fff3040
334@00004188: $26 <= 00003040
338@0000418c: $26 <= 00003044
362@00003048: $31 <= 00003050
366@0000304c: $31 <= 00003051
398@00004180: $26 <= 00000010
402@00004184: $26 <= 00003051
410@00004188: $26 <= 00003050
414@0000418c: $26 <= 00003054
438@00003058: $ 8 <= 00007f00
442@00003060: *00000000 <= 00007f00
442@0000305c: $10 <= 00007f20
450@00003064: $ 8 <= 00007f00
470@00004180: $26 <= 00000010
474@00004184: $26 <= 00003068
482@00004188: $26 <= 00003068
486@0000418c: $26 <= 0000306c
522@00004180: $26 <= 00000010
526@00004184: $26 <= 0000306c
534@00004188: $26 <= 0000306c
538@0000418c: $26 <= 00003070
574@00004180: $26 <= 00000010
578@00004184: $26 <= 00003070
586@00004188: $26 <= 00003070
590@0000418c: $26 <= 00003074
626@00004180: $26 <= 00000010
630@00004184: $26 <= 00003074
638@00004188: $26 <= 00003074
642@0000418c: $26 <= 00003078
678@00004180: $26 <= 00000010
682@00004184: $26 <= 00003078
690@00004188: $26 <= 00003078
694@0000418c: $26 <= 0000307c
730@00004180: $26 <= 00000010
734@00004184: $26 <= 0000307c
742@00004188: $26 <= 0000307c
746@0000418c: $26 <= 00003080
782@00004180: $26 <= 00000010
786@00004184: $26 <= 00003080
794@00004188: $26 <= 00003080
798@0000418c: $26 <= 00003084
818@00003084: $ 9 <= 00000000
822@00003088: $ 8 <= 00007f04
838@00003084: $ 9 <= 00000000
842@00003088: $ 8 <= 00007f08
858@00003084: $ 9 <= 00000000
862@00003088: $ 8 <= 00007f0c
894@00004180: $26 <= 00000010
898@00004184: $26 <= 00003084
906@00004188: $26 <= 00003084
910@0000418c: $26 <= 00003088
930@00003088: $ 8 <= 00007f10
946@00003084: $ 9 <= 00000000
950@00003088: $ 8 <= 00007f14
966@00003084: $ 9 <= 00000000
970@00003088: $ 8 <= 00007f18
986@00003084: $ 9 <= 00000000
990@00003088: $ 8 <= 00007f1c
1022@00004180: $26 <= 00000010
1026@00004184: $26 <= 00003084
1034@00004188: $26 <= 00003084
1038@0000418c: $26 <= 00003088
1058@00003088: $ 8 <= 00007f20
1074@00003094: $ 8 <= 00003000
1094@00004180: $26 <= 00000010
1098@00004184: $26 <= 00003098
1106@00004188: $26 <= 00003098
1110@0000418c: $26 <= 0000309c
1130@0000309c: $ 8 <= 7fff0000
1134@000030a0: $ 8 <= 7fffffff
1154@00004180: $26 <= 00000010
1158@00004184: $26 <= 000030a4
1166@00004188: $26 <= 000030a4
1170@0000418c: $26 <= 000030a8
1206@00004180: $26 <= 00000010
1210@00004184: $26 <= 000030a8
1218@00004188: $26 <= 000030a8
1222@0000418c: $26 <= 000030ac
1258@00004180: $26 <= 00000014
1262@00004184: $26 <= 000030ac
1270@00004188: $26 <= 000030ac
1274@0000418c: $26 <= 000030b0
1310@00004180: $26 <= 00000014
1314@00004184: $26 <= 000030b0
1322@00004188: $26 <= 000030b0
1326@0000418c: $26 <= 000030b4
1362@00004180: $26 <= 00000014
1366@00004184: $26 <= 000030b4
1374@00004188: $26 <= 000030b4
1378@0000418c: $26 <= 000030b8
1394@000030b8: *00000004 <= 00000000
1398@000030bc: *00000004 <= 00000000
1402@000030c0: *00000004 <= 00000000
1410@000030c4: $ 8 <= 00007f00
1430@00004180: $26 <= 00000014
1434@00004184: $26 <= 000030c8
1442@00004188: $26 <= 000030c8
1446@0000418c: $26 <= 000030cc
1482@00004180: $26 <= 00000014
1486@00004184: $26 <= 000030cc
1494@00004188: $26 <= 000030cc
1498@0000418c: $26 <= 000030d0
1534@00004180: $26 <= 00000014
1538@00004184: $26 <= 000030d0
1546@00004188: $26 <= 000030d0
1550@0000418c: $26 <= 000030d4
1586@00004180: $26 <= 00000014
1590@00004184: $26 <= 000030d4
1598@00004188: $26 <= 000030d4
1602@0000418c: $26 <= 000030d8
1622@000030d8: $ 9 <= 00007f30
1630@000030e0: $ 8 <= 00007f04
1650@000030e0: $ 8 <= 00007f08
1682@00004180: $26 <= 00000014
1686@00004184: $26 <= 000030dc
1694@00004188: $26 <= 000030dc
1698@0000418c: $26 <= 000030e0
1718@000030e0: $ 8 <= 00007f0c
1750@00004180: $26 <= 00000014
1754@00004184: $26 <= 000030dc
1762@00004188: $26 <= 000030dc
1766@0000418c: $26 <= 000030e0
1786@000030e0: $ 8 <= 00007f10
1806@000030e0: $ 8 <= 00007f14
1826@000030e0: $ 8 <= 00007f18
1858@00004180: $26 <= 00000014
1862@00004184: $26 <= 000030dc
1870@00004188: $26 <= 000030dc
1874@0000418c: $26 <= 000030e0
1894@000030e0: $ 8 <= 00007f1c
1926@00004180: $26 <= 00000014
1930@00004184: $26 <= 000030dc
1938@00004188: $26 <= 000030dc
1942@0000418c: $26 <= 000030e0
1962@000030e0: $ 8 <= 00007f20
1982@000030e0: $ 8 <= 00007f24
2014@00004180: $26 <= 00000014
2018@00004184: $26 <= 000030dc
2026@00004188: $26 <= 000030dc
2030@0000418c: $26 <= 000030e0
2050@000030e0: $ 8 <= 00007f28
2082@00004180: $26 <= 00000014
2086@00004184: $26 <= 000030dc
2094@00004188: $26 <= 000030dc
2098@0000418c: $26 <= 000030e0
2118@000030e0: $ 8 <= 00007f2c
2150@00004180: $26 <= 00000014
2154@00004184: $26 <= 000030dc
2162@00004188: $26 <= 000030dc
2166@0000418c: $26 <= 000030e0
2186@000030e0: $ 8 <= 00007f30
2202@000030ec: $ 8 <= 00003000
2222@00004180: $26 <= 00000014
2226@00004184: $26 <= 000030f0
2234@00004188: $26 <= 000030f0
2238@0000418c: $26 <= 000030f4
2258@000030f4: $ 8 <= 7fff0000
2262@000030f8: $ 8 <= 7fffffff
2282@00004180: $26 <= 00000014
2286@00004184: $26 <= 000030fc
2294@00004188: $26 <= 000030fc
2298@0000418c: $26 <= 00003100
2334@00004180: $26 <= 00000014
2338@00004184: $26 <= 00003100
2346@00004188: $26 <= 00003100
2350@0000418c: $26 <= 00003104
2370@00003104: $ 8 <= 7fff0000
2374@00003108: $ 9 <= 7fffffff
2394@00004180: $26 <= 00000014
2398@00004184: $26 <= 0000310c
2406@00004188: $26 <= 0000310c
2410@0000418c: $26 <= 00003110
2446@00004180: $26 <= 00000030
2450@00004184: $26 <= 00003110
2458@00004188: $26 <= 00003110
2462@0000418c: $26 <= 00003114
2498@00004180: $26 <= 00000020
2502@00004184: $26 <= 00003114
2510@00004188: $26 <= 00003114
2514@0000418c: $26 <= 00003118
2550@00004180: $26 <= 00000014
2554@00004184: $26 <= 00003118
2562@00004188: $26 <= 00003118
2566@0000418c: $26 <= 0000311c
2602@00004180: $26 <= 00000030
2606@00004184: $26 <= 0000311c
2614@00004188: $26 <= 0000311c
2618@0000418c: $26 <= 00003120
2658@00004180: $26 <= 00000014
2662@00004184: $26 <= 00003124
2670@00004188: $26 <= 00003124
2674@0000418c: $26 <= 00003128
2694@00003128: $ 9 <= 7fffffff
2714@00004180: $26 <= 00000020
2718@00004184: $26 <= 0000312c
2726@00004188: $26 <= 0000312c
2730@0000418c: $26 <= 00003130
2746@00003130: \*00000000 <= 00000000
2770@00004180: $26 <= 00000030
2774@00004184: $26 <= 00003134
2782@00004188: $26 <= 00003134
2786@0000418c: $26 <= 00003138
2822@00004180: $26 <= 00000020
2826@00004184: $26 <= 00003138
2834@00004188: $26 <= 00003138
2838@0000418c: $26 <= 0000313c
2858@0000313c: $ 8 <= 80000000
2878@00004180: $26 <= 00000030
2882@00004184: $26 <= 00003140
2890@00004188: $26 <= 00003140
2894@0000418c: $26 <= 00003144

三、 思考题

(一)请查阅相关资料,说明鼠标和键盘的输入信号是如何被 CPU 知晓的?
	鼠标和键盘等外设并不是直接与CPU相连的,中间需要通过软件来连接,这个软件也就是我们熟知的驱动。驱动和硬件之间通过操作系统进行处理。
(二)请思考为什么我们的 CPU 处理中断异常必须是已经指定好的地址?如果你的 CPU 支持用户自定义入口地址,即处理中断异常的程序由用户提供,其还能提供我们所希望的功能吗?如果可以,请说明这样可能会出现什么问题?否则举例说明。(假设用户提供的中断处理程序合法)

​ CPU处理中断异常使用指定好的地址,有利于操作系统对中断处理程序的设计,更有利于异常中断的处理。如果CPU支持用户自定义入口地址,只要定义与其他外设设备的DM空间不冲突,理论上也是可以的,但如果发生地址冲突,则可能因为地址内容覆盖而造成无法挽回的损失。

(三)为何与外设通信需要 Bridge?

​ 因为实际生活中I/O设备相对于CPU是外置的,而系统桥正是模拟了各种I/O接口,通过Bridge来实现CPU和各种外设之间的数据交互。

(四) 请阅读官方提供的定时器源代码,阐述两种中断模式的异同,并针对每一种模式绘制状态移图

在模式0下:当计数器倒计数为 0 后,计数器停止计数,此时控制寄存器中的使能 Enable 自动变为 0。当使能 Enable 被设置为 1 后,初值寄存器值再次被加载至计数器, 计数器重新启动倒计数。 模式 0 通常用于产生定时中断。例如,为操作系统的时间片调度机制提供定 时。模式 0 下的中断信号将持续有效,直至控制寄存器中的中断屏蔽位被设置为 0。

在模式1下:当计数器倒计数为 0 后,初值寄存器值被自动加载至计数器,计数器继续倒 计数。 模式 1 通常用于产生周期性脉冲。例如,可以用模式 1 产生步进电机所需的 步进控制信号。不同于模式 0,模式 1 下计数器每次计数循环中只产生一周期的中断信号。

模式0计时结束后,一直保持中断,直到en或IM被修改,模式1计时结束后,中断一个周期,再重新计数。可以理解为中断保持的逻辑不同。

img

(五)倘若中断信号流入的时候,在检测宏观 PC 的一级如果是一条空泡(你的 CPU 该级所有信息均为空)指令,此时会发生什么问题?在此例基础上请思考:在 P7 中,清空流水线产生的空泡指令应该保留原指令的哪些信息?

​ 在阻塞时我们会往流水线中插入 nop,这个 nop 的 pcbd 信号都是 0。此时宏观 PC 会显示错误的值。并且如果此时发生了中断,就会导致 EPC 存入错误的值。

​ 所以在P7中,清空流水线产生的空泡指令应该保留原址令的 PCbd

(六)为什么 jalr 指令为什么不能写成 jalr $31, $31?

​ 如果写成jalr $31 $31的话,相当于PC<=GPR[$31],GPR[$31]<=PC+4,按照流水线的执行顺序会先将PC+4进行Grf中内部转发,所以相当于进行了一次PC<=PC+4,而没有起到跳转到GPR[$31]的效果。

选做题

(一)请详细描述你的测试方案及测试数据构造策略。

​ 由于p7的实现方式复杂性,所以通过自动化生成数据的效果并不好,所以我只能通过手动构造异常数据和各种支持不同位置中断的tb来实现,并使用数据对拍。

​ 数据构造策略就是分别实现不同的数据之间组合模拟各种常见的异常,以及在中断发生在跳转指令、延迟槽、阻塞指令等不同情况,从而实现测试方案。

​ 之后后续借助于我自己的自动化测试工具和课程组的Mars,我实现了自行测试。

四.自动化测试

命令行自动测试

具体源码可见:Charles_BUAA_CO_2022/p7/statistic at main · Charles2530/Charles_BUAA_CO_2022 (github.com)

Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os
i = 1
while i <= 5000:
print('num = ' + str(i))
os.chdir('D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic')
os.system('python test.py')
os.system(
'java -jar Mars.jar nc db lg ex mc LargeText 100000 test.asm >mips_out.txt')
os.system(
'java -jar mars.jar db mc CompactDataAtZero a dump .text HexText ../mips/code.txt test.asm > mips_log.txt')
os.system(
'java -jar Mars.jar a db mc CompactDataAtZero dump 0x00004180-0x00004ffc HexText ../mips/handler.txt test.asm > mips_log.txt')
os.chdir('D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\mips')
os.system(
'D:\\SoftWare\\ISE\\14.7\\ISE_DS\\ISE\\bin\\nt64\\fuse -nodebug -prj mips.prj \
-o mips.exe mips_txt 1>../statistic/isim_log.txt 2>../statistic/isim_warning_log.txt')
os.system(
'mips.exe -nolog -tclbatch mips.tcl > isim_out.txt')
os.chdir('D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic')
os.system('python diff.py')
i = i+1
Folding coding
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
import random


# Calc_r = ['add', 'sub', 'and', 'or', 'slt', 'sltu']
Calc_r = ['add', 'and', 'or', 'slt', 'sltu']
Calc_i = ['ori', 'addi', 'ori']
Load = ['lb']
# Load = ['lw', 'lb', 'lh']
# Store = ['sw', 'sb', 'sh']
Store = ['sb']
B_type = ['beq', 'bne']
J_type = ['jal']
Shift = []
Shift_v = []
md = ['mult', 'multu']
# md = ['mult', 'multu', 'div', 'divu']
mf_mt = ['mfhi', 'mflo', 'mthi', 'mtlo']
# exception = ['mfc0', 'mtc0', 'eret']
exception = ['mfc0', 'mtc0']
# exception = []

filename = "D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\test.asm"
# 输出文件位置
# label = [0, 1]
label = [0]
# 输出label的编号范围,事先存入0防止在第一次输出标签前出现跳转指令
cnt = 0
# 可执行代码的行数
flag = 2
# 当前所标出过的编号号码
jal = []
# 使用过的jal对应标签编号
R_num = len(Calc_r)
I_num = len(Calc_i)
L_num = len(Load)
S_num = len(Store)
B_num = len(B_type)
J_num = len(J_type)
MD_num = len(md)
MF_num = len(mf_mt)
EX_num = len(exception)
# 各类指令出现频率设置
f_all = 1
f_R = 4*random.randint(1, 4)*f_all
f_I = 4*random.randint(1, 4)*f_all
f_L = 4*random.randint(1, 4)*f_all
f_S = 4*random.randint(1, 4)*f_all
f_B = 0*random.randint(1, 2)*f_all
f_J = 0*random.randint(1, 2)*f_all
f_md = 2*random.randint(1, 4)*f_all
f_mf = 2*random.randint(1, 4)*f_all
f_ex = 20*random.randint(1, 4)*f_all
num = f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num + \
f_B*B_num+f_J*J_num+f_md*MD_num+f_mf*MF_num+EX_num*f_ex+1
begin = 27
end = 31


class get_Ori:
def __init__(self):
# 对应指令生成随机数
self.rs = random.randint(begin, end)
self.rt = random.randint(begin, end)
self.imm16 = random.randint(0, 100)
self.main()

def main(self):
if self.rs == 28 or self.rs == 29 or self.rs == 30:
self.rs = self.rs-3
if self.rt == 28 or self.rt == 29 or self.rt == 30:
self.rt = self.rt-3
self.code = 'ori' + ' ' + '$' + \
str(self.rt) + ',' + '$' + \
str(self.rs) + ',' + str(self.imm16) + '\n'


class get_Code:
def __init__(self):
# 对应指令生成随机数
self.rs = random.randint(begin, end)
self.rt = random.randint(begin, end)
self.rd = random.randint(begin, end)
self.imm16 = random.randint(0, 100)
self.imm26 = random.randint(0, 100)
# self.mem = random.randint(0, 100)
self.ex = random.randint(6, 8)*2
self.mem = random.randint(0, 3)
# self.mem = random.randint(0, 3)
# 存储指令类型
self.list = []
# get函数
self.get_R()
self.get_I()
self.get_L()
self.get_S()
self.get_B()
self.get_J()
self.get_MD()
self.get_MF()
self.get_Label()
self.get_EX()
self.main()

def get_R(self):
random1 = random.randint(0, R_num - 1)
type1 = Calc_r[random1]
self.list.append(type1)

def get_I(self):
random2 = random.randint(0, I_num - 1)
type2 = Calc_i[random2]
self.list.append(type2)

def get_L(self):
random3 = random.randint(0, L_num - 1)
type3 = Load[random3]
self.list.append(type3)

def get_S(self):
random4 = random.randint(0, S_num - 1)
type4 = Store[random4]
self.list.append(type4)

def get_B(self):
random5 = random.randint(0, B_num - 1)
type5 = B_type[random5]
self.list.append(type5)

def get_J(self):
random6 = random.randint(0, J_num - 1)
type6 = J_type[random6]
self.list.append(type6)

def get_MD(self):
random7 = random.randint(0, MD_num - 1)
type7 = md[random7]
self.list.append(type7)

def get_MF(self):
random8 = random.randint(0, MF_num - 1)
type8 = mf_mt[random8]
self.list.append(type8)

def get_EX(self):
random9 = random.randint(0, EX_num - 1)
type9 = exception[random9]
self.list.append(type9)

def get_Label(self):
random10 = random.randint(0, len(label)-1)
ran = label[random10]
return ran

def main(self):
sel = random.randint(0, num+100)
if self.rs == 28 or self.rs == 29 or self.rs == 30:
self.rs = self.rs-3
if self.rt == 28 or self.rt == 29 or self.rt == 30:
self.rt = self.rt-3
if self.rd == 28 or self.rd == 29 or self.rd == 30:
self.rd = self.rd-3
# 控制参数类型
if sel in range(0, f_R*R_num):
# 通过控制随机数的范围来决定输出各种指令的频率,并用各种指令的数目保证各指令出现概率基本相同
self.code = self.list[0] + ' ' + '$' + \
str(self.rd) + ' ' + ',' + '$' + \
str(self.rs) + ' ' + ',' + '$' + str(self.rt) + '\n'
elif sel in range(f_R*R_num, f_R*R_num+f_I*I_num):
self.code = self.list[1] + ' ' + '$' + \
str(self.rt) + ',' + '$' + \
str(self.rs) + ',' + str(self.imm16) + '\n'
elif sel in range(f_R*R_num+f_I*I_num, f_R*R_num+f_I*I_num+f_L*L_num):
if self.list[2] == 'lb' or self.list[2] == 'lbu':
self.code = self.list[2] + ' ' + '$' + \
str(self.rt) + ',' + str(self.mem) + \
'(' + '$0' + ')'+'\n'
elif self.list[2] == 'lh' or self.list[2] == 'lhu':
self.code = self.list[2] + ' ' + '$' + \
str(self.rt) + ',' + str(self.mem) + \
'(' + '$' + str((self.rt+1) % 17+3) + ')'+'\n'
else:
self.code = self.list[2] + ' ' + '$' + \
str(self.rt) + ',' + str(self.mem) + \
'(' + '$' + str((self.rt+1) % 17+3) + ')'+'\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num):
if self.list[3] == 'sb':
self.code = self.list[3] + ' ' + '$' + \
str(self.rt) + ',' + str(self.mem) + \
'(' + '$0' + ')'+'\n'
elif self.list[3] == 'sh':
self.code = self.list[3] + ' ' + '$' + \
str(self.rt) + ',' + str(self.mem) + \
'(' + '$' + str((self.rt+1) % 17+3) + ')'+'\n'
else:
self.code = self.list[3] + ' ' + '$' + \
str(self.rt) + ',' + str(self.mem) + \
'(' + '$' + str((self.rt+1) % 17+3) + ')'+'\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num):
self.code = self.list[4] + ' ' + '$' + \
str(self.rt) + ',' + '$' + \
str(self.rs) + ',' + 'label_' + \
str(self.get_Label()) + '\n' +\
'nop' + '\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num):
if self.list[5] == 'jal':
node = self.get_Label()
self.code = self.list[5] + ' ' + \
'label_' + str(node) + '\n' +\
'nop' + '\n'
jal.append(node)
elif self.list[5] == 'j':
self.code = self.list[5] + ' ' + \
'label_' + str(self.get_Label()) + '\n' +\
'nop' + '\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num+f_md*MD_num):
self.code = self.list[6] + ' ' + '$' + \
str(self.rt) + ',' + '$' + \
str(self.rs) + '\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num+f_md*MD_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num+f_md*MD_num+f_mf*MF_num):
self.code = self.list[7] + ' ' + '$' + \
str(self.rs) + '\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num+f_md*MD_num+f_mf*MF_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num+f_md*MD_num+f_mf*MF_num+EX_num*f_ex):
if self.list[8] == 'eret':
self.code = 'eret'+'\n'
else:
self.code = self.list[8] + ' ' + '$' + \
str(self.rs) + ','+'$14' + '\n'
else:
self.code = 'lui' + ' ' + \
'$' + str(self.rt) + ',' + str(self.imm16) + '\n'


with open(filename, 'w+') as f:
f.write('.text'+'\n')
f.write('mtc0 $0,$12'+'\n')
f.write('ori $at,$0,0xfffc'+'\n')
for cnt in range(2*begin, 2*end):
b = get_Ori()
f.write(b.code)
cnt = 0
f.write('syscall'+'\n')
for cnt in range(0, num):
a = get_Code()
# if cnt == 0:
# f.write('jal'+' label_0'+'\n'+'nop'+'\n')
f.write(a.code)
if random.randint(0, num) == 1 and label != []:
# 通过控制random范围来决定标签和jr出现的概率
f.write('label_' + str(flag) + ':' + '\n')
label.append(flag)
flag = flag+1
# f.write('jr $ra' + '\n')
# f.write('nop' + '\n')
# if cnt == 10:
# f.write('jr $ra' + '\n')
# f.write('nop' + '\n')
# if cnt == 5:
if cnt == num-20:
# f.write('jal'+' label_1' + '\n')
# f.write('nop' + '\n')
f.write('label_0' + ':' + '\n')
# f.write('addi $ra,$ra,1'+'\n')
# f.write('jr $ra' + '\n')
# f.write('nop' + '\n')
# f.write('label_1' + ':' + '\n')
f.write('.ktext 0x4180'+'\n' +
'_main_handler:'+'\n' +
' mfc0 $k0, $13'+'\n' +
' mfc0 $k0, $14'+'\n' +
' andi $k0, $k0, 0xfffc'+'\n' +
' addi $k0, $k0, 4'+'\n' +
' mtc0 $k0, $14'+'\n' +
' sb $0, 0x7f20($0)'+'\n' +
' eret'+'\n')
f.close()

Folding coding
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
# from filediff.diff import file_diff_compare
import os

tmp = open(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\mips_out.txt", "r")
out = open(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\mips_out_another.txt", "w")
std = open(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\mips\\isim_out.txt", "r")
isim = open(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\isim_out.txt", "w")
while True:
line = tmp.readline()
if not line:
break
line = line.replace(" ", "")
line = line.replace("\t", "")
out.write(line)
out.write("\n")
while True:
line = std.readline()
if not line:
break
if line[0].isspace():
line = line.replace(" ", "")
line = line.replace("\t", "")
isim.write(line)
elif line[0] == '@':
line = line.replace(" ", "")
line = line.replace("\t", "")
isim.write(line)
isim.write("\n")
out.close()
tmp.close()
std.close()
isim.close()
result = open(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\result.txt", "a+")
os.system("cmp.exe")
result.write(
"---------------------------------test------------------------------------------- "+"\n\n")
result.close()

Folding coding
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
92
93
94
95
96
97
98
99
#include <bits/stdc++.h>
using namespace std;
#define M 100007
#define N 1007
#define INF 0x3f3f3f3f
#define ll long long
#define db double

int main() {
char s1[400], s2[400];
char s3[400], s4[400];
int flag = 1;
FILE* fp1;
FILE* fp2;
FILE* fp3;
FILE* fp4;
FILE* fp5;
FILE* fp6;
fp1 = fopen(
"D:\\coding_file\\study\\Lesson\\co_"
"lesson\\lesson\\p7\\statistic\\mips_out_another.txt",
"r");
fp2 = fopen(
"D:\\coding_file\\study\\Lesson\\co_"
"lesson\\lesson\\p7\\statistic\\isim_out.txt",
"r");
fp3 = fopen(
"D:\\coding_file\\study\\Lesson\\co_"
"lesson\\lesson\\p7\\statistic\\result.txt",
"a+");
fp4 = fopen(
"D:\\coding_file\\study\\Lesson\\co_"
"lesson\\lesson\\p7\\statistic\\test.asm",
"r");
fp5 = fopen(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\p7_"
"error_code.asm",
"a+");
fp6 = fopen(
"D:\\coding_file\\study\\Lesson\\co_"
"lesson\\lesson\\p7\\statistic\\isim_out_another.txt",
"w");
int i = 0, j = 0;
int n = 0;
int line = 0;
int l1 = 0, l2 = 0;
while (fgets(s1, 400, fp1)) {
string s = s1;
// printf("%d", s.find("$0"));
if (s.find("$0") != -1) {
continue;
}
line++;
if (s1[0] == 'I') {
fgets(s1, 400, fp1);
fgets(s1, 400, fp1);
}
fgets(s2, 400, fp2);
if (s2[0] == 'I') {
fgets(s2, 400, fp2);
fgets(s2, 400, fp2);
}
l1++;
l2++;
for (i = 0; s1[i] != '\0'; i++) {
if (s1[i] == '@') {
break;
}
}
for (j = 0; s2[j] != '\0'; j++) {
if (s2[j] == '@') {
break;
}
}
fprintf(fp6, "%s", s2 + j);
if (strcmp(s1 + i, s2 + j)) {
fprintf(fp3, "Error in line %d\n", line);
fprintf(fp3, "get :%s", s2);
fprintf(fp3, "expect :%s\n\n", s1 + i);
flag = 0;
}
}
if (flag) {
fprintf(fp3, "Accept\n");
printf("Accept\n");
} else {
printf("Error\n");
fprintf(fp5, "#error begin with\n");
while (fgets(s3, 400, fp4)) {
fputs(s3, fp5);
}
fprintf(fp5, "#error end\n\n\n");
}
fclose(fp1);
fclose(fp2);
fclose(fp3);
return 0;
}

反编译工具
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# -*- coding: utf-8 -*-
import time


print("Function: machine code to mips code\n\
Instructions included: 56 (55 from mips-c instruction set and 'nop')\n\
Please copy the machine code into in.txt and put it into the same folder \
as this .py file.\nPress Enter to continue")


def bi_to_hex(a):
return hex(int(str(int(a, 2))))


machine = []
hex_to_bi = {"0": "0000", "1": "0001", "2": "0010", "3": "0011",
"4": "0100", "5": "0101", "6": "0110", "7": "0111",
"8": "1000", "9": "1001", "a": "1010", "b": "1011",
"c": "1100", "d": "1101", "e": "1110", "f": "1111"}
reg = {"0": "$0", "1": "$at", "2": "$v0", "3": "$v1",
"4": "$a0", "5": "$a1", "6": "$a2", "7": "$a3",
"8": "$t0", "9": "$t1", "10": "$t2", "11": "$t3",
"12": "$t4", "13": "$t5", "14": "$t6", "15": "$t7",
"16": "$s0", "17": "$s1", "18": "$s2", "19": "$s3",
"20": "$s4", "21": "$s5", "22": "$s6", "23": "$s7",
"24": "$t8", "25": "$t9", "26": "$k0", "27": "$k1",
"28": "$gp", "29": "$sp", "30": "$fp", "31": "$ra"}


mark = 0
while mark == 0:
input()
try:
file_in = open('in.txt', 'rt')
file_out = open('out.txt', 'wt')
mark = 1
except:
print("in.txt does not exist. Please try again.")

out = ["" for i in range(20000)]
labelcount = 1
mipscount = 0
label = {}
for line in file_in:
machine.append(line[:-1])

for hexcode in machine:

bicode = ""
for char in hexcode:
bicode += hex_to_bi[char]

op = bicode[0:6]
func = bicode[26:32]
rs = reg[str(int(bicode[6:11], 2))]
rt = reg[str(int(bicode[11:16], 2))]
rd = reg[str(int(bicode[16:21], 2))]
shamt = bicode[21:26]
imm = bi_to_hex(bicode[16:32])
mips = ""

if op == '000000':
itype = "R"
elif op == '000010' or op == '000011':
itype = "J"
else:
itype = "I"

if itype == "J":
if op == '000010':
mips = "j "
elif op == '000011':
mips = "jal "
linenum = int((int(str(int(bicode[6:32]+"00", 2)))-12288)/4)
if not linenum in label.keys():
labelname = "label_"+str(labelcount)
out[linenum] = labelname+": "+out[linenum]
if linenum > 0:
out[linenum-1] += "\n"
label[linenum] = labelcount
labelcount += 1
mips = mips+labelname
else:
labelname = "label_"+str(label[linenum])
mips = mips+labelname

elif itype == "R":
if bicode == '00000000000000000000000000000000':
mips = "nop"
elif func == '100000':
mips = "add "+rd+", "+rs+", "+rt
elif func == '100001':
mips = "addu "+rd+", "+rs+", "+rt
elif func == '100100':
mips = "and "+rd+", "+rs+", "+rt
elif func == '001101':
mips = "break"
elif func == '011010':
mips = "div "+rs+", "+rt
elif func == '011011':
mips = "divu "+rs+", "+rt
elif func == '001001':
mips = "jalr "+rd+", "+rs
elif func == '001000':
mips = "jr "+rs
elif func == '010000':
mips = "mfhi "+rd
elif func == '010010':
mips = "mflo "+rd
elif func == '010001':
mips = "mthi "+rd
elif func == '010011':
mips = "mtlo "+rd
elif func == '011000':
mips = "mult "+rs+", "+rt
elif func == '011001':
mips = "multu "+rs+", "+rt
elif func == '100111':
mips = "nor "+rd+", "+rs+", "+rt
elif func == '100101':
mips = "or "+rd+", "+rs+", "+rt
elif func == '000000':
mips = "sll "+rd+", "+rt+", "+shamt
elif func == '000100':
mips = "sllv "+rd+", "+rt+", "+rs
elif func == '101010':
mips = "slt "+rd+", "+rs+", "+rt
elif func == '101011':
mips = "sltu "+rd+", "+rs+", "+rt
elif func == '000011':
mips = "sra "+rd+", "+rt+", "+shamt
elif func == '000111':
mips = "srav "+rd+", "+rt+", "+rs
elif func == '000010':
mips = "srl "+rd+", "+rt+", "+shamt
elif func == '000110':
mips = "srlv "+rd+", "+rt+", "+rs
elif func == '100010':
mips = "sub "+rd+", "+rs+", "+rt
elif func == '100011':
mips = "subu "+rd+", "+rs+", "+rt
elif func == '001100':
mips = "syscall"
elif func == '100110':
mips = "xor "+rd+", "+rs+", "+rt

elif itype == "I":
if op == '001000':
mips = "addi "+rt+", "+rs+", "+imm
elif op == '001001':
mips = "addiu "+rt+", "+rs+", "+imm
elif op == '001100':
mips = "andi "+rt+", "+rs+", "+imm
elif op == '000100':
mips = "beq "+rs+", "+rt+", "
elif op == '000001' and bicode[11:16] == '00001':
mips = "bgez "+rs+", "
elif op == '000111':
mips = "bgtz "+rs+", "
elif op == '000110':
mips = "blez "+rs+", "
elif op == '000001' and bicode[11:16] == '00000':
mips = "bltz "+rs+", "
elif op == '000101':
mips = "bne "+rs+", "+rt+", "
elif op == '010000' and func == '011000':
mips = "eret"
elif op == '100000':
mips = "lb "+rt+", "+imm+"("+rs+")"
elif op == '100100':
mips = "lbu "+rt+", "+imm+"("+rs+")"
elif op == '100001':
mips = "lh "+rt+", "+imm+"("+rs+")"
elif op == '100101':
mips = "lhu "+rt+", "+imm+"("+rs+")"
elif op == '001111':
mips = "lui "+rt+", "+imm
elif op == '100011':
mips = "lw "+rt+", "+imm+"("+rs+")"
elif op == '010000' and bicode[6:11] == '00000':
mips = "mfc0 "+rt+", "+rd
elif op == '010000' and bicode[6:11] == '00100':
mips = "mtc0 "+rt+", "+rd
elif op == '001101':
mips = "ori "+rt+", "+rs+", "+imm
elif op == '101000':
mips = "sb "+rt+", "+imm+"("+rs+")"
elif op == '101001':
mips = "sh "+rt+", "+imm+"("+rs+")"
elif op == '001010':
mips = "slti "+rt+", "+rs+", "+imm
elif op == '001011':
mips = "sltiu "+rt+", "+rs+", "+imm
elif op == '101011':
mips = "sw "+rt+", "+imm+"("+rs+")"
elif op == '001110':
mips = "xori "+rt+", "+rs+", "+imm
if mips[0] == 'b':
imm = bicode[16:32]
immv = int(15*imm[0]+imm, 2)
if imm[0] == '1':
immv = -immv
linenum = int((mipscount+1+immv)/4)
if not linenum in label.keys():
labelname = "label_"+str(labelcount)
try:
out[linenum] = labelname+": "+out[linenum]
except:
pass
if linenum > 0:
out[linenum-1] += "\n"
label[linenum] = labelcount
labelcount += 1
mips = mips+labelname
else:
labelname = "label_"+str(label[linenum])
mips = mips+labelname
mips += "\n"
out[mipscount] += mips
mipscount += 1

print("".join(out))
file_out.write("".join(out))
# print(out)
print("MIPS code has been written in out.txt")
time.sleep(5)
file_in.close()
file_out.close()
  • Title: P7 Design documents
  • Author: Charles
  • Created at : 2022-12-26 21:12:09
  • Updated at : 2023-11-05 21:36:02
  • Link: https://charles2530.github.io/2022/12/26/p7-design-documents/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
P7 Design documents