4. 컴퓨터 구조에 대한 두 번째 이야기

JAMM·2021년 7월 19일
0

CS

목록 보기
3/12
post-thumbnail

4장. 컴퓨터 구조에 대한 두 번째 이야기


1. 컴퓨터 구조의 접근방법


컴퓨터를 디자인하자


  • 가상의 컴퓨터를 디자인하면서 생각

  • CPU 디자인: H/W 전문가(로직), ASIC 전문가, Algorithm 전문가, Interface 전문가, 프로그램 전문가(레지스터, 명령어 디자인), ...

  • 프로그래머 관점

    • 컴퓨터 구조를 잘 아는 프로그래머도 컴퓨터 디자인에 참여한다.
    • 컴퓨터 디자인은 레지스터와 명령어 디자인
  • CPU의 레지스터만을 디자인 대상으로 삼는다고 가정하다

    • CPU의 특성을 알 필요가 있는 시스템 프로그래머(CPU에 종속적인 어셈블리 프로그래밍을 하는 개발자)의 관점은 레지스터에 집중되기 때문에, 레지스터에 대한 이해가 중요
  • 레지스터 디자인의 핵심

    1. 레지스터는 몇 비트로 구성할 것인가? - 32비트, 64비트

      • n 비트 → n 비트
      • n 비트 시스템에서 레지스터도 n 비트로 구성한다.
    2. 몇 개 정도로 레지스터를 구성할 것인가?

    3. 레지스터 각각을 무슨 용도로 사용할 것인가?

      • 레지스터의 용도를 정하게 되면, 명령어를 단순화 시킬 수 있고 속도도 빠르게 할 수 있다.
      ex) 16비트를 갖는 8개의 레지스터의 용도 예시
      r0: 범용 레지스터
      r1: 범용 레지스터
      r2: 범용 레지스터
      r3: 범용 레지스터
      r4: ir(instruction register)
      r5: sp(stack pointer)
      r6: ir(link register)
      r7: pc(program counter)

→ 레지스터를 디자인 했다면, 이제 CPU에게 일을 시키기 위한 명령어 구조 및 명령어 종류를 디자인해야 함


