Control Unit
Single Cycle MIPS 에서 Control Unit은 위와 같은 구조로 되어있다.
fetch한 명령어에서 Opcode를 가져와서 해석하는 Main Decoder, Funct 를 가져와서 해석하는 ALU Decoder로 구성된다.
(사실 구분하지 않고 한번에 만들도록 구현할 수도 있지만, 이렇게 분리해서 구현할 수도 있다.)
Opcode 를 가져오면 이를 통해 가져온 명령어의 타입이 R, I, J 중에 어떤 타입인지 알 수 있다.
만약 가져온 명령어의 타입이 R 타입이 아니라면 Funct 필드로서 가져온 데이터는 무시한다.
먼저 Main Decoder가 내보내는 컨트롤 신호의 종류를 복습해보자.
1. MemToReg
연산한 결과를 메모리에 쓸 지, 레지스터에 쓸 지 결정하는 플래그이다.
2. MemWrite
메모리에 대한 Write Enable 플래그이다.
3. Branch
조건 분기 여부를 나타내는 플래그이다.
4. ALU Src
ALU에 들어가는 두번째 src가 레지스터에서 오는지, immediate 에서 오는지 결정한다.
5. Reg Dst
연산 결과를 레지스터의 rd, rt 중 어떤 값이 가리키는 곳에 넣을지를 결정한다.
6. RegWrite
레지스터에 대한 Write Enable 플래그이다.
참고로 레지스터 쓰기 신호는 branch 하거나 sw 명령어를 실행할 때는 off 로 되어야한다.
(뒤에서 더 자세히 정리한다.)
ALU Op 의 경우, Main Decoder 가 파악한 내용을 토대로 어떤 ALU 연산을 취해야할 지 ALU Control 신호를 생성하도록 ALU Decoder에 넘기는 신호이다.
이 역시 뒤에서 더 자세히 정리한다.
ALU Decoder
먼저 ALU Decoder의 입력과 그에 따른 출력의 진리표를 살펴보자.
ALU Decoder 하나만 보고 생각을 해보면 ALU Decdoer는 Funct 로 6bit 입력과 ALU OP로 2bit 입력이 주어진다. 그리고 이 입력으로부터 ALU Control 신호 3bit 출력이 나온다.
이 ALU Control 신호로 ALU가 갖는 7가지 기능 중 어떤 기능을 선택할지 결정하게 된다.
먼저 ALU Op 는 강의록에서는 아래와 같이 구현하였다.
00 이면 덧셈, 01 이면 뺄셈, 10 이면 Funct 필드를 보고 결정, 11은 무시한다.
우선 OP Code를 읽으면, 이를 통해 명령어가 lw 인지, sw인지, 둘 다 아닌지 3가지 경우를 모두 판별할 수 있다.
lw, sw는 I 타입의 명령어로 정의되어 있기 때문에, OP 코드만 보고 바로 판별할 수 있다.
따라서 OP Code 만 읽으면 ( lw, sw, 이외의 명령어 ) 중에 1가지를 바로 특정할 수 있다.
지금 Main Decoder는 lw 나 sw 를 만나면 반드시 00 을 ALU Op로 내보내도록 한다고 약속하자.
그리고 ALU Decoder는 ALU OP 가 00 이면 funct 필드의 값이 어떤 값이든 상관없이 반드시 덧셈 기능을 수행한다고 약속하자.
이렇게 정한 약속은 합리적인 약속이다.
왜냐하면 메모리에 접근하려면 반드시 메모리의 주소를 얻기위해 base 주소와 immediate 필드의 값을 더해야 하고, 오직 이 목적때문에 ALU를 사용하기 때문이다.
비슷한 과정으로, 현재 명령어가 beq, bne 인지는 Op Code 만으로 즉시 판별할 수 있으며,
이를 판별했다면 이 명령어에 대해서는 반드시 뺄셈을 사용하도록 유도해도 문제가 없다는 것을 알 수 있다.
beq, bne는 입력으로 들어온 두 값이 같은지 다른지 판별할 때 뺄셈을 사용하며, 오직 이 목적만을 위해 ALU를 사용하기 때문이다. 따라서 beq, bne에 대해서는 01 을 ALU OP로 내보내고, 이를 받은 ALU Decoder는 funct 필드의 값에 무관하게 반드시 뺄셈을 수행하도록 약속하는 것은 합리적이다.
따라서 ALU Decoder에 대한 진리표를 우선 위와 같이 작성할 수 있다.
그 외의 명령어의 경우에는 Funct 필드를 봐야 ALU의 동작을 결정할 수 있다.
따라서 위에서 정한 ALU Op 값의 의미표에 따라 ALU OP 필드의 값은 10 로 내보내면 된다.
따라서 전체 진리표는 아래와 같이 작성할 수 있다.
1X 일 때 Funct 에 따른 ALU Control의 동작은 그냥 저렇게 정했다고 이해하자.
테이블에서 X 가 들어간 부분은 '아무 값이든 상관없다' 는 의미이다.
ALU Op 에서 10 이 아니라 1X 로 작성한 이유는, 11이 사용되지 않는 옵션이라서 왼쪽 비트가 1 이기만 하면 오른쪽 비트의 값이 1이든 0이든 상관없이 Funct 필드를 보는 것으로 해석할 수 있기 때문이다.
두번째 뺄셈 부분도 01 대신 X1 로 쓴 이유는 11 이 사용되지 않기 때문에, 오른쪽 비트가 1이기만 하면 뺄셈을 의미할 수 밖에 없어서 저렇게 표기할 수 있었다.
따라서 만약 11을 절대 정의하지 않는다고 가정하면, ALU Deocoder는 ALU OP의 최상위 비트만 보면 Funct 를 볼 지, 보지 않을지 결정할 수 있다.
Main Decoder
현재 가정으로는 Main Decoder의 유일한 입력은 명령어의 OP Code 필드 6bit 이다.
이 정보만으로 명령어를 식별한 뒤, 알맞은 컨트롤 신호를 내보내면 된다.
이때 각 명령어의 분류에 다라 어떤 신호를 내보내는지를 위와 같이 나타낼 수 있다.
이 표를 작성할 때는, RegWrite, MemWrite만 주의하면 된다.
이 값은 "정말 레지스터/메모리에 작성하는 동작을 수행할 때" 이외에는 반드시 0으로 설정해둔다.
첫번째 R-type 명령어는
- ALU의 연산 결과를 레지스터에 저장하는 명령어이므로, Reg Write 가 1로 활성화
- rd 필드를 사용하므로 Reg Dst 활성화
- ALU Src로 rt를 사용하므로 비활성화 (imm을 사용할 때 활성화됨)
- 분기하는 것과 관련이 없으므로 Branch는 비활성화
- 연산 결과를 메모리에 저장하는 것이 아니므로 MemWrite는 비활성화
- 메모리의 데이터가 아니라 ALU의 연산 결과를 레지스터로 가져와야 하므로 MemToReg 비활성화
- ALU OP는 Funct 필드를 봐야하므로 10
와 같이 설정된다.
lw 명령어의 경우, 메모리에서 읽은 데이터를 레지스터에 써야 하는 I 타입 명령어이다.
I타입 이므로 RegDst 는 rt를 목적지로 하기에 활성화
imm를 사용하기 위해 ALU Src 도 활성화
메모리에서 읽은 데이터를 레지스터의 write data로 세팅해야 하므로 MemToReg 활성화
sw 명령어의 경우, 레지스터에 값을 쓰는 것이 아니므로, RegDst는 어떤 값이든 상관없다.
하지만 레지스터에 값을 쓰는 것이 아니므로, RegWrite는 분명하게 0으로 막아두어야 한다.
MemToReg의 경우도 메모리에 값을 쓴 이후에 레지스터의 Write Data 세팅을 하지 않으므로 아무 값으로 세팅해도 된다.
beq 명령어의 경우, 뺄셈한 결과를 레지스터에 저장하는 건 아니다.
따라서 RegWrite를 비활성화, RegDst, MemToReg는 아무값이나 상관없고, branch 활성화, MemWrite 비활성화만 신경쓰면 된다. (ALU Src는 branch 주소를 계산할 때 imm 필드를 사용하므로 1로 세팅)
Example
이렇게 Control Unit의 디자인을 완료했다.
이제 예제로서 addi 명령어를 이 Control Unit 디자인에서 실행시킬 수 있을지 확인해보자.
addi 를 실행시키려면 Main Decoder는 어떤 출력을 내보내야할까?
1. RegWrite
더한 결과를 레지스터에 써야 하므로, 활성화한다.
2. RegDst
I 타입의 목적지는 rd가 아니라 rt 이다. 따라서 비활성화한다.
3. ALU Src
imm 데이터를 src로 넣으므로 활성화한다.
4. Branch
비활성화한다.
5. MemWrite
메모리에 값을 쓰는 것이 아니므로 비활성화한다.
6. MemToReg
메모리에서 읽은 값을 레지스터로 가져오는 것이 아니므로 비활성화한다.
7. ALU OP
addi는 I 타입으로서 Function 필드를 보지 않고 반드시 덧셈을 수행한다. 따라서 00 으로 설정한다.
이번엔 J 타입을 한번 살펴보자.
J타입을 볼 때는 jump 라는 컨트롤 옵션이 추가된다.
j 명령어를 쓴다면 이때는 jump 옵션 외에는 아무것도 볼 게 없다.
26bit imm 필드를 점프할 주소로 연산하여 보기 때문에 Funct 필드도 보지않고, ALU를 사용하지도 않는다.
따라서 jump 라는 output을 하나 추가하고 아래와 같이 세팅하면 된다.
이때 필요없다고 해서 RegWrite, MemWrite을 X로 비워두면 안된다.
쓰기를 하지 않을 때는 반드시 0으로 꺼둬야 한다!
베릴로그
Main Decoder의 베릴로그 코드이다.
controls 는 9bit의 변수이고, regwrite부터 aluop까지 8개 변수에 묶여서 할당된다.
(assign 구문)
always @(*) 의 의미는 항상 begin - end 구문을 실행한다는 의미로, 조합회로를 나타낸다.
op 에 따라서 서로 다른 9bit 컨트롤 신호를 반환하는 것을 나타낸다.
이때 최하위 2bit는 aluop 에 들어간다.
ALU Decoder의 베릴로그 코드이다.
마찬가지로 조합회로로서 동작하며, aluop 입력에 따라 케이스를 나눠 다르게 처리하는 것을 볼 수 있다.
'CS > 컴퓨터 구조' 카테고리의 다른 글
[컴퓨터 구조] 17. Pipeline MIPS (1) - 기본 아이디어 (1) | 2024.05.30 |
---|---|
[컴퓨터 구조] 16. Single Cycle MIPS - 성능 (0) | 2024.05.29 |
[컴퓨터 구조] 14. Single Cycle MIPS - 회로 정리 & ALU (0) | 2024.04.21 |
[컴퓨터 구조] 13. Single Cycle MIPS - Execute (0) | 2024.04.19 |
[컴퓨터 구조] 12. Single Cycle MIPS - Decode (0) | 2024.04.19 |