CPU가 수행하는 일

명렁어 집합 구조에서의 명령어는 CPU를 위한 명령어임.
=> CPU가 명령어를 처리할 때 어떤 메카니즘으로 하는지에 대해 알아볼 것임.

  • CPU는 주 메모리에서 명령어를 하나 가져와서 처리하고, 그 다음에 또 가져오고 하나씩 처리함.
  • 명령어 인출과 해독(Instruction Fetch & Decode)
    -> 기억장치로부터 명령어를 읽어오고, 읽어온 명령어가 무엇인지 해독
    -> 명령어가 어떤 형식에 맞춰서 봉투에 담겨온다고 쳤을 때, 봉투를 다 열기 전까지는 모름
  • 데이터 인출(Data Fetch, Load)
    -> 기억장치나 I/O 장치에서 데이터를 읽어옮.
  • 데이터 처리(Data Process)
    -> 산술적 혹은 논리적 연산을 수행
  • 데이터 저장(Data Store)
    -> 수행한 결과를 저장 (=쓰기, write)

=> 인출/처리/저장 등은 명령어 종류에 따라 선택적으로 수행

CPU의 기본 구성

ALU: 각종 산술/논리연산들을 수행하는 회로들로 이루어진 HW 모듈

-> 산술, 논리 연산등을 함.

레지스터(register): CPU 안의, 엑세스 속도가 가장 빠른 기억장치

-> 다른 주 기억장치, 보조 기억장치보다 빠름. CPU와 가까이 있을수록 가장 빠르기 때문에(CPU가 제일 빨라서)
-> CPU 내부에 둘 수 있는 레지스터의 수는 제한적

  • Program Counter: 다음 명령어의 주소 보관
    -> 각 명렬어가 인출된 후에는 자동적으로 일정 크기(한 명령어 길이)만큼 증가
    -> 분기 명령어가 실행되는 경우에는 목적지 주소로 갱신
  • Instruction R: 가장 최근 가져온 명령어 보관
  • ACCumlator: 데이터의 일시적 보관(ALU와 붙어 있음)
  • Memory Address R: CPU가 접근하려는 메모리 주소 보관
  • Memory Buffer R: CPU가 메모리에서 읽거나 저장할 데이터 자체를 보관

대부분의 시스템에서 레지스터의 크기는 CPU가 한 번에 처리할 수 있는 데이터 비트 수(word 길이)와 동일
-> 만약 CPU가 한번에 처리할 수 있는 데이터가 8 bit이면 레지스터의 크기 또한 8 bit이다. => 이 컴퓨터는 8 bit 인 것임.
-> 요즘 64bit 컴퓨터는, 레지스터의 크기가 64bit이고, CPU가 한번에 처리할 수 있는 데이터의 비트 수도 8bit이다.

데이터 적재/저장 명령어 실행과정

  • 적재(load): CPU가 메모리에 있는 데이터를 읽어옴
    -> 프로세서는 데이터가 있는 메모리 주소를 MAR에 보낸다.
    -> MAR이 지정하는 메모리 주소에 있는 데이터를 읽어와 MBR에 저장한다.

  • 저장(store): CPU가 메모리에 데이터를 기록
    -> 프로세서는 데이터를 저장할 메모리 주소를 MAR에 보낸다.
    -> 프로세서는 데이터를 MBR에 저장한다.
    -> 메모리는 MAR이 저장하는 위치에 MBR의 내용을 저장한다.

  • 명령어 사이클(instruction cycle): 명령어를 수행하는 일련의 과정
    -> 컴퓨터마다 명령어 실행과정이 동일하지는 않으나, 일반적으로 거치는 최소 과정을 포함

명령어 사이클

  • CPU가 한 개의 명령어를 실행하는 데 필요한 전체 처리 과정
    -> CPU가 프로그램 실행을 시작하는 순간부터 전원을 끄거나 회복 불가능한 오류가 발생하여 중단될 때까지 반복
    -> 2개의 부사이클로 분리

  • 인출 사이클(fetch cycle)
    -> CPU가 기억장치로부터 명령어를 읽어오는 단계
    -> MAR <- PC => 현재 PC가 가리키는 내용(메모리 주소)을 MAR로 전송
    -> MBR <- M[MAR], PC <- PC + 1 => 메모리는 MAR이 지시하는 메모리의 내용인 명령어를 MBR로 보내고, PC가 다음 명령어를 가리키도록 PC의 내용을 갱신한다.(여기서 1은 명령어의 길이로 가정)
    -> PC가 이미 MAR로 넘겨줬기 때문에, 1을 더함으로써 다음 명령어를 가리킬 수 있도록 한다.
    -> IR <- MBR => MBR에 있는 명령어를 IR에 저장. MBR 과 IR는 메모리간의 통신? Buffer 역할을 한다.

