1.3 ID Stage
ID_Stage는 현재의 Instruction이 들어오면 해독하여 Opcode, Rs, Rt, Rd 등으로 구분짓는 역할을 한다. Opcode는 Control unit으로 전송되어 각 명령어에 맞는 Control signal을 제어하며, 나머지 Rs, Rt, Rd의 값을 받아 내부의 레지스터 파일을 이용하여 레지스터를 새로운 값으로 갱신하거나, 레지스터 번호에 맞는 데이터 값을 추출한다. I-type instruction의 경우, [15:0] instruction을 sign-extended로 보내 32비트로 만들어준다.
또한 beq instruciton이 들어오게 될 경우 먼저 두 값이 같은지를 ID stage에서 판별하고 Branch equal 신호를 보내 Stall 여부를 미리 결정하는 역할도 한다.
module ID_Stage( input wire clk, input wire rst, input wire [31:0] IFtoID_PC, input wire [31:0] IFtoID_inst, input wire [4:0] writeReg, input wire [31:0] writeData, input wire RegWrite, input wire [31:0] EX_ALUresult, MEM_ALUresult, input wire [1:0] ForwardA, ForwardB, output wire [31:0] IDtoEX_ReadData1, output wire [31:0] IDtoEX_ReadData2, output wire [31:0] IDtoEX_Imm, output wire [5:0] IFtoID_Op, //31:26 output wire [4:0] IFtoID_Rs, //25:21 output wire [4:0] IFtoID_Rt, //20:16 output wire [4:0] IFtoID_Rd, //15:11 output wire [5:0] funct, output wire [31:0] Branch_Addr, output wire branch_equal, output wire [31:0] output_8, output_9, output_10, output_11, output_12, output_13, output_14, output_15 ); // Register File wire [31:0] RD1, RD2, zero, at, v0, v1, a0, a1, a2, a3, t0, t1, t2, t3, t4, t5, t6, t7, s0, s1, s2, s3, s4, s5, s6, s7, t8, t9, k0, k1, gp, sp, fp, ra; wire [4:0] RR1, RR2, WR; wire [31:0] WD; wire Control_RegWrite; wire [15:0] imm16; wire [31:0] imm32; // using sign extension wire [31:0] sll_out; reg [31:0] fwd_RD1, fwd_RD2; // Instruction Decode assign RR1 = IFtoID_inst[25:21]; assign RR2 = IFtoID_inst[20:16]; assign WR = writeReg; assign WD = writeData; assign Control_RegWrite = RegWrite; // calculate branch address Shifting SHIFT (.Din(imm32), .shamt(5'd2), .left(1'b1), .Dout(sll_out)); Adder ADD_addr (.operandA(IFtoID_PC), .operandB(sll_out), .sum(Branch_Addr)); RegisterFile u0 ( .RD1(RD1), .RD2(RD2), .zero(zero), .at(at), .v0(v0), .v1(v1), .a0(a0), .a1(a1), .a2(a2), .a3(a3), .t0(t0), .t1(t1), .t2(t2), .t3(t3), .t4(t4), .t5(t5), .t6(t6), .t7(t7), .s0(s0), .s1(s1), .s2(s2), .s3(s3), .s4(s4), .s5(s5), .s6(s6), .s7(s7), .t8(t8), .t9(t9), .k0(k0), .k1(k1), .gp(gp), .sp(sp), .fp(fp), .ra(ra), .RR1(RR1), .RR2(RR2), .WR(WR), .WD(WD), .WriteReg(Control_RegWrite), .clk(clk), .rst(rst) ); SignExt u1 ( .Y(imm32), .X(imm16) ); always @(ForwardA, RD1, MEM_ALUresult, EX_ALUresult) begin case(ForwardA) default: fwd_RD1 <= RD1; 1: fwd_RD1 <= MEM_ALUresult; 2: fwd_RD1 <= EX_ALUresult; endcase end always @(ForwardB, RD2, MEM_ALUresult, EX_ALUresult) begin case(ForwardB) default: fwd_RD2 <= RD2; 1: fwd_RD2 <= MEM_ALUresult; 2: fwd_RD2 <= EX_ALUresult; endcase end // register file output assign IDtoEX_ReadData1 = (WR == RR1)? WD:RD1; assign IDtoEX_ReadData2 = (WR == RR2)? WD:RD2; assign IDtoEX_Imm = imm32; assign imm16 = IFtoID_inst[15:0]; assign IFtoID_Op = IFtoID_inst[31:26]; assign IFtoID_Rs = IFtoID_inst[25:21]; assign IFtoID_Rt = IFtoID_inst[20:16]; assign IFtoID_Rd = IFtoID_inst[15:11]; assign funct = IFtoID_inst[5:0]; assign output_8 = t0; assign output_9 = t1; assign output_10 = t2; assign output_11 = t3; assign output_12 = t4; assign output_13 = t5; assign output_14 = t6; assign output_15 = t7; assign branch_equal = (fwd_RD1 == fwd_RD2); endmodule |
1.3.1 Register file
Register file 동작 설명
Register file은 instruction을 읽고 해당 레지스터 번호를 찾아 데이터 값을 읽어내거나 데이터를 갱신하는 역할을 한다. 추가적으로 t0, t1, t2, t3 레지스터의 경우 자주 사용하는 레지스터 파일이기에 직접 값을 확인할 수 있도록 output으로 연결하였다.
module RegisterFile(RD1, RD2, zero, at, v0, v1, a0, a1, a2, a3, t0, t1, t2, t3, t4, t5, t6, t7, s0, s1, s2, s3, s4, s5, s6, s7, t8, t9, k0, k1, gp, sp, fp, ra, RR1, RR2, WR, WD, WriteReg, clk, rst); output [31:0] RD1, RD2, zero, at, v0, v1, a0, a1, a2, a3, t0, t1, t2, t3, t4, t5, t6, t7, s0, s1, s2, s3, s4, s5, s6, s7, t8, t9, k0, k1, gp, sp, fp, ra; input [4:0] RR1, RR2, WR; input [31:0] WD; input clk, rst, WriteReg; // control signal wire [31:0] WSel; wire [31:0] Q [31:0]; Decoder_5to32 U0 (.Y(WSel), .X(WR), .En(WriteReg)); Mux_32 U1 (.Y(RD1), .Sel(RR1), .X0(Q[0]), .X1(Q[1]), .X2(Q[2]), .X3(Q[3]), .X4(Q[4]), .X5(Q[5]), .X6(Q[6]), .X7(Q[7]), .X8(Q[8]), .X9(Q[9]), .X10(Q[10]), .X11(Q[11]), .X12(Q[12]), .X13(Q[13]), .X14(Q[14]), .X15(Q[15]), .X16(Q[16]), .X17(Q[17]), .X18(Q[18]), .X19(Q[19]), .X20(Q[20]), .X21(Q[21]), .X22(Q[22]), .X23(Q[23]), .X24(Q[24]), .X25(Q[25]), .X26(Q[26]), .X27(Q[27]), .X28(Q[28]), .X29(Q[29]), .X30(Q[30]), .X31(Q[31]) ); Mux_32 U2 (.Y(RD2), .Sel(RR2), .X0(Q[0]), .X1(Q[1]), .X2(Q[2]), .X3(Q[3]), .X4(Q[4]), .X5(Q[5]), .X6(Q[6]), .X7(Q[7]), .X8(Q[8]), .X9(Q[9]), .X10(Q[10]), .X11(Q[11]), .X12(Q[12]), .X13(Q[13]), .X14(Q[14]), .X15(Q[15]), .X16(Q[16]), .X17(Q[17]), .X18(Q[18]), .X19(Q[19]), .X20(Q[20]), .X21(Q[21]), .X22(Q[22]), .X23(Q[23]), .X24(Q[24]), .X25(Q[25]), .X26(Q[26]), .X27(Q[27]), .X28(Q[28]), .X29(Q[29]), .X30(Q[30]), .X31(Q[31]) ); dff Reg0 (.Q(Q[0]), .D(WD), .En(WSel[0]), .clk(clk), .rst(rst)); dff Reg1 (.Q(Q[1]), .D(WD), .En(WSel[1]), .clk(clk), .rst(rst)); dff Reg2 (.Q(Q[2]), .D(WD), .En(WSel[2]), .clk(clk), .rst(rst)); dff Reg3 (.Q(Q[3]), .D(WD), .En(WSel[3]), .clk(clk), .rst(rst)); dff Reg4 (.Q(Q[4]), .D(WD), .En(WSel[4]), .clk(clk), .rst(rst)); dff Reg5 (.Q(Q[5]), .D(WD), .En(WSel[5]), .clk(clk), .rst(rst)); dff Reg6 (.Q(Q[6]), .D(WD), .En(WSel[6]), .clk(clk), .rst(rst)); dff Reg7 (.Q(Q[7]), .D(WD), .En(WSel[7]), .clk(clk), .rst(rst)); dff Reg8 (.Q(Q[8]), .D(WD), .En(WSel[8]), .clk(clk), .rst(rst)); dff Reg9 (.Q(Q[9]), .D(WD), .En(WSel[9]), .clk(clk), .rst(rst)); dff Reg10 (.Q(Q[10]), .D(WD), .En(WSel[10]), .clk(clk), .rst(rst)); dff Reg11 (.Q(Q[11]), .D(WD), .En(WSel[11]), .clk(clk), .rst(rst)); dff Reg12 (.Q(Q[12]), .D(WD), .En(WSel[12]), .clk(clk), .rst(rst)); dff Reg13 (.Q(Q[13]), .D(WD), .En(WSel[13]), .clk(clk), .rst(rst)); dff Reg14 (.Q(Q[14]), .D(WD), .En(WSel[14]), .clk(clk), .rst(rst)); dff Reg15 (.Q(Q[15]), .D(WD), .En(WSel[15]), .clk(clk), .rst(rst)); dff Reg16 (.Q(Q[16]), .D(WD), .En(WSel[16]), .clk(clk), .rst(rst)); dff Reg17 (.Q(Q[17]), .D(WD), .En(WSel[17]), .clk(clk), .rst(rst)); dff Reg18 (.Q(Q[18]), .D(WD), .En(WSel[18]), .clk(clk), .rst(rst)); dff Reg19 (.Q(Q[19]), .D(WD), .En(WSel[19]), .clk(clk), .rst(rst)); dff Reg20 (.Q(Q[20]), .D(WD), .En(WSel[20]), .clk(clk), .rst(rst)); dff Reg21 (.Q(Q[21]), .D(WD), .En(WSel[21]), .clk(clk), .rst(rst)); dff Reg22 (.Q(Q[22]), .D(WD), .En(WSel[22]), .clk(clk), .rst(rst)); dff Reg23 (.Q(Q[23]), .D(WD), .En(WSel[23]), .clk(clk), .rst(rst)); dff Reg24 (.Q(Q[24]), .D(WD), .En(WSel[24]), .clk(clk), .rst(rst)); dff Reg25 (.Q(Q[25]), .D(WD), .En(WSel[25]), .clk(clk), .rst(rst)); dff Reg26 (.Q(Q[26]), .D(WD), .En(WSel[26]), .clk(clk), .rst(rst)); dff Reg27 (.Q(Q[27]), .D(WD), .En(WSel[27]), .clk(clk), .rst(rst)); dff Reg28 (.Q(Q[28]), .D(WD), .En(WSel[28]), .clk(clk), .rst(rst)); dff Reg29 (.Q(Q[29]), .D(WD), .En(WSel[29]), .clk(clk), .rst(rst)); dff Reg30 (.Q(Q[30]), .D(WD), .En(WSel[30]), .clk(clk), .rst(rst)); dff Reg31 (.Q(Q[31]), .D(WD), .En(WSel[31]), .clk(clk), .rst(rst)); assign zero = Q[0]; assign at = Q[1]; assign v0 = Q[2]; assign v1 = Q[3]; assign a0 = Q[4]; assign a1 = Q[5]; assign a2 = Q[6]; assign a3 = Q[7]; assign t0 = Q[8]; assign t1 = Q[9]; assign t2 = Q[10]; assign t3 = Q[11]; assign t4 = Q[12]; assign t5 = Q[13]; assign t6 = Q[14]; assign t7 = Q[15]; assign s0 = Q[16]; assign s1 = Q[17]; assign s2 = Q[18]; assign s3 = Q[19]; assign s4 = Q[20]; assign s5 = Q[21]; assign s6 = Q[22]; assign s7 = Q[23]; assign t8 = Q[24]; assign t9 = Q[25]; assign k0 = Q[26]; assign k1 = Q[27]; assign gp = Q[28]; assign sp = Q[29]; assign fp = Q[30]; assign ra = Q[31]; endmodule |
1.3.2 Sign-Extended
Sign-Extended 동작 설명
Sign-Extended는 I-type instruction에서 사용하며, 명령어의 [15:0]bit를 받아 32bit로 확장하는 역할을 한다.
module SignExt(Y, X); output [31:0] Y; input [15:0] X; assign Y[15:0] = X; assign Y[31:16] = {16{X[15]}; endmodule |
1.4 EX Stage
EX_Stage 동작 설명
EX_Stage는 ID_Stage로부터 전달된 데이터를 처리하는 과정이다. 이 단계에서는 ALUOp 제어 신호를 이용하여 ALU 제어 장치가 ALU가 수행할 연산을 결정하고, ALU에서 필요한 계산을 실행한 후, 그 결과를 다음 Stage로 전달한다. 데이터 하자드(Data Hazard)가 발생하여 Forwarding이 필요한 경우, Forwarding Unit은 Mux 선택 신호를 전달하여 ALU에서 연산을 수행할 데이터를 결정한다. 그 후 해당 데이터를 ALU로 전달하여 연산을 수행한다.
module EX_Stage( // datapath input input wire [31:0] IDtoEX_ReadData1, input wire [31:0] IDtoEX_ReadData2, // Read Data from Register File input wire [31:0] IDtoEX_Imm, // Sign extended immediate value input wire [4:0] IDtoEX_Rt, input wire [4:0] IDtoEX_Rd, // Destination Register // instruction input input wire [5:0] funct, // Function code // control inputs input wire [1:0] ALUop, input wire ALUSrc, input wire RegDst, // Forwarding inputs input wire [31:0] EXtoMEM_ALUresult, input wire [31:0] WB_ALUresult, // forwarding control input wire [1:0] ForwardA, ForwardB, // datapath output output wire [31:0] ALUresult, output wire [31:0] EX_ReadData2, output wire [4:0] RegDest // Write data destination ); wire [31:0] ALUSrc_out, SL2_out; wire [3:0] ALUcontrol; wire [31:0] FWA_out, FWB_out; // ALU Control ALU_Control alu_control (.ALUop(ALUop), .funct(funct), .ALUctrl(ALUcontrol)); // ALU ALU alu ( .operandA(FWA_out), .operandB(ALUSrc_out), .aluControl(ALUcontrol), .result(ALUresult) ); // ALU Source MUX Mux_2 mux_ALUSrc (.X0(FWB_out), .X1(IDtoEX_Imm), .sel(ALUSrc), .Y(ALUSrc_out)); // Forward A Mux_4 mux_FWA ( .X0(IDtoEX_ReadData1), .X1(WB_ALUresult), .X2(EXtoMEM_ALUresult), .X3(/*None*/), .sel(ForwardA), .Y(FWA_out) ); // Forward B Mux_4 mux_FWB ( .X0(IDtoEX_ReadData2), .X1(WB_ALUresult), .X2(EXtoMEM_ALUresult), .X3(/*None*/), .sel(ForwardB), .Y(FWB_out) ); // Select Destination Register for writing data Mux5_2 mux_RegDst (.X0(IDtoEX_Rt), .X1(IDtoEX_Rd), .sel(RegDst), .Y(RegDest)); assign EX_ReadData2 = FWB_out; endmodule |
1.4.1 ALU (Arithmetic Logic Unit)
ALU 동작 설명
ALU Control Unit으로부터 ALU Control 신호를 전달받아 제어신호에 해당하는 연산을 진행하는 조합회로이다. 현재 구현된 연산과 ALU Control 신호의 관계는 다음과 같다.
ALU Control Line | Operation |
0000 | and |
0001 | or |
0010 | add |
0110 | sub |
0111 | slt |
1100 | nor |
module ALU( input wire [31:0] operandA, input wire [31:0] operandB, input wire [3:0] aluControl, output reg [31:0] result ); always @(operandA or operandB or aluControl) begin casex (aluControl) 4'b0000: result = operandA & operandB; // and 4'b0001: result = operandA | operandB; // or 4'b0010: result = operandA + operandB; // add 4'b011x: begin result = operandA - operandB; // sub or slt if(aluControl[0] == 1) // slt if(operandA < operandB) result = 32'd1; else result = 32'd0; end 4'b1100: result = ~(operandA | operandB); // nor default: result = 32'h0; endcase end endmodule |
1.4.2 ALU Control
ALU Control Unit 동작 설명
ALUOp 제어 신호와 Instruciton 중 funct 영역을 이용하여 ALU에서 실행할 연산을 정해주는 제어 신호를 ALU에 전달한다. Instruction별로 ALUOp와 funct 코드에 따른 ALU control line은 다음과 같다.
Instructoin | ALUop | funct | ALUcontrol |
lw | 0 0 | X X X X X X | 0 0 1 0 |
sw | 0 0 | X X X X X X | 0 0 1 0 |
beq | 0 1 | X X X X X X | 0 1 1 0 |
add | 1 0 | 1 0 0 0 0 0 | 0 0 1 0 |
sub | 1 0 | 1 0 0 0 1 0 | 0 1 1 0 |
and | 1 0 | 1 0 0 1 0 0 | 0 0 0 0 |
or | 1 0 | 1 0 0 1 0 1 | 0 0 0 1 |
slt | 1 0 | 1 0 1 0 1 0 | 0 1 1 1 |
module ALU_Control( input wire [1:0] ALUop, input wire [5:0] funct, output reg [3:0] ALUctrl ); always @(ALUop or funct) begin case (ALUop) 2'b00: ALUctrl = 4'b0010; // lw, sw 2'b01: ALUctrl = 4'b0110; // beq 2'b10: if (funct == 6'b100000) // add ALUctrl = 4'b0010; else if (funct == 6'b100010) // sub ALUctrl = 4'b0110; else if (funct == 6'b100100) // and ALUctrl = 4'b0000; else if (funct == 6'b100101) // or ALUctrl = 4'b0001; else if (funct == 6'b101010) // slt ALUctrl = 4'b0111; else if (funct == 6'b100111) // nor ALUctrl = 4'b0000; else ALUctrl = 4'b0010; default: ALUctrl = 4'd0; endcase end |
1.5 MEM Stage
MEM_Stage 동작 설명
MEM_Stage에서는 데이터 메모리에 접근하여 메모리에 데이터를 쓰거나 읽는 작업을 수행하는 단계이다. lw, sw와 같은 Instruction에 의해서 실행되는 영역이며, 메모리 접근이 없는 경우 ALU의 결과를 그대로 다음 Stage로 넘겨준다.
module MEM_Stage( // system clock input wire clk, // datapath input input wire [31:0] EXtoMEM_ALUresult, input wire [31:0] EXtoMEM_ReadData2, input wire [4:0] EXtoMEM_RegDest, // control input input wire MemWrite, input wire MemRead, // datapath output output wire [31:0] MEM_ReadData, output wire [31:0] MEM_ALU_result, output wire [4:0] MEM_RegDest, // memory output output wire [31:0] output_memory8, output_memory9, output_memory10, output_memory11, output wire [31:0] output_memory12, output_memory13, output_memory14, output_memory15 ); // Data Memory DataMemoryUnit Data_MEM ( .clk(clk), .memWrite(MemWrite), .memRead(MemRead), .address(EXtoMEM_ALUresult), .writeData(EXtoMEM_ReadData2), .readData(MEM_ReadData), .memory8(output_memory8), .memory9(output_memory9), .memory10(output_memory10), .memory11(output_memory11), .memory12(output_memory12), .memory13(output_memory13), .memory14(output_memory14), .memory15(output_memory15) ); // select signal of PC source assign MEM_RegDest = EXtoMEM_RegDest; assign MEM_ALU_result = EXtoMEM_ALUresult; endmodule |
1.5.1 Data Memory
Data Memory 동작 설명
Data Memory는 프로그램을 실행하면서 사용되는 데이터들이 저장되어있는 메모리이다. ALU에서 계산된 메모리 주소에 접근하여 메모리에 데이터를 읽어오거나 해당 메모리에 데이터를 쓰는 동작을 한다. 현재 프로젝트에서는 프로세서의 동작에 초점을 두고 구현하고 있기 때문에 메모리는 레지스터들을 이용하여 간단하게 구현하였다. lw, sw instruction만 구현하였고 ALU에서 구현한 연산에 의해 메모리는 Byte Address가 아닌 Word Address 방식으로 접근하도록 구현하였다.
module DataMemoryUnit( input wire clk, input wire memWrite, input wire memRead, input wire [31:0] address, input wire [31:0] writeData, output reg [31:0] readData, output wire [31:0] memory8, memory9, memory10, memory11, output wire [31:0] memory12, memory13, memory14, memory15 ); reg [31:0] memory [0:1023]; always @(negedge clk) begin if (memWrite) memory[address] <= writeData; if (memRead) readData <= memory[address]; end initial begin memory[8] = 32'h0000000a; memory[9] = 32'h0000000b; memory[10] = 32'h0000000c; memory[11] = 32'h0000000d; memory[12] = 32'h0000000e; memory[13] = 32'h0000000f; memory[14] = 32'h00000003; memory[15] = 32'h00000006; end assign memory8 = memory[8]; assign memory9 = memory[9]; assign memory10 = memory[10]; assign memory11 = memory[11]; assign memory12 = memory[12]; assign memory13 = memory[13]; assign memory14 = memory[14]; assign memory15 = memory[15]; endmodule |
1.6 WB Stage
WB_Stage 동작 설명
WB_Stage에서는 Instruction 실행이 완료되고 결과를 Register File에 쓰는 단계이다. 연산결과 또는 메모리로부터 읽어온 데이터를 목적지에 해당하는 레지스터에 업데이트한다.
module WB_Stage( // datapath input input wire [31:0] MEMtoWB_ReadData, input wire [31:0] MEMtoWB_ALU_result, input wire [4:0] MEMtoWB_RegDest, // control input input wire MemtoReg, // datapath output output wire [31:0] WB_WriteReg, output wire [4:0] WB_RegDest ); Mux_2 mux_WB ( .X0(MEMtoWB_ReadData), .X1(MEMtoWB_ALU_result), .sel(MemtoReg), .Y(WB_WriteReg) ); assign WB_RegDest = MEMtoWB_RegDest; endmodule |
'전공 이야기 > Digital System Design' 카테고리의 다른 글
#6 MIPS Pipeline Processor Design (4) - Hazard, Top module (0) | 2023.12.26 |
---|---|
#5 MIPS Pipeline Processor Design (3) - Pipes, Control (0) | 2023.12.26 |
#3 MIPS Pipeline Processor Design (1) - IF Stage, Sub Unit (0) | 2023.12.26 |
#2 Verilog (0) | 2023.12.24 |
#1 FPGA (0) | 2023.12.24 |