设计文档
前情提要
本次搭建需要实现的指令有
- add,sum,ori,lui
- sw,lw
- beq
- jal,jr
- nop
在此基础之上,本人额外添加了lh,lb,sh,sb,j指令。
verilog的基本构建思路源始于logisim,Tunnel化的logisim电路为verilog代码的编写提供了很大的便利和可扩展性。
verilog的编写仍然从数据通路基础部件和控制器两方面来书写,不过额外的是,在mips.v中应当实现所有的基础接线操作。
或者说,mips.v是最独特的一环。
一切源代码仅供参考,千万不要抄袭!!
数据通路的构建
PC
PC作为CPU中的时序电路核心器件,其功能实现也较为简单,只有同步复位操作和npc赋值操作。
直接由logisim文件分析PC模块的接口
名称 |
位宽 |
方向 |
npc |
32 |
I |
clk |
1 |
I |
reset |
1 |
I |
pc |
32 |
O |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
`default_nettype none
module PC(
input wire [31:0] npc,
input wire clk,
input wire reset,
output wire [31:0] pc
);
reg [31:0] PCreg;
always @(posedge clk) begin
if (reset) begin
PCreg <= 32'h00003000;
end
else begin
PCreg <= npc;
end
end
assign pc = PCreg;
endmodule
|
NPC
NPC与P3不同,加入了jal,jr指令,使得其增加了两种全新跳转方式:
- 26位立即数经位拼接得到直接地址
- 32为GRF数据得到直接地址
再加上原来的pc+4与pc+4+sign_ext(imm)
共有四种跳转逻辑,需要较多的控制信号
br 和 zero 控制 相对寻址跳转
j 控制imm_26绝对寻址跳转
jr 控制$ra绝对寻址跳转
接口信息如下:
名称 |
位宽 |
方向 |
pc |
32 |
I |
imm_16 |
16 |
I |
imm_26 |
26 |
I |
reg_ra |
32 |
I |
br |
1 |
I |
zero |
1 |
I |
j |
1 |
I |
jr |
1 |
I |
pc_4 |
32 |
O |
npc |
32 |
O |
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
|
`default_nettype none
module NPC(
input wire br,
input wire zero,
input wire [15:0] imm_16,
input wire [25:0] imm_26,
input wire [31:0] pc,
input wire [31:0] reg_ra,
input wire j,
input wire jr,
output wire [31:0] pc_4,
output wire [31:0] npc
);
wire [31:0] j_pc;
wire [31:0] br_pc;
assign pc_4 = pc + 32'd4;
assign j_pc = {pc[31:28],imm_26,{2{1'b0}}};
assign br_pc = pc_4 + {{15{imm_16[15]}},imm_16[14:0],{2{1'b0}}};
assign npc = (jr) ? reg_ra :
(j) ? j_pc :
(br && zero) ? br_pc : pc_4;
endmodule
|
IM
IM作为需要读取指令的单位,需要调用readmemh
为了保证读取的正确顺序,定义寄存器堆时使用[0:4095]
也可以添加参数控制readmemh读取到的起始位置与终止位置
由于该指令寄存器堆应当存储4096*32bit
然而起始位置却是0x00003000
因此可以先对pc减去0x00003000
再取[13:2]作为指令寄存器堆的读取地址
名称 |
位宽 |
方向 |
pc |
32 |
I |
reset |
1 |
I |
clk |
1 |
I |
Instruction |
32 |
O |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
`default_nettype none
module IM(
input wire [31:0] pc,
input wire reset,
input wire clk,
output wire [31:0] Instruction
);
wire [31:0] address;
reg [31:0] InstrMemory [0:4095];
always @(posedge clk) begin
if(reset) begin
$readmemh("code.txt", InstrMemory);
end
end
assign address = pc - 32'h00003000;
assign Instruction = InstrMemory[address[13:2]];
endmodule
|
EXT
注意位拼接操作的写法!!不能少打大括号
位拼接“0”时,一定要控制有多少个0
例如16个“0”,应使用{16{1’b0}}或{16’b0}
名称 |
位宽 |
方向 |
EXTOp |
1 |
I |
imm_16 |
16 |
I |
IMM_32 |
32 |
O |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
`default_nettype none
module EXT(
input wire EXTOp,
input wire [15:0] imm_16,
output wire [31:0] IMM_32
);
wire [31:0] signed_IMM;
wire [31:0] unsigned_IMM;
assign signed_IMM = {{16{imm_16[15]}},imm_16};
assign unsigned_IMM = {{16{1'b0}},imm_16};
assign IMM_32 = EXTOp ? signed_IMM : unsigned_IMM;
endmodule
|
GRF
GRF的最特殊性在于其0号寄存器
在此有两种处理方案
- 初始时为所有寄存器赋0
之后不对0号寄存器
进行写入
- 读
0号寄存器
时始终输出0
由于个人偏好,希望各个寄存器中的值保持正确,本人采用了第一种写法
根据题目要求,需要输出寄存器被写时的各项参数,需要将pc传入其中
接口如下所示:
名称 |
位宽 |
方向 |
pc |
32 |
I |
reset |
1 |
I |
clk |
1 |
I |
WE |
1 |
I |
A1 |
5 |
I |
A2 |
5 |
I |
A3 |
5 |
I |
WD |
32 |
I |
RD1 |
32 |
O |
RD2 |
32 |
O |
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
|
`default_nettype none
module GRF(
input wire [31:0] pc,
input wire [4:0] A1,
input wire [4:0] A2,
input wire [4:0] A3,
input wire [31:0] WD,
input wire reset,
input wire clk,
input wire WE,
output wire [31:0] RD1,
output wire [31:0] RD2
);
reg [31:0] grf [31:0];
integer i;
always @(posedge clk) begin
if (reset) begin
for (i = 0; i < 32;i = i + 1) begin
grf[i] <= 32'b0;
end
end
else begin
if(WE && A3) begin
grf[A3] <= WD;
$display("@%h: $%d <= %h", pc, A3, WD);
end
end
end
assign RD1 = grf[A1];
assign RD2 = grf[A2];
endmodule
|
ALU
实现基本与logisim同理,但是操作更为简单
注意使用`define以增强代码可读性
名称 |
位宽 |
方向 |
ALUOp |
2 |
I |
num_1 |
32 |
I |
num_2 |
32 |
I |
zero |
1 |
O |
out |
32 |
O |
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
|
`default_nettype none
`define ADD 2'b00
`define SUB 2'b01
`define ORI 2'b10
`define LUI 2'b11
module ALU(
input wire [1:0] ALUOp,
input wire [31:0] num_1,
input wire [31:0] num_2,
output wire zero,
output wire [31:0] out
);
wire [31:0] add;
wire [31:0] sub;
wire [31:0] ori;
wire [31:0] lui;
assign add = num_1 + num_2;
assign sub = num_1 - num_2;
assign ori = num_1 | num_2;
assign lui = {num_2[15:0],{16{1'b0}}};
assign zero = (num_1 == num_2) ? 1'b1 : 1'b0;
assign out = (ALUOp == `ADD) ? add :
(ALUOp == `SUB) ? sub :
(ALUOp == `ORI) ? ori : lui;
endmodule
|
DM
仅对于lw和sw而言,DM本应是极为简单的。
但是正因为sh,sb的特殊性,我尝试了对其功能的描述
sh,sb指令的核心问题,在于我们不能直接做到针对一个字部分内容的修改
因为我们的寄存器都是以32bit为单位的
因此我们要先把它所在寄存器的值取出来,进行位拼接操作得到该寄存器应存入的新数,再存入DM之中
由于添加了指令,我的WE与RE信号都进行了调整
控制信号取值 |
含义 |
00 |
以w为单位 |
01 |
以h为单位 |
10 |
以b为单位 |
11 |
不予使能 |
同样地,为了输出中间参数,pc需要被引入。
名称 |
位宽 |
方向 |
pc |
32 |
I |
data |
32 |
I |
addr |
32 |
I |
WE |
2 |
I |
RE |
2 |
I |
clk |
1 |
I |
reset |
1 |
I |
out |
32 |
O |
我还添加了word,half,by_te等中间变量,便于我后续的描述
名称 |
位宽 |
方向 |
pc |
32 |
I |
data |
32 |
I |
addr |
32 |
I |
WE |
2 |
I |
RE |
2 |
I |
clk |
1 |
I |
reset |
1 |
I |
out |
32 |
O |
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
|
`default_nettype none
`define SW 2'b00
`define SH 2'b01
`define SB 2'b10
`define LW 2'b00
`define LH 2'b01
`define LB 2'b10
module DM(
input wire [31:0] pc,
input wire [31:0] data,
input wire [31:0] addr,
input wire [1:0] WE,
input wire [1:0] RE,
input wire clk,
input wire reset,
output wire [31:0] out
);
reg [31:0] DataMemory [0:3071];
integer i;
wire [11:0] word;
wire half;
wire [1:0] by_te;
assign word = addr[13:2];
assign by_te = addr[1:0];
assign half = addr[1];
always @(posedge clk) begin
if (reset) begin
for (i = 0; i < 3072; i = i + 1) begin
DataMemory[i] <= 32'b0;
end
end
else begin
case (WE)
`SW: begin
DataMemory[word] <= data;
$display("@%h: *%h <= %h", pc, addr, data);
end
`SH: begin
case (half)
1'b1: begin
DataMemory[word] <= {data[15:0], DataMemory[word][15:0]};
end
1'b0: begin
DataMemory[word] <= {DataMemory[word][31:16], data[15:0]};
end
default: begin
end
endcase
end
`SB: begin
case (by_te)
2'b11: begin
DataMemory[word] <= {data[7:0], DataMemory[word][23:0]};
end
2'b10: begin
DataMemory[word] <= {DataMemory[word][31:24], data[7:0], DataMemory[word][15:0]};
end
2'b01: begin
DataMemory[word] <= {DataMemory[word][31:16], data[7:0], DataMemory[word][7:0]};
end
2'b00: begin
DataMemory[word] <= {DataMemory[word][31:8], data[7:0]};
end
default: begin
end
endcase
end
default: begin
end
endcase
end
end
assign out = (RE == `LW) ? DataMemory[word] :
(RE == `LH && half == 1'b1) ? {{16{DataMemory[word][31]}},DataMemory[word][31:16]} :
(RE == `LH && half == 1'b0) ? {{16{DataMemory[word][15]}}, DataMemory[word][15:0]} :
(RE == `LB && by_te == 2'b11) ? {{24{DataMemory[word][31]}}, DataMemory[word][31:24]} :
(RE == `LB && by_te == 2'b10) ? {{24{DataMemory[word][23]}}, DataMemory[word][23:16]} :
(RE == `LB && by_te == 2'b01) ? {{24{DataMemory[word][15]}}, DataMemory[word][15:8]} :
(RE == `LB && by_te == 2'b00) ? {{24{DataMemory[word][7]}}, DataMemory[word][7:0]} : 32'b0;
endmodule
|
控制器的编写
在搭建数据通路的过程中,控制信号的要求也已经浮出水面。
关键还是由IM到指令,由指令到控制信号的过程
不过加入了其他操作,使得DMWr,DMRd发生了一定的改变
以及额外添加的Link,J,Jr等控制信号
相较于P3的BSel,此处本人将其更名为ImmSel,强调其解决的是立即数与寄存器谁参与ALU运算的问题
名称 |
位宽 |
方向 |
opcode |
6 |
I |
funct |
6 |
I |
Br |
1 |
O |
EXTOp |
1 |
O |
RFWr |
1 |
O |
DMWr |
2 |
O |
DMRd |
2 |
O |
ALUOp |
2 |
O |
WrSel |
1 |
O |
WdSel |
1 |
O |
ImmSel |
1 |
O |
Link |
1 |
O |
J |
1 |
O |
Jr |
1 |
O |
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
|
`default_nettype none
module CTRL(
input wire [5:0] opcode,
input wire [5:0] funct,
output wire Br,
output wire EXTOp,
output wire RFWr,
output wire [1:0] DMWr,
output wire [1:0] DMRd,
output wire [1:0] ALUOp,
output wire WrSel,
output wire WdSel,
output wire ImmSel,
output wire Link,
output wire J,
output wire Jr
);
wire add;
wire sub;
wire ori;
wire lui;
wire sw;
wire sh;
wire sb;
wire lw;
wire lh;
wire lb;
wire beq;
wire j;
wire jal;
wire jr;
wire R;
assign R = (opcode == 6'b000000);
assign add = R && (funct == 6'b100000);
assign sub = R && (funct == 6'b100010);
assign jr = R && (funct == 6'b001000);
assign j = (opcode == 6'b000010);
assign jal = (opcode == 6'b000011);
assign ori = (opcode == 6'b001101);
assign lui = (opcode == 6'b001111);
assign sw = (opcode == 6'b101011);
assign sh = (opcode == 6'b101001);
assign sb = (opcode == 6'b101000);
assign lw = (opcode == 6'b100011);
assign lh = (opcode == 6'b100001);
assign lb = (opcode == 6'b100000);
assign beq = (opcode == 6'b000100);
assign Br = beq;
assign Jr = jr;
assign Link = jal;
assign J = jal || j;
assign WdSel = lw || lh || lb;
assign WrSel = add || sub;
assign ImmSel = ori || lui || sw || sh || sb || lw || lh || lb;
assign EXTOp = sw || sh || sb || lw || lh || lb;
assign ALUOp = sub ? 2'b01 :
ori ? 2'b10 :
lui ? 2'b11 : 2'b00;
assign RFWr = add || sub || ori || lui || lw || lb || lh || jal;
assign DMRd = lw ? 2'b00 :
lh ? 2'b01 :
lb ? 2'b10 : 2'b11;
assign DMWr = sw ? 2'b00 :
sh ? 2'b01 :
sb ? 2'b10 : 2'b11;
endmodule
|
mips.v
前面说了那么多,其实都是相对独立的结构
那么接下来,才是verilog描述的独特之处
才能更深刻地展示verilog的抽象功能
接口十分简单,毕竟真正来自外部的信号只有clk和reset
名称 |
位宽 |
方向 |
clk |
1 |
I |
reset |
1 |
I |
1
2
3
4
5
|
`default_nettype none
module mips(
input wire clk,
input wire reset
);
|
接线逻辑
在我书写这一板块的过程中,我曾选用了将所有的wire先定义好,再全部统一赋值,再逐个接接口的方法
结果这样导致的问题(或者说我曾经出现的bug)
就是你不能确定你需要的信号都得到了定义与赋值
因此,我秉承了P3的基本思路,对各个部件的外部接线进行分析。
分析方法:
- 先定义所有需要的输出信号
- 输入信号分为
- 直接与其他某部件的输出信号直接相连
- 由其他部件的输出信号相互作用(计算or选择)得到
- 为这些相互作用进行assign处理
基础信号
名义上如此,其实这是在模拟我没有模块化的Splitter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 基础信号定义
wire [5:0] opcode;
wire [4:0] rs;
wire [4:0] rt;
wire [4:0] rd;
wire [4:0] shamt;
wire [5:0] funct;
assign opcode = Instr[31:26];
assign rs = Instr[25:21];
assign rt = Instr[20:16];
assign rd = Instr[15:11];
assign shamt = Instr[10:6];
assign funct = Instr[5:0];
wire [15:0] imm_16;
wire [25:0] imm_26;
assign imm_16 = Instr[15:0];
assign imm_26 = Instr[25:0];
|
CTRL
定义大量控制信号
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
|
//控制信号定义
wire Br;
wire EXTOp;
wire RegWrite;
wire [1:0] DMWr;
wire [1:0] DMRd;
wire [1:0] ALUOp;
wire WrSel;
wire WdSel;
wire ImmSel;
wire Link;
wire J;
wire Jr;
CTRL Ctrl(
.opcode(opcode),
.funct(funct),
.Br(Br),
.EXTOp(EXTOp),
.RFWr(RegWrite),
.DMWr(DMWr),
.DMRd(DMRd),
.ALUOp(ALUOp),
.WrSel(WrSel),
.WdSel(WdSel),
.ImmSel(ImmSel),
.Link(Link),
.J(J),
.Jr(Jr)
);
|
PC
定义输出信号pc
1
2
3
4
5
6
7
8
|
// PC 输出信号
wire [31:0] pc;
PC Pc(
.npc(npc),
.clk(clk),
.reset(reset),
.pc(pc)
);
|
IM
定义输出信号Instr
1
2
3
4
5
6
7
8
9
10
|
// IM输出信号
wire [31:0] Instr;
IM Im(
.pc(pc),
.Instruction(Instr),
.clk(clk),
.reset(reset)
);
|
EXT
定义输出信号IMM_32
1
2
3
4
5
6
7
8
|
// EXT输出信号
wire [31:0] IMM_32;
EXT Ext(
.EXTOp(EXTOp),
.imm_16(imm_16),
.IMM_32(IMM_32)
);
|
NPC
定义输出信号npc和pc_4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// NPC输出信号
wire [31:0] npc;
wire [31:0] pc_4;
NPC Npc(
.br(Br),
.zero(zero),
.imm_16(imm_16),
.imm_26(imm_26),
.pc(pc),
.reg_ra(RD1),
.j(J),
.jr(Jr),
.pc_4(pc_4),
.npc(npc)
);
|
GRF
定义输出信号RD1,RD2
定义输入信号,并给出其assign语句实现赋值
要知道,输入信号一定要从其他部件的输出信号或者外部的clk 与reset中得到
如果盲目定义输入信号,却不予有意义的赋值,就会产生xxxzzz等问题
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
|
// GRF 输入信号·中间变量
wire [31:0] LinkPC;
wire [4:0] A1;
wire [4:0] A2;
wire [4:0] RegAddr;
wire [31:0] RegData;
// GRF 中间变量的赋值
assign LinkPC = pc_4;
assign A1 = rs;
assign A2 = rt;
assign RegAddr = Link ? 5'b11111 :
WrSel ? rd : rt;
assign RegData = Link ? LinkPC :
WdSel ? MemReadData : ALUData;
// GRF 输出信号
wire [31:0] RD1;
wire [31:0] RD2;
GRF Grf(
.clk(clk),
.reset(reset),
.WE(RegWrite),
.pc(pc),
.A1(A1),
.A2(A2),
.A3(RegAddr),
.WD(RegData),
.RD1(RD1),
.RD2(RD2)
);
|
ALU
输出信号zero和ALUData
定义输入信号num_1,num_2并给出assign
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// ALU 输入信号·中间变量
wire [31:0] num_1;
wire [31:0] num_2;
// ALU 中间变量的赋值
assign num_1 = RD1;
assign num_2 = ImmSel ? IMM_32 : RD2;
// ALU 输出信号
wire zero;
wire [31:0] ALUData;
ALU Alu(
.ALUOp(ALUOp),
.num_1(num_1),
.num_2(num_2),
.zero(zero),
.out(ALUData)
);
|
DM
这两个输入变量的赋值是否没有意义?
MemAddr一定是ALUData?
MemData一定是RD2?
可能确实如此。
但是这样的命名使得整体逻辑更为清晰,也增强了可扩展性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// DM 输入信号·中间变量
wire [31:0] MemAddr;
wire [31:0] MemData;
// DM 中间变量的赋值
assign MemAddr = ALUData;
assign MemData = RD2;
// DM 输出信号
wire [31:0] MemReadData;
DM Dm(
.pc(pc),
.data(MemData),
.addr(MemAddr),
.WE(DMWr),
.RE(DMRd),
.clk(clk),
.reset(reset),
.out(MemReadData)
);
endmodule
|
测试文档
我仍然采用了对拍的方法,并自行构造了一定数据。
对于新增指令,我着重加强了对jr,jal的测试
以下是一组测试数据,重点观察$ra取值的改变是否正常
1
2
3
4
5
6
7
8
9
|
0c000c06 // jal label
341f0000 //ori $ra, $0, 0
0c000c06 //jal label
341f0000 //ori $ra, $0, 0
0c000c06 //jal label
341f301c //ori $ra, $0, 0x0000301c
//label:
03e00008 //jr $ra
03fff820 //add $ra, $ra, $ra
|
可知$ra会在pc+4和0不断跳转
直到变为0x0000301c后跳转到add语句变为0x00006038
下面是我的输出结果,与MIPS结果一致
1
2
3
4
5
6
7
|
@00003000: $31 <= 00003004
@00003004: $31 <= 00000000
@00003008: $31 <= 0000300c
@0000300c: $31 <= 00000000
@00003010: $31 <= 00003014
@00003014: $31 <= 0000301c
@0000301c: $31 <= 00006038
|
思考题
阅读下面给出的 DM 的输入示例中(示例 DM 容量为 4KB,即 32bit × 1024字),根据你的理解回答,这个 addr 信号又是从哪里来的?地址信号 addr 位数为什么是 [11:2] 而不是 [9:0] ?
addr信号来自于ALU的计算,即GRF[base]+sign_ext(offset)
其中addr[11:2]意为取ALU计算结果从第2位开始的12位数字作为address。
其原因在于对于lw指令,第0位和第1位均为0,自第2位起开始计数才是以32bit为单位,才能与DM寄存器堆的寄存器编号相对应。
思考两种控制器设计的译码方式,给出代码示例,并尝试对比各方式的优劣。
控制信号每种取值所对应的指令:
举例:
1
2
3
4
5
6
7
|
assign Br = beq;
assign Jr = jr;
assign Link = jal;
assign J = jal || j;
assign WdSel = lw || lh || lb;
assign WrSel = add || sub;
assign EXTOp = sw || sh || sb || lw || lh || lb;
|
每条指令对应的控制信号取值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
always@(*) begin
case (Operation)
`ADD : begin
Br = 1'b0;
Jr = 1'b0;
Link = 1'b0;
J = 1'b0;
WrSel = 1'b1;
...
end
...
default : begin
end
end
|
分析优劣:
由指令确定其控制信号:
优点:考虑全面,不会有漏掉的指令,对每个指令也不会有漏掉的控制信号
上机新增指令时直接对其分析,一个也不会漏
缺点:代码重复冗长
产生新的控制信号时还需要对每个指令都加句话
对控制信号归纳其生效指令:
优点:本质上是上一种方法的总结归纳,代码量少
缺点:自行归纳时某些控制信号可能会忘记考虑一些指令
PS:解决方法:对新指令在演草纸上进行所有控制信号枚举,之后再进行归纳,用于代码书写
在相应的部件中,复位信号的设计都是同步复位,这与 P3 中的设计要求不同。请对比同步复位与异步复位这两种方式的 reset 信号与 clk 信号优先级的关系。
同步复位:clk优先(还必须得是上升沿)
异步复位:reset优先
C 语言是一种弱类型程序设计语言。C 语言中不对计算结果溢出进行处理,这意味着 C 语言要求程序员必须很清楚计算结果是否会导致溢出。因此,如果仅仅支持 C 语言,MIPS 指令的所有计算指令均可以忽略溢出。 请说明为什么在忽略溢出的前提下,addi 与 addiu 是等价的,add 与 addu 是等价的。提示:阅读《MIPS32® Architecture For Programmers Volume II: The MIPS32® Instruction Set》中相关指令的 Operation 部分。
1
2
|
if temp32 ≠ temp31 then
SignalException(IntegerOverflow)
|
这是add和addu、addi和addiu的唯一区别
u的本意即为不考虑输出
那在忽略输出的前提下自然就等价了。