Home CPU의 구조와 기능(1)
Post
Cancel

CPU의 구조와 기능(1)

CPU의 구조와 기능

CPU는 기억장치에 저장되어 있는 프로그램 코드인 명령어들을 실햄함으로써 프로그램 수행이라는 컴퓨터의 기본적인 기능을 수행하는데, 그를 위하여 CPU가 수행해야 하는 세부적인 동작들을 순서대로 나열하면 다음과 같습니다.

  1. 명령어 인출(Instruction fetch): 기억장치로부터 명령어를 읽어옵니다.
  2. 명령어 해독(Instruction decode): 수행해야 할 동작을 결정하기 위하여 명령어를 해독합니다.
  3. 데이터 인출(Data fetch): 명령어 실행을 위하여 데이터가 필요한 경우에는 기억장치 혹은 I/O 장치로부터 그 데이터를 읽어옵니다.
  4. 데이터 처리(Data process): 데이터에 대한 산술적 혹은 논리적 연산을 수행합니다.
  5. 데이터 저장(Data store): 수행한 결과를 저장합니다.

위 동작들 중에서 첫 번째 및 두 번째 동작은 모든 명령어들에 대하여 공통적으로 수행되지만, 세 번째부터 다섯 번째까지의 동작들은 명령어에 따라 필요한 경우에만 수행됩니다. 위와 같은 동작들을 수행하기 위하여 CPU가 포함해야 하는 내부 구성요소들을 상펴본 다음에, 명령어 실행 과정의 분석을 통하여 CPU의 동작 원리를 설명하겠습니다. 추가로 명령어 실행 속도를 높이기 위한 주요 기술인 파이프라이닝(pipelining)의 원리와 명령어 형식에 대해서도 분석하겠습니다.

CPU의 기본 구조

CPU는 산술논리연산장치(Arithmetic and Logical Unit: ALU))와 레지스터 세트(register set) 및 제어 유닛(control unit)으로 구성됩니다.

ALU

ALU는 각종 산술 연산들과 논리 연산들을 수행하는 회로들로 이루어진 하드웨어 모듈입니다. 사칙연산의 산술 연산과 AND, OR, NOT 연산 등이 있는 논리 연산으로 구성되어 있습니다.

레지스터

CPU 내부에 위치한 기억장치로서, 엑세스 속도가 컴퓨터의 기억장치들 중에서 가장 빠릅니다. 그러나 레지스터는 내부 회로가 복잡하여 비교적 큰 공간을 차지하기 때문에, 많은 수의 레지스터들을 CPU의 내부에 포함시키기는 어렵습니다.

제어 유닛

프로그램 코드(명령어)를 해석하고, 그것을 실행하기 위한 제어 신호들(control signals)을 순차적으로 발생하는 하드웨어 모듈입니다. 즉, 명령어 실행에 필요한 각종 정보들의 전송 통로와 방향을 지정해주고, CPU 내부 요소들과 시스템 구성 요소들의 동작 시간도 결정합니다. CPU가 제공하는 명령어들의 수가 많아질수록 제어 유닛의 내부 회로는 더 복잡해지는데, 복잡도를 줄이기 위하여 제어 유닛의 동작을 소프트웨어로 처리해주는 방법이 마이크로프로그래밍(microprogramming) 입니다. 하지만 이 방법을 이용하면 명령어 실행 시간이 길어지기 때문에, 최근에는 명령어의 수를 가능한 줄이고 명령어 형식을 단순화함으로써, 하드웨어만으로 명령어를 실행할 수 있도록 하는 RISC(Reduced Instruction Set Computer) 설계 개념도 많이 사용되고 있습니다.

cpu-structure-bus

명령어 실행

CPU는 기억장치에 저장되어 있는 명령어들을 인출하여 실행함으로써 실제적인 작업을 수행합니다. CPU가 한 개의 명령어를 실행하는 데 필요한 전체 과정을 명령어 사이클(instruction cycle)이라고 합니다. CPU가 기억장치로부터 명령어를 읽어 오는 명령어 인출(instruction fetch) 단계와 인출된 명령어를 실행하는 명령어 실행(instruction execution) 단계로 이루어집니다.

