CS/Computer Organization Design (2023-1)

[컴퓨터 구조] 4. 프로세서

샤아이인 2023. 6. 16.

 

4.1 서론

4.1.1) 구현에 대한 개요

우선 모든 명령어의 첫 2단계는 다음과 같이 동일하다.

  1. 프로그램 카운터(PC)를 프로그램이 저장되어 있는 메모리에 보내서 메모리로부터 명령어를 가져온다.
  2. 읽을 레지스터를 선택하는 명령어 필드를 사용하여 하나 또는 2개의 레지스터를 읽는다. 워드 적재 명령어는 레지스터 하나만 읽으면 되지만 대부분의 다른 명령어는 레지스터 2개를 읽는다.

이 2단계 이후는 명령어의 종류에 따라서 달라진다.

다행히 3가지 명령어 종류(메모리 참조, 산술/논리 연산, 분기) 각각에 대해서는 명령어가 무엇인지에 상관없이 필요한 행동들이 대부분같다. MIPS 명령어 집합은 단순하고 규칙적이기 때문에 여러 종류의 명령어 실행이 비슷해서 구현이 간단하다.

 

예를 들어 Jump명령어를 제외한 모든 명령어가 레지스터를 읽은 후에는 ALU를 사용한다.

메모리 참조 명령어는 주소 계산을 위하여 ALU를 사용하고, 산술/논리 명령어는 연산을 수행하기 위하여 ALU를 사용하고, 분기 명령어는 비교하기 위하여 사용한다.

 

ALU를 사용한 후에는 필요한 행동이 명령어 종류마다 다르다.

메모리 참조 : 메모리에 접근

저장 명령어 : 데이터를 기록하기 위하여 접근

적재 명령어 : ALU나 메모리에서 온 데이터를 레지스터에 써야한다.

분기 명령어 : 비교 결과에 따라서 다음 명령어의 주소를 바꿀 수도 있고, PC값을 4만큼 증가시켜 다음 명령어의 주소를 갖게 할 수도 있다.

 

다음 그림은 MIPS 구현을 상위 수준에서 본 그림이다.

프로세서의 흐름을 거의 다 보여주고 있지만 명령어 실행에 중요한 두가지 측면이 빠져있다.

첫째 : 서로 다른 근원지에서 나온 데이터가 같은 유닛으로 가는 곳이 몇군데 있다.

PC에 들어갈 값은 두 덧셈기 중 하나에서 나오고, 레지스터 파일에 쓰일 데이터는 ALU나 데이터 메모리에서 나오며, ALU의 두 번째 입력은 레지스터나 명령어의 수치 필드에서 나온다.

 

실제로 이들 데이터 선을 단순히 그냥 연결할수는 없다. 그러므로 다수의 근원지 중에서 하나를 선택하여 그것만을 목적지로 보내는 구성 요소를 추가하여야 한다. 이를 multiplexor라 불리는 소자를 사용하여 이루어진다.

사실 data selector라는 이름이 더 적절하다.

 

둘째 : 어떤 유닛들은 명령어의 종류에 따라 다르게 제어되어야 하는데 이 부분이 빠져있다.

데이터 메모리는 적재 명령어 일때는 읽기, 저장 명령어 일때는 쓰기를 해야 한다.

레지스터 파일은 적재 명령어나 산술/논리 연산 명령어일 때만 쓰기를 한다.

multiplexor처럼 ALU으 제어선도 명령어 필드 값에 따라 정해지며 제어선이 연산을 통제한다.

 

다음 그림은 직전으 그림의 데이터페스에 주요 기능 유닛을 위한 제어선과 필요한 멀티플렉서 3개를 추가한 그림이다.

제어 유닛(control unit)은 명령어를 입력으로 받아서 기능 유닛들과 두 멀티플렉서의 제어선 값을 결정한다.

세 번째 멀티플렉서는 PC + 4와 분기 목적지 주소 중 어느 것을 PC에 써야 할지 결정하는 것 인데, ALU의 Zero출력으로 제어된다.

이 출력의 값은 beq 명령어의 비교 결과로 결정된다.

 

4.2 논리 설계 관례

MIPS를 구현하는 데이터패스 요소는 조합 소자와 상태 소자 2가지 종류의 논리 소자들로 구성된다.

 

▶ 조합 소자 (cominational element)

