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

컴퓨터 구조 Pipelining hazards ( stage수 차이, Structure hazards, Data hazard, Control hazard, Critical path, pipeline, 파이프라인, 구조적 해져드, 데이터 해져드, 컨트롤 해져드 )

by 전컴반 2022. 3. 16.
반응형

single cycle은 딜레이가 길다. 그래서 산업에서는 쓰지 않는다. 그렇다면 어떻게 더 나은 결과를 도출할 수 있을까??

2가지 정도 생각할 수 있다.

 

1. 반도체 기술이 발전하는 것이다. 트랜지스터의 사이즈를 줄이는 것이다. 즉, 공정 기술의 발전

2. 새로운 컴퓨터 구조를 고안해 내는 것이다. 이것이 바로 Pipelineing이다. 이번엔 Pipelining에 대해 알아보자

 

Pipelining

 

Pipelining이란, 명령어를 하나하나씩 실행하는 게 아니라 병렬적으로 실행하는 것이다.

응답 시간에 대해선 single cycle과 다를 게 없지만 throughput에는 더 나은 성능을 낸다. 정확한 설명은 이전 포스팅을 참고하면 된다.

 

https://wpaud16.tistory.com/214?category=943795 

 

컴퓨터 구조 single cycle, pipeline 차이 ( cpu동작 순서, Respone time, Throughput )

마이크로 아키텍처 ISA에는 3가지 정도의 동작 방법이 있다. 1. Single cycle : 한 번의 클럭에 하나의 명령어를 실행한다. 2. Multi- cycle 3. Pipeline : 모든 명령어의 과정을 병렬적으로 실행한다. 이번에

wpaud16.tistory.com

 

여기선 Pipelining은 5 단계의 동작으로 나눠져 이루어진다고 가정하자.

 

IF (Instruction Fetch) -> ID (Insetuction Decoding) -> EX (Execute address calculation) -> MEM (Memory Access) -> WB(Write Back) 

+ 각각의 단계를 구분 짓기 위해 F/F이 들어간다. 

 

저작권침해의사없음

 

각각의 명령어 동작에서 메모리의 읽고 쓰기를 어떻게 나눠서 볼까 했을 때,

오른쪽으로 검은색은 read operand이다.

왼쪽으로 검은색은 write operand이다.

 

단순히 동작하면 좋겠지만 pipeline에는 여러 문제가 있다. 이런 문제를 Hazard라고 한다.

 


 

그 전에 stage 수에 따른 장단점을 알고 가자. 이게 겁나 헷갈렸다.

 

5-stage는 5 clk만에 이루어진다는 의미다. 5-stage만 존재하는 것이 아니라 3-stage, 2-stage와 같이 여러 stage로 나눠서 볼 수 있는데. 어떤 차이가 있을까?

 

5-stage와 3-stage를 비교하면

5-stage는 F-D-E-M-W으로 나눠서 동작하고, 3-stage는 F-D-E으로 나눠서 동작한다.

 

5-stage에서는 동작을 쪼갰기 때문에 한 클럭마다 약 1초가 든다고 가정하면, 동작 하나를 마치는데 5초가 걸린다.  

3-stage에서는 동작을 덜 쪼갰기 때문에 F-D단계는 똑같겠지만 E단계에서는 M-W 동작까지 해야 하기 때문에 3초가 소요된다. 그러니 F-D 동작에서도 어쩔 수 없이 3초의 클럭을 사용해야 한다. 왜냐면 제일 긴 클럭을 기준으로 하기 때문이다. 즉, 동작 하나를 마치는데 9초가 걸린다는 의미다. 

 

 

이렇게 되면 throughput에 차이가 생기는데 5-stage가 3-stage보다 더 높은 throughput을 가진다. 즉 단위 시간당 더 많은 동작을 할 수 있다는 의미다. 그럼 무조건 stage의 수를 늘리는게 좋은 거 아닌가 싶다.

 

하지만 적은 stage 수를 사용하면 사용하는 F/F가 더 적게 들어가니 간소화될 수 있고 소형화될 수 있다. 그러니 응답시간이 크게 중요하지 않다면 3-stage를 사용하는 것이 더 합리적일 수도 있다.

 

Hazards

 

