전공 이야기/Digital System Design

#5 MIPS Pipeline Processor Design (3) - Pipes, Control

[감자] 2023. 12. 26. 20:00

1.7 Pipe Register

 

 파이프 레지스터(pipe register)는 컴퓨터 아키텍처에서 파이프라인(pipeline) 구조를 구성하는 레지스터이다. 파이프 레지스터는 Pipeline stage 간의 데이터 흐름을 제어하고, 각 스테이지에서 수행되는 동작의 결과를 일시적으로 저장하는 역할을 한다.

 

 파이프 레지스터는 주로 명령어가 프로세서의 다양한 단계를 거치면서 실행되는 동안 발생하는 지연을 최소화하기 위해 사용된다. 각 스테이지는 한 번에 하나의 명령어를 처리하며, 다음 명령어는 이전 명령어가 다음 스테이지로 이동할 때까지 현재 스테이지에 머무르게 됩니다.

 

 이러한 파이프 레지스터는 CPU의 성능을 향상시키기 위해 사용되며, 파이프라인의 깊이와 관련이 있다. 파이프라인이 더 깊으면 명령어의 처리 속도는 빨라질 수 있지만, 파이프라인 깊이가 깊을수록 지연이 증가한다. 이번에 설계한 processor의 파이프 개수는 총 4개로 5단계 pipeline으로 진행한다.

 

 

 1.7.1 IF_ID Regster

 

IF_ID Regster 동작 설명

 IF_ID Register에서는 IF stage에서 넘어온 Program Counter값과 Instruction을 저장하는 레지스터다. 리셋 신호(rst) 혹은 IF_Flush신호가 들어오면 모든 값을 0으로 변환시켜준다. PC와 Inst값은 매 클락 prev_IF_PC, prev_IF_inst라는 레지스터에 추가적으로 저장되고 있으며 Stall이 일어날 경우 다음 신호에서 prev의 값이 대신 사용된다. 기본적으로는 Hazard Detection Unit으로부터 IF_ID_Write 신호가 상시 전달되고 있으며 Hazard 가 일어나 IF_ID_Write 신호가 꺼지면 prev_IF_PC, prev_IF_inst 값이 출력으로 대신 연결된다.

 

module IFtoID_Register (
    input wire clk, rst,
    input wire [31:0] IF_PC, IF_inst,
    input wire IF_ID_Write, IF_Flush,
   
    output reg [31:0] ID_PC, ID_inst);
 
 
    reg [31:0] prev_IF_PC;      //이전 저장
    reg [31:0] prev_IF_inst;    //이전 저장
 
 
  always @ (posedge clk or posedge rst or posedge IF_Flush) begin
    if (rst || IF_Flush) begin
        ID_PC <= 0;
        ID_inst <= 0;
        prev_IF_PC <= 0;      //이전 저장
        prev_IF_inst <= 0;
    end
   
    else begin
        if(IF_ID_Write) begin
            ID_PC <= IF_PC;
            ID_inst <= IF_inst;
        end
        else begin
            ID_PC <= prev_IF_PC;
            ID_inst <= prev_IF_inst;
        end
      prev_IF_PC <= IF_PC;
      prev_IF_inst <= IF_inst;
    end
  end
endmodule

 

 

 

 

 

1.7.2 ID_EX Regster

 

ID_EX Regster 동작 설명

 ID_EX Register에서는 ID stage에서 넘어온 Decode된 instruction 값(ID_ReadData1, ID_ReadData2, ID_Rs, ID_Rt)이 전달되어 저장된다. 추가로 Control signal에서 전달된 3stage, 4stage, 5stage에서 사용될 Control 신호들도 저장된다. 매 클락 신호(clk)가 올라갈 때 작동하며, 리셋 신호(rst)가 들어오면 모든 값이 0으로 변경한다.

 