명령어 구조 및 명령어 디자인


  • 레지스터와 명령어의 상관관계: 레지스터 구성형태에 따라서 명령어의 구조가 달라진다.

  • 가상 16비트 레지스터 디자인하도록 설정

  • 명령어의 기본 모델

    • 16비트 레지스터를 가지고 있기 때문에 명령어도 16비트로 설정한다.
    • 꼭 비트 수를 동일하게 맞춰주지 않아도 된다.
  • 16비트 명령어의 활용방안

    • 16비트의 명령어를 가지고 만들어 낼 수 있는 명령어의 총 개수는 2의 16승인 65536이 되는데. 이는 기존 CPU의 독립적인 명령어 개수가 세 자리 수가 넘어가지 않는 다는 상황에서 너무 비효율적이다.
    • 명령어의 구성을 예를 들어보면 '레지스터 r1에 있는 값과 숫자 7을 더해서 레지스터 r2에 저장"의 구성을 취하는데, 이 때 (덧셈 연산 명령어, 레지스터 r1 명령어, 레지스터 r2 명령어, 숫자)의 명령어 구획이 나누어지게 되고 앞서 예시를 들었던 하나의 명령어 문장 안에 총 4개의 정보를 담을 수 있다.
    • 이와 같이 16비트의 명령어 안에 각각 담아야 할 정보에 대한 명령어 구획을 나누어 구분할 수 있는 제한을 두면 효율적으로 명령어를 활용할 수 있다.
  • 사칙연산 명령어 구성 (사칙연산)

    연산의 의미 | 심볼 | 2진코드
    덧셈       | ADD | 001
    뺄셈       | SUB | 010
    곱셈       | MUL | 011
    나눗셈      | DIV | 100
    • 명령어에 따라서 조합이 달라질 수 있다. 아래의 예시는 가상의 사칙연산 명령어 조합
    • (예약, 2비트) (연산자, 3비트) (저장소, 3비트) (피연산자1, 4비트) (피연산자2, 4비트)
      • 저장소: 레지스터 (모든 레지스터는 3비트로 표현할 수 있기 때문에)
      • 피연산자 1, 2: 레지스터 or 숫자 (피연산자를 나타내는 4비트로 구성되기 때문에, 하나의 비트를 희생하여 이를 구분할 수 있는 제한을 두어야 함)
    • 문제점:
      • 숫자를 표현하는 피연산자가 표현할 수 있는 숫자는 세 개의 비트만 활용하므로 여덟 개 밖에 되지 않는다.
    • 주의할 점:
      • 피연산자 위치에 레지스터 또는 숫자가 올 수 있는데, 이를 2진 코드로 어떻게 구분할 수 있을까?
      • 피연산자 위치의 첫 번째 이진 코드를 0과 1로 구분하여, 0일 때 숫자 그리고 1일 때 레지스터 심볼에 대한 2진코드를 위치시킬 수 있다.
      • 이렇듯 명령어를 구성할 때, 명령어의 역할을 명확하게 설계를 해야한다.
      • 이러한 명령어를 해독하여 역할을 지정해주는 역할은 CPU의 ALU에 의해서 진행이 되는데, 여기서 볼 수 있듯이 CPU를 디자인할 때, 명령어를 디자인하는 프로그래머와 CPU 디자인을 하는 전문가가 함께 진행해야 한다.
  • 명령어 구조를 단순화해야 하는 이유

    • 하나의 명령어를 실행하는 과정 'Fetch → Decode → Execution'의 단계는 각각의 단계마다 1클럭이 사용된다. 따라서 하나의 명령어를 실행하는데 3클럭이 필요하다.
    1. RISK
      • Reduced: 명령어를 간소화 시킨 구조
      • 명령어의 수를 대폭 줄이고, 명령어 길이를 일정하게 디자인해서 탄생한 구조
      • 이러한 RISC 구조가 높은 성능을 내는 데 유리한 이유?
        • 모든 명령어는 F → D → E의 순서로 실행된다.
        • 1 2 3 4 5 6 7
        • F D E
        • F D E
        • F D E
        •   F D E
        •     F D E
        • 이처럼 5개의 명령어를 실행하기 위해서 7클럭이 사용된다.
        • 이렇듯 고성능 컴퓨터에서는 속도를 빠르게 하기 위해 RISK 구조로 가게된다.
        • 클럭이 높은 것이 단순히 좋은 것이 아니고, 한 클럭당 처리할 수 있는 효율성을 고려해야 한다.
    2. CISK (Complex Instruction Set Computer)
      • Complex: 명령어를 다양화한 구조지만, 그만큼 복잡해진다.
      • 명령어의 종류가 많다 ~= 다양한 조합이 가능하다.
      • 수십 줄에 걸쳐서 구현해야 하는 기능을 단 한 줄로 완성 시킬 수 있고, 필요에 따른 명령어 길이가 유동적이다. 따라서 메모리를 효율적으로 사용할 수 있다.
      • 반면, 일단 명령어의 수가 많고 그 크기가 일정치 않기 때문에 CPU가 복잡히지고 성능 향상에 제한이 따른다.

2. LOAD & STORE 명령어 디자인


  • 명령어의 제한 (RISC의 특징)

    1. 사칙연산의 피연산자는 숫자 or 레지스터

    2. 연산결과는 레지스터에 저장

    3. 레지스터를 통해서 모든 연산을 진행하겠다.

      → 즉, 메인 메모리 주소 정보를 사칙연산의 피연산자로 올 수 있도록 명령어 구조를 설계하지 않고

      → 위와 같은 명령어의 제한이 있기 때문에, LOAD와 STORE 명령어가 필요하다.

      → 따라서, 메인 메모리에 저장된 데이터를 레지스터로 일단 옮기고 연산을 진행해야 하는데 이를 위해 레지스터와 메인 메모리 사이에서 데이터를 전송할 수 있는 명령어가 필요함

      RAM (Main Memory)                                                   Register
      int a=10: 0x10번지에 할당    ---LOAD-->
      int b=20: 0x20번지에 할당    <--STORE--    (예약, 2비트) (ADD, 3비트) (c, 3비트) (a, 4비트) (b, 4비트)
      int c=0:  0x30번지에 할당
      
      c = a + b
  • Load 명령어 (메인 메모리 → 레지스터)

    • (예약, 2비트) (Load, 110) (destination(r1), 3비트) (source(0x20), 8비트)
    • destination: 데이터를 저장할 레지스터 정보
    • source: 데이터를 읽어올 메모리의 주소 정보
    • Load 명령어가 위치한 레지스터의 2진코드는 4개의 연산 2진코드와 겹치지 않게 구성한다.
    • 데이터를 불러올 메모리의 주소에 있는 데이터 정보(source)를 r1이라는 레지스터(destination)에 저장하라
  • Store 명령어 (레지스터 → 메인 메모리)

    • (예약, 2비트) (Store, 111) (source(r1), 3비트) (destination(0x20), 8비트)
    • source: 데이터를 읽어올 레지스터 정보
    • destination: 데이터를 저장할 메모리의 주소 정보
    • Store 명령어가 위치한 레지스터의 2진코드는 4개의 연산 2진코드와 겹치지 않도록 구성한다.
    • 데이터를 읽어올 레지스터의 정보(source)를 데이터를 저장할 메모리의 주소 정보(destination)에 저장한다.
