Pipelining이란?
파이프라이닝이란, 병렬적으로 여러개의 명령어를 동시에 처리하는 것이다.
멀티사이클 데이터패스를 보면서 파이프라이닝에 대한 아이디어가 떠오르는 것은 자연스럽다. 애시당초 파이프라이닝을 위해 RISC-V 명령어 집합이 설계된 것이기 때문이다. 이를 위해 RISC-V의 모든 명령어는 같은 길이를 갖는다. 또한 이를 위해 멀티사이클 데이터패스에서 하나의 CPU를 단계별로 구간을 쪼개어 사용하는 것이다. 쪼개진 단계 개수만큼 여러개의 명령어가 하나의 CPU에 동시에 존재할 수 있기 때문이다.
하지만 이러한 파이프라이닝이 실제적인 구현에서 항상 가능한 것은 아니다.
Hazard
다음 명령어가 다음 클럭사이클에 실행될 수 없는 상황을 해저드hazard라고 한다. 이러한 해저드의 종류는 1) 구조적 해저드, 2) 데이터 해저드, 3) 제어 해저드, 이렇게 3가지가 있다.
구조적 해저드Structural Hazard
하드웨어가 특정 병렬 조합을 지원하지 못해서 실행되지 못하는 해저드이다. 예컨대, 3 사이클 먼저 실행된 첫번째 명령어가 메모리에 접근단계를 시작하려할 때, 네번째 명령어가 병렬 실행되어 인스트럭션 메모리에 접근하려고 하는 경우, 만약 하드웨어가 지원하는 메모리가 두 개가 아닌 하나라면, 해저드를 피할 수 없다.
데이터 해저드Data Hazard
다음 명령어의 어떤 단계가 이전 명령어의 단계를 기다려야 하기 때문에 발생하는 해저드이다. 예컨대,
add x19, x0, x1
sub x2, x19, x3sub 명령어는 add 명령어의 합을 바탕으로 계산이 이뤄져야 한다. 따라서 별다른 조치가 없다면, 다음 명령어가 실행되기까지 꽤 많은 사이클을 낭비해야 할 것이다.
이에 대한 해결책엔 여러가지가 있다.
forwarding(or bypassing)
다음 명령어가 앞선 명령어의 결과를 사용하기 위해 앞선 명령어가 완전히 끝날 때까지 기다릴 필요는 없다는 데에 착안한다.
add와 sub명령어가 IF RF EX WB 의 4단계를 거칠때, add는 WB 단계를 건너뛰고 sub명령어의 EX단계 인자로 바로 사용한다. 즉, 이로써, 지연없이 파이프라이닝이 이뤄지게 한다. 하지만 add가 아닌 load 명령어였다면, 어떨까?
ld x19, 0(x1)
sub x2, x19, x3이 경우, ld 명령어는 IF RF EX MEM WB의 5단계를 거친다. load를 위해서는 메모리에 접근해야 하기 때문에 MEM 단계 이후에나 x19에 저장될 데이터를 알 수 있다. 따라서, 포워딩이 일전에 비해 더 늦게 작동하고, 이 말인즉슨, 1 사이클의 지연이 필요하다는 말이기도 하다. 즉, ld와 sub 명령어 사이에 버블이 존재하게 된다.
제어 해저드Control Hazard
다음에 어떤 명령어를 실행해야 할지 알기 위해서 이전 명령어의 결과를 기다려야 하기 때문에 발생하는 해저드이다. 주로 분기 명령어를 통해 다음 명령어의 주소를 계산해야 하는 경우 발생한다.
beq x1, x0, 40
...
(beq의 목적지에 있는 명령어)
or x7, x8, x9beq가 완료되기까지 3사이클이 필요하기 때문에 beq와 or 사이에는 한 사이클의 버블이 필요하다.