Pipeline에는 

3가지 문제가 있다.

 

1. Structure hazard

2. Data hazard

3. Control hazard

 

하나씩 보자

 

Structure hazard

 

구조적 해저드는 동시에 뭔가를 수행했을 때 하나의 resource를 두고 서로 충돌하는 걸 말한다. CPU와 메모리는 1:1로 데이터를 주고받기에 이런 문제가 생긴다.

 

만약, lw 명령어에서 MEM과 add 명령어에서 IF가 같은 time clk에 메모리로 접근한다면 어떻게 해야 할까?? 간단하다 그냥 서로 분리된 포트를 사용하여 길을 만들어주면 된다. 즉, 새로운 길을 만들어주면 된다.

 

Data hazard

 

데이터 해저드는 바로 예를 보는 게 좋을 것 같다. 

 

 

위에서 s0에 대해 어떻게 할까? add 명령어가 끝나야 sub 명령어에서 s0를 쓰는데 말이다. 

add 명령어 WB 동작하기 전에 sub 명령어 ID동작에서 필요한 문제가 생긴다. 우린 add동작이 끝난 s0가 필요하다. 이때 Forwarding을 이용한다.

 

Forwarding이란 무엇일까? WB가 끝날 때까지 기다리지 않고 add 명령어의 EX에서 나온 값을 바로 sub 명령어의 EX에 넣어주는 것이다. 어차피 EX 한 값을 WB 하기 때문이다. 

 

Forwarding에도 2가지 경로가 있는데 선택할 수 있다.

 

 

1.

클럭 전에 입력을 주는 것이다. 위의 그림에선 파란색에 해당된다. 조금 더 자세히 보면, F/F 전에 Forwarding 하는 것이다. 만약 E에서 D으로 입력을 준다고 하면, 기본적으로 연결돼 있는 레지스터 출력과 새로운 Forwarding 중에 하나를 선택해야 하기 때문에 MUX가 들어간다.

 

2.

클럭 직후에 입력을 주는 방법이다. 위에선 주황색에 해당된다. 이 단계 역시 메모리로 들어가기 전의 출력이 E단계의 입력으로 들어가는데 역시 MUX로 선택하여 들어간다.

+ 모든 F/F에는 Clk이 연결돼 있다는 걸 기억하자.

 

 

근데 MUX의 Control signal은 어떻게 확인할 수 있을까?? 그건 앞의 명령어에서 destination과 다음 혹은 그다음 명령어의 sorce를 비교하여 Forwarding이 필요한지 안 한지 판단한다.

 

그럼 언제 비교할까?? 

첫 번째 명령어의 목적지와 소스 레지스터를 알게 되는 Decoding stage가 끝난 다음부터 비교할 수 있다.

두 번째 명령어의 레지스터도 Fetch stage에서 알긴 알 수 있기 때문이다.

즉, Decoding stage에서 나온 destination register이 MEM 혹은 WB stage에서 나와 EX단으로 돌아가고 Fetch stage 혹은 ID stage에서의 soure registers를 EX단에서 Forwarding Unit이 비교하여 control signal을 발생한다.

 

다른 단계들에서의 forwarding 또한 유사하게 이루어진다.

 

 

근데 만약 add가 아니라 lw동작이라면 얘기가 달라진다. lw명령어는 EX단계가 아니라 MEM 단계가 끝나야 s0 데이터를 쓸 수 있기 때문이다. 이런 경우에는 단순 Forwarding으로 해결할 수 없다.

이럴 땐 어쩔 수 없이 sub명령어의 ID단계에서 한 클럭 쉬어야 한다. 버블을 집어넣는 것이다. 전문 용어로는 이걸 Hardware interlock이라 한다. (Load-Use Case)

 

컴파일러는 이런 걸 알기 때문에 최대한 hardware interlock을 안 쓰려고 한다. 예를 보자.

A = B+E;

C = B +F; 

 

lock 발생, 13번의 클럭이 필요하다

lw t1, 0(t0) // B를 불러옴 
lw t2, 4(t0) // E를 불러옴
add t3, t1, t2 // B + E 동작, 이때 lock 발생
sw t3, 12(t0) // 결과를 A에 저장