instruction-cycle

명령어를 실행하기 위하여 기본적으로 필요한 CPU 내부 레지스터들을 살펴 보면 아래와 같습니다.

  • 프로그램 카운터(Program Counter: PC): 다음에 인출될 명령어의 주소를 가지고 있는 레지스터입니다. 각 명령어가 인출된 후에는 그 내용이 자동적으로 1(혹은 명령어 길이에 해당하는 주소 단위의 수만큼)이 증가되며, 분기(branch) 명령어가 실행되는 경우에는 그 목적지 주소로 갱신됩니다.
  • 누산기(Accumulator: AC): 데이터를 일시적으로 저장하는 레지스터입니다. 이 레지스터의 비트 수는 CPU가 한 번에 연산 처리할 수 있는 데이터 비트의 수, 즉 단어의 길이와 같습니다.
  • 명령어 레지스터(Instruction Register: IR): 가장 최근에 인출된 명령어가 저장되어 있는 레지스터입니다.
  • 기억장치 주소 레지스터(Memory Address Register: MAR): PC에 저장된 명령어 주소가 시스템 주소 버스로 출력되기 전에 일시적으로 저장되는 주소 레지스터로 이 레지스터의 출력 선들이 주소 버스 선들과 직접 접속됩니다.
  • 기억장치 버퍼 레지스터(Memory Buffer Register: MBR): 기억장치에 저장될 데이터 혹은 기억장치로부터 읽혀진 데이터가 일시적으로 저장되는 버퍼 레지스터로 입력 및 출력 선들은 데이터 버스 선들과 직접 접속됩니다.

register-data-bus

인출 사이클

CPU는 각 명령어 사이클의 시작 단계에서 프로그램 카운터(PC)가 가리키는 기억장치의 위치로부터 명령어를 인출해 옵니다. 그럼 다음에 CPU는 PC의 내용을 1씩 증가시킴으로써 명령어들을 기억장치에 저장되어 있는 순서대로 읽어 올 수 있도록 해줍니다.

인출 사이클에서 각 단계별로 수행되는 동작을 마이크로-연산(micro-operation)으로 표현하면 다음과 같습니다.

주기동작
$t_{0}$MAR $\leftarrow$ PC
$t_{1}$MBR $\leftarrow$ M[MAR], PC $\leftarrow$ PC + 1
$t_{2}$MBR

여기서 $t_{0}$ , $t_{1}$ 및 $t_{2}$는 CPU 클록의 각 주기를 가리킵니다. 즉, 명령어 인출에는 세 개의 CPU 클록 주기만큼 시간이 걸립니다. 인출 사이클에서 가장 먼저 수행되는 동작은 현재의 PC 내용을 CPU 내부 버스를 통하여 MAR로 보내는 것입니다. 그렇게 되면, 시스템 주소 버스와 직접 접속된 MAR을 통하여 주소가 기억장치로 전송됩니다.

두 번째 주기에서는 그 주소가 지정하는 기억장치 위치로부터 읽혀진 명령어가 데이터 버스를 통하여 MBR로 적재되며, 그와 동시에 PC의 내용에 1을 더하여 다음 명령어 주소를 가리키게 합니다. 만약 기억장치 주소가 바이트 단위고 명령어 길이는 16비트라면, 한 명령어는 두 개의 주소에 걸쳐 저장되어 한 명령어를 읽은 다음에 PC의 내용에 2를 더해야 다음 명령어가 저장된 위치를 지정할 수 있게 됩니다.

마지막 세 번째 주기에서는 MBR에 저장되어 있는 명령어 코드가 명령어 레지스터인 IR로 이동됩니다.

실행 사이클