여기서 질문! 왜 데이터 값이랑, 주소를 저장하는 공간이 2개씩 있는거지..?

  • 실행 사이클(execution cycle)
    -> 명령어를 해독하고 실행하는 단계
    -> 인출 사이클때보다 작업량이 더 많기 때문에, 오늘날 컴퓨터는 실행 사이클을 다수의 단계로 분할
    -> 전송 연산: CPU내 레지스터와 메모리 사이에 데이터를 교환
    -> 처리 연산: 산술논리장치를 사용하여 데이터를 조작(데이터형식 변환(int -> float))
    -> 제어 연산: 프로그램의 실행 순서를 제어(순차, 무조건 분기, 조건 분기 등등)
    -> 입출력 연산: CPU 내 레지스터와 I/O 장치 간에 데이터를 이동

Fake Simple 컴퓨터 명령어 집합

  • 명령어 형식
    -> 16bit로 구성
    -> 연산부호가 4 bits 혹은 16 bits
    -> 0부터 15번까지 n번 째 비트가 있음

  • 명령어
    -> LDA: 메모리의 내용을 누산기에 적재(메모리에서 CPU로 가져오는 일)
    -> STA(store): 누산기의 내용을 메모리에 저장(CPU의 내용을 메모리로 가져오는 일)
    -> ADD: 누산기의 내용과 메모리의 내용을 덧셈
    -> SUB: 누산기의 내용과 메모리의 내용을 뺄셈
    -> JMP: 지정한 주소로 분기
    -> CAL: 프로시저 호출
    -> HLT: 프로그램 종료
    -> CPL: 누산기의 내용을 보수화
    -> IAC: 누산기의 내용을 하나 증가
    -> RET: 프로시저 복귀

  • 데이터 형식
    -> 16bit로 구성하고 정수만 취급
    -> 15번 bit는 부호를 의미하고, 0~14번 bit는 정수 크기를 의미. 15번 bit => 0: 양수, 1: 음수

  • 다음과 같은 메모리 상태를 가정
    -> 명령어는 메모리 0100 번지부터 시작
    -> 데이터는 메모리 0500 번지와 0502번지에 숫자 0001, 0002이 저장
    -> 1 Byte 단위로 주소를 매김.
    -> 0100이 a의 주소이면, 0101은 a를 가리키고 0102가 그 다음 b의 주소이다.
    -> 메모리에서는 대부분 주소를 Byte 단위로 표시.

  • 이런 일을 하고 싶다면?
    -> 메모리 0500번지와 0502번지 숫자를 더해 0502 번지에 저장하고
    -> 0300번지 명령어로 분기하라

=> 수식으로 표현하자면 M[502] <- M[500] + M[502]
=> PC <- 300 요정도 된다.

LDA 500  =>  1500
ADD 502  =>  3502
STA 502  =>  2502 //누산기의 내용과 메모리의 내용을 더해서 누산기에 저장했으니, 다시 이 값을 메모리에 돌려줘야함. 따라서 STA를 써야함.
JMP 300  =>  5300

조건분기 명령어

  • 조건 생성, 비교 명령과 분기 명령
    -> 비교와 분기작업을 하나로 융합된 명령어 구조도 있음
    -> 비교 - 산술, 논리 연상 - 결과에 따라 분기 결정
  • 연산 결과를 저장하는 플래그(flag) 레지스터
    -> 상태(Status) 레지스터/비트, 조건 코드(Condition code) 레지스터/비트 라고 부름
    -> 플래그 레지스터는 1 bit를 씀
    -> 올림수(Carry): 연산 결과에 올림수가 있으면 1, 없으면 0
    -> 오버플로우(overflow): 연산 결과에 오버플로우가 있으면 1, 없으면 0
    -> 부호(Sign)/음수(Negative): 연산 결과가 음수면 1, 양수 혹은 이면 0
    -> 영(Zero): 연산 결과가 0이면 1, 0이 아니면 0

