CPU 명령어 실행 상태와 파이프라인 흐름

CPU가 Fetch, Decode, Execute, Memory Access, Write Back 상태를 거쳐 명령어를 처리하는 흐름과 파이프라인 동작을 정리한다.

2026-08-14 · 최초 발행 2026-01-03

명령어 사이클을 나누는 CPU 상태

CPU는 메모리에서 명령어를 가져온 뒤 해석하고 실행하며, 필요할 때 메모리에 접근한 결과를 레지스터에 기록한다. 이 명령어 실행 과정을 상태로 분해한 것이 CPU Major State다.

일반적인 구분은 Fetch(인출), Decode(해석), Execute(실행), Memory Access(메모리 접근), Write Back(쓰기)다. 각 상태는 정해진 순서와 시스템 클록에 맞춰 진행된다. 파이프라인 구조에서는 서로 다른 명령어가 각기 다른 상태를 동시에 처리할 수 있다.

명령어 사이클은 명령어 하나를 끝까지 실행하는 전체 과정이며, Major State는 그 과정을 세부 작업으로 나눈 단위다. 이 분해가 파이프라인 구현의 기반이 된다.

InstructionCycleFetchDecodeExecuteMemoryAccessWriteBack

Fetch부터 Write Back까지의 경로

Fetch에서 명령어를 가져온다

Fetch는 다음에 실행할 명령어를 메모리에서 읽어 IR(Instruction Register)에 보관하는 상태다. PC 값을 MAR에 복사하고, 메모리 읽기를 요청해 받은 데이터를 MBR에 저장한 뒤 IR로 옮긴다. 이후 PC는 다음 명령어 주소를 가리키도록 증가한다.

일반적으로 1 클록 사이클이 걸리지만, 캐시 미스가 발생하면 추가 사이클이 필요하다.

Decode에서 실행 방식을 결정한다

Decode는 IR에 들어온 명령어를 해석해 제어 신호를 만드는 상태다. Opcode를 추출하고 디코딩하며, 피연산자 주소와 필요한 레지스터를 파악한다. 일반적으로 1 클록 사이클이 필요하고, 복잡한 명령어는 추가 사이클을 쓸 수 있다.

Execute에서 연산과 분기를 처리한다

Execute에서는 ALU 또는 분기 로직으로 실제 작업을 수행한다. 레지스터나 메모리에서 피연산자를 읽어 ALU에 전달하고, ADD, SUB, AND 등의 연산 결과를 만든다. 이때 Zero, Carry 등의 플래그도 설정된다.

단순 연산은 1 클록 사이클로 처리할 수 있지만, 복잡한 연산은 여러 사이클이 걸린다.

Memory Access는 Load와 Store에 필요하다

Memory Access는 메모리 읽기와 쓰기를 담당하며 Load/Store 명령어에서 사용된다. Load는 계산한 주소를 MAR에 설정하고 메모리에서 읽은 데이터를 MBR에 받는다. Store는 MAR에 주소를 설정한 뒤 MBR에 저장할 데이터를 넣고 메모리 쓰기를 요청한다.

캐시 히트 시 1 클록 사이클이 걸리고, 캐시 미스 시 수십~수백 사이클이 걸린다.

Write Back으로 결과를 레지스터에 남긴다

Write Back은 Execute 결과 또는 Memory 상태에서 읽은 데이터를 선택해 목적지 레지스터에 기록하는 단계다. 레지스터 파일이 업데이트되면 해당 명령어의 실행이 완료된다. 일반적으로 1 클록 사이클이 필요하다.

Fetch(1 cycle)Decode(1 cycle)Execute(1+ cycle)Memory(1+ cycle)Write Back(1 cycle)다음명령어

단순 프로세서가 보는 Fetch–Decode–Execute

단순화한 프로세서 모델은 Fetch, Decode, Execute의 3단계로 명령어 실행을 표현한다. 단순 프로세서, 교육용 모델, 초기 컴퓨터 아키텍처에서 주로 쓰이는 관점이다.

