[無에서 시작하는 컴퓨터&과학]

[컴퓨터][27] 컴퓨터 완성 : Non-ALU 명령어 및 플래그 (데이터 명령어 / 분기 명령어 / 직접 분기 명령어 / 조건 분기 명령어)

core-basic 2024. 10. 29. 16:11
728x90

※ 해당 게시글은 주제를 탐구하면서 주관적인 생각을 정리 한 글입니다.
※ 해당 게시글은 J. 클라크 스코트, 『그래서 컴퓨터는 어떻게 동작하나요?』, 지유록, 인사이트(2019) 책을 참고하였습니다. 


이전 글을 통해

ALU(Arithmetic Logic Unit), CU(Contol Unit), RAM(Random Access Memory)

의 개념을 재정리하면서 ALU 연산 명령어만 처리할 수 있는

컴퓨터(CPU와 RAM)를 logisim 프로그램을 통해 구현해 보았다.

 

[컴퓨터][26] 산술논리장치, 제어장치, RAM 총정리 및 CPU 구현 (ALU : Arithmetic Logic Unit, CU : Contol Unit, R

※ 해당 게시글은 주제를 탐구하면서 주관적인 생각을 정리 한 글입니다. ※ 해당 게시글은 J. 클라크 스코트, 『그래서 컴퓨터는 어떻게 동작하나요?』, 지유록, 인사이트(2019) 책

core-basic.tistory.com

그러면 이어서 ALU 연산 명령어 외에도, RAM을 참조하는 명령어와

반복적인 명령어 실행 시 RAM을 효율적으로 활용하기 위한 분기 명령어

그리고 플래그에 관련된 명령어 등 

Non-ALU 명령어에 대해서 알아보겠다.

< Non-ALU 명령어 및 플래그 >


< Non-ALU 명령어 >

Non-ALU 명령어는

RAM을 참조하는 명령어와

반복적인 명령어 실행 시 RAM을 효율적으로 활용하기 위한 분기 명령어

그리고 플래그에 관련된 명령어 등

기본적인 8bit 명령어 레지스터에 따라

OPcode는 3bit 이므로 총  8가지의 Non-ALU 명령어가 제공된다.

Non-ALU 명령어는 명령어 레지스터에서

최상위 비트가 0일 때 처리되는 명령어로

이진수 코드는  [0|000 XXXX] ~ [0|111 XXX]까지 해당된다. 


< ALU 명령어와 Non-ALU 명령어 결합 >

이전 글을 통해 구현한 컴퓨터에 대해서

각 장치는 이미 기존에 존재하는 ALU 명령어 처리 과정에 맞추어

제어장치와 연결되어 있다.

이에 따라 Non-ALU 명령어를 추가하기 위해서는

전체적인 배선 변경이 필요하다.

이때 ALU와 Non-ALU 배선은 다음과 같이 결합하여 연결한다.

※ && : AND(그리고) , || : OR(또는) 

( (STEP 신호 && ALU 신호 [1] && OPcode [000])
||

(STEP 신호 && N-n-ALU 신호 [0] && OPcode [000]) )
&&
clk 신호

 

이를 통해 RAM을 참조하는 명령어와 반복적인 명령어 실행 시

RAM을 효율적으로 활용하기 위한 분기 명령어들이 구현될 수 있다.

※ Non-ALU 명령어가 추가된 컴퓨터는 제작하지 않았으며, 기회가 된다면 설계하여 추후 수정 할 계획이다.
※ 2024-12-01 Non-ALU 명령어가 추가된 컴퓨터 제작을 완료하였으며, 해당 내용은 하단 부분에서 확인할 수 있다.


< Non-ALU 명령어 : LOAD/STORE 명령어 >

LOAD(로드)/STROE(저장) 명령어를 통해

데이터 레지스터에 있는 값을 메모리에 저장(STORE) 하거나

메모리에 있는 값을 데이터 레지스터 로드(LOAD) 할 수 있다.


[1] LOAD 명령어 [0|000 RA|RB]

RB가 저장하고는 값을 RAM 주소로 하여
해당 주소에 있는 명령어/데이터를 RA로 출력한다.
( LOAD RA, RB )

< 동작 과정 >

[STEP 4] 
: RB 출력하여 MAR에 입력

RB 출력 제어기
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [000] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

MAR 입력 제어기
: (STEP 4 신호 && NOT(Non-ALU [0]) && OPcode [000]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s

 

[STEP 5]
: 이전 스탭 MAR에 의해 RAM 활성화된 레지스터 값 출력하여 RA로 입력

RAM 출력 제어기
: (SETP 5 신호 && NOT(Non-ALU [0]) && OPcode [000] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

RA 입력 제어기
: (STEP 5 신호 && NOT(Non-ALU [0]) && OPcode [000]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s


[2] STORE 명령어 [0|001 RA|RB]

RB가 저장하고 있는 값을 RAM 주소로 하여,
RA에 있는 값을 해당 주소에 저장한다.
( STORE RA, RB )

<동작 과정>

[STEP 4]
: RB 출력하여 MAR에 입력

RB 출력 제어기
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [001] || 또 다른 ALU/Non-ALU 연산 ) && clk_e 

MAR 입력 제어기
: (STEP 4 신호 && NOT(Non-ALU [0]) && OPcode [001]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s

 

[STEP 5]
: MAR에 의해 RAM 활성화된 레지스터에 RA값 입력

RA 출력 제어기
: (SETP 5 신호 && NOT(Non-ALU [0]) && OPcode [001] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

RAM 입력 제어기
: (STEP 5 신호 && NOT(Non-ALU [0]) && OPcode [001]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s


< Non-ALU 명령어 : 데이터 명령어 >

기존에 LOAD 명령어는 레지스터에 저장되어 있는 주소를 참조하여

해당 주소에 있는 값을 레지스터로 가져오는 방식이었다면,

데이터 명령어는 사용자가 지정한 주소를 참조하여

해당 주소에 있는 값을 레지스터로 가져오는 방식이다.


[3] 데이터 명령어 [0|010 RA|XX], [8bit:데이터 가져올 RAM 주소]

 주어진 RAM주소에서 바이트 데이터를 하나 가져와서 레지스터에 LOAD
( DATA RA, xxxx xxxx )

<동작 과정>

[STEP 4]
: PC 출력하여 MAR에 입력
(이때 다음 명령어는 데이터(주소) 이므로
한 번 더 IAR +1 하기 위해 SET1, ACC 입력 활성화)

PC 출력 제어기
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [010] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

SET 1
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [010] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

MAR 입력 제어기
: (STEP 4 신호 && NOT(Non-ALU [0]) && OPcode [010]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s

ACC 입력 제어기
: (STEP 4 신호 && NOT(Non-ALU [0]) && OPcode [010]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s

 

[STEP 5]
: RAM 활성화된 레지스터 값 출력하여 RA로 입력

RAM 출력 제어기
: (SETP 5 신호 && NOT(Non-ALU [0]) && OPcode [010] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

RA 입력 제어기
: (STEP 5 신호 && NOT(Non-ALU [0]) && OPcode [010]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s

 

[STEP 6]
: STEP1~3 이후 PC(IAR) 값 한 번 더 +1

ACC 출력 제어기
: (SETP 6 신호 && NOT(Non-ALU [0]) && OPcode [010] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

PC 입력 제어기
: (STEP 6 신호 && NOT(Non-ALU [0]) && OPcode [010]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s


< Non-ALU 명령어 : 분기 명령어 >

분기 명령어를 통해

커다란 용량의 프로그램(명령어들의 집합) 또는

반복적인 작업을 할 때 RAM 용량 부족 현상 및 비효율적인 처리 방식을

해소할 수 있다.


[4] 분기 명령어 [0|011 RA|XX]

: RA에 들어있는 주소로 분기한다.
( JMPR RA )

<동작 과정>

[STEP 4]
: RA 레지스터 출력하여 PC(IAR) 레지스터가 입력받아 분기한다.

RA 출력 제어기
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [011] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

PC 입력 제어기
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [011] || 또 다른 ALU/Non-ALU 연산  && clk_s


< Non-ALU 명령어 : 직접 분기 명령어 >

직접 분기 명령어를 통해

사용자가 제시하는 주소로 분기할 수 있다.


[5] 직접 분기 명령어 [0|100 00|00], [8bit:분기할 RAM 주소]

: 명령어 뒤에 따르는 두 번째 바이트에 존재하는 주소로 분기한다.
( JMP Addr )

 

<동작 과정>

[STEP 4]
: STEP 1~3 이후 한 번 더 증가된 PC 출력하여 MAR에 입력한다.

PC 출력 제어기
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [100] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

MAR 입력 제어기
: (STEP 4 신호 && NOT(Non-ALU [0])  && OPcode [100 || 또 다른 ALU/Non-ALU 연산 ) && clk_s

 

[STEP 5]
: RAM 활성화된 레지스터 값 출력하여 PC(IAR)에 입력하여 분기한다.

RAM 출력 제어기
: (SETP 5 신호 && NOT(Non-ALU [0]) && OPcode [100] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

PC 입력 제어기
: (STEP 5 신호 && NOT(Non-ALU [0]) && OPcode [100]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s


< Non-ALU 명령어 : 조건 분기 명령어 >

조건 분기 명령어에서 조건이란

자리 올림 또는 A > B,  A = B, Zero 신호에 해당된다.

이에 따라 각 조건/상태가 성립할 경우에만

분기할 수 있도록 처리할 수 있다.

이때 8bit 명령어 레지스터의 하위 4bit가 각 조건의 신호와 연결된다.

<자리올림 1비트>

자리올림은 ALU에서 출력되고, 입력된다.

자리올림은 가산기(덧셈회로) 또는 시프트 장치에서만 출력된다.

별도의 조치가 없으면 다음 ALU 연산에 자리올림 입력으로 사용된다.

<A = B 1비트>

ALU에는 두 입력이 같은 경우 신호를 주는 상태 비트가 있다.

<A > B 1비트>

ALU에는 첫 번째 입력이 두 번째 입력보다 더 큰 경우
신호를 주는 상태 비트가 있다.

< Zero 1비트>

ALU에는 연산 출력 결과가 0이면 신호를 주는 상태 비트가 있다.

 

이에 따라 플래그는 분기의 조건/상태가 된다.

( JC/JA/JE/JZ/ JCA/JCE/JCZ JAE/JAZ  Addr)

 

조건/플래그는 앞서 언급하였듯이

ALU 연산이 진행될 때 신호가 생성된다.

ALU 연산은 STEP 5에서 진행되며,

그때 ACC와 함께 4개의 플래그에 값이 채워진다.

 

그런데 STEP 5가 끝나면 해당 플래그 신호들은 없어진다.

이에 따라 플래그를 다음 연산에 적용시키거나, 분기 조건을 사용하기 위해

8bit 플래그 레지스터(Flag Register)가 도입되었다.

하지만 앞서 언급한 조건은 4개에 이므로 4bit만 사용한다.

그리고 플래그 레지스터 입력 제어기는 다음과 같이 연결한다.

플래그 레지스터 입력 제어기 : STEP 5 && ALU [1] && clk_s  

 

플래그 레지스터는 다음 ALU 명령어가 실행되어

새로운 값을 받기 전까지는 계속 유지할 수 있다.

이에 따라 ALU 명령어 실행 후 해당 플래그를 이용해

여러 번 조건 분기 명령어를 사용해도 ALU 연산만 진행하지 않으면

플래그 값은 변화하지 않는다.

 


[6] 조건 분기 명령어 [0|101 C|A|E|Z], [8bit:분기할 RAM 주소]

: 조건/상태에 따라서 다른 주소로 분기하거나, 분기하지 않는다.

C : 자리 올림 플래그
A : A > B 플래그
E : A= B 플래그
Z : Zero 플래그 

<동작 과정>

[STEP 4]
: PC 출력하여 MAR에 입력
(이때 다음 명령어는 데이터(주소) 이므로 한 번 더
IAR+1 하기 위해 SET1, ACC 입력 활성화)

PC 출력 제어기
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [101] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

SET 1
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [101] || 또 다른 ALU/Non-ALU 연산 ) && clk_e

MAR 입력 제어기
: (STEP 5 신호 && NOT(Non-ALU [0]) && OPcode [101]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s

ACC 입력 제어기
: (STEP 5 신호 && NOT(Non-ALU [0]) && OPcode [101]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s

[STEP 5]
: 조건에 해당되는 플래그가 활성화되지 않아 분기하지 않을 때,
다시 ACC 출력하여 PC로 입력한 후 다음 명령어 진행

ACC 출력 제어기
: STEP 5 신호 && ALU [0] && OPcode [101] && clk_e

PC 입력 제어기
: STEP 5 신호 && ALU [0] && OPcode [101] && clk_s

※ STEP 1에서 PC+1 할 때 자리 올림 비트가 영향을 주는 것을 방지하기 위해 "자리 올림 입력 제어기" : ‘STEP 5 신호 AND 플래그 레지스터 자리올림 비트’한다

STEP 5에서 ALU 연산을 진행할 때 자리 올림 플래그를 추가하여 연산을 진행하는데 만약 연산 결과가 자리올림 플래그에 다시 변화를 주면, 곧바로 변화된 플래그로 재연산되는 오류가 발생한다. 따라서, 플래그 레지스터의 출력 부분에 ‘자리올림 임시 저장소(carry Temp)’를 추가하며 임시 저장소는 다시 자리올림 입력과 연결된다. 이에 따라 STEP 4에서 'ALU 입력제어기'와 같이 자리올림 임시 저장소도 입력제어기를 활성화를 하여, 플래그 레지스터의 자리올림값을 받는다. 이후 STEP 5에서 연산할 때 자리올림 저장소만 출력 및 이용하도록 하여, 실질적인 자리올림 플래그가 변화되어도 상관없도록 한다.

※ 자리 올림 임시 저장소(Carry Temp) 출력 제어 신호 : STEP 5 && ALU [1] && clk_e

[STEP 6]
: 조건에 해당되는 플래그가 활성화되어 주어진 주소로 분기할 때,
STEP4에서 MAR에 의해 활성화된 RAM의 레지스터 값을 출력하여
PC에 입력해 분기한다.

RAM 출력 제어기
: ( "C || A || E || Z” && STEP 6 신호 && NOT(Non-ALU [0]) && OPcode [101]  || 또 다른 ALU/Non-ALU 연산 ) && clk_e

PC 입력 제어기
: ( "C || A || E || Z” && STEP 6 신호 && NOT(Non-ALU [0]) && OPcode [101]  || 또 다른 ALU/Non-ALU 연산 ) && clk_s

( C : 자리올림(Carry), A : A가 더 큼(Above), E : A와 B가 같음(Equal), Z : 연산 결과가 0이다. Zero)

 

복수의 플래그 비트를 선택하여
그중 하나만 참일 때만 분기하는 명령어도 가능하다.
(복수로 추가하지 않는다면 해당 조건을 아예 확인도 안 한다.)

JCA/JCE/JCZ/JAE/JAZ 등등


< 플래그 레지스터 초기화 명령어 >

플래그 레지스터는 조건 분기 명령어를 처리할 때 필요하다.

그런데 다음과 같은 문제가 발생할 수 있다.

자리올림(C)이 발생하였을 때를 조건으로 하는

조건 분기 명령어에 대해 해당 조건이 성립하여 실행된 경우

이후 ALU 연산 명령어를 진행한다면 자리올림(C) 값은 여전히 ‘1’이므로

자리올림 입력이 있는 가산기 회로나 시프트 회로의 경우

의도하지 않은 자리 올림값이 추가되어

연산되는 문제가 발생할 수 있다.

이에 따라 ‘플래그 레지스터 초기화 명령어’를 추가하여

플래그 레지스터의 모든 비트를 0으로 초기화하여 해결할 수 있다.


[7] 플래그 초기화 [0|110 0000]

: 플래그 레지스터의 값을 모두 0으로 초기화한다.

 

<동작 과정>

※ XOR 연산 결과에 별도로 NOT게이트를 연결하면 연산 결과가 0인지 확인할 수 있다.
※ 이전 글을 통해 구현했던 ALU에서 A와 B의 위치를 변경한다.
※ 이전 글을 통해 구현한 ALU에서 ADD 연산의 디코더 연결 순서와 위치를 다음과 같이 변경한다.
000 ADD [000으로 반드시 해야 플래그 레지스터 초기화를 할 수 있다.]

[STEP 4]
: SET 1을 활성화하여, B = 0000 0001 , A = 0000 0000을
덧셈하여 플래그 초기화
(ADD의 OPcode = 000 이므로, 기본적으로 덧셈연산 진행)

SET1 활성화
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [110] || 또 다른 ALU/Non-ALU 연산) && clk_e

Flags 입력 제어기
: (SETP 4 신호 && NOT(Non-ALU [0]) && OPcode [110] || ALU 연산 일 때) && clk_s

플래그 레지스터 변화
자리 올림 플래그 (C : Carry) → [A]0000 0000 + [B]0000 0001 = 0000 0001 → C = 0

A > B 플래그 (A : Above) → [A]0000 0000 < [B]0000 0001 → A = 0

A = B 플래그 (E : Equal) → [A]0000 0000 ≠ [B]0000 0001 → E = 0)

Zero 플래그 (Z : Zero) → [A]0000 0000 + [B]0000 0001 = 0000 0001 → Z = 0)

 

2024-11-28
※ 추가적으로 Non-ALU 명령어를 설계하면서
입력제어기와 SET 1과 결합된 입력(B)을 제외한
또 다른 ALU 입력(A)에 SET 0 장치가 추가로 필요하다는 것을 알게 되었다.
왜냐하면,
특히 플래그 레지스터 초기화 명령어에 대해서
우선 모든 출력을 차단하여 버스를 0000 0000으로 만든 후
ALU의 첫 번째 입력 바이트가 0이 되도록 만들어야 하는데
실질적으로 모든 출력을 차단하면 버스에는 0000 0000이 아닌 XXXX XXXX값
즉, 아무 값도 아닌 상태 : 고 임피던스 상태(High Impedance)를 유지하게 된다.

따라서 레지스터 초기화 명령어를 실행하다 보면,
모든 출력 차단 : 0000 0000(A)
 SET 1 활성화 : 0000 0001(B)
이후
0000 0000(A) + 0000 0001(B)을 진행해야 하는데
0000 0000(A)이 실질적으로는 XXXX XXXX(A)가 되어 오류가 발생한다.

그래서 이를 해결하기 위해 SET 1과 같은 SET 0 장치를 추가하여,
레지스터 초기화 명령어(0110 XXXX)를 실행할 때만
STEP 4에서 해당 장치를 활성화하여, 정상적인 연산을 진행할 수 있도록 한다.

SET 0 활성화 
: SETP 4 신호 && (NOT(Non-ALU [0]) && OPcode [110])  && clk_e  


< NOP : No Operation  명령어 >

NOP 명령어는 명령어를 가져오는 STEP 1~3 처리되지 않도록 하여

프로그램을 일시 중지시키는 명령어이다.

※ 기존 STEP 1~3 처리 과정은 다음과 같다.

더보기

[STEP 1]

PC(Progrem Counter) 출력 제어기 : STEP 1 신호 and 출력 제어 클럭 신호(clk_e)

[SET 1] : STEP 1 신호 and 출력 제어 클럭 신호(clk_e)

MAR 입력제어기 : STEP 1 신호 and 입력 제어 클럭 신호(clk_s)

[ACC 입력제어기] : STEP 1 신호 and 입력 제어 클럭 신호(clk_s)

 

[STEP 2]

RAM 출력 제어기
: STEP 2 신호 and 출력 제어 클럭 신호(clk_e)

IR 입력 제어기
: STEP 2 신호 and 입력 제어 클럭 신호(clk_s)

 

[STEP 3]

ACC 출력 제어기
: STEP 3 신호 and 출력 제어 클럭 신호(clk_e)

PC 입력 제어기
: STEP 3 신호 and 입력 제어 클럭 신호(clk_s)


[8] NOP : No Operation  명령어 [0|111 0000]

Non-ALU [0] 그리고 OPcode [111] 일 경우에 

명령어를 가져오는 절차가 활성화되지 않도록 하여

프로그램을 일시 중지한다.

 

<동작 과정>

[STEP 1]

PC(Progrem Counter) 출력 제어기
: STEP 1 신호 && 출력 제어 클럭 신호(clk_e)
&& NOT ( NOT(Non-ALU [0]) && OPcode [111] )

[SET 1]
: STEP 1 신호 && 출력 제어 클럭 신호(clk_e)
&& NOT ( NOT(Non-ALU [0]) && OPcode [111] )

MAR 입력제어기
: STEP 1 신호 && 입력 제어 클럭 신호(clk_s)
&& NOT ( NOT(Non-ALU [0]) && OPcode [111] )

[ACC 입력제어기]
: STEP 1 신호 && 입력 제어 클럭 신호(clk_s)
&& NOT ( NOT(Non-ALU [0]) && OPcode [111] )

 

[STEP 2]

RAM 출력 제어기
: STEP 2 신호 && 출력 제어 클럭 신호(clk_e)
&& NOT ( NOT(Non-ALU [0]) && OPcode [111] )

IR 입력 제어기
: STEP 2 신호 && 입력 제어 클럭 신호(clk_s)
&& NOT ( NOT(Non-ALU [0]) && OPcode [111] )

 

[STEP 3]

ACC 출력 제어기
: STEP 3 신호 && 출력 제어 클럭 신호(clk_e)
&& NOT ( NOT(Non-ALU [0]) && OPcode [111] )

PC 입력 제어기
: STEP 3 신호 && 입력 제어 클럭 신호(clk_s)
&& NOT ( NOT(Non-ALU [0]) && OPcode [111] )

 

다음 주제는 어셈블러와 어셈블리어에 대해 탐구해 보겠다.


※ 2024-12-01 (수정 및 추가) 

< Non-ALU 명령어와 플래그를 위한 컴퓨터 재설계 >

앞서 언급하였듯이 Logisim 프로그램을 통해

산술 논리 장치(ALU), 제어장치(CU), 메모리(Memory) 등

컴퓨터 구현에 필요한 기본적인 장치들을 제작했지만,

Non-ALU 명령어와 플래그를 구현하지 못했던 가장 큰 이유는 배선 문제였다.

기존에 구현한 컴퓨터 설계에서 Non-ALU 명령어를 구현할 경우,

수많은 배선들이 서로 교차하며

장치 간 연결이 매우 복잡하고, 난잡해지는 문제가 발생했었다.

 

따라서 필자는 이를 해결하기 위해

결과적으로 다음과 효율적인 같은 배선 시스템을 설계했다.

해당 배선 시스템의 핵심 원리는

여러 장치들을 연결하는 주요 배선들을 위와 같이 사각형으로 배치하여

상하좌우 모든 방향에서 장치들을 연결할 수 있도록 하여

보다 깔끔하고, 효율적으로 여러 장치들과 연결할 수 있도록 배치하였다.

특히 클럭과 스테퍼는

모든 장치의 입출력제어기와 연결되기 때문에 정중앙에 배치하였다.

가장 안쪽 사각형을 STEP 6으로 배치하면서

STEP6, STEP5, STEP4, STEP3, STEP1, STEP1, clk_s, clk_e

순서대로 사각형을 형성해 가도록 하였다.

그리고 이어서 제어장치와 같이 각 장치들을 연결하는 주요 배선인

명령어 레지스터 역시 ALU일 때와 Non-ALU때를 분리하여

각각 8BIT씩 즉, 8개의 사각형 배선을 형성하여 여러 장치들과 연결하였다.

그리고 최외각에는 후에 설명할 플래그를 안쪽부터

Z(Zero), E(Equal), A(Above), C(Carry) 순서대로

1bit씩 총 4개의 사각형 배선을 형성하도록 하여,

특정 플래그값이 필요한 경우 활용할 수 있도록 배치하였다.

이처럼 여러 장치가 특정 스텝 신호, 입출력신호, ALU/Non-ALU 신호

그리고 필요에 따라 플래그까지 조합하여

입출력제어기의 활성화 및 비활성화를 효율적으로 관리할 수 있도록 하고,

각 신호를 쉽게 끌어다 쓸 수 있는

배치도를 고심하고 반복적으로 개선한 끝에 끝에 도출해 냈다.


< 플래그 >

플래그란 실질적으로 조건 분기 명령어가 처리될 때,

참고하는 조건에 해당된다.

그리고 플래그는 대부분 ALU 연산에 의해 활성화되고, 비활성화된다.

앞서 구현한 컴퓨터에서 제공되는 조건/플래그는 총 4개로 다음과 같다.

C(Carry) : 자리 올림이 발생하였는지 확인한다.
A(Above) : A > B 인지 확인한다.
E(Equal) : A = B 인지 확인한다.
Z(Zero) : 연산 결과가 0인지 확인한다.

그러면 이어서 각 플래그를 어떻게 생성 및 출력하는지 파악해 보겠다.

 

C(Carry) : 자리올림

수정된 컴퓨터를 기준으로 보았을 때자리올림은

총 3가지 ALU 연산에 의해 출력된다.

번째는 전가산기(덧셈회로)
두 번째는 왼쪽 시프트 연산(SHL)
세 번째는 오른쪽 시프트 연산(SHR)

이에 따라 각 연산 과정 중 하나라도 자리올림이 발생하게 되면

자리 올림이 출력 및 활성화된다.

그래서 다음 이미지와 같이 각 자리올림 출력값을

각 연산의 OPcode와 AND  연산한 후

OR 게이트로 연결하여 최종 자리올림 출력을 나타낸다.

※ 해당 이미지는 Logisim 프로그램을 통해 제작하였습니다.

이때 각 연산의 OPcode와 AND 연산하는 이유는 해당 연산이

실제로 수행되고 있는지 확인함으로써 정확한 플래그 결과를

출력하기 위해서이다.

 

A(Above) : A > B
E(Equal) : A = B

(Above) : A > B와 E(Equal) : A = B 플래그는

이전 글들을 통해 구현한 비교기에 의해 출력된다.

나눗셈을 위해서는 비교기가 필요했으며,
이는 결국 조건분기명령어를 사용해야 나눗셈을 처리할 수 있다는 것과 같다.

비교기 회로는 다음 이미지와 같으며, 해당 회로를 통해 
A(Above), E(Equal) 플래그가 출력된다.

※ 해당 이미지는 Logisim 프로그램을 통해 제작하였습니다.

그리고 비교기 내부에는 XOR 게이트가 사용되기 때문에,

A(Above), E(Equal) 플래그뿐만 아니라,

XOR 연산 결과도 함께 출력할 수 있도록 구현할 수 있다.

※ 해당 이미지는 Logisim 프로그램을 통해 제작하였습니다.

 

 

Z(Zero) : 연산 결과 0

(Zero) 플래그는 연산 결과가 0인 경우 활성화 되는 플래그이다.

이에 따라 연산 결과가 0이면 1을 출력하고,

연산 결과가 0이 아니면 0을 출력한다.

이를 구현하는 방법은 OR 게이트와 NOT게이트를 이용하면 된다.

연산 결과의 각 비트를 모두 OR연산해 주면,

각 비트가 모두 0일 때만 0을 출력하게 된다.

이후 NOT 게이트를 연결시켜 주면

각 비트가 모두 0일 때만 1을 출력하도록 할 수 있다.

이를 회로로 표현하면 다음과 같다.

※ 해당 이미지는 Logisim 프로그램을 통해 제작하였습니다.

 

그래서 위와 같은 회로를 제로 연산기(Zero)라고 표현하여

ALU 출력 부분에 연결시키면 다음과 같이

ALU 연산 결과에 따라 Zero 플래그 및 다른 플래그들이

활성화 및 비활성화되는 것을 확인할 수 있다.

※ 해당 이미지는 Logisim 프로그램을 통해 제작하였습니다.


< 플래그 레지스터 >

앞서 언급하였듯이 플래그들은 대부분

ALU 연산이 진행되고 출력제어기에 의해 출력될 때 설정된다.

실질적으로 모든 ALU 연산은 STEP 4에서 A, B 입력을 받아

모든 연산 장치의 입력값으로 들어가 처리된다.

 

하지만 각 연산 장치와 연결된 출력제어기가 출력을 제어하기 때문에

실질적으로 외부 버스로 까지는 출력되지 않는다.

 

이후 STEP5에서 OPcode 입력을 받아

대응되는 연산의 출력제어기를 활성화시켜

해당 연산 결과만 출력할 수 있도록 한다.

이에 따라 플래그 역시 ‘ALU 연산’에서 ‘STEP 5’가 진행될 때 설정되어야

다음 명령어로 조건분기문이 오는 경우

이전 연산을 기준으로 플래그를 참조하여 조건분기문을 처리할 수 있다.

하지만,

플래그는 ‘ALU 연산’+‘STEP5’가 아님에도 설정되는 경우가 발생한다.

대표적인 예시가

메모리로부터 명령어를 가져오는 단계 : STEP1~STEP3을

처리하는 과정에서 프로그램 카운터(PC)를 증가시킬 때

ALU의 전가산기(덧셈 회로)가 사용된다.

이에 따라 플래그 값이 변화하게 된다.

그래서 정확한 조건을 참조하고 분기하기 위해서는

플래그값을 유지 및 저장할 “플래그 레지스터”가 요구된다.

 

플래그 레지스터의 회로와 자체제작 기호는 다음과 같다.

플래그 레지스터는 일반적인 8BIT 레지스터와 같으며,

플래그 개수에 따라 하위 비트부터 사용한다.

ex) C, A, E, Z 총 4개의 플래그가 사용하므로, 하위 4bit만 사용한다.

 

그래서 다음과 같이 ALU에 플래그 레지스터를 연결할 수 있음을 알 수 있다.

※ 해당 이미지는 Logisim 프로그램을 통해 제작하였습니다.

그리고 현재 글을 통해 제작한 컴퓨터에서 플래그 레지스터는

ALU 연산이 진행될 때 그리고 플래그 레지스터 초기화 연산을 진행할 때만,

플래그 레지스터의 입력제어(Set)를 활성화한다.

플래그 레지스터 입력 제어기 
: ((STEP 5 && ALU [1]) || (STEP 4 && Non-ALU [0][110]) && clk_s  

(만약 다른 명령어에서도 플래그를 조작할 필요가 있다면 전선을 연결하여 추가시킬 수 있다.)

※ 해당 이미지는 Logisim 프로그램을 통해 제작하였습니다.


< 자리 올림 임시 저장소 > 

자리올림 입력에 대해서, 자리올림 입력은

이전 연산 결과로 인해 설정된 자리 올림 값을

입력받아 다음 연산에 사용된다.

※ 해당 이미지는 Logisim 프로그램을 통해 제작하였습니다.

하지만 위의 이미지와 같이 자리올림 출력을

그대로 다시 자리올림 입력으로 연결하면,

그 즉시 반복적으로 연산이 진행되는 문제가 발생한다.

구체적으로,

STEP 5가 진행되는 동안 자리올림 출력이 입력으로 다시 들어가

연산이 수행되고, 이로 인해 변화된 자리올림출력이

다시 입력으로 들어가는 과정을 반복하게 된다.

 

이러한 문제를 해결하기 위해

자리올림 출력과 입력 사이에

“자리 올림 값을 임시로 저장할 메모리 회로:자리 올림 임시 저장소”가

요구된다.

다음은 자리 올림 임시 저장소의 회로와 설계에 맞춘 자체 제작 기호이다.

해당 회로는 1bit 레지스터를 사용하며,

위와 같은 문제를 해결하기 위해 특정 신호에서만

자리올림 입력값을 출력하여, ALU가 입력받을 수 있도록 한다.

그래서 다음 회로와 같이 ALU 연산이 진행될 때

자리올림 임시 저장소는 연산 결과로 변화된 자리올림 값을 입력받고,

출력 제어 출력 신호에 맞추어 출력하여 안정적으로 동작하도록 한다.

※ 해당 이미지는 Logisim 프로그램을 통해 제작하였습니다.


< 컴퓨터 최종 완성 >

따라서 이번 글까지 포함하여 다음과 같은 8BIT 컴퓨터를 제작할 수 있었다.


※ 2024-12-01 (수정 및 추가 마침) 


※ 해당 게시글은 주제를 탐구하면서 주관적인 생각을 정리 한 글입니다.
※ 해당 게시글은 J. 클라크 스코트, 『그래서 컴퓨터는 어떻게 동작하나요?』, 지유록, 인사이트(2019) 책을 참고하였습니다. 

728x90