
MIPS의 기본 구현은 명령어를 실행하기 위해 필요한 회로 요소들-예를 들어 MUX(멀티플렉서)와 제어선(Control lines)-로 구성되어 있다. 이 구현에서는 총 세 개의 주요 MUX가 존재하며, 각각은 명령어의 종류에 따라 서로 다른 경로를 선택하게 도와주는 역할을 한다.
먼저, 가장 위쪽에 있는 MUX는 PC(Program Counter)를 다음에는 어떤 값으로 바꿀지를 결정한다. 일반적으로 PC + 4가 되어 다음 명령어로 넘어가지만, 만약 지금 실행 중인 명령어가 branch(분기) 명령어이고, 그 분기 조건이 충족된다면, 이 MUX는 분기 목적지 주소(branch destination address)로 PC 값을 바꾸게 된다. 이 MUX는 단순히 제어선 하나로 작동하지 않고, ALU에서 나오는 Zero 신호와 Branch 신호라는 두 신호를 AND 게이트로 결합하여 제어한다. 즉, ALU 연산 결과가 0이라는 뜻의 Zero 신호와 "이건 분기 명령이다"라는 Branch 신호가 둘 다 참일 때만 분기 주소로 점프한다.
다음으로, 중간에 위치한 MUX는 레지스터 파일로 다시 써넣을 데이터를 결정한다. 이건 R-type이나 lw(load word) 명령어에서 사용된다. R-type 명령어라면 ALU 결과를 다시 레지스터에 써야 하고, lw 명령어라면 메모리에서 읽어온 값을 써야한다. 따라서 이 MUX는 ALU 결과와 데이터 메모리의 출력 값 중에서 어떤 걸 레지스터에 쓸지 선택한다. 어떤 걸 쓸지는 제어 유닛의 MemToReg 신호에 따라 결정된다.
이 MUX는 ALUSrc라는 제어 신호를 통해 어떤 입력이 들어갈지 결정된다. 이 외에도 여러 개의 제어선(control lines)이 함께 동작한다.
이러한 제어 신호들은 모두 명령어 종류에 따라 다르게 설정되며, 명령어에 맞는 회로 동작이 이루어지도록 도와준다.
명령어를 실행하려면, 가장 먼저 해야 할 일은 명령어 메모리에서 해당 명령어를 읽어오는 일이다. 이 작업을 위해서는 총 세 가지 요소가 필요하다.

명령어 메모리(Instruction Memory) : 명령어들을 저장하는 공간이다. 이 메모리는 단지 명령어를 읽기만 하면 되므로, 제어 신호 없이 주소를 주면 곧바로 그 위치의 명령어를 출력한다. 따라서 이 메모리는 읽기 전용(Read-only)처럼 작동하며, 특별한 read 신호는 필요 없다.(실제 프로그램을 메모리에 로드할 때는 쓰기가 필요하기도하다. 여기선 생략)
프로그램 카운터(Program Counter, PC) : 현재 실행할 명령어의 주소를 저장하는 32비트 레지스터이다. 모든 클럭 사이클의 끝마다, 이 PC는 업데이트되며 쓰기 제어 신호 없이 자동으로 새 값을 받는다.
더하기 장치(Adder) : PC 값을 4 더해서 다음 명령어 주소를 계산한다. ALU와 같은 구조지만 항상 두 입력을 더하도록 고정되어 있다.
위 세 가지 요소가 함께 작동하면, PC가 가리키는 주소의 명령어를 메모리에서 읽어오고, 동시에 PC 값에 4를 더해 다음 명령어 주소를 준비할 수 있다.

Fetch Stage : PC -> Instruction Memory -> 명령어 읽기, 동시에 PC + 4 계산
# Example: R-format
add $t1, $t2, $t3
이를 구현하기 위해 필요한 구성 요소는 다음과 같다.
레지스터 파일(Register File) : MIPS에는 32개의 범용 레지스터가 존재하며, 이들은 모두 하나의 구조(register file) 안에 저장되어 있다. 이 구조 안에서는 다음과 같은 일이 일어난다.
ALU(산술/논리 연산 유닛) : 레지스터 파일에서 읽어온 두 값은 ALU로 전달되어 연산이 수행된다. 예를 들어 add, sub, and, or, slt 같은 연산이 가능하며, 연산 결과는 다시 레지스터 파일에 쓰이게 된다.

R-format 실행 :
1. 두 개의 레지스터 값 읽기
2. ALU 연산 수행
3. 결과를 다시 레지스터에 저장(RegWrite 제어 신호 필요)
lw $t1, offset($t2) // 메모리에서 값을 읽어 $t1에 저장
sw $t1, offset($t2) // $t1의 값을 메모리에 저장
이 명령어들은 먼저 메모리 주소를 계산해야 한다. 메모리 주소는 두 부분을 더해서 만들어진다.
이 두 값을 더해 실제 메모리 주소를 만들고, 이 주소에 따라 데이터를 읽거나 쓴다.
$t1에 들어 있는 값을 메모리에 저장해야 하므로, 먼저 $t1을 레지스터 파일에서 읽어야 한다. 이후 계산된 메모리 주소에 해당 값을 저장(Write)한다. 이때 데이터 메모리(data memory)는 다음과 같은 기능이 필요하다.
계산된 메모리 주소로부터 데이터를 읽어야(read)하며, 이 값은 $t1에 저장되어야 하므로, 결국 레지스터 파일에 쓰기(Write)를 해야 한다. 따라서 데이터 메모리는 다음이 필요하다.
데이터 메모리는 단순한 조합 논리가 아니라 상태 요소(state element)이다. 쓰기 연산은 클럭 엣지(시점)에 맞춰 이루어지며, 이를 위해 메모리는 쓰기 신호(write enable)가 필요하다. 읽기 연산은 그냥 주소만 넣는다고 되는 것이 아니라, 명시적인 read 신호가 있어야 한다. 잘못된 주소를 읽으려고 하면 시스템에 문제가 생길 수 있기 때문이다.

