본문 바로가기
시스템 반도체/컴퓨터 구조 (RISC-V)

컴퓨터 구조 cpu 동작 ( Fetch, Decodeing, Excution, Data processing, Memory Access, Branch, cpu 동작 순서 )

by 전컴반 2022. 3. 8.
반응형
CPU 동작

 

1. Fetch

- PC (Program Counter)가 가리키는 메모리의 주소에 접근하여 해당 명령어의 Machine Code CPU Register로 읽어오는 동작이다.

 

2. Decoding

- CPU Register로 가져온 Machine Code Opcode Function 정보를 이용하여 어떤 명령어인지 확인하는 동작으로 operands 레지스터 번호 혹은 imm을 가져온다.

 

3. Excution

- 알아낸 동작에 맞게 ALU을 이용하여 연산을 수행하는 동작이다. 이 단계는 데이터 처리, 메모리 접근, Branch를 결정한다.

 

4. Memory

- 메모리에 접근하여 Load/Store를 하는 동작이다.

5. WriteBack
- 메모리로부터 Load/Store한 후에 해당 결과에 따라 다시 CPU로 결과를 보내고 PC를 업데이트하는 동작이다.

 

Feth 동작을 살펴보자

 

Fetch

 

32bit일 때 PC는 32개의 F/F으로 구성돼 있다. reset을 이용하여 처음 시작 위치를 정할 수 있는데 RISC-V에선 0으로 정한다. 아래 그림을 보자. D플립플롭 32개로 돼 있는 PC레지스터이다.

 

 

먼저 res로 지정한 위치의 주소를 전달하는데 클럭이 뛰고 바로 0이 전달되는 게 아니라, delay가 있다. 이유는 플립플롭의 동작 시간이 있어 delay가 생긴다. 

 

주소를 전달됨과 동시에 ALU를 이용하여 +4를 한다. 즉, 다음 주소를 PC에 업데이트시키는 것이다.

next_pc에 주황색도 연산을 하느라 delaly가 생기는 것이다. 그렇게 다음 주소를 연산하여 대기하고 있다가 clk이 튀면 그때 F/F이 동작하여 다음 PC 주소를 전달하는 방식이다. 

 

verilog로는 아래와 같이 표현할 수 있다

 

module rv32i_cpu (
    input clk;
    input reset;
    output reg [31:0] pc 
    );
    
    always @(posedge clk, posedge reset)
    begin
        if (reset) pc <= 32'h0;
        else pc <= pc + 4;
    end
endmodule

 

Decodeing

 

fetch를 통해 가져온 머신 코드에서 opcode를 통해 어떤 명령어인지 알아낸다. 그리고 어떤 명령어인지에 따라 operands 레지스터 번호 혹은 imm을 sign extension 하여 ALU로 집어넣는다.

다음으로 Control Unit을 이용하여 control signal을 구현한다. 이때 기준은 opcode, funct7, funct3로 판단한다. ALU나 MUX와 같은 선택 신호가 필요한 애들은 Control Unit으로 판단한다.  

 

 

sign extension

module sign_ext(input  [11:0] a,
                output [31:0] y);
    
    assign y = {{20{a[11]}}, a};

endmodule

 

Execution

 

Execution에선 3단계의 실행이 이루어진다.

첫 번째로 Data processing이 이루어진다. 사실 앞선 모든 동작들도 이에 해당한다.

두 번째로 memory access가 동작한다. 메모리로부터 읽고 쓰는 것에 해당된다

세 번째로 Branch가 실행된다

 

한 단계씩 보자

 

Data processing

 

R-type과 I-type으로 구분할 수 있다. 레지스터가 정해져 있는 타입과 imm타입이 있기 때문이다. 근데 어떻게 구분하여 ALU로 들어가는 입력을 정할 수 있을까?

 

 

rs1은 둘 다 존재한다. 이에 무조건 ALU로 바로 입력을 줄 수 있다.

하지만 rs2를 입력으로 넣을지 imm을 입력으로 넣을지는 타입에 따라 다르다. 어떻게 해야 할까? 바로 중간에 MUX를 이용하여 선택적으로 하나의 입력을 넣을 수 있다. MUX가 있으니 당연히 sel신호가 필요하다. sel 신호를 ALUSrc가 0일 때는 rs2을 출력하고 1일 때는 imm을 내보내는 것이다. 

 

 

Memory access

 

I-type과 s-type이 있다. 이유는 메모리를 읽고 쓰는 타입이 다르기 때문이다.

 

 

I-type는 lw는 읽어 저장하는 것이 목적이므로 저장 목적지 레지스터가 있다. 여기서 중요한 건 위에서 봤던 것처럼 ALU를 거쳐 나오는 데이터는 Data processing에서도 있다. 무슨 말인지 잘 모르겠다.

예를 들어, add를 했을 때 ALU를 거쳐 나온 결과는 메모리 값이 들어가는 것이 아니라 바로 레지스터로 들어간다.

하지만 lw를 하면 ALU를 한 뒤에 메모리로 갔다가 메모리 값이 레지스터로 들어가는데 이걸 구분해야 한다. 그래서 MUX가 추가된다. 역시 sel 신호가 존재한다.

 

s-type는 sw는 메모리에 저장하면 끝이라 목적지 레지스터가 없다. sw는 연산 결과의 주소에 레지스터의 값을 집어넣는 것이다. 예를 들어 sw t2, 8(s3)라고 하면 s3 + 8 한 주소에 레지스터 t2의 값을 집어넣는다. 그러니 t2의 값은 따로 연산을 하지 않으니 바로 메모리에 연결한다. 아래서 rs2의 빨간 선에 해당된다.

 

Branch

 

I-type과 j-type이 있다. Conditional과 UnConditional이 있기 때문이다.

 

I-type은 Conditional일 때인데 아래 imm을 뽑아낸 다음에 다시 재배열을 거쳐서 sing extension 한 다음에 현재의 PC에 sing extension 한 값을 더하여 이동한다. 만약 조건이 맞지 않는다면 PC + 4를 진행하여 계속 동작한다.

 

 

먼저 sign extension 한 값을 왼쪽으로 1만큼 shift 해야 한다. 왜냐면 LSB는 변하지 않으니 컴파일하지 않기 때문이다. 

그리곤 현재의 PC값과 adder를 통해 더한다. 

그 다름으로는 PC +4 한 값을 다음 PC로 해야 하는지, PC + sing extension 한 값을 다음 PC로 해야 하는지 정해야 한다.

즉, 조건이 맞는지 아닌지 판단해야 한다. 그러니 당연히 MUX가 필요하다. 이때 sel신호는 어떻게 정해지냐면, Control Unit에서 Branch 명령어와 조건이 맞다면 ALU에서의 출력 값과 AND 게이트를 통해 1을 보낸다. 그러면 label의 주소로 이동한다. 0이라면 PC + 4를 다음 PC로 보낸다. 

 

 

 

j-type은 Unconditional일 때 사용하는데. 마찬가지로 imm을 추출하여 지정된 곳으로 PC에 지정한 imm을 집어넣는다.

 

 

beq는 파란색 jump는 빨간색으로 그렸다. beq와 마찬가지로 PC + 4를 정해야 하므로 retrun 주소를 정한다.

동작은 똑같다.  

 

반응형

댓글