module IDtoEX_Register(
    input wire clk,
    input wire rst,
   
    //input from ID_Stage
    input wire [31:0] ID_ReadData1, ID_ReadData2,
    input wire [31:0] ID_Imm,
    input wire [4:0] ID_Rs, ID_Rt, ID_Rd,
    input wire [5:0] funct,
    //input from Control
    input wire [1:0] ALUOp,
    input wire ALUSrc, RegDst, MemRead, MemWrite, RegWrite, MemtoReg,
 
 
    // outputs to EX_Stage
    output reg [31:0] IDtoEX_ReadData1,IDtoEX_ReadData2, // Read Data from Register File
    output reg [31:0] IDtoEX_Imm, // Sign extended immediate value
    output reg [4:0] IDtoEX_Rt, IDtoEX_Rd, // Destination Register
 
    // Control unit to use in 3 stage
    output reg [1:0] EX_ALUOp,
    output reg EX_ALUSrc,
    output reg EX_RegDst,
   
 
    // outputs to Forwarding unit
    output reg [4:0] Forwarding_Rs,
   
   
    // outputs to ALU control
    output reg [5:0] ALUcontrol_funct, // Function code
   
    // outputs to next pipe
    output reg IDtoEX_MemRead, IDtoEX_MemWrite,     // stage 4?????? ??????
    output reg IDtoEX_RegWrite, IDtoEX_MemtoReg      // stage 5?????? ??????
    );
   
    always @ (posedge clk or posedge rst) begin
    if (rst) begin
        IDtoEX_ReadData1 <= 0;
        IDtoEX_ReadData2 <= 0;
        IDtoEX_Imm <= 0;
        IDtoEX_Rt <= 0;
        IDtoEX_Rd <= 0;
        Forwarding_Rs <= 0;
        ALUcontrol_funct <= 0;
        EX_ALUOp <= 0;
        EX_ALUSrc <= 0;
        EX_RegDst <= 0;
        IDtoEX_MemRead <= 0;
        IDtoEX_MemWrite <= 0;
        IDtoEX_RegWrite <= 0;
        IDtoEX_MemtoReg <= 0;
    end
    else begin
        IDtoEX_ReadData1 <= ID_ReadData1;
        IDtoEX_ReadData2 <= ID_ReadData2;
        IDtoEX_Imm <= ID_Imm;
        IDtoEX_Rt <= ID_Rt;
        IDtoEX_Rd <= ID_Rd;
        EX_ALUOp <= ALUOp;
        EX_ALUSrc <= ALUSrc;
        EX_RegDst <= RegDst;
        Forwarding_Rs <= ID_Rs;
        ALUcontrol_funct <= funct;
        IDtoEX_MemRead <= MemRead;
        IDtoEX_MemWrite <= MemWrite;
        IDtoEX_RegWrite <= RegWrite;
        IDtoEX_MemtoReg <= MemtoReg;
    end
  end
endmodule

 

 

 

 

1.7.3 EX_MEM Regster

 

 EX_MEM Regster 동작 설명

 

 EX_MEM Register에서는 EX stage에서 구한 계산의 결과값, Register의 목적지, Stage 4와 Stage 5에서 사용할 Control signal 등이 저장되어이 있는 파이프 레지스터다. ID_EX Register와 마찬가지로 매 클락 신호(clk)가 올라갈 때 작동하며, 리셋 신호(rst)가 들어오면 모든 값이 0으로 변경한다.

 

module EXtoMEM_Register(
    input wire clk, rst,
   
    // datapath input
    input wire [31:0] EX_ALUresult,
    input wire [31:0] EX_ReadData2,
    input wire [4:0] EX_RegDest,
 
    // control input
    // MEM
    input wire EX_MemRead,
    input wire EX_MemWrite,
    // WB
    input wire EX_MemtoReg,
    input wire EX_RegWrite,
 
    // datapath output
    output reg [31:0] EXtoMEM_ALUresult,
    output reg [31:0] EXtoMEM_ReadData2,
    output reg [4:0] EXtoMEM_RegDest,
   
    // control output
    output reg MEM_MemRead,
    output reg MEM_MemWrite,
    // WB
    output reg MEM_MemtoReg,
    output reg MEM_RegWrite
);
 
