一、实验目的
1 掌握复杂系统设计方法。
2 深刻理解计算机系统硬件原理。
二、实验内容
1)设计一个基于MIPS指令集的CPU,支持以下指令:{add, sub, addi, lw, sw, beq, j, nop};
2)CPU需要包含寄存器组、RAM模块、ALU模块、指令译码模块;
3)该CPU能运行基本的汇编指令;(D~C+)
以下为可选内容:
4)实现多周期CPU(B-~B+);
5)实现以下高级功能之一(A-~A+):
(1)实现5级流水线CPU;
(2)实现超标量;
(3)实现4路组相联缓存;
可基于RISC V 、ARM指令集实现。
如发现代码为抄袭代码,成绩一律按不及格处理。
三、实验要求
编写相应测试程序,完成所有指令测试。
四、实验代码及结果
本实验所完成的内容为:实现5级流水线CPU。其中考虑到的冒险有:
数据冒险:
add和addi的冒险;R型指令与R型指令的冒险;add与lw的冒险;lw与sw的冒险。
控制冒险:
beq的冒险;j的冒险。
- 源代码
1.1 程序计数器PC
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 |
module PC( input clk, input Rst, input [31:0] PCin, input [2:0] PCStall, output reg [31:0] PCout );
initial begin PCout <= 0; end
always@(posedge clk or posedge Rst) begin if(Rst) begin PCout <= 0; end else if(PCStall) begin PCout <= PCout; end else PCout = PCin;
end
endmodule |
1.2 指令寄存器Imem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
`define DATA_WIDTH 32 module IMem( input [31:0] A, output [`DATA_WIDTH-1:0] RD ); parameter IMEM_SIZE = 32;
reg [31:0] RAM[31:0];
initial $readmemb("D:/class/professtional class/ComputerStructure_experiments/experiment_6/memfile2.dat",RAM); //$readmemb("D:/ram_data_b.txt",RAM,0,1023) 读取二进制数据文件 //$readmamh(D:/ram_data_b.txt",RAM,0,1023) 读取十六进制数据文件 assign RD = RAM[A]; endmodule |
1.3 控制单元Control_Unit
1.3.1 Control_unit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
module Control_unit( input [5:0] Op, Funct, output MemToReg, MemWrite, output ALUSrc, output RegDst, RegWrite, output Jump, output [2:0] ALUControl, output Branch );
wire [1:0] ALUOp; //Main Decoder MainDec2 MainDec_1(Op, MemToReg, MemWrite, Branch, ALUSrc, RegDst, RegWrite, Jump, ALUOp); //ALU Decoder ALUDec ALUDec_1(Funct, ALUOp, ALUControl);
endmodule |
1.3.2 MainDec
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
module MainDec2(//主译码器 input [5:0] Op, output MemToReg, MemWrite, output Branch ,ALUSrc, output RegDst, RegWrite, output Jump, output [1:0] ALUOp);
reg [8:0] Controls; //
assign {RegWrite, RegDst, ALUSrc, Branch, MemWrite,MemToReg, Jump, ALUOp} =Controls;
always@(*) case(Op) 6'b000000: Controls <=9'b110000010;// RTYPE or nop 6'b100011: Controls <=9'b101001000;// LW:需要ALU计算 rs+ offset 6'b101011: Controls <=9'b001010000; // SW:需要ALU 计算 rs + offset 6'b000100: Controls <=9'b000100001;//BEQ 6'b001000: Controls <=9'b101000000; //ADDI 6'b000010: Controls <=9'b000000100;//J default: Controls <=9'bxxxxxxxxx;// illegal Op endcase endmodule |
1.3.3 ALUDec
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
module ALUDec(//ALU泽码器 input [5:0] Funct, input [1:0] ALUOp, output reg [2:0] ALUControl); always@(*) case(ALUOp) 2'b00: ALUControl<=3'b010;// add (for 1w/sw/addi) 2'b01:ALUControl<=3'b110;// sub (for beq) default: case(Funct) // R-type Instructions 6'b100000:ALUControl<=3'b010; // add 6'b100010:ALUControl<=3'b110;// sub 6'b100100:ALUControl<=3'b000;//and 6'b100101:ALUControl<=3'b001;// or 6'b101010:ALUControl<=3'b111;// slt default :ALUControl<=3'bxxx;//??? or nop endcase endcase endmodule |
1.4 寄存器RegFile
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 |
`define DATA_WIDTH 32 `define SIZE 32 module RegFile(CLK, WE3, RA1,RA2,WA3, WD3, RD1,RD2,Rst); parameter ADDR_SIZE = 5; input CLK,WE3; input [ADDR_SIZE-1:0] RA1, RA2, WA3; input Rst; input [`DATA_WIDTH-1:0] WD3; output [`DATA_WIDTH-1:0] RD1,RD2; reg [`DATA_WIDTH-1:0] rf[2 ** ADDR_SIZE-1:0]; integer i; initial begin rf[1] <= 32'b0000_0000_0000_0000_0000_0000_0000_0101; rf[2] <= 32'b0000_0000_0000_0000_0000_0000_0000_0001; //用于测试复位功能 end always @(posedge CLK or posedge Rst) begin if(Rst) begin for(i=0;i<`DATA_WIDTH;i=i+1) rf[i]=`SIZE'b0; end else if(WE3) begin rf[WA3] <= WD3; end
end assign RD1 = (RA1 !=0)? rf[RA1]:0; assign RD2 = (RA2 !=0)? rf[RA2]:0; endmodule |
1.5 符号扩展 SigExt16_32
1 2 3 4 5 6 7 8 |
module SigExt16_32(In,Out); input [15:0] In; output [31:0] Out; assign Out={{16{In[15]}},In[15:0]}; endmodule |
1.6 算术逻辑单元 ALU
1.6.1 ALU
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 ALU(F, ZF, A, B, OP); parameter SIZE = 32; output reg [SIZE-1:0] F; output reg ZF; input [SIZE-1:0] A, B; input [2:0] OP; wire CF; parameter ALU_AND = 3'b000; parameter ALU_ADD = 3'b010; parameter ALU_SUB = 3'b110; parameter ALU_SLT = 3'b111;
parameter ALU_SLL = 3'b011; parameter ALU_OR = 3'b001; parameter ALU_XOR = 3'b100; parameter ALU_NOR = 3'b101; wire [7:0] EN; wire [SIZE-1:0] Fw, Fa;
//数据流描述 assign Fa = A & B;
//行为描述 always@(*) begin case(OP) ALU_AND: begin F <= Fa; end ALU_OR : begin F <= A|B; end ALU_XOR : begin F <= A^B; end ALU_NOR : begin F <= ~(A|B); end default: F = Fw; endcase
//0标志位 ZF = (F == 0); end
//结构化描述 //3-8编码,生成片选EN信号 decoder_38 decoder(OP[2:0], EN);
//算术加法 ADD add1(Fw, CF, A,B,EN[2]); //算术减法 SUB sub1(Fw, CF, A,B,EN[6]); //比较,若A<B, 则输出1,否则输出0. SLT slt1(Fw, A, B, EN[5]);
//逻辑左移 SLL sll1(Fw, A, B, EN[7]);
endmodule |
1.6.2 decoder_38
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
module decoder_38( input wire [3:1] Y, output reg [7:0] I ); always@(Y) begin casex(Y) 3'b000: I = 8'b0000_0001; 3'b001: I = 8'b0000_0010; 3'b010: I = 8'b0000_0100; 3'b011: I = 8'b0000_1000; 3'b100: I = 8'b0001_0000; 3'b101: I = 8'b0010_0000; 3'b110: I = 8'b0100_0000; 3'b111: I = 8'b1000_0000; default: I = 8'b0000_0001; endcase end endmodule |
1.6.3 ADD
1.ADD top
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
module ADD(F, CF, A, B, EN); parameter N = 32; output reg [N-1:0] F; output reg CF; input [N-1:0] A,B; input EN; wire [N-1:0] F_temp; wire CF_temp; reg Cl=0; adder_32bit t1(F_temp, CF_temp, A, B, Cl); always@(EN,F_temp,CF_temp) begin if(EN == 0) begin F <= 32'bz; CF <= 1'bz; end else begin F <= F_temp; CF <= CF_temp; end end endmodule |
1.2 adder_32bit
1 2 3 4 5 6 7 8 9 |
module adder_32bit(S, Co, A, B, Cl); input [31:0] A, B; input Cl; output [31:0] S; output Co; wire C; adder_16bit t0(S[15:0], C, A[15:0], B[15:0], Cl), t1(S[31:16], Co, A[31:16], B[31:16], C); endmodule |
1.3 adder_16bit
1 2 3 4 5 6 7 8 9 |
module adder_16bit(S, Co, A, B, Cl); input [15:0] A, B; input Cl; output [15:0] S; output Co; wire C; adder_8bit t0(S[7:0], C, A[7:0], B[7:0], Cl), t1(S[15:8], Co, A[15:8], B[15:8], C); endmodule |
1.4 adder_8bit
1 2 3 4 5 6 7 8 9 10 |
module adder_8bit(S, Co, A, B, Cl); input [7:0] A, B; input Cl; output [7:0] S; output Co; wire C;
adder_4bit t0(S[3:0], C, A[3:0], B[3:0], Cl), t1(S[7:4], Co, A[7:4], B[7:4], C); endmodule |
1.5 adder_4bit
1 2 3 4 5 6 7 8 9 10 |
module adder_4bit(S, Ch, A, B, Cl); input [3:0] A, B; input Cl; output [3:0] S; output Ch; wire C;
adder_2bit t0(S[1:0], C, A[1:0], B[1:0], Cl), t1(S[3:2], Ch, A[3:2], B[3:2], C); endmodule |
1.6 adder_2bit
1 2 3 4 5 6 7 8 9 10 |
module adder_2bit(S, Co, A, B, Ci); input [1:0] A, B; input Ci; output [1:0] S; output Co; wire C; full_adder t0(S[0], C, A[0], B[0], Ci), t1(S[1], Co, A[1], B[1], C);
endmodule |
1.7 full_adder
1 2 3 4 5 6 7 8 |
module full_adder(S, Co, A, B, Ci); output S, Co; input A, B, Ci; wire S1, D1, D2; half_adder HA1(S1, D1, A, B); //A + B ,进位为 D1, S1 为不计进位的加法结果 half_adder HA2(S, D2, S1, Ci); //S1 + Ci ,进位为D2, or G1(Co, D2, D1); // D2 和 D1 不会同时为 1,只要两个半加有一个进位,全加器就进位 endmodule |
1.8 half_adder
1 2 3 4 5 6 7 |
module half_adder( output S,C, input A,B ); xor(S,A,B); and(C,A,B); endmodule |
1.6.4 SUB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
module SUB(F, CF, A, B, EN); parameter N = 32; output reg [N-1:0] F; output reg CF; input [N-1:0] A,B; input EN; always@(EN,A,B) begin if(EN == 0) begin F <= 32'bz; CF <=1'bz; end else begin if(A<B) CF <= 1; else CF <= 0; F <= A - B; end end endmodule |
1.6.5 SLT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
module SLT(F, A, B, EN); parameter N = 32; output reg [N-1:0] F; input [N-1:0] A,B; input EN; always@(A,B,EN) begin if(EN == 1) begin if(A < B) F <= 1; else F <= 0; end else F <= 32'bz; end endmodule |
1.6.6 SLL
1 2 3 4 5 6 7 8 9 10 |
module SLL(F,A,B,EN); parameter N = 32; output reg[N-1:0] F; input [N-1:0] A,B; input EN; always@(A,B,EN) begin if(EN == 1) F <= B << A; else F <= 32'bz; end endmodule |
1.7 内存 DataMemory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
module DataMemory( input CLK, input [31:0] address, input [31:0] write_data, input mem_write, output [31:0] read_data ); reg [31:0] memory [0:1023]; initial begin memory[0] <= 32'b0000_0000_0000_0000_0000_0000_0000_0001; memory[1] <= 32'b0000_0000_0000_0000_0000_0000_0000_0011; end always@(posedge CLK) case(mem_write) 1:memory[address]<=write_data; endcase assign read_data = memory[address]; endmodule |
1.8 多路选择器
1.8.1 32位2选1多路选择器
1 2 3 4 5 6 7 8 9 10 |
module mux32_2 ( input wire [31:0] a, input wire [31:0] b, input wire select, output wire [31:0] out ); assign out = (select) ? b : a; endmodule |
1.8.2 32位4选1多路选择器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
module mux32_4(A0, A1, A2, A3, S, out); input [31:0] A0, A1, A2, A3; input [1:0] S; output [31:0] out; reg [31:0] select_value; always @(*) begin case(S) 2'b00: select_value = A0; 2'b01: select_value = A1; 2'b10: select_value = A2; 2'b11: select_value = A3; endcase end assign out = select_value; endmodule |
1.8 顶层模块(包含消除冒险部分)
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 277 278 279 280 281 282 283 284 285 286 287 288’ 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
module CPU( input clk, input rst, //复位 output [31:0] result //输出结果 ); //输入指令 wire [31:0] instr;
// ALU结果 wire [31:0] alu_result;
// 程序计数器 reg [31:0] PCin; reg [31:0] PCin1,PCin2; reg [31:0] PCin3; wire [31:0] PCout; //最终PC reg [2:0] PCStall; //控制冒险用 reg [4:0] rtI,rsI; reg [5:0] opcodeI; //控制单元相关变量 wire Branch; wire Zero; reg [5:0] opcode,Funct; reg [4:0] rs, rt, rd; wire MemtoReg, MemWrite; reg PCSrc; wire ALUSrc; wire RegDst, RegWrite; wire Jump; wire [2:0] ALUControl; wire [4:0] WriteReg;
//寄存器相关变量 wire [31:0] SrcA; wire [31:0] SrcB; wire [31:0] ReadData; wire [31:0] RD1; wire [31:0] RD2; wire [31:0] WD3; //符号扩展相关变量 reg [15:0] imm; wire [31:0] SignImm;
//流水线寄存器 //IF_ID reg [31:0] NPCD; reg [31:0] IR; reg [2:0] IF_IDStall; //ID_EX reg RegWriteE,MemtoRegE,MemWriteE,BranchE; reg [2:0] ALUControlE; reg ALUSrcE,RegDstE; reg [4:0] rsE,rtE,rdE; reg [31:0] SignImmE,NPCE,WriteDataE; wire [4:0] WriteRegE; reg [31:0] RD1E,RD2E; wire [31:0] temp; reg [5:0]opcodeE; //EX_MA reg RegWriteM,MemtoRegM,MemWriteM,BranchM,ZeroM; reg [31:0] alu_resultM,WriteDataM,NPCM; reg [4:0] WriteRegM; reg PCSrcM; //MA_WB reg RegWriteW,MemtoRegW; reg [4:0] WriteRegW; reg [31:0] ReadDataW,alu_resultW; //前推解决数据冒险(当前指令位R型,后续指令R或I型,且当前指令的目的寄存器是后续指令的源寄存器 reg [1:0] forward_rs,forward_rt; reg [1:0] forward_rs_2,forward_rt_2;
//IF阶段 PC pc( .clk(clk), .Rst(rst), .PCStall(PCStall), .PCin(PCin), .PCout(PCout) );
always @(*) begin PCin1 = PCout + 1; //自增
if(PCSrcM == 1) PCin3 <= NPCM; else PCin3 <= PCin1; if(Jump) PCin <= SignImm; else PCin <= PCin3; end //立即数(j指令)或者 自增/beq增 IMem imem( .A(PCout), .RD(instr) );
//IF_ID always @(posedge clk or posedge rst) begin if(rst) begin NPCD <= 32'bx; IR <= 32'bx; IF_IDStall <= 0; PCStall <= 0; end //插入空指令 else if(IF_IDStall) begin IR <= 32'bx; IF_IDStall <= IF_IDStall - 1; PCStall <= PCStall - 1; end else begin NPCD <= PCout+1; IR <= instr; end end always @(*) begin opcodeI <= instr[31:26]; rtI <= instr[20:16]; rsI <= instr[25:21]; end //ID阶段
always @* begin // 从指令中提取字段 opcode = IR[31:26]; rs = IR[25:21]; rt = IR[20:16]; rd = IR[15:11]; Funct = IR[5:0]; imm = IR[15:0];
end
//控制单元 Control_unit cu( .Op(opcode), .Funct(Funct), .MemToReg(MemtoReg), .MemWrite(MemWrite), .ALUSrc(ALUSrc), .RegDst(RegDst), .RegWrite(RegWrite), .Jump(Jump), .ALUControl(ALUControl), .Branch(Branch) );
//寄存器 RegFile rf( .Rst(rst), .CLK(clk), .WE3(RegWriteW), .RA1(rs), .RA2(rt), .WA3(WriteRegW), .WD3(result), .RD1(RD1), .RD2(RD2) );
//符号扩展 SigExt16_32 sigext( .In(imm), .Out(SignImm) ); //ID_EX always @(posedge clk or posedge rst) begin if(rst) begin RegWriteE <=0; MemtoRegE <=0; MemWriteE <=0; BranchE <=0; ALUControlE <=0; ALUSrcE <=0; RegDstE <=0; rsE <=0; rtE <=0; rdE <=0; SignImmE <=0; NPCE <=0; RD1E <=0; RD2E <=0; opcodeE <= 0; end else begin RegWriteE <= RegWrite; MemtoRegE <= MemtoReg; MemWriteE <= MemWrite; BranchE <= Branch; ALUControlE <= ALUControl; ALUSrcE <= ALUSrc; RegDstE <= RegDst; rsE <= rs; rtE <= rt; rdE <= rd; SignImmE <= SignImm; NPCE <= NPCD; RD1E <= RD1; RD2E<= RD2; opcodeE <= opcode; end end
//EX阶段
//前推解决数据冒险 always@(*) begin //当前指令为R型指令,后续指令为R型或I型,前一个的rd是后续指令的rs/rt if(opcodeE == 6'b000000 )begin if(rdE == rs) begin forward_rs <= 2'b01; end else forward_rs <= 2'b00; if(rdE == rt) begin forward_rt <= 2'b01; end else begin forward_rt <= 2'b00; end end //当前指令为addi指令,后续指令为R型或addi,当前的目的寄存器rt是后一个的rs else if(opcodeE == 6'b001000) begin if(rtE == rs) begin forward_rs <= 2'b01; end else begin forward_rs <= 2'b00; end if(rtE == rt) begin forward_rt <= 2'b01; end else begin forward_rt <= 2'b00; end end
//当前指令为sw指令,后续指令为lw指令: else if(opcodeE == 6'b101011) begin if(opcode == 6'b100011) begin //sw的rs + offset 等于了 lw 的 rs + offset if(alu_result == alu_resultM) begin
end end // end
//当前指令为lw指令,后续指令为R型指令,且lw的rt 等于了 R型指令的rs/rt else if(opcodeE == 6'b100011 && opcode == 6'b000000) begin if( rtE == rs ) begin forward_rs <= 2'b10; forward_rt <= 2'b00; end else if ( rtE == rt) begin forward_rs <= 2'b00; forward_rt <= 2'b10; end else begin forward_rs <= 2'b00; forward_rt <= 2'b00; end end else begin forward_rs <= 2'b00; forward_rt <= 2'b00; end //插入1个空指令解决诸如指令1addi的rt 等于 指令3add的 rs/rt(如果从指令1取指算作第一个周期,那么指令1完成在第六个周期,但是add在第二个周期就从寄存器取值了 /* if(opcodeE == 6'b001000 & opcodeI == 6'b000000)begin if( rtE == rtI || rtE == rsI) begin IF_IDStall <= 1; PCStall <= 1; end end */ end //forward信号需要延迟一个clk周期赋值 always @(posedge clk) begin forward_rs_2 <= forward_rs; forward_rt_2 <= forward_rt; end
//分支预测解决控制冒险 //j 指令 跳转地址在 译码阶段就可以得出,流水线只需停顿一个周期。 always @(*) begin if(opcodeI == 6'b000010) begin IF_IDStall <= 1; PCStall <= 1; end // beq 指令 跳转地址 在MA阶段才能得出(后续进入了3条指令,流水线需要把IF_ID,ID_EX的寄存器清清零(只需要控制住不往内存或寄存器里写即可) if(PCSrcM == 1) begin RegWriteE = 0; MemWriteE = 0; RegWriteM = 0; MemWriteM = 0; end end //SrcBE RD2为rt内值,SignImm为立即数 mux32_4 mux_SrcA( .A0(RD1E), .A1(alu_resultM), .A2(ReadData), .A3(), .S(forward_rs_2), .out(SrcA) ); mux32_4 mux_SrcB_WriteData( .A0(RD2E), .A1(alu_resultM), .A2(ReadData), .A3(), .S(forward_rt_2), .out(temp) ); mux32_2 mux32_21( .a(temp), .b(SignImmE), .select(ALUSrcE), .out(SrcB) );
ALU alu( .F(alu_result), .ZF(Zero), .A(SrcA), .B(SrcB), .OP(ALUControlE) );
//WriteReg选择写入的寄存器 RegDst=0 选rt,1选rd mux5_2 mux5_21( .a(rtE), .b(rdE), .select(RegDstE), .out(WriteRegE) );
//EX_MA always @(posedge clk or posedge rst) begin if(rst) begin RegWriteM <= 0; MemtoRegM <= 0; MemWriteM <= 0; BranchM <= 0; ZeroM <= 0; alu_resultM <= 0; WriteDataM <= 0; WriteRegM <= 0; NPCM <= 0; end else begin RegWriteM <= RegWriteE; MemtoRegM <= MemtoRegE; MemWriteM <= MemWriteE; BranchM <= BranchE; ZeroM <= Zero; alu_resultM <= alu_result; WriteDataM <= WriteDataE; // WriteRegM <= WriteRegE; // NPCM <= NPCE + SignImmE; end end always @(*) begin PCSrcM = BranchM & ZeroM; WriteDataE = temp; end //MA阶段 DataMemory dm( .CLK(clk), .address(alu_resultM), .write_data(WriteDataM), //RD2为rt寄存器内容 .mem_write(MemWriteM), .read_data(ReadData) );
//MA_WB always @(posedge clk or posedge rst) begin if(rst) begin RegWriteW <= 0; MemtoRegW <= 0; WriteRegW <= 0; alu_resultW <= 0; ReadDataW <= 0; end else begin RegWriteW <= RegWriteM; MemtoRegW <= MemtoRegM; WriteRegW <= WriteRegM; alu_resultW <= alu_resultM; ReadDataW <= ReadData; end end //WB阶段 //选择写入寄存器的数据,ALU运算结果或者内存读入的数据(lw) mux32_2 mux32_22( .a(alu_resultW), .b(ReadDataW), .select(MemtoRegW), .out(result) );
endmodule |
2.仿真代码
1 2 3 4 5 6 7 8 9 10 11 12 |
module cpu_sim; reg clk; reg rst;
initial begin clk = 1;rst = 0; #5 rst = 1; #5 rst = 0; end always #20 clk=~clk; CPU t1(.clk(clk),.rst(rst)); endmodule |
测试文档:
指令 opcode rs rt rd shamt Funct 31:26 25:21 20:16 15:11 10:6 5:0 // 001000 00011 00001 00000 00000 010000 //0.addi rs=3(0) + imm=16存放到rt = 1 // 001000 00011 00010 00000 00000 001000 //1.addi rs=3(0) + imm=8存放到rt = 2 // 000000 00010 00010 00011 00000 100000 //2.add rs=2(8) + rt=2(8),存放到rd=3(16) //数据冒险1 与 指令 1 // 000000 00011 00011 00100 00000 100000 //3.add rs=3(16) + rt=3(16),存放到rd=4 //数据冒险2,rs=上一个rd // 000000 00001 00010 00101 00000 100010 //4.sub rs=1(16) - rt=2(8) ,存放到 rd = 5 // 100011 00000 00110 00000 00000 000001 //5.lw rs=0, rt=6, offset=1 将地址为 rs + offset =1(3)的内存中的值加载到 rt=6 寄存器中。 // 000000 00110 00010 00111 00000 100000 //6.add rs=6(3) + rt=2(8)=11,存放到rd=7 //数据冒险3,与lw // 101011 00000 00001 00000 00000 000100 //7.sw rs=0 rt=1, offset=4 将 rt=1 寄存器中的值(8)存储到地址为 rs + offset=4 的内存中。 // 100011 00000 01000 00000 00000 000100 //8.lw rs=0, rt=8, offset=4 将地址为 rs + offset =4(16)的内存中的值加载到 rt=8 寄存器中。 //与上一个指令产生数据冒险4 // 000100 00011 00010 00000 00000 000100 //9.beq 相等转移 如果 rs 和 rt 寄存器中的值相等,则跳转到当前指令地址加上 offset 的地址。(这里为不相等,不跳转) // 000000 00000 00000 00000 00000 000000 //10.nop 空指令 // 000100 00001 00001 00000 00000 000100 //11.beq 相等转移 如果 rs 和 rt 寄存器中的值相等,则跳转到当前指令地址加上 offset=4 的地址。 // 000000 00000 00000 00000 00000 000000 //12.nop 空指令 // 000000 00000 00000 00000 00000 000000 //13.nop 空指令 // 000000 00000 00000 00000 00000 000000 //14.nop 空指令 // 000000 00000 00000 00000 00000 000000 //15.nop 空指令 // 000010 00000 00000 00000 00000 000000 //16.j 无条件跳转到目标地址(绝对地址=0)。 |
2.结果
2.1 复位
5ns时触发复位信号上升沿,rf内所有数据置为0.
2.2 0.addi rs=3(0) + imm=16存放到rt = 1
1.取指(0-40ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 001000 00011 00001 00000 00000 010000 //0.addi rs=3(0) + imm=16存放到rt = 1
并暂存指令和PC。
2.译码(40-80ns)
根据IR的指令,经过控制单元获得相应的控制信号,暂存入级间寄存器中,并从寄存器堆中读出数据RD1=0。
3.执行(80-120ns)
ALU根据SrcA(0),SrcB(16)和 ALUOP 进行运算,并把结果存入寄存器中。
4.访存(120-160ns)
此指令无访存操作。
5.写回(160-200ns)
将运算结果result=16写回寄存器中。
200ns时写回完成,rf[1]=16。
2.3 addi rs=3(0) + imm=8存放到rt = 2
1.取指(40-80ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 001000 00011 00010 00000 00000 001000 //1.addi rs=3(0) + imm=8存放到rt = 2
并暂存指令和PC。
2.译码(80-120ns)
根据IR的指令,经过控制单元获得相应的控制信号,暂存入级间寄存器中,并从寄存器堆中读出数据RD1=0。
3.执行(120-160ns)
ALU根据SrcA(0),SrcB(8) ALUOP 进行运算,并把结果存入寄存器中。
4.访存(160-200ns)
此指令无访存操作。
5.写回(200-240ns)
将运算结果result=8写回寄存器中。
240ns时写回完成,rf[2]=8.
.
2.4 add rs=2(8) + rt=2(8),存放到rd=3(16) //数据冒险1 与 指令 2.3
1.取指(80-120ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 000000 00010 00010 00011 00000 100000 //2.add rs=2(8) + rt=2(8),存放到rd=3(16) //数据冒险1 与 指令 1
并暂存指令和PC。
2.译码(120-160ns)
根据IR的指令获得相应的控制信号,暂存入寄存器中。
此时根据上一个指令的操作码和rd,和本指令的操作码、rs、rt,检测到了rs和rt的数据冒险,于是将前推信号forward_rs和forward_rt置为1.
3.执行(160-200ns)
根据前推信号forward_rs_2和forward_rt_2,SrcA,SrcB等于上一个指令执行阶段的结果8.
ALU根据SrcA(8),SrcB(8)和 ALUOP 进行运算,并把结果存入寄存器中。
4.访存(200-240ns)
此指令无访存操作。
5.写回(240-280ns)
将运算结果result=16写回寄存器中。
280ns时写回完成,rf[3]=16.
2.5 add rs=3(16) + rt=3(16),存放到rd=4 //数据冒险2,rs=上一个rd
1.取指(120-160ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 000000 00011 00011 00100 00000 100000 //3.add rs=3(16) + rt=3(16),存放到rd=4 //数据冒险2,rs=上一个rd
并暂存指令和PC。
2.译码(160-200ns)
根据IR的指令获得相应的控制信号,暂存入寄存器中。
此时根据上一个指令的操作码和rd,和本指令的操作码、rs、rt,检测到了rs、rt的数据冒险,于是将前推信号forward_rs 、forward_rt置为1.
3.执行(200-240ns)
根据前推信号forward_rs_2和forward_rt_2,SrcA,SrcB等于上一个指令执行阶段的结果16.
ALU根据SrcA(16),SrcB(16)和 ALUOP 进行运算,并把结果存入寄存器中。
4.访存(240-280ns)
此指令无访存操作。
5.写回(280-320ns)
将运算结果result=32写回寄存器中。
320ns时写回完成,rf[3]=32.
2.6 sub rs=1(16) - rt=1(16) ,存放到 rd = 5
1.取指(160-200ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 000000 00001 00001 00101 00000 100010 //4.sub rs=1(16) - rt=1(16) ,存放到 rd = 5
并暂存指令和PC。
2.译码(200-240ns)
根据IR的指令,经过控制单元获得相应的控制信号,暂存入级间寄存器中,并从寄存器堆中读出数据RD1=16,RD2=16。
3.执行(240-280ns)
ALU根据SrcA(16),SrcB(16)和 ALUOP 进行运算,并把结果存入寄存器中。
4.访存(280-320ns)
此指令无访存操作。
5.写回(320-360ns)
将运算结果result=0写回寄存器中。
360ns时写回完成,rf[5] = 0.
2.7 lw rs=0, rt=6, offset=1 将地址为 rs + offset =1(3)的内存中的值(16)加载到 rt=6 寄存器中。
1.取指(200-240ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 100011 00000 00110 00000 00000 000001 //5.lw rs=0, rt=6, offset=1 将地址为 rs + offset =1(3)的内存中的值加载到 rt=6 寄存器中。
并暂存指令和PC。
2.译码(240-280ns)
根据IR的指令,经过控制单元获得相应的控制信号,暂存入级间寄存器中。
3.执行(280-320ns)
ALU根据SrcA(0),SrcB(1)和 ALUOP 进行运算,并把结果存入寄存器中,传递给DataMemory模块的address,作读取内存的地址(1)。
4.访存(320-360ns)
根据地址address,读出内存中的数据,存入ReadData(3)中。
5.写回(360-400ns)
将ReadData=3写回寄存器中。
400ns时写入完成,rf[6]=3.
2.8 add rs=6(3) + rt=2(8)=11,存放到rd=7 //数据冒险3,与lw
1.取指(240-280ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 000000 00110 00010 00111 00000 100000 //6.add rs=6(3) + rt=2(8)=11,存放到rd=7 //数据冒险3,与lw
并暂存指令和PC。
2.译码(280-320ns)
根据IR的指令获得相应的控制信号,暂存入寄存器中。
此时根据上一个指令的操作码和rt,和本指令的操作码、rs,检测到了rs的数据冒险,于是将前推信号forward_rs置为2.
并从寄存器堆中读出数据RD2=8。
3.执行(320-360ns)
根据前推信号forward_rs_2,SrcA等于上一个指令执行阶段的ReadData(3).
ALU根据SrcA(3),SrcB(8)和 ALUOP 进行运算,并把结果存入寄存器中。
4.访存(360-400ns)
此指令无访存操作。
5.写回(400-440ns)
将运算结果result=11写回寄存器中。
440ns时写回完成,rf[7] = 11.
2.9 sw rs=0 rt=1, offset=4 将 rt=1 寄存器中的值(16)存储到地址为 rs + offset=4 的内存中。
1.取指(280-320ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 101011 00000 00001 00000 00000 000100 //7.sw rs=0 rt=1, offset=4 将 rt=1 寄存器中的值(16)存储到地址为 rs + offset=4 的内存中。
并暂存指令和PC。
2.译码(320-360ns)
根据IR的指令,经过控制单元获得相应的控制信号,暂存入级间寄存器中,并从寄存器堆中读出数据RD2=16作写入内存的值。
3.执行(360-400ns)
ALU根据SrcA(0),SrcB(1)和 ALUOP 进行运算,并把结果存入寄存器中,传递给DataMemory模块的address,作写入内存的地址(1)。
4.访存(400-440ns)
根据地址address和控制信号MemWriteM,将值16写入内存中。
440ns时写入完成,memory[4]=16.
5.写回(440-480ns)
此指令无写回操作。
2.10 lw rs=0, rt=8, offset=4 将地址为 rs + offset =4(16)的内存中的值加载到 rt=8 寄存器中。 //与上一个指令产生数据冒险4
1.取指(320-360ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 100011 00000 01000 00000 00000 000100 //8.lw rs=0, rt=8, offset=4 将地址为 rs + offset =4(16)的内存中的值加载到 rt=8 寄存器中。 //与上一个指令产生数据冒险4
并暂存指令和PC。
2.译码(360-400ns)
根据IR的指令,经过控制单元获得相应的控制信号,暂存入级间寄存器中。
3.执行(440-440ns)
ALU根据SrcA(0),SrcB(4)和 ALUOP 进行运算,并把结果存入寄存器中,传递给DataMemory模块的address,作读取内存的地址(4)。
4.访存(440-480ns)
根据地址address,读出内存中的数据,存入ReadData(16)中。
5.写回(480-520ns)
将ReadData=16写回寄存器中。
520ns时写入完成,rf[8]=16.
2.11 beq 相等转移 如果 rs=3 和 rt =2寄存器中的值相等,则跳转到当前指令地址加上 offset 的地址。(这里为不相等,不跳转)
1.取指(360-400ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 000100 00011 00010 00000 00000 000100 //9.beq 相等转移 如果 rs 和 rt 寄存器中的值相等,则跳转到当前指令地址加上 offset 的地址。(这里为不相等,不跳转)
并暂存指令和PC。
2.译码(400-440ns)
根据IR的指令获得相应的控制信号,暂存入寄存器中。
并从寄存器堆中读出数据RD1=16,RD2=8。
3.执行(440-480ns)
ALU根据SrcA(16),SrcB(8)和 ALUOP 进行(SUB)运算,并把结果存入寄存器中。
结果不为0,Zero信号=0,PCSrc=0,不跳转。
4.访存(480-520ns)
此指令无访存操作。
5.写回(520-560ns)
此指令无写回操作。
2.12 nop 空指令
1.取指(400-440ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 000000 00000 00000 00000 00000 000000 //10.nop 空指令
并暂存指令和PC。
2.译码(440-480ns)
根据IR的指令获得相应的控制信号,暂存入寄存器中。
3.执行(480-520ns)
此指令该阶段无操作。
4.访存(520-560ns)
此指令无访存操作。
5.写回(560-600ns)
此指令无写回操作。
2.13 beq 相等转移 如果 rs 和 rt 寄存器中的值相等,则跳转到当前指令地址加上 offset=4 的地址。
1.取指(400-440ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 000100 00001 00001 00000 00000 000100 //11.beq 相等转移 如果 rs 和 rt 寄存器中的值相等,则跳转到当前指令地址加上 offset=4 的地址。
并暂存指令和PC。
2.译码(440-480ns)
根据IR的指令获得相应的控制信号,暂存入寄存器中。
并从寄存器堆中读出数据RD1=16,RD2=16。
3.执行(480-520ns)
ALU根据SrcA(16),SrcB(16)和 ALUOP 进行(SUB)运算,并把结果存入寄存器中。
结果不为0,Zero信号=1,PCSrc=Zero & Branch = 1,需要跳转。此时将与寄存器,内存写入相关的控制变量全部置0:RegWriteE = 0; MemWriteE = 0; RegWriteM = 0; MemWriteM = 0,避免beq后续的流水向寄存器和内存写入值,然后下一个PC根据PCSrc=1,为跳转地址NPCM(16)。
4.访存(520-560ns)
此指令无访存操作。
5.写回(560-600ns)
此指令无写回操作。
2.14 j 无条件跳转到目标地址(绝对地址=0)
1.取指(600-640ns)
从指令寄存器中取出指令,并使用加法器修正PC值。
// 000010 00000 00000 00000 00000 000000 //16.j 无条件跳转到目标地址(绝对地址=0)。
并暂存指令和PC。
跳转地址在 译码阶段就可以得出,流水线只需停顿一个周期。所以将流水线停滞一个周期(640-680),避免后续指令进入流水线。
2.译码(640-680ns)
根据IR的指令获得相应的控制信号Jump=1和立即数SignImm=0,更新此时的PCin=0.
在680时便从地址为0开始取指令。
五、调试和心得体会
1.在Run Simulation时,如果想要查看所调用模块内变量的值,可以先在Simulation Settings 里,找到 Simulation,勾选xsim_simulate,log.all_signals*,就可以在仿真的时候,拖动变量进入图标即可查看其值。
如,想查看ALUSrc,就将其拖动值右侧变量列中,便可查看。