프로그램 카운터 설명: CPU의 책갈피
프로그램 카운터가 다음 명령을 가리키고 순차 코드에서 전진하며 분기, 점프, 예외, 리셋에 의해 어떻게 다른 위치로 바뀌는지 설명합니다.
요약: 프로그램 카운터(PC)는 일부 아키텍처에서 명령 포인터라고도 하며, 다음에 인출할 명령을 가리킵니다. 순차 실행에서는 해독된 명령 길이만큼 전진하고, 분기, 점프, 예외, 인터럽트, 리셋은 다른 다음 PC 값을 선택합니다.
프로그램 카운터는 명령 인출의 중심에 있는 특수 목적의 아키텍처 상태 요소입니다. 교육용 CPU에서는 명령 메모리 주소를 구동하는 실제 레지스터일 수 있습니다. 현대 프로세서에서는 예측, 큐, 캐시, 주소 변환, 투기적 인출 때문에 구현이 더 분산되어 있지만, 아키텍처는 여전히 다음 명령의 주소를 정의합니다.
이 글에서는 PC가 저장하는 값, 증가 방식, 분기와 점프가 PC를 다른 위치로 전환하는 방식, 그리고 레지스터·가산기·멀티플렉서로 PC를 배선하는 방법을 살펴봅니다. 각 부품은 순차 명령 실행기 템플릿에서 직접 조작할 수 있는 동작 회로로 연결됩니다.
프로그램 카운터란?
프로그램 카운터는 메모리 속 다음 명령을 가리키는 단 하나의 역할을 맡은 레지스터입니다. 사용자 코드가 자유롭게 읽고 쓰는 범용 레지스터가 아닙니다. CPU의 제어 논리가 구동하는 특수 목적 레지스터입니다.
가장 단순한 기계에서는 PC의 폭과 메모리의 주소 지정 가능 단위가 코드 주소 공간을 제한합니다.
- 8비트 PC는 개의 주소 지정 가능한 위치를 선택할 수 있습니다.
- 16비트 PC는 개의 위치를 선택할 수 있습니다.
- 32비트 PC는 개의 가능한 비트 패턴을 가집니다.
- 64비트 PC는 개의 가능한 비트 패턴을 가집니다.
이 위치들이 바이트인 것은 바이트 주소 지정 아키텍처에서만 그렇습니다. 실제 프로세서는 명목상의 레지스터 폭보다 적은 주소 비트만 구현할 수 있고, 가상 명령 주소를 물리 메모리로 변환하며, 정렬 또는 정규 주소 규칙을 적용할 수 있습니다.
DigiSim의 PROGRAM_COUNTER_8BIT 부품은 8비트 경우를 구현합니다. 이는 시뮬레이터에서 실제 프로그램을 실행할 만큼은 크면서도 비트 단위로 살펴볼 수 있을 만큼 작습니다.
PC는 인출 단계의 맨 앞에 놓입니다
인출-해독-실행 주기의 인출 단계는 다음 네 신호를 순서대로 읽습니다.
- PC가 현재 값을 주소 버스에 실어 보냅니다.
- 메모리가 해당 주소에 있는 명령어를 반환합니다.
- 명령어가 명령 레지스터에 래치됩니다.
- 다음 인출에서 다음 명령을 읽도록 PC가 갱신됩니다.
1~3단계는 더 넓은 범위의 인출 설명에서 다룹니다. 이 글이 다루는 것은 4단계입니다.
PC는 왜 자동으로 증가해야 할까?
PC가 그 자리에 머문다면 CPU는 같은 명령을 영원히 인출합니다. 그래서 하드웨어는 기본적으로 매 인출 주기 뒤 한 단계 앞으로 나아가도록 PC에 자동 증가 회로를 연결합니다.
증가는 전용 가산기, 보통 이진 덧셈 완전 정복에서 자세히 다룬 작은 리플 캐리 가산기로 계산합니다. 이 가산기의 입력은 현재 PC와 상수 오프셋이며, 출력은 PC의 데이터 입력으로 되돌아가 다음 클록 에지에서 PC에 래치됩니다.
PC + 1과 PC + 4: 명령 폭이 중요한 이유
PC에 더하는 상수는 항상 문자 그대로의 값 1이 아니라 주소 지정 가능 단위로 나타낸 명령의 폭입니다.
| 아키텍처 | 명령 폭 | 증가량 | 이유 |
|---|---|---|---|
| DigiSim 8비트 교육용 CPU | 1바이트(1워드) | PC + 1 | 각 주소가 완전한 명령 하나를 담음 |
| 압축 명령을 사용하지 않는 MIPS; RISC-V | 4바이트 | PC + 4 | 메모리는 바이트 주소 지정이며, 이 인코딩의 폭은 4바이트 |
| ARM (A32) | 4바이트 | PC + 4 | 같은 이유 |
| C 확장을 사용하는 RISC-V; Arm Thumb/Thumb-2 | 2 또는 4바이트 | PC + 해독된 길이 | 둘 다 16비트 인코딩을 포함하며 32비트 인코딩도 가능 |
| x86 | 1~15바이트 | PC + (해독된 길이) | 가변 길이 명령어이므로 디코더가 길이를 보고 |
아키텍처 원리는 같습니다. 순차 다음 주소는 현재 명령의 인코딩을 따릅니다. 고정 폭 ISA는 상수 증가량을 사용할 수 있습니다. 가변 폭 ISA는 고성능 프런트엔드가 해독 완료 전에 그 경계를 예측하고 인출하더라도 현재 명령의 길이를 결정해야 합니다.
부울 대수적 설명
명령 폭이 고정된 아키텍처에서 일 때, 기본 다음 PC 값은 다음과 같습니다.
DigiSim 8비트의 경우 이므로 증가는 단순히 +1 가산기입니다.
분기와 점프는 어떻게 PC를 재정의할까?
순차 실행은 지루한 경우입니다. 실제 프로그램에는 루프, 조건문, 함수 호출, 반환이 필요하며, 이 모두는 PC가 다음 순차 주소가 아닌 다른 곳으로 이동해야 함을 뜻합니다.
두 가지 흔한 제어 전송 종류가 이 변화를 잘 보여 줍니다.
- 점프(무조건): 조건과 무관하게 PC를 대상 주소로 바꿉니다.
- 분기(조건부): ISA가 정한 조건이 참일 때만 대상을 선택하고, 그렇지 않으면 순차 주소를 선택합니다. 어떤 ISA는 제로, 캐리, 부호, 오버플로 같은 상태 플래그를 검사합니다. 기본 RISC-V를 비롯한 다른 ISA는 레지스터를 직접 비교하고 산술 조건 코드 레지스터를 두지 않습니다. CPU 플래그 가이드는 플래그 기반 설계를 설명합니다.
두 연산 모두 결국 PC에 비순차 값을 쓰는 일입니다.
대상 주소는 어디서 오는가?
대상은 세 방식 중 하나로 인코딩됩니다.
- 즉시값(PC 상대): 명령어에 오프셋이 들어 있습니다. 다음 PC는
PC + offset입니다. RISC-VBEQ, ARMB, x86 짧은 점프가 모두 이 형식을 사용합니다. 장점은 위치 독립 코드입니다. - 직접 또는 영역 상대: 명령어가 대상 비트를 제공하며, 이는 고정된 기준 주소 또는 현재 PC의 상위 비트와 결합될 수 있습니다. 고전적인 MIPS
J가 익숙한 예입니다. 완전한 절대 주소는 일반적인 고정 폭 명령어 하나에 들어가는 경우가 드뭅니다. - 레지스터 간접: 대상은 범용 레지스터 또는 ALU 출력에 있습니다. RISC-V
JALR, ARMBX, x86JMP rax가 예입니다. 이 형식은 함수 포인터, 가상 디스패치, 계산된goto를 지원합니다.
작은 단일 주기 데이터패스에서는 이런 후보들이 멀티플렉서에 입력될 수 있습니다. 더 고급 구현에서는 선택된 아키텍처 경로가 은퇴하기 전 여러 장치에서 대상을 계산·예측·대기열 처리할 수 있습니다.
구현: 레지스터 + 증가기 + 멀티플렉서
PC의 하드웨어 구조는 CPU에서 가장 깔끔한 데이터 경로 중 하나입니다.
┌──────────────┐
PC current ─────▶│ Adder (+W) │──── PC + W ──┐
└──────────────┘ │
▼
┌───────────────┐
│ MUX (2:1) │
target_addr ─────────────────▶│ sel = take? │──── next_PC
└───────────────┘
│
▼
┌───────────────┐
clock ─────────────────────▶│ PC register │──── current_PC
└───────────────┘
세 부분으로 이루어집니다.
- 현재 주소를 보관하는 레지스터입니다. 교육용 데이터패스에서는 주소 버스 폭에 맞게 넓힌 에지 트리거 저장소로 만든 8비트 레지스터입니다.
- 한 입력을 PC에, 다른 입력을 상수 폭 에 연결한 가산기입니다.
- 한 입력은 가산기(기본 순차 경로)에서, 다른 입력은 분기 대상(분기 채택 경로)에서 오는 2:1 멀티플렉서입니다. 선택선은 제어 장치가 구동하며, 현재 명령이 점프이거나 조건이 참으로 평가된 분기일 때 활성화됩니다. 멀티플렉서는 데이터 트래픽 제어기의 주제입니다.
매 클록 에지마다 PC는 멀티플렉서가 현재 선택한 값을 래치합니다. 이 하나의 래치가 “전진”과 “분기”를 가르는 전체 메커니즘입니다.
멀티플렉서의 선택 입력 확장
실제 설계는 같은 멀티플렉서에 더 많은 소스를 추가합니다.
| 소스 | 선택되는 경우 |
|---|---|
| PC + W | 기본값 — 순차 실행 |
| PC + offset (PC 상대) | 조건부 분기, 짧은 점프 |
| 직접 대상 / 벡터 | 직접 점프, 리셋 또는 아키텍처가 정한 제어 전송 |
| 레지스터 / ALU 출력 | 간접 점프, 함수 반환, 선택문 분기 |
| 리셋 / 부트 벡터 | 전원 인가 또는 외부 리셋 뒤 |
| 예외 / 인터럽트 벡터 | 트랩 뒤 |
선택선은 1비트에서 3비트로 넓어지지만 구조적 발상은 변하지 않습니다.
리셋 동작: PC는 어디서 시작할까?
CPU의 전원이 켜지거나 리셋되면 PC는 알려진 주소를 가져야 합니다. 그렇지 않으면 실행은 무작위 위치에서 시작됩니다.
흔한 규약은 두 가지입니다.
- PC = 0: 실행은 주소
0x0000…0에서 시작합니다. DigiSim 교육용 CPU는 위치 0에 첫 명령을 두는 이 단순한 규약을 사용합니다. - 부트 벡터: 0이 아닌 고정 주소입니다. x86은 콜드 리셋 시 물리 주소
0xFFFFFFF0(BIOS ROM 근처인 4 GB 공간의 맨 위)으로 갑니다. ARM Cortex-M 코어는 주소0x00000004에 저장된 리셋 벡터에서 초기 PC를 읽습니다. 일부 6502 계열 칩은0xFFFC/0xFFFD에서 리셋 벡터를 읽습니다.
하드웨어에서 “리셋”은 PC의 비동기 클리어(또는 동기 로드) 입력에 연결된 선입니다. 리셋이 활성화되면 멀티플렉서와 무관하게 PC는 부트 값으로 강제됩니다.
예제: 다섯 명령 루프
인 8비트 CPU에서 다음 의사 프로그램을 생각해 봅시다.
0x00: LDI R0, 5 ; load 5 into R0
0x01: LDI R1, 0 ; load 0 into R1
0x02: ADD R1, R0 ; R1 = R1 + R0
0x03: DEC R0 ; R0 = R0 - 1, sets Z flag if R0 hits 0
0x04: BNZ 0x02 ; if Z == 0, branch back to 0x02
0x05: HLT
PC 값을 단계별로 살펴보면 다음과 같습니다.
| 주기 | 인출 시 PC | 명령 | 인출 뒤 | 분기 채택? | 다음 PC |
|---|---|---|---|---|---|
| 1 | 0x00 | LDI R0, 5 | PC + 1 = 0x01 | 해당 없음 | 0x01 |
| 2 | 0x01 | LDI R1, 0 | PC + 1 = 0x02 | 해당 없음 | 0x02 |
| 3 | 0x02 | ADD R1, R0 | PC + 1 = 0x03 | 해당 없음 | 0x03 |
| 4 | 0x03 | DEC R0 (R0=4, Z=0) | PC + 1 = 0x04 | 해당 없음 | 0x04 |
| 5 | 0x04 | BNZ 0x02 | PC + 1 = 0x05; target = 0x02 | 예 (Z=0) | 0x02 |
| 6 | 0x02 | ADD R1, R0 | … | … | … |
5주기에서 교육용 데이터패스는 Z 플래그가 클리어되어 있으므로 순차 다음 주소(0x05) 대신 분기 대상(0x02)을 선택합니다. 실제 제어 흐름 장치는 더 정교하지만, 아키텍처 결과는 여전히 폴스루 경로와 제어 전송 대상 중 하나를 선택하는 것입니다.
흔한 함정
- PC + W의 오프바이원 오류. 흔한 시뮬레이터 버그는 명령 폭이 4바이트인 아키텍처에
PC + 1을 쓰는 것입니다. CPU는 각 명령의 두 번째 바이트를 다음 명령의 시작으로 인출하며, 모든 프로그램이 무작위 데이터인 것처럼 동작합니다. - 인출이 메모리를 읽기 전에 PC를 갱신하는 문제. 메모리를 읽는 동안 PC는 현재 주소를 유지해야 합니다. 조합 논리로 갱신하지 말고 클록 에지에서 갱신하세요.
- 리셋 누락. 리셋 선이 없는 PC는 전원 인가 시 플립플롭이 무작위화하는 값으로 부팅됩니다. 언제나 리셋을 연결하세요.
- 잘못된 정렬로 분기. 요구되는 명령 정렬은 ISA 및 확장에 따라 다릅니다. 32비트 정렬 명령만 있는 기계는
0x1003같은 대상을 거부하며, 16비트 인코딩이 있는 ISA는 더 많은 대상을 허용할 수 있습니다. 보편적인 가정을 하드코딩하지 말고 아키텍처 규칙을 적용하세요.
DigiSim에서 만들어 보기
순차 명령 실행기 템플릿을 여세요. 다음을 확인할 수 있습니다.
- 인출 경로 맨 위의
PROGRAM_COUNTER_8BIT블록; - 매 클록 주기마다
1을 더하도록 고정 배선된 가산기; PC + 1과 제어 장치가 구동하는 대상 주소 중 하나를 선택하는 멀티플렉서;- PC를
0x00으로 강제하는 리셋 스위치.
클록을 수동으로 진행하며 PC가 전진하는 모습을 보세요. 그런 다음 분기 명령이 포함된 프로그램을 불러와 멀티플렉서가 “순차”에서 “대상”으로 바뀌는 순간을 관찰하세요. 그 한 비트가 가장 순수한 형태의 제어 흐름입니다.
더 큰 그림에서 PC의 위치
프로그램 카운터는 더 큰 CPU 데이터 흐름의 한 노드입니다. PC가 가리키는 명령은 명령 레지스터 및 해독 처리를 위해 캡처됩니다. 산술 결과는 플래그 기반 분기 설계가 참조하는 CPU 플래그를 갱신할 수 있습니다.
전체 반복을 보려면 인출-해독-실행 사례 연구를 읽고, 순차 명령 실행기 템플릿을 불러와 분기 명령을 한 단계씩 실행하세요. PC가 처음으로 비순차적으로 점프하는 모습을 보면 추상화는 더 이상 다이어그램이 아니라 회로가 됩니다.