always @(posedge clk or posedge rst) begin
        if(rst) begin
            // datapath
            EXtoMEM_ALUresult <= 0;
            EXtoMEM_ReadData2 <= 0;
            EXtoMEM_RegDest <= 0;
   
            // control
            MEM_MemRead <= 0;
            MEM_MemWrite <= 0;
            MEM_MemtoReg <= 0;
            MEM_RegWrite <= 0;
        end
        else begin
            // datapath
            EXtoMEM_ALUresult <= EX_ALUresult;
            EXtoMEM_ReadData2 <= EX_ReadData2;
            EXtoMEM_RegDest <= EX_RegDest;
   
            // control
            MEM_MemRead <= EX_MemRead;
            MEM_MemWrite <= EX_MemWrite;
            MEM_MemtoReg <= EX_MemtoReg;
            MEM_RegWrite <= EX_RegWrite;
        end
    end
 
endmodule

 

 

 

 

 

1.7.4 MEM_WB Regster

 

 MEM_WB Regster 동작 설명

 

 MEM_WB Register에서는 lw instruction이 들어왔을 때 Data Memory를 통해 읽은 Data 값, ALU를 통해 나온 계산의 결과 값, 새롭게 쓰여질 레지스터의 주소 및 WB 레지스터에서 사용되는 Control signal에 대한 정보를 저장한다. 해당 레지스터도 마찬가지로 클락 신호(clk)와 리셋 신호(rst)에 따라 값이 0으로 갱신되거나, 이전 stage에서 값을 받아온다.

 

module MEMtoWB_Register (
    input clk, rst,
 
    // datapath input
    input wire [31:0] MEM_ReadData,
    input wire [31:0] MEM_ALU_result,
    input wire [4:0] MEM_RegDest,
// control input
    input wire MEM_MemtoReg,
    input wire MEM_RegWrite,
 
    // datapath output
    output reg [31:0] MEMtoWB_ReadData,
    output reg [31:0] MEMtoWB_ALU_result,
    output reg [4:0] MEMtoWB_RegDest,
 
    // control output
    output reg MEMtoWB_MemtoReg,
    output reg MEMtoWB_RegWrite
 
);
 
    always @(posedge clk) begin
        // datapath
        MEMtoWB_ReadData <= MEM_ReadData;
        MEMtoWB_ALU_result <= MEM_ALU_result;
        MEMtoWB_RegDest <= MEM_RegDest;
 
        // control
        MEMtoWB_MemtoReg <= MEM_MemtoReg;
        MEMtoWB_RegWrite <= MEM_RegWrite;
    end
 
endmodule

 

 

 

 

 

 

1.8 Control

 

 

Control unit 동작 설명

 

 Control Unit은 ID stage에서 Opcode를 받아 각 instruction에 맞는 Control Signal을 보내주는 역할을 한다. 해당 Processor에서는 총 9가지의 Control Signal 곧 ALUSrc,       RegDst, ALUOp, MemRead, MemWrite, RegWrite, MemtoReg, Branch, IF_Flush를 사용한다.

 

 

ALUSrc는 Sign-extended Unit을 통해 넘어온 값과, Register File의 Output ReadData2로부터 나온 값 중 어떤 값을 ALU에서 계산하게 할 지를 결정한다.

RegDst는 instruction의 [20:16]bit와 [15:11]bit 중에서 목적지를 결정한다.

ALUOp는 ALU Control을 통해 add, sub, and, or, nor 중 어떤 계산을 해야할지를 결정한다. 이는 ALU 부분에 더욱 자세히 기술되어 있다.

MemRead는 Data Memory가 받은 주소값을 통해 데이터를 읽을 수 있도록 하는 신호로 lw instruction에서 사용한다.