이 모델에서는 Fetch가 PC → MAR → Memory → IR 경로로 명령어를 가져오고, Decode가 IR → Instruction Decoder로 명령어를 전달한다. Execute는 연산을 수행하고 결과를 저장한다.

메모리 접근과 쓰기 작업이 Execute에 포함되므로, 세부 상태를 나눈 구조보다 파이프라인 최적화가 어렵고 성능 향상에도 제한이 있다.

상태 내부에서 오가는 데이터와 제어 신호

Fetch의 레지스터 전송 언어는 다음과 같다.

MAR ← PC
MBR ← Memory[MAR]
IR ← MBR
PC ← PC + instruction_size

이 과정에는 MAR_WRITE, MEM_READ, IR_WRITE, PC_INCREMENT 제어 신호가 사용된다. 캐시 미스는 추가 사이클을 만들고, 페이지 폴트는 OS 개입을 유발하며, 인터럽트는 인터럽트 핸들러 호출로 이어진다.

Decode는 IR의 상위 비트에서 Opcode 필드를 꺼내 제어 신호 패턴으로 변환한다. 이어서 레지스터, 즉시값, 메모리 가운데 어떤 피연산자 모드인지 파악하고 소스 및 목적지 레지스터를 식별한다. ALU_OP, REG_READ, REG_WRITE, MEM_ACCESS 같은 신호가 이 상태에서 결정된다.

Execute의 ALU 연산은 다음처럼 표현할 수 있다.

Result ← Operand1 OP Operand2

대표적인 연산은 다음과 같다.

  • ADD R1, R2: R1 ← R1 + R2
  • SUB R3, R4: R3 ← R3 - R4
  • AND R5, R6: R5 ← R5 AND R6

연산 결과에 따라 Zero Flag, Carry Flag, Overflow Flag, Sign Flag가 설정된다. 분기 명령어는 플래그를 확인해 조건을 평가하고, PC + offset으로 분기 주소를 계산해 조건이 만족되면 PC를 갱신한다.

Load와 Store의 메모리 상태는 각각 다음과 같다.

Address ← Base + Offset
MAR ← Address
MBR ← Memory[MAR]
Address ← Base + Offset
MAR ← Address
MBR ← RegisterValue
Memory[MAR] ← MBR

주소 계산에는 직접 주소(Address = Immediate), 레지스터 간접(Address = Register), 베이스 + 오프셋(Address = Base + Offset), 인덱스(Address = Base + Index \* Scale) 방식이 사용된다.

Write Back은 ALU 결과와 메모리 데이터 중 저장할 값을 고른 뒤 목적지 레지스터에 기록한다.

Register[DestReg] ← Result

파이프라인에서는 데이터 해저드를 해결해야 하며, Forwarding으로 결과를 다음 명령어에 전달할 수 있다.

Execute결과결과선택Memory데이터목적지레지스터식별레지스터파일쓰기다음명령어로Forwarding

클록 경계에서 달라지는 실행 흐름

각 상태는 클록 에지에서 시작해 고정된 시간 동안 수행된다. 클록 주파수는 성능을 결정하는 요소다.

3GHz CPU를 예로 들면 1 클록 사이클은 1/3 ns = 0.33 ns다. 5단계 파이프라인에서는 명령어 완료에 최소 5 사이클이 필요하며, 이상적인 경우 CPI(Cycles Per Instruction)는 1이다.

정상적인 전환은 명령어 인출이 끝나면 Fetch에서 Decode로, 디코딩이 끝나면 Decode에서 Execute로 이어진다. Execute 뒤에는 메모리 접근이 필요한 명령어만 Memory로 이동하며, 필요하지 않으면 Write Back으로 바로 진행한다. Memory가 끝나면 Write Back으로 전환된다.

인터럽트, 예외, 페이지 폴트는 이 정상 경로를 바꾼다. Fetch 중 인터럽트가 발생하면 인터럽트 핸들러로 점프하고, Execute 중 예외는 예외 핸들러 호출로 이어진다. Memory 중 페이지 폴트가 발생하면 OS가 페이지를 로드한다.

파이프라인에서 겹쳐 실행되는 상태