문제 해결

1. 명령어 (LOAD r1 0x10)
메인 메모리 주소 0x10에 위치한 a=10이라는 데이터 정보를 레지스터 r1에 저장
2. 명령어 (LOAD r2 0x20)
메인 메모리 주소 0x20에 위치한 b=20이라는 데이터 정보를 레지스터 r2에 저장
3. 명령어 (ADD r3 r1 r2)
레지스터 r1에 저장된 a=10과 레지스터 r2에 저장된 b=10의 데이터 정보를 ADD 연산으로 더한
c 값인 c=30을 레지스터 r3에 할당
4. 명령어 (STORE r3 0x30)
레지스터 r3에 할당된 c=30을 메인 메모리 주소 0x30에 저장

3. Direct 모드의 문제점과 Indirect 모드의 제안


  • 앞서 보았던 LOAD 명령어의 구성을 보게되면, 레지스터의 정보를 저장할 곳은 세 개의 비트수로 표현되며 문제가 없지만 메인 메모리의 주소값을 나타내는 부분은 여덟 개의 비트 수가 할당되어서 표현할 수 있는 범위가 0x0000 ~ 0x00ff까지 제한되어 있다.
  • 이 때, 만약 범위 밖에 있는 메인 메모리 주소 값을 참조하려면 문제가 된다.
  • direct 모드: 메모리의 주소 값을 직접 지정해서 메모리의 값을 참조한다.
  • inderect 모드: 메모리의 주소 값에 가서 존재하는 또 다른 메모리의 주소 값을 참조해서 그 값을 읽어오는 것
문제

Ram(Main Memory)
int a = 10;  0x0010번지에 할당
int b = 20;  0x0100번지에 할당
int c = 0;   0x0020번지에 할당

c = a + b

- c = a + b 연산을 수행하기 위해서, 
a와 b의 값이 할당된 메모리 주소 값을 통해 메모리 값을 레지스터에 할당하여 연산을 진행해야 함.
- 하지만 이때 b의 값이 할당된 메모리 주소 값을 우리가 디지안한 모든 명령어로는 16진수의 0x100을
표현할 방법이 없다.

1. 명령어 (LOAD r1 0x0010)
메인 메모리 값 0x0010에 할당된 a=10이라는 데이터 정보를 레지스터 r1에 저장

2. 명령어 (MUL r0 4 4)
4x4=16이라는 값을 레지스터 r1에 저장
2. 명령어 (MUL r2 4 4)
4x4=16이라는 값을 레지스터 r2에 저장
3. 명령어 (MUL r3 r0 r2)
r0에 저장된 16과 r2에 저장된 16을 곱하여 256이라는 값을 레지스터 r3에 저장
256이라는 숫자는 16진수로 100이라는 숫자를 표현한다.
따라서 우리는 명령어 상으로 0x0100이라는 숫자를 표현하지 못하지만,
위의 연산 결과를 통해서 r3라는 레지스터에 0x0100을 저장할 수 있다.
4. 명령어 (SOTRE r3 0x0030)
16진수 0x0030이라는 메인 메모리의 주소 값에 r3 레지스터에 저장된 0x0100이라는
16진수를 저장한다.
5. 명령어 (LOAD r2 [0x0030])
[0x0030]은 메인 메모리 0x0030번지에 저장된 메모리 주소 값을 참조하여, 
그 메모리 주소 값에 할당된 메모리 값 즉 b=20을 레지스터 r2에 저장하겠다는 의미 (indirect mode)

6. 명령어 (ADD r3 r1 r2)
레지스터 r3에 레지스터 r1에 할당된 a=10과 레지스터 r2에 할당된 b=20이라는 값을 ADD 명령어를 수행한
30이라는 값을 할당한다.

Reference

0개의 댓글