실행 사이클에서는 CPU가 인출된 명령어 코드를 해독(decode)하고, 그 결과에 따라 필요한 연산을 아래와 같이 대표적으로 4가지로 수행합니다.

  • 데이터 이동: CPU와 기억장치 간 혹은 CPU와 I/O 장치 간에 데이터를 이동합니다.
  • 데이터 처리: 데이터에 대하여 산술 혹은 논리 연산을 수행합니다.
  • 데이터 저장: 연산결과 데이터 혹은 입력장치로부터 읽어들인 데이터를 기억장치에 저장합니다.
  • 프로그램 제어: 프로그램 실행 순서를 결정합니다.

실행 사이클에서 수행되는 마이크로-연산들은 명령어의 종류에 따라 서로 달라지므로, 각 분류에 해당하는 명령어를 한 개씩 예를 들어 설명하겠습니다. 먼저 가상의 CPU의 명령어 형식을 정의해야 하는데, 명령어는 아래와 가이 CPU가 수행할 연산을 지정해주는 연산 코드와 그 연산의 수행에 필요한 오퍼랜드로 구성되며, 오퍼랜드(addr)는 명령어가 사용할 데이터가 저장되어 있는 기억장치의 주소를 가리킨다고 가정합니다.

연산 코드오퍼랜드(addr)
  

데이터 이동

실행 사이클에서 첫 번째로 수행해야 할 연산은 IR에 저장된 명령어의 오퍼랜드, 즉 addr를 MAR을 통해 기억장치로 보내어 데이터를 인출하는 것입니다. 먼저, 첫 번째 연산 분류인 데이터 이동을 위한 명령어의 한 예로서, ‘LOAD addr’ 명령어의 경우를 보면 기억장치에 저장되어 있는 데이터를 CPU 내부 레지스터인 AC로 적재(load)하게 되는데, 그러한 동작을 위하여 실행 사이클 동안에 수행되어야 하는 마이크로-연산들을 보면 다음과 같습니다.

주기동작
$t_{0}$MAR $\leftarrow$ IR(addr)
$t_{1}$MBR $\leftarrow$ M[MAR]
$t_{2}$AC $\leftarrow$ MBR

첫 번째 주기에서는 명령어 레지스터 IR에 적재된 명령어의 오퍼랜드인 주소(addr)를 MAR를 통하여 기억장치로 보냅니다. 두 번째 주기에서는 그 주소가 지정하는 기억 장소로부터 데이터를 인출하여 MBR에 저장하며, 그 데이터를 세 번째 주기 동안에 AC 레지스터에 적재함으로써 LOAD 명령어의 실행이 완료됩니다.

데이터 저장

두 번째 분류인 데이터 저장을 위한 명령어의 예로는 AC 레지스터의 내용을 기억장치에 저장하기 위한 ‘STA addr’ 명령어의 실행 사이클은 다음과 같은 마이크로-연산들로 이루어집니다.

주기동작
$t_{0}$MAR $\leftarrow$ IR(addr)
$t_{1}$MBR $\leftarrow$ AC
$t_{2}$M[MAR] $\leftarrow$ MBR

여기서도 첫 번째 주기에서는 데이터를 저장할 기억 장소의 주소를 MAR로 보냅니다. 다음 주기에서는 저장할 데이터를 버퍼 레지스터인 MBR로 이동시킨 결과로, 주소와 데이터가 모두 기억장치로 보내어졌습니다. 마지막 세 번째 분류인 주기에서는 그 주소가 지정하는 기억 장소에 데이터가 저장됩니다.

데이터 처리

세 번째 분류인 데이터 처리를 위한 명령어의 예로서, 기억장치에 저장된 데이터를 AC 레지스터의 내용과 더하고, 그 결과를 다시 AC 레지스터에 저장하는 ‘ADD addr’ 명령어의 마이크로-연산들은 다음과 같습니다.

주기동작
$t_{0}$MAR $\leftarrow$ IR(addr)
$t_{1}$MBR $\leftarrow$ M[MAR]
$t_{2}$AC $\leftarrow$ $AC + MBR$