데이터 값에 대해 연산을 수행하는 소자로, 출력이 현재 입력에 의해서만 결정된다. 그러므로 같은 입력이 주어지면 항상 같은 출력을 낸다.

위에서 본 그림 4.1의 ALU가 조합 소자의 예시이다.

 

▶ 상태 소자 (state element)

소자에 내부 기억 장소가 있으면 상태를 갖게 된다.

컴퓨터의 전원 플러그를 뺴더라도 플러그를 빼기 전의 값을 상태 소자에 넣어 주면 전과 똑같은 상태에서 다시 시작할 수 있다.

이 상태 소자들이 컴퓨터를 완전히 특징짓는다고 볼 수 있다.

그림 4.1의 명령어 메모리, 데이터 메모리, 레지스터 등이 상태 소자이다.

 

상태 소자는 적어도 2개의 입력과 1개의 출력을 갖는다. 꼭 있어야 되는 입력은 기록할 데이터와 clock이다.

클럭 입력은 데이터 값이 소자에 기록되는 시점을 결정한다. 상태 소자의 출력은 이전 클럭 사이클에 기록된 값이다.

 

논리적으로 가장 간단한 상태 소자중 하나는 D플립플롭인데 이 D 플립플롭에는 2개의 입력과 하나의 출력이 있다.

MIPS에는 플립플롭 말고도 두가지 상태 소자가 더 사용된다. 메모리와 레지스터가 있다.

상태 소자에 언제 쓸것인가는 클럭이 결정하지만, 상태 소자의 값을 읽는것은 언제라도 가능하다.

 

상태를 포함하는 논리 구성 요소들을 순차 회로(sequential circuit)이라 부르는데, 이는 이들의 출력이 입력뿐만 아니라 내부 상태에 따라서도 달라지기 때문이다. 예를 들어 레지스터 파일의 출력은 입력되는 레지스터 번호와 전에 레지스터에 기록된 값 모두에 영향을 받는다.

 

4.2.1) 클러킹 방법론

clocking methodology은 신호를 읽고 쓰는 시점을 정의한다. 신호를 읽고 있는데 동시에 누군가 새로운 값을 쓴다면, 읽은 값이 옛 값일 수도 있고 새로 쓴 값일 수도 있고 심지어는 두 갑이 뒤섞인 것이 될수도 있기 때문이다.

따라서 읽기와 쓰기의 타이밍을 명확하게 지정하는 것이 중요하다.

 

edge-triggered clocking 방법론을 생각해보자.

edge-triggered clocking 방법론은 순차 논리 소자에 저장된 값은 클럭 에지에서만 바꿀 수 있다는 것을 의미한다.

클럭 에지란 그림 4.3에서 보듯이 낮은 값에서 높은 값 혹은 그 반대로의 빠른 변이를 말한다.

상태 소자들만이 데이터 값을 저장할 수 있기 때문에 모든 조합 논리는 상태 소자에서 입력을 받고 상태 소자로 출력을 내보낸다.

조합 논리의 입력은 이전 클럭 사이클에서 상태 소자에 쓴 값들이고, 출력은 다음 클럭 사이클에서 조합 논리나 상태 소자가 사용할 수 있는 값들이다.

 

그림 4.3은 조합 회로를 둘러싸고 있는 2개의 상태 소자를 보여주고 있다. 이 회로는 하나의 클럭 사이클에 동작한다.

즉 모든 신호가 한 클럭 이내에 상태소자 1에서 나와서 조합 회로를 거쳐 상태 소자 2까지 전달되어야 한다.

신호가 상태소자 2에 도착하는 데 필요한 시간이 clock cycle의 길이를 결정하게 된다.

 

매 클럭 에지마다 상태 소자에 쓰기가 행해지는 경우는 앞으로 쓰기 제어 신호(control signal)을 표시하지 않겠다.

반대로 상태 소자가 매 클럭마다 갱신되는 것이 아니라면 쓰기 제어 신호가 분명하게 표시되어야 한다.

클럭 신호와 쓰기 제어 신호는 상태 소자의 입력이며, 쓰기 제어 신호가 인가되고 활성화 클럭 에지일 때만 상태 소자가 변하게 된다.

 

"인가된 (asserted)"라는 용어는 논리적으로 높은 신호를 표시하며 "인가(assert)"라는 용어는 신호를 높은 값으로 만든다는 뜻이다.