MemWrite는 Data Memory에 값을 갱신하게 하는 신호로 sw instruction에서 사용한다.

Branch는 Beq instruction을 통해 Program Counter의 값을 이동시킬 때 사용하는 신호이다.

IF_Flush는 Hazard가 발생한 경우, IF_ID Register의 값을 0으로 만들어 Stall이 일어나게 한다.

 

 

각 instruction에 따른 Control signal 값은 다음 표와 같다.

 

Instruction Opcode Control signal
lw 100011 ALUSrc = 1'b1;
MemRead = 1'b1;
RegWrite = 1'b1;
sw 101011 ALUSrc = 1'b1;
MemWrite = 1'b1;
beq 000100 ALUOp = 1'b1;
      Branch = 1’b1; (branch_equal이 1이면)
IF_Flush = 1'b1; (Branch가 1이면)
MemtoReg = 1'b1;
addi 001000 ALUSrc = 1'b1;
ALUOp = 2'b10;
RegWrite = 1'b1;
MemtoReg = 1'b1;
R-type 000000 ALUOp = 2'b10;
RegDst = 1'b1;
RegWrite = 1'b1;
MemtoReg = 1'b1;

쓰여있지 않은 값들은 0이다.

 

 

 beq instruction의 경우 두 값이 같아야 주소값의 이동이 일어나므로 먼저 두 값이 같은지 여부를 받고 같을 때에만 Branch Signal이 켜진다. 또한 Branch가 켜지면 값을 Stall 시킬 수 있도록 IF_Flush에 값을 준다.

 

module Control(
    input wire rst,
    input wire hazard_detected,
    input wire [5:0] opcode,
    input wire branch_equal,
    input wire [31:0] IF_PC,
    output reg [1:0] ALUOp,
    output reg ALUSrc, RegDst,
 
    output reg MemRead, MemWrite,
    output reg RegWrite, MemtoReg,
    output reg Branch,
    output reg IF_Flush
);
   
    always @* begin
      ALUSrc = 1'b0;
      RegDst = 1'b0;
      ALUOp = 2'b00;
      MemRead = 1'b0;
      MemWrite = 1'b0;
      RegWrite = 1'b0;
      MemtoReg = 1'b0;
      Branch = 1'b0;
      IF_Flush = 1'b0;
   
      if(!hazard_detected && !rst && !(IF_PC == 4)) begin
        case(opcode)
          6'b100011 : begin // LW instruction
                        ALUSrc = 1'b1;
                        MemRead = 1'b1;
                        RegWrite = 1'b1;
                        //MemtoReg = 1'b1;
                      end
          6'b101011 : begin // SW instrtuction      
                        ALUSrc = 1'b1;
                        MemWrite = 1'b1;
                      end
 
6'b001000 : begin // Addi instruction     
                        ALUSrc = 1'b1;
                        ALUOp = 2'b10;
                        RegWrite = 1'b1;
                        MemtoReg = 1'b1;
                      end
          6'b000100 : begin // BEQ instruction     
                        ALUOp = 1'b1;
                        Branch = (branch_equal == 1);
                        MemtoReg = 1'b1;
                        if(Branch) begin
                          IF_Flush = 1'b1;
                        end
                      End
 
          6'b000000 : begin // R-type instruction      
                        ALUOp = 2'b10;
                        RegDst = 1'b1;
                        RegWrite = 1'b1;
                        MemtoReg = 1'b1;
                      end
          default : begin
                          ALUSrc = 1'b0;
                          RegDst = 1'b0;
                          ALUOp = 2'b00;
                          MemRead = 1'b0;
                          MemWrite = 1'b0;
                          RegWrite = 1'b0;
                          MemtoReg = 1'b0;
                          Branch = 1'b0;
                          IF_Flush = 1'b0;
                     end
        endcase
      end
    end
endmodule