lw t4, 8(t0) // B는 위에서 불러왔기 때문에 F만 불러옴
add t5, t1, t4 // B + F 동작, 이때 lock 발생
sw t5, 16(t0) // 결과를 C에 저장

 

lock 발생시키지 않기 위해 컴파일러가 코드 순서를 바꾼다. 11번의 클럭이 필요하다

lw t1, 0(t0) // B를 불러옴 
lw t2, 4(t0) // E를 불러옴
lw t4, 8(t0) // F를 불러옴

add t3, t1, t2 // B + E 동작
sw t3, 12(t0) // 결과를 A에 저장

add t5, t1, t4 // B + F 동작
sw t5, 16(t0) // 결과를 C에 저장

 

더 봐보자

예를 들어, Decoding stage에서 멈추면 다음 Pipeline의 Fetch stage 또한 멈춰야 한다. 멈춘다는 건 이제 알겠는데 어떻게 멈출 수 있을까?? 가만히 아무것도 하지 않는다면 꼬이게 된다. 그래서 nop이라는 no operation 명령어를 집어넣는다.

 

 

nop는 어떻게 만들까?? stage를 구분하기 위해 있는 F/F 에 enable을 추가하여 데이터의 이동을 제어한다.

또한 control signal에서 모든 신호를 0으로 하여금 아무런 동작을 하지 않게 한다. 즉, Control signal 앞 단에 MUX를 추가하여 조건에 따라 동작을 결정할 수 있다.

또 궁금증이 생긴다. enable, MUX의 신호는 누가 발생할까? Hazard Detection Unit이 만든다.

 

Control hazard

 

Control hazard는 branch 명령어로부터 파생된다. branch는 비교를 통해 조건을 확인하는데 EX stage에서 조건이 맞는지 아닌지 판별 난다.

만약 맞다면 labels에 맞는 명령어의 Fetch stage로 이동한다. 근데 pipeline이라 add, sw와 같은 명령어가 이미 cpu에 들어와 있다. 이에 cpu는 자연스레 bubble (nop)로 처리하는데 이게 control hazard이다.

 

 

하지만 이와 같은 상황이라면 bubble이 2번 생기는데 1번만 생기게 하고 싶다. 어떻게 해야 할까??

 

바로 beq의 Decoding stage에서 조건을 미리 판별하면 된다. add명령어는 이미 Fetch가 실행된 상태지만 sw 명령어는 아직 Fetch가 실행되기 전이라 1번의 bubble만 가능하다.

Decoding stage에서 rs1, rs2를 adder나 XOR 같은 하드웨어에 집어넣어 판별한 신호와 control unit에서 나온 beq신호와 AND 연산을 한 신호가 다음 PC에 PC + 4를 할지, beq imm의 sign extension 한 값을 넣을지에 대한 MUX의 sel신호로 들어간다.

 

이렇게 간단히 함으로 bubble을 한 번만 실행하면 되겠지만 control hazard는 큰 문제가 있다.

위에선 FDEMW로 5 단계가 있다고 했지만 실제로는 더 깊게 이루어져 있다. 가령 38 stage라고 하면 F1, F2, F3, F4, D1, D2... WB38 이런 식이다.

이런 상황에서 13번째에 beq가 조건을 만족하여 이동한다면, 다음 명령어는 이미 12번째까지 실행됐는데 이 12개를 다 nop로 처리하기엔 큰 페널티가 있다.

그래서 나온 게 제일 처음 Fetch stage에서 branch의 결과를 예측하는 것이다. 이것이 Branch Prediction이다. cpu안에 독립적으로 크게 차지하고 있다. 어떻게 예측하냐면 이전 기록을 통해 알 수 있다. 학부 수준에선 이 정도만 알면 된다.

 

Critical path

 

Tc   = max {

      tpcq + tmem + tsetup  ,                                      // IF stage

      tpcq  + tRFread + teq + tAND + tmux + tsetup,       // ID stage

      tpcq + tmux + tmux + tALU + tsetup ,                   // EX stage

      tpcq + tmemread + tsetup ,                                // MEM stage

      tpcq + tmux + tRFsetup                                     // WB stage

                 }

 

가장 긴 시간으로 클럭의 주기를 정한다.

반응형

댓글