반대로 비인가(deassert), 비인가된(deasserted)라는 용어도 사용된다.

 

그림 4.4에서 보는 바와 같이 에지 구동 방법론은 레지스터 내용을 읽고 그 값을 조합 회로로 보내고 같은 레지스터에 쓰는 작업 모두가 한 클럭 사이클에 일어나는 것을 허용한다.

 

이후 블로그 글에서는 계속 상향 클럭 에지를 사용할 것 이다.

 

4.3 데이터 패스 만들기

이번에는 MIPS의 명령어들이 어떤 데이터패스 구성 요소(datapath element)들을 필요로 하는지 살펴보는 것으로 추상화의 최상위 단계를 시작하자.

 

다음 그림4.5(a)는 우리에게 필요한 첫 번째 구성 요소를 보여주고 있다. 프로그램의 명령어를 저장하고 있다가 주소가 주어지면 명령어를 읽어서 보내주는 메모리유닛이다.

4.5(b)는 Program Counter, PC를 보여준다. PC는 현재 명령어의 주소를 가지고 있는 레지스터이다

끝으로 PC를 다음 명령어 주소로 증가시키는 덧셈기가 필요하다. 이 덧셈기는 조합 회로로서 ALU를 가지고 쉽게 만들수 있다.

 

1) 어느 명령이던 실행하기 위해서는 메모리에서 명령어를 가져오는 것으로 시작해야 한다.

2) 다음 명령어 실행 준비를 위해서 PC가 다음 명령어를 가리키도록 4만큼 증가시켜야 한다.

 

이제 이 구성요소들을 합쳐서 데이터페스를 다음과 같이 만들어보자!

이제 R-type 명령어를 생각해보자. 모든 R타입 명령어들은 2개의 레지스터를 읽고 레지스터 내용에 ALU연산을 수행한 후, 그 결과를 레지스터에 쓴다. 이들은 산술/논리 명령어 라고도 불리는데, 이는 이 명령어들이 산술, 논리 연산을 수행하기 때문이다.

(add, sub, AND, OR, slt 명령어가 포함)

 

프로세서의 범용 레지스터 32개는 레지스터 파일(register file)이라고 하는 구조속에 들어 있다.

레지스터 파일은 레지스터들을 모아놓은 것 인데, 파일 내의 레지스터 번호를 지정하면 어느 레지스터라도 읽고 쓸수가 있다.

레지스터 파일은 컴퓨터의 레지스터 상태를 갖고 있다. 레지스터에서 읽어 들인 값들을 연산하려면 ALU가 필요하다.

 

R타입 명령어들은 피연산자로 레지스터 3개를 사용하기 때문에, 매 명령어마다 레지스터 파일에서 데이터 워드 2개를 읽고 데이터 워드 하나를 써야 한다.

 

레지스터에서 데이터 워드를 읽기 위해서는 레지스터의 입력과 출력이 하나씩 필요하다.

읽을 레지스터 번호를 지정하는 입력과 레지스터에서 읽은 값을 내보내는 출력이다.

 

데이터 워드를 쓰기 위해서는 입력이 2개 필요하다.

한 입력은 쓸 레지스터 번호를 지정하고 다른 입력은 레지스터에 쓸 데이터 값을 제공한다.

 

레지스터 파일은 Read register 입력에 실리는 번호에 해당하는 레지스터의 내용을 항상 출력한다.

그러나 write는 쓰기 제어 신호에 의해 제어되므로 클럭 에지에서 쓰기가 일어나려면 이 제어 신호가 인가되어야 한다.

따라서 전체적으로 4개의 입력과 2개의 출력이 필요하다.

전체적으로 4개의 입력(레지스터 번호 3개, 데이터 1개)과 2개의 출력(모두 데이터)이 필요하다.

 

레지스터 번호 입력은 32개의 레지스터 중 하나를 지정해야 하므로 5비트 크기인 반면(2^5), 데이터 입력과 데이터 출력 버스는모두 32비트 폭을 갖는다.

 

그림 4.7(b)는 ALU를 보여주고 있다. ALU는 32비트 입력 2개를 받아서 32비트 결과가 0인지 아닌지를 나타내는 1비트 신호를 만든다.

4비트 ALU 제어 신호또한 있다.

 

