busy table: 올바른 레지스터 번호에도 옛 데이터가 남아 있을 때
새 목적지 레지스터를 배정받은 명령이 있다고 하자. 그 번호로 레지스터 파일을 읽으면 원하는 값을 얻을 수 있을까. 번호가 올바른 것과 값이 준비된 것은 서로 다른 조건이다. busy table은 이 틈을 표시한다. 저장 공간은 마련되었지만, 아직 그 공간을 채울 생산자가 끝나지 않았다는 사실을 추적한다.
번호는 주소이고 busy는 준비 상태다
비순차 실행 코어에서 논리 이름 x5가 물리 번호 p20에 대응한다면, 매핑은 x5의 현재 버전을 어디서 찾을지 알려 준다. busy table은 p20에 필요한 결과가 도착했는지 알려 준다. BOOM 문서는 각 물리 레지스터의 준비 상태를 추적하는 표로 이를 정의한다. 모든 소스가 준비되어야 명령이 실행 후보가 된다. BOOM, The Busy Table
다음 가상 명령열을 따라가 보자. 설명에서는 busy=1을 결과 대기, busy=0을 준비 완료로 정의한다. 실제 회로의 비트 극성까지 지정하는 예는 아니다.
I1: x5 = load[x1] → 목적지 p20
I2: x6 = x5 + 1 → 소스 p20, 목적지 p21
I1에 p20을 배정하면 p20은 결과 대기 상태가 된다. I2는 매핑을 읽어 정확히 p20을 소스로 기록한다. 그렇지만 그 시점에 busy[p20]=1이라면 실행할 수 없다. p20 저장 칸에 이전 사용자의 비트가 남아 있더라도, 그것은 I1의 load 결과가 아니다.
I1의 유효한 결과 40이 도착해 p20을 채우면 준비 상태가 바뀐다. I2는 p20의 값 40을 사용해 41을 계산할 수 있다. 이름 변환만 하고 준비 상태를 검사하지 않았다면, 우연히 남아 있던 데이터를 새 결과처럼 읽는 오류가 생긴다.
새로 들어오는 명령과 이미 기다리는 명령
busy table을 읽는 순간만으로 전체 대기 과정을 설명할 수는 없다. 어떤 명령은 생산자가 이미 끝난 뒤 들어온다. 이 경우 표를 읽으면 처음부터 소스가 준비되었다는 것을 알 수 있다. 다른 명령은 생산자가 끝나기 전에 issue queue에 들어와 기다린다. 이 명령에는 나중의 결과 도착을 알려 줄 길이 필요하다.
BOOM의 issue slot은 각 입력이 준비되어 있는지를 보관하고, 모든 입력이 준비되면 request를 낸다. 그중 실행 포트가 받아들일 수 있는 명령을 선택한다. 따라서 “소스가 준비되었다”는 것과 “이번 사이클에 실행된다”는 것도 같지 않다. 다른 명령이 선택되거나, 해당 연산 장치가 바쁘면 계속 기다린다. BOOM, Issue Slot와 Issue Select Logic
여기에는 놓치기 쉬운 경계가 있다. I2가 p20의 상태를 읽는 시점과 I1의 완료 알림이 같은 경계에 걸린다면, 표의 새 상태를 보거나 완료 알림을 받아야 한다. 둘 다 놓치면 값은 존재하는데 I2가 계속 잠드는 상황이 된다. 어느 경로를 우선하는지는 구현에 달렸지만, 상태를 처음 읽는 경로와 이후에 깨우는 경로 사이에 틈이 없어야 한다는 조건은 같다.
준비된 번호가 곧 빈 번호는 아니다
busy=0인 p20을 새 목적지로 배정해도 된다고 생각하면 또 다른 문제가 생긴다. 준비된 p20은 여전히 x5의 현재 값일 수 있고, 더 오래된 명령의 입력일 수도 있다. 다시 써 버리면 읽어야 할 값을 잃는다.
BOOM은 사용하지 않는 물리 번호를 관리하는 free list를 별도로 두고, 이전 목적지 번호는 이를 대체한 명령이 commit할 때 반환한다. busy table의 질문은 “이 번호의 결과를 사용할 수 있는가”이고, free list의 질문은 “새 결과를 위해 이 번호를 배정할 수 있는가”다. ready와 free는 같은 상태가 아니다. BOOM, The Free List와 Stale Destination Specifiers
반대로 번호를 재사용한다면 새 생산자의 결과 대기를 다시 표시해야 한다. 이전 생산자의 준비 완료 상태를 그대로 남기면 새 명령이 옛 데이터를 읽는다. 잘못 예측한 경로에서 취소된 연산의 늦은 응답도 같은 맥락에서 처리해야 한다. 그 응답이 이미 재배정된 번호의 상태를 잘못 바꾸지 않도록, 유효한 완료인지 확인하고 취소 규칙을 지켜야 한다.
모든 준비 알림이 같은 시점에 오지는 않는다
일정한 시간에 결과를 전달할 수 있는 연산은 실제 저장 완료보다 먼저 의존 명령을 깨울 수 있다. BOOM이 설명하는 ALU의 fast wakeup은 issue 시점에 알림을 보내고 bypass로 데이터를 전달하는 방식이다. 여기서 알림은 데이터 값 자체가 아니라, 정해진 경로를 통해 필요한 시점에 값이 올 것이라는 계약에 가깝다.
같은 문서에서 load와 가변 지연 연산 등의 slow wakeup은 writeback 쪽에서 발생한다. 캐시에서 언제 값이 올지 모르는 연산에 고정 시간 ALU와 같은 전제를 쓰면 준비되지 않은 입력을 소비할 수 있다. 이 구분은 해당 BOOM 설명의 경로 구성에 대한 것이며, 모든 코어가 모든 연산에 같은 정책을 쓴다는 뜻은 아니다. BOOM, Wake-up
busy table을 보면 값의 이름과 값의 도착을 분리해 생각할 수 있다. 올바른 물리 번호를 찾고, 그 번호의 새 값이 준비되었는지 확인하고, 이미 기다리는 명령에도 상태 변화를 전달해야 한다. 이 세 과정이 연결되어야 명령은 오래 기다리지 않으면서도 이전 사용자의 데이터를 새 결과로 착각하지 않는다.