첫 번째 주기 동안에 명령어의 오퍼랜드(addr)를 주소 버스를 통하여 기억장치로 보내고, 두 번째 주기에서 해당 기억 장소로부터 데이터를 인출하여 MBR에 적재합니다. 그리고 세 번째 주기에서 그 데이터와 AC 레지스터의 내용을 더하고, 결과값을 다시 AC 레지스터에 저장합니다.

프로그램 제어

일반적으로 명령어들은 기억장치에 저장되어 있는 순서대로 실행됩니다. 그런 경우에는 명령어 실행 순서를 결정하기 위하여 별도의 명령어가 필요하지 않고, 프로그램 카운터(PC)에 의해 자동적으로 순서가 결정됩니다. 그러나 현재의 PC 내용이 가리키는 위치가 아닌 다른 위치의 명령어로 실행 순서를 바꾸도록 해주는 명령어들이 있습니다. 이러한 명령어들을 분기(branch) 명령어라고 부르는데 그 예로 ‘JUMP addr’ 명렁어는 아래와 같이 한 주기만에 실행이 완료됩니다.

주기동작
$t_{0}$PC $\leftarrow$ IR(addr)

즉, 이 명령어에 포함된 오퍼랜드(addr)는 분기 목적지 주소(branch target address)를 가리키기 때문에, 실행 사이클에서는 그 주소를 PC로 적재하기만 하면 됩니다. 그렇게 되면, 다음 명령어 인출 사이클에서는 그 주소가 가리키는 기억 장소로부터 명령어가 인출되므로, 명령어 실행 순서가 바뀌게 됩니다.

인터럽트 사이클

대부분 컴퓨터터들은 프로그램 처리 중에 CPU로 하여금 순차적인 명령어 실행을 중단하고 다른 프로그램을 처리하도록 요구할 수 있는 인터럽트(interrupt) 메커니즘을 제공합니다. 외부로부터 인터럽트 요구가 들어오게 되면, CPU는 원래의 프로그램 수행을 중단하고, 요구된 인터럽트를 처리해주기 위한 프로그램을 먼저 수행하는데 이런 프로그램을 인터럽트 서비스 루틴(Interrupt service routine: ISR) 이라고 부릅니다.

CPU는 인터럽트 요구가 들어오면 인터럽트 서비스 루틴을 수행하고, 중단하였던 원래 프로그램의 수행을 계속할 수 있어야하기 때문에 아래와 같은 동작을 수행합니다.

  1. 현재의 PC 내용을 스택(stack)에 저장합니다. 이는 인터럽트 처리 완료 후 복귀할 주소를 저장해두기 위한 절차입니다.
  2. 해당 인터럽트 서비스 루틴을 호출하기 위하여 그 루틴의 시작 주소를 PC에 적재합니다.

이와 같이 인터럽트 요구 신호를 검사하고, 현재의 PC 내용을 스택에 저장한 다음에, PC에 해당 ISR의 시작 주소를 적재하는 과정을 인터럽트 사이클(interrupt cycle)이라고 부릅니다. CPU가 인터럽트 사이클을 시작하기 전에, 인터럽트가 가능한 상태로 세트되어 있는지를 먼저 확인해야합니다.

여기서 인터럽트를 받을 수 있는 상태를 ‘인터럽트 가능(interrupt enabled)’ 상태라고 하며, 인터럽트를 받을 수 없는 상태를 ‘인터럽트 불가능(interrupt disabled)’ 상태라고 부르넨ㄷ, 이 상태는 CPU가 해당 명령어(EI 혹은 DI 명령어)를 수행함으로써 변경시킬 수 있습니다. 인터럽트 요구가 들어오는 경우, 인터럽트 사이클 동안 수행되는 동작들을 마이크로-연산으로 표현하면 다음과 같습니다.