즉, 정리하면
| 단계 | 설명 |
|---|---|
| 1. 레지스터 읽기 | $t2, $t1에서 값 읽기 (base와 store할 값) |
| 2. offset 처리 | 16비트 → 32비트로 Sign-Extend |
| 3. 주소 계산 | ALU로 base + offset 수행 |
| 4. 메모리 접근 | sw: 메모리에 값 저장lw: 메모리에서 값 읽기 |
| 5. 레지스터 쓰기 | lw인 경우 읽어온 값을 $t1에 저장 |
beq $t1, $t2, offset
위 명령어는 다음과 같은 작업을 한다.
1. $t1과 $t2를 비교하여 값이 같다면,
2. 현재 명령어 위치에서 offset 만큼 떨어진 곳으로 분기(점프)한다.
이 명령어를 처리할 때는 분기 주소(branch target address)를 올바르게 계산해야 하는데, 그 과정에 두 가지 중요한 규칙이 있다.
단순히 분기 주소만 계산한다고 끝나는 것이 아니라, 실제로 분기를 할지 말지를 결정해야 한다.

결과적으로 datapath는 두 가지를 수행해야 한다.
1. 분기 주소 계산(PC + offset << 2)
2. 두 레지스터 값 비교($t1 - $t2 == 0 ?)
이러한 분기 처리 구조는 아래와 같은 특징이 있다.
정리하면,
| 단계 | 설명 |
|---|---|
| 1. 레지스터 비교 | ALU로 $t1 - $t2 계산 → Zero 여부 확인 |
| 2. offset 처리 | offset 부호 확장 → 2비트 shift |
| 3. 분기 주소 계산 | PC + 4 + (offset << 2) |
| 4. 분기 여부 결정 | Zero == 1이면 분기, 아니면 PC + 4 유지 |
| 5. MUX로 PC 선택 | 조건 만족 시 분기 주소 / 아니면 다음 주소 |
지금까지 살펴본 MIPS 명령어의 세 가지 주요 유형들을 각각을 실행하기 위한 datapath 구조로 따로 살펴보았다. 이제 이들을 하나의 통합된 단일 datapath로 결합하면 아래와 같다.
결합할 때 주의할 점은, 동일한 하드웨어 자원은 한 명령어 내에서 두 번 사용이 불가능하다. 이를 해결하기 위해서는 MUX나 별도 장치로 해결해야한다. 또한 명령어 메모리와 데이터 메모리의 분리가 필요하다. 명령어를 실행하는 동안 동시에 데이터를 읽거나 써야 하는 경우가 많기 때문에, 명령어용 메모리와 데이터용 메모리는 서로 다른 메모리로 구성해야 한다. 공유 가능한 자원의 경우-예를 들어 ALU, Register file-하나만 두고, 서로 다른 명령어들이 쓸 수 있도록 입력 경로를 MUX로 전환한다.
R-type과 Memory 명령어는 아래와 같은 차이점이 있다.
| 구분 | R-type | lw/sw |
|---|---|---|
| ALU 입력 | 두 개 모두 레지스터 값 | 하나는 레지스터, 하나는 sign-extended offset |
| 결과 저장 위치 | ALU 결과를 레지스터에 저장 | lw는 메모리에서 읽은 값을 저장 |
이 차이를 해결하기 위해 ALU 입력 MUX(ALUSrc), Register Write Data MUX(MemToReg) 두 개의 MUX를 사용한다. 이렇게 두 개의 MUX를 추가하면, 하나의 ALU, 하나의 레지스터 파일만으로도 두 명령어 유형을 모두 처리 가능하다.


| 구성 요소 | 역할 |
|---|---|
| Instruction Memory | 명령어 읽기 (읽기 전용, PC 사용) |
| Register File | 명령어에 사용될 레지스터 값 읽기/쓰기 |
| ALU | 산술 연산 또는 주소 계산 |
| Sign Extension Unit | 16비트 offset을 32비트로 확장 |
| Shift Left 2 | offset을 워드 주소로 변환 |
| Data Memory | lw/sw를 위한 데이터 읽기/쓰기 |
| MUX (2개) | ALU 입력 선택, 레지스터 쓰기 값 선택 |
| PC + 4 Adder | 기본적인 명령어 순차 실행 주소 계산 |
| Branch Target Adder | 분기 주소 계산 (PC + offset << 2) |
| Control Unit | 모든 경로 제어 신호 생성 (ALUSrc, MemToReg, Branch, etc.) |