MIPS의 word 적재 명령어와 저장 명령어를 생각해보자.

lw $t1, offset_value($t2)
sw $t1, offset_value($t2)

베이스 레지스터에 명령어에 포함되어 있는 16비트 부호있는 변위 필드를 더하여 메모리 주소를 계산한다.

 

저장 명령어 이면 저장할 값을 레지스터 파일에서 읽어와야 하는데 이 값은 $t1에 있다.

적재 명령어 이면 메모리로부터 읽어 들인 값을 지정된 레지스터($t1)에 써야 한다.

따라서 그림 4.7의 레지스터 파일과 ALU가 둘 다 필요하다.

 

그 외에도 명령어의 16비트 변위 필드 값을 32비트 부호있는 값으로 부호 확장(sign-extension)하기 위한 유닛이 필요하며 또 읽고 쓸 데이터 메모리가 필요하다.

 

데이터 메모리는 저장 명령어일 때만 쓰기를 해야 한다. 따라서 데이터 메모리는 읽기와 쓰기 제어 신호, 주소 입력, 메모리에 쓸 데이터 입력이 필요하다. 다음 그림과 같다

 

beq 명령어는 비교할 레지스터 2개와 16비트 변위의 3 피연산자를 갖는다.

변위는 분기 목적지 주소(branch target address)를 분기 명령어 주소에 대한 상대적인 값으로 표현하는데 사용된다.

beq명령어를 구현하기 위해서는 부호확장한 명령어 변위 필드의 값을 PC에 더해서 분기 목적지 주소를 계산해야 한다.

 

분기 목적지 주서만 계산하면 되는 것이 아니고, 실행할 다음 명령어가 (1)분기 명령어 뒤에 있는 명령어가 될지? (2) 분기 목적지 주소에 있는 명령어가 될지를 판단해야 한다.

조건이 사실일 때(피연산자가 같은 값일때) 분기 목적지 주소가 새로운 PC값이 되며 분기가 일어났다(branch taken)이라고 말한다.

피연산자 값이 같지 않으면 증가된 PC값이 새 PC값이 된다. 이 경우 분기가 일어나지 않았다(branch not taken)이라 말한다.

 

따라서 분기 데이터페스는 분기 목적지 주소를 계산하고, 레지스터 내용을 비교하는 2가지 일을 해야한다.

분기 목적지 주소를 계산하기 위해서 분기 데이터패스에는 부호확장 유닛과 덧셈기가 있다.

비교 연산을 수행하기 위해서는 레지스터 피연산자가 2개 필요하고, 이들을 읽기 위해서 그림 4.7(a)와 같은 레지스터 파일이 필요하다.

또한 ALU를 사용하여 결과가 0인지를 나타내는 출력 신호를 제공하기 때문에, 두 레지스터 피연산자를 제어 신호와 함께 ALU로 보내 뺄셈을 한다.

 

ALU의 Zero 신호가 인가되면 두 값이 같다는 것을 의미한다.

 

점프 명령어는 명령어의 하위 26비트를 2비트 만큼 왼쪽으로 자리이동한 값으로 PC의 하위 28비트를 대체한다.

2장에서 설명한 바와 같이 이 자리이동은 점프 변위 뒤에 00만 덧붙이면 된다.


레지스터 파일 하나와 ALU 하나만을 사용하는 데이터패스를 만들기 위해서는 두 번째 ALU입력에 두종류의 다른 근원지를, 그리고 레지스터에 저장할 데이터 입력에도 2개의 다른 근원지를 입력할 수 있어야 한다. 

따라서 ALU입력에 멀티플레서 하나, 레지스터 파일의 데이터 입력에 멀티플렉서 하나를 설치해야 한다.

이제 명령어 인출을 위한 데이터패스, R타입 명령어와 메모리 명령어를 위한 데이터패스, 분기 명령어를 위한 데이터패스를 하나로 합쳐서 MIPS 구조를 위한 단순화된 데이터패스를 만들 수 있다.

이제 단순한 구현의 데이터패스가 완성되었으니, 제어 유닛을 추가해야 한다.

제어 유닛을 필요한 입력들을 받아들여 각 상태 소자의 쓰기 신호, 각 멀티플렉서의 선택 신호, 그리고 ALU 제어 신호를 만들어 내야 한다.

댓글