5단계 파이프라인에서는 5개 명령어가 동시에 서로 다른 상태를 처리한다. 이상적으로 매 사이클마다 1개 명령어가 완료되며, 이론적으로 처리량은 5배 증가한다.

사이클 Fetch Decode Execute Memory Write Back
1 I1 - - - -
2 I2 I1 - - -
3 I3 I2 I1 - -
4 I4 I3 I2 I1 -
5 I5 I4 I3 I2 I1
6 I6 I5 I4 I3 I2

이 겹침은 데이터 해저드, 제어 해저드, 구조 해저드를 동반한다. 데이터 해저드는 의존성이 있는 명령어에서, 제어 해저드는 분기 명령어에서, 구조 해저드는 자원 충돌에서 발생한다.

Cycle 1I1: FetchCycle 2I1: DecodeI2: FetchCycle 3I1: ExecuteI2: DecodeI3: FetchCycle 4I1: MemoryI2: ExecuteI3: DecodeI4: Fetch

상태마다 역할이 달라지는 레지스터

Fetch에서는 PC가 다음 명령어 주소를 보관하고 인출 후 증가한다. MAR은 PC 값을 받아 메모리 주소를 저장하며, MBR은 메모리 데이터를 임시로 저장해 명령어를 수신한다. IR은 MBR에서 복사된 현재 명령어를 보관한다.

Decode에서는 IR이 디코더에 명령어를 전달하고 Opcode와 Operand를 분리한다. Control Unit 내부 레지스터는 제어 신호 패턴을 저장한다.

Execute는 R0, R1 등의 범용 레지스터에서 피연산자를 읽어 ALU 입력으로 사용한다. ALU 레지스터에는 Accumulator(ACC)와 Temporary Register가 포함되며, 플래그 레지스터는 Zero, Carry, Overflow, Sign 상태를 기록한다.

Memory에서는 MAR이 접근 대상 메모리 주소를, MBR이 Load의 메모리 → MBR 및 Store의 MBR → 메모리 데이터 전달을 담당한다. 베이스와 인덱스 레지스터는 주소 계산에 쓰인다. Write Back에서는 목적지 레지스터가 결과를 저장하고 레지스터 파일을 업데이트한다.

명령어에 따라 달라지는 상태 경로

산술·논리 연산의 예로 ADD R1, R2, R3 (R1 ← R2 + R3)를 보면, Fetch에서 IR ← Memory[PC], PC++를 수행하고 Decode에서 Opcode = ADD, Src1 = R2, Src2 = R3, Dest = R1을 해석한다. Execute는 R1 ← R2 + R3을 수행하며 Memory는 건너뛴다. Write Back에서 레지스터 R1을 업데이트한다.

이 경로는 Fetch → Decode → Execute → Write Back이다.

LOAD R1, 100(R2) (R1 ← Memory[R2 + 100])는 Decode에서 Opcode = LOAD, Base = R2, Offset = 100, Dest = R1을 파악하고, Execute에서 Address ← R2 + 100을 계산한다. Memory에서 MBR ← Memory[Address]를 수행한 뒤 Write Back에서 R1 ← MBR을 기록한다.

STORE R1, 100(R2) (Memory[R2 + 100] ← R1)는 Decode에서 Opcode = STORE, Src = R1, Base = R2, Offset = 100을 처리하고, Execute에서 Address ← R2 + 100을 계산한다. Memory는 Memory[Address] ← R1을 수행하며, 결과 레지스터가 없으므로 Write Back은 건너뛴다.

분기 명령어 BEQ R1, R2, label (if R1 == R2 then PC ← label)는 Decode에서 Opcode = BEQ, Src1 = R1, Src2 = R2, Offset = label을 해석한다. Execute는 Compare R1, R2; if equal then PC ← PC + Offset을 수행하고 Memory와 Write Back은 건너뛴다.

분기 성공 시에는 다음 명령어를 무효화하는 파이프라인 플러시가 발생하고, 분기 실패 시에는 정상 진행한다.

CPU명령어 사이클컴퓨터구조파이프라인레지스터