Processor
기본적인 MIPS 구현
- 핵심적인 MIPS 명령어 집합의 부분집합을 구현한다.
- 메모리 참조 명령어 (lw, sw 등)
- 산술 - 논리 명령어 (add, sub, AND, OR, slt 등의 R 타입)
- 분기 명령어 (beq, j 등)
이는 단순히 구현하거나,
컴퓨터 구조의 8원칙 중 하나인 파이프라이닝으로 구현할 수 있다.
명령어의 단계
1, 2단계는 공통이다.
- 명령어를 받는다. (fetch) 이후 PC 를 증가시킨다.
-
명령어를 해독한다. (opcode 분류, 레지스터 읽기)
- 실행한다.
- 메모리 참조 명령어의 경우 : 주소 계산을 위해,
- 산술 - 논리 명령어의 경우 : 연산 수행을 위해,
- 분기 명령어의 경우 : 비교하기 위해 ALU 를 사용한다.
- 결과를 저장한다.
-
메모리 참조 명령어의 경우
sw 라면 데이터 기록하기 위해 메모리 접근,
lw 라면 데이터 읽기 위하여 메모리 접근 후 레지스터에 저장 - 산술 - 논리 명령어의 경우 : ALU 연산 결과를 레지스터에 저장
- 분기 명령어의 경우 : 비교 결과에 따라 PC 에 값을 저장
(참이면 PC에 새 주소를, 거짓이면 4 증가)
-
따라서 명령어들마다 연결되어야 하는 회로가 다르므로,
설계의 효율성을 위해 멀티플렉서를 사용한다.
이는 서로 다른 곳에서 나온 데이터가 같은 유닛으로 갈 때 효율적이다.
단계적으로 데이터패스를 구현해보자.
데이터패스 만들기
명령어 인출
첫째로 명령어를 저장하는 명령어 메모리가 필요하다.
이는 읽기 전용이고, 출력은 입력 주소가 지정하는 번지의 명령어만 해당한다.
실행 중인 명령어의 주소가 저장되는
Program Counter, 일명 PC가 필요하다.
이는 사이클 한 번 당 항상 증가하므로 쓰기 제어신호는 필요 없다.
PC를 32비트 상수, 즉 4바이트 씩 더해줄 덧셈기가 필요하다.
PC 에서 명령어가 출력되는 순간 그 PC 값은 덧셈기로 가서 4와 더해진 후 다시 PC에 들어간다.
R 형식 명령어
R 형식 명령어는 3단계에서 연산 수행을 위해 ALU 를 사용하고,
4단계에서 그 결과를 레지스터에 저장했다.
R 형식의 명령어는
op | rs | rt | rd | shamt | funct |
rs 와 rt 를 funct 한 후,
혹은 rt 를 shamt 만큼 shift 한 후 rd 에 저장하는 명령어이다.
따라서 이는 rs rt rd 의 5비트 짜리 명령어를 다 쓰게 되는데,
명령어에 따라 rd 를 쓸 지 안 쓸지,
뒤의 16비트를 저장할 주소로 판별할지 말지를 정해주기 위해,
그리고 명령어에서 쓰인 레지스터들을 선택하기 위해 MUX가 사용된다.
위 그림에서, r 타입 명령어는 data 에 접근해서 값을 수정하지 않기 때문에,
이를 방지해주기 위해 RegWrite 라는 셀렉터가 필요함을 알 수 있다.
또한 레지스터를 선택할 때도,
입력된 레지스터 번호에 따라서 회로를 유동적으로 뗐다 붙였다 할 수 없으므로
MUX 를 써서 해당되는 레지스터에만 신호를 줌으로서 선택을 한다.
메모리 참조 명령어의 실행
메모리 참조 명령어인 sw, lw 는
op | rs | rt | 16비트 변위 |
의 명령어 구조를 가진다.
차이가 있다면, sw 는 레지스터의 값을 해당 메모리에 쓰고
lw 는 메모리에서 읽어온 값을 레지스터에 쓴다.
역시 문제는, 우리의 MIPS 는 명령어 종류에 상관 없이 회로를 공유하기 때문에
유닛에 셀렉터를 달아주어야 한다.
R 타입 명령어에서 레지스터 파일에 RegWrite 라는 셀렉터를 달아주었듯이
데이터 메모리 유닛에는 MemWrite 과 MemRead 를 달아주어야 한다.
어차피 MemWrite 는 MemRead 과 보수 관계니까 두 개가 무슨 소용이냐? 할 수 있지만
앞서 말했듯 이 회로는 R 타입 명령어도 공유하는 회로이므로,
둘 다 하지 않을 경우도 생각해야 한다.
Base 주소 지정 방식을 따라서
명령어의 rt 부분의 주소와 16비트가 Sign-Extend 된 값을 더해야 하므로
Sign-Extend 와 ALU 유닛이 추가적으로 붙는다.
분기 명령어의 실행
분기 명령어는 위 메모리 참조 명령어와 구조가 같다.
단, 분기 명령어는 PC상대주소지정으로,
PC + 4 + 부호 확장된 16비트 * 4 (왼쪽으로 2번 shift) 가 최종 주소가 된다.
이후 4단계에서 연산(비교) 결과에 따라 PC 값을 결정한다.
주소를 더해서 얻는다는 점과, 왼쪽으로 2번 자리 이동한다는 점,
그리고 부호 확장이 일어난다는 점에서
Sign-Extend, ALU, Shift-Left-2 Unit 이 추가적으로 필요하다.
ALU 는 비교하는 곳에서 이미 쓰고 있으므로 주소를 더하는 연산하는 유닛이 따로 필요하다.
자리이동 유닛과 부호확장 유닛의 구현
자리 이동 유닛은 위에 두 개를 지운다. (rotation 이 아니므로)
이후 29번째 비트를 31번쨰 비트로 27을 30으로 …
마지막 원래 1번째 0번째 비트에다가는 각각 0을 넣는다.
부호확장은 15번째 ~ 0번째까지 그대로 간다.
나머지 16개 자리에다가는 15번째 자리 수가 그대로 복사된다.
왜 부호확장을 하냐?
PC는 32비트이다.
32비트랑 덧셈하려면 32비트 수가 필요하다.
그러나 그 앞의 빈 16개 수를 0이나 1로 막 채워버리면 부호가 바뀌어 버린다.
lw 에서 이 수는 레지스터를 base 로 하는 offset 으로 쓰이고
beq 에서는 PC 를 base 로 하는 offset 으로 쓰이므로 음수든 양수든 올 수 있기 때문에
부호를 유지하지 않은 채 그냥 더해버리면 잘못된 메모리에 접근할 수 있다.