주기동작
$t_{0}$MBR $\leftarrow$ PC
$t_{1}$MAR $\leftarrow$ SP, PC $\leftarrow$ ISR의 시작 주소
$t_{2}$M[MAR] $\leftarrow$ MBR, SP $\leftarrow$ $SP - 1$

여기서 SP는 CPU 내부에 있는 특수 목적용 레지스터들 중 하나인 스택 포인터(stack pointer)를 의미하는데, 그 내용은 항상 스택의 최상위(top of stck: TOS)의 주소를 가리킵니다. 첫 번째 주기에서는 PC의 내용이 MBR로 보내집니다. 두 번째 주기에서는 SP의 내용이 MAR을 통하여 주소 버스로 나가게 되며, PC에는 인터럽트 서비스 루틴의 시작 주소가 적재됩니다. 마지막 주기에서는 MBR에 저장되어 있떤 원래의 PC 내용을 스택에 저장하며, 동시에 SP의 내용을 1 감소시켜 TOS의 주소를 수정합니다.

다중 인터럽트

인터럽트 서비스 루틴의 명렁어들이 실행되고 있는 동안에 다른 외부 장치가 인터럽트 요구를 발생할 수도 있는데, 이것을 다중 인터럽트(multiple interrupt)라고 합니다. 다중 인터럽트를 처리하는 방법으로는 두가지가 있습니다. 첫 번째 방법은 CPU가 인터럽트 서비스 루틴을 처리하고 있는 도중에는 새로운 인터럽트 요구가 들어오더라도 인터럽트 사이클을 수행하지 않도록 하는 즉, 인터럽트 불가능 상태일 때는 CPU가 인터럽트 요구 신호를 검사하지 않도록 하는 방법입니다. 이렇게 되면 그 루틴을 처리하는 동안에 발생한 인터럽트 요구는 대기 상태로 남아 있다가, CPU가 다시 인터럽트 가능 상태로 바뀐 후 인식됩니다.

두 번째 방법은 인터럽트 요구들 간에 우선순위를 정하고, 우선순위가 낮은 인터럽트 요구를 처리하고 있는 동안에 우선순위가 더 높은 인터럽트 요구가 들어오면, 현재의 인터럽트 서비스 루틴의 수행을 중단하고 새로운 인터럽트를 처리하도록 하는 것입니다. 이 방법은 스텍에 원래의 주 프로그램(main program)으로 복귀하기 위한 주소뿐 아니라, 첫 번째 인터럽트 서비스 루틴으로 복귀하는데 사용될 주소도 저장되어야합니다.

간접 사이클

연산에서 사용될 데이터의 주소가 기억장치의 어떤 위치에 저장되어 있고, 명령어에 포함되어 있는 주소는 그 위치를 가리키고 있는 경우 이와 같은 실행 사이클이 시작되기 전에, 그 데이터의 실제 주소를 기억장치로부터 읽어오는 과정이 먼저 수행되어야 합니다. 이 과정을 간접 사이클(indirect cycle)이라고 합니다. 간접 사이클은 인출 사이클과 실행 사이클에 위치하며, 항상 수행되는 것이 아닌 명령어 내의 특정 비트가 세트된 경우에만 수행합니다.

간접 사이클에서는 다음과 같은 마이크로-연산들이 수행됩니다.

주기동작
$t_{0}$MAR $\leftarrow$ IR(addr)
$t_{1}$MBR $\leftarrow$ M[MAR]
$t_{2}$IR(addr) $\leftarrow$ MBR

즉, 인출 사이클에서 읽혀져 IR에 적재되어 있는 명령의 주소 필드 내용을 다시 기억장치로 보내어서, 데이터의 실제 주소를 인출하여 IR의 주소 필드에 저장합니다.

해당 글은 컴퓨터구조론(김종현 저)을 읽고 공부한 내용을 정리한 글입니다.

오탈자 및 오류 내용을 댓글 또는 메일로 알려주시면, 검토 후 조치하겠습니다.

스프링 컨테이너(Spring Container)

DL(Dependency Lookup)