프로시저의 호출/복귀 과정

  • cal proc: #proc은 프로시저 이름, 어셈블리 -> 기계어 과정 후 프로시저 시작주소로 변환

  • 인출 사이클은 모든 명령어들이 똑같음.

  • 실행 사이클: TOS(top of stack) <- PC; PC <- proc
    -> 실행 사이클에서, 복귀 후 수행할 명령어 주소(복귀주소)를 TOS(Top of Stack)에 저장

  • 인수(argument) 사용시 좀 더 복잡한 과정 필요

  • Q. 복귀주소 저장시, Stack이 아닌 그냥 register에 저장한다면?
    -> 조건 분기같은 것을 할 때, stack이 아니라면, 순서가 꼬일 것 같음. 먼저 끝내고 나와야 하는 루프? 나 과정이 있을 텐데.. 그,, 우리 막 파이썬이나, 객체지향에서도 코딩할 때, 어느 클래스에서 다른 클래스의 메소드를 쓰고, 그 메소드에서 또 다른 클래스의 메소드를 쓸 때, 이게 하나씩 하나씩 끝내고 돌아오는 것처럼, 이것도 마찬가지일듯 ㅇㅇ

  • ret #proc 프로시저 종료 후 호출 프로그램으로 복귀

  • 인출 사이클은 역시 똑같음.

  • 실행 사이클: PC <- TOS (아까 넣어놨던 Stack에서 꺼내옴)

아아아 알겠다. cal로 다른 프로시저에 가기전에 stack에 주소를 약간 백업? 을 하는거고, ret시 아까 cal 때 백업해놨던 프로시저로 돌아가는 거 ㅇㅇ
기말에 무조건 나옴!

왜 CPU 내 기억장치(Register)가 필요한가?

  • CPU 내부에 기억장치가 없는 가상의 컴퓨터를 가정해보자
    -> M-M(메모리-메모리) 컴퓨터: CPU 내부에 데이터 저장 불가( 모든 데이터는 메모리에서 CPU를 경유하여 메모리로 전송)
    -> 데이터가 필요한 경우, 명령어의 피연산자 필드에 메모리 전체 주소를 명시

  • 폰 노이먼 모델에서 발생 가능한 (최소한의) 데이터 트래픽 추정
    -> 시스템은 아래 조건을 따른다고 가정
    -> 연산 부호 크기는 8비트(바이트)
    -> 연산마다 최대 2개의 피연산자 사용(첫번 째 피연산자는 source & destination)
    -> 메모리 주소는 2바이트
    -> 데이터 크기는 4바이트

  • 명령어 인출 트래픽
    -> 한 명령어당 5바이트 * 명령어 개수

  • 데이터 이동 트래픽(1)
    -> a번지의 데이터를 메모리에서 CPU로 갖고 왔다가 다시 메모리 y번지로 저장. => 한 mov 명령어당 2회 이동 * 4바이트

  • 데이터 이동 트래픽(2)
    -> mul y,x
    -> y번지와 x번지의 데이터를 CPU로 갖고 와 곱셈 수행 후, 결과 데이터를 y번지에 저장(한 명령어당 2개 데이터 * 4바이트 + 1개 결과데이터4바이트 => 34바이트
    -> mul과 add 연산시 트래픽양 동일하므로, subtoal_mul/add = 명령어당 34바이트n개 mul/add 명령어

  • 다 더한게 총 메모리 트래픽

만약 CPU에 기억장치가 있다면?

  • 데이터 트래픽 감소
    -> 반복적으로 사용되는 피연산자를 CPU 내부의 기억장치에 보관
    -> 메모리를 다시 참조할 필요성 제거

  • 명령어 트래픽 감소
    -> 대용량 메모리를 위한 긴 주소 대신에 소규모 기억장치(레지스터)의 짧은 주소 사용
    -> 레지스터 개수 = 레지스터 주소 bit 수
    -> 레지스터 개수 << 메모리 enty 수
    -> 피연산자 필드가 짧아지면 명령어의 길이가 축소

profile
일단 배우는거만 정리해보자 차근차근,,

0개의 댓글