※ 해당 게시글은 주제를 탐구하면서 주관적인 생각을 정리 한 글입니다.
※ 해당 게시글은 J. 클라크 스코트, 『그래서 컴퓨터는 어떻게 동작하나요?』, 지유록, 인사이트(2019) 책을 참고하였습니다.
이전 글을 통해서 다음과 같은 키보드의 원리를 파악할 수 있었다.
키보드의 각 키(문자)는 아스키코드(ASCII CODE)와 같은
이진수 코드와 스위치 회로를 대응시켜
특정 키를 누르면, 해당 키에 대응되는 아스키코드에 따른
전기적 신호(0/1)를 생성되고,
이를 메모리 회로에 저장하는 원리를 따른다는 것을 알 수 있었다.
추가적으로 컴퓨터(CPU)와 키보드 사이에
키보드 인터페이스(어댑터)를 설계하여,
특정 키를 눌렀을 때 생성되는 이진수 코드/신호를 임시 저장하고,
컴퓨터(CPU)가 주기적으로 I/O 명령어를 통해
키보드와 채널 연결을 할 때만
해당 이진수 코드(신호)를 가져올 수 있도록
할 수 있다는 것도 알 수 있었다.
하지만, 이와 같은 동작은 컴퓨터와 키보드 내부에서
회로적으로 동작하는 과정이며,
이외의 다른 모든 명령어들 역시
전기/전자적 관점에서 동작함으로써
시각적으로 직접 확인하기 어렵다.
그래서 사용자가 명령어를 잘 입력하였는지,
또, 컴퓨터가 입력된 명령어를 잘 처리하고 있는지 등을
시각적으로 확인하기 위해
모니터와 같은 출력장치가 도입되었다.
덕분에 프로그래머들은 이전 보다 더 쉽고, 편리하게
프로그래밍을 진행할 수 있게 되었다.
그렇다면 모니터는 어떠한 원리를 통해 동작하는지
이번 글을 통해서 탐구해 보겠다.
< 입출력(I/O) 장치 : 모니터의 원리 >
< 모니터 : 디스플레이 >
" 모니터란 무엇인가? "
" 모니터는 어떠한 원리로 동작하는가? "
모니터는 TV, 디스플레이와 같은 원리를 기반으로 하고 있다.
즉, 기본적으로 모니터는 이미지를
빛을 통해 출력하는 기능을 가진 장치이다.
또한, 여러 개의 연속된 이미지를 빠르게
출력하면 움직이는 화면, 동영상을 표현할 수 있다.
이미지를 출력하는 과정에 대해 좀 더 세부적으로 살펴보면
다음과 같다.
우선 디스플레이를 표현하고자 할 때
1280x720, 1920x1080 등과 같은 숫자를 붙여서
해상도를 표현한다.
이에 대해 만약 '256x256 디스플레이'가 주어진다면
해당 디스플레이는 다음과 같은 구조로
이루어져 있다는 것을 의미한다.
가로줄 : 빛을 낼 수 있는 소자 256개 배열
세로줄 : 빛을 낼 수 있는 소자 256개 배열
이에 따라 결과적으로,
가로와 세로 각각 256개의 소자가 격자로 배치된
정사각형 형태의 디스플레이라고 볼 수 있다.
이때 하나의 소자는 픽셀(px:pixel)이라고 부른다.
즉, 픽셀은 이미지의 한 점을 표현할 수 있다.
더 나아가 하나의 점, 하나의 픽셀로부터
다양한 색상을 출력하기 위해서
하나의 픽셀은 대부분 R, G, B 3개의 소자로 구성되어 있다.
R(Red) : 빨간색 빛을 내는 소자
G(Green) : 초록색 빛을 내는 소자
B(Blue) : 파란색 빛을 내는 소자
※ Red, Green, Blue는 빛의 삼원색으로 각 빛의 밝기를 조절해 혼합하면 모든 색상의 빛을 표현할 수 있다.
이에 따라 하나의 픽셀은 565 RGB, 888 RGB와
같은 포맷이 기본적으로 사용되는데
각각의 의미는 다음과 같다.
< 565 RGB : 16BIT >
5R : 빨간색 밝기를 5비트로 조절(0~31 단계)
6G : 초록색 밝기를 6비트로 조절(0~63 단계)
5B : 파란색 밝기를 5비트로 조절(0~31 단계)
※ 초록색에 더 많은 비트를 할당하는 이유는 사람의 눈은 비교적 초록색에 민감하기 때문이다.
<888 RGB : 24BIT >
8R : 빨간색 밝기를 8비트로 조절(0~255 단계)
8G : 초록색 밝기를 8비트로 조절(0~255 단계)
8B : 파란색 밝기를 8비트로 조절(0~255 단계)
< Xterm 256 : 8BIT (+888 RGB) >
Xterm 256 포맷은 256가지의 색을
8BIT로 표현할 수 있지만,
실제 출력은 888 RGB를 따른다.
8BIT 값(0~2255)은 미리 정의된 256개의
888 RGB 색상이 출력된다.
예를 들어 '196 (1100 0100)'이라는 값을 입력하면,
이는 '888 RGB : RGB (255, 0, 0)' 즉, 빨간색에 해당되며,
해당 색상이 출력되도록 회로가 동작한다.
결국, 디스플레이(모니터)는 빨간색(R), 초록색(G), 파란색(B)
빛을 내는 소자들을 하나의 점, 즉 픽셀을 구성단위로 하여
여러 개의 픽셀을 격자 형태로 배열하여,
입력된 신호에 따라 각 픽셀의 RGB 밝기를 조절하여
원하는 이미지를 출력할 수 있도록 해주는 장치이다.
"그렇다면 이미지는 무엇인가?"
결국 이미지는 숫자/픽셀로 표현된 시각적 데이터의 집합이다.
그래서 모니터에 출력할 이미지, 즉 시각 정보를
픽셀 단위로 쪼개어 각 픽셀을 색깔을 숫자(비트)로 표현한다.
이후, 컴퓨터에 입력된 해당 비트 정보는
모니터로 전달하여 이미지를 출력한다.
추가적으로 모니터를 표현할 때 해상도와 더불어
30Hz, 60Hz, 144Hz, 240Hz 등과 같은 수치를
추가하여 표현한다.
해당 수치는 "화면 주사율(Refresh Rate)"를 뜻하며
모니터가 1초에 화면을 몇 번 갱신할 수 있는지
나타내는 값이다.
30Hz: 1초에 화면을 30번 갱신
60Hz: 1초에 화면을 60번 갱신
144Hz: 1초에 화면을 144번 갱신
240Hz: 1초에 화면을 240번 갱신
만약 매우 간단한 16x16 모니터가 주어졌다고 가정해 보면,
(컬러 포맷 : XTerm 256 (8BIT))
해당 모니터는 총 256개의 픽셀로 구성되어 있으며,
하나의 픽셀은 8BIT로 표현이 가능하다.
그리고 각 픽셀의 좌표는 다음과 같이
4BIT+4BIT=8BIT로 표현할 수 있다.
[ Y , X ]
[0000, 0000] | [0000, 0001] | ... | [0000, 1111] |
[0001, 0000] | [0001, 0001] | ... | [0001, 1111] |
... | ... | ... | ... |
[1111, 0000] | [1111, 0001] | ... | [1111, 1111] |
이에 따라 모니터는
좌측 상단 픽셀 [0000, 0000]부터 시작해서
해당 행의 우측 끝에 [0000, 1111] 도달하면
다음 행으로 넘어가고, 이와 같은 행위를 반복하여
우측 하단 픽셀 [1111, 1111]까지 도달하면
다시 [0000, 0000]으로 되돌아가는 순회 작업을 반복한다.
이를 통해 모든 픽셀은 순차적으로
컴퓨터(CPU)로부터 제공되는 새로운 컬러값으로 갱신할 수 있다.
위와 같은 과정을 "스캔 (SCAN)"이라고 부르며
이에 따라
30Hz 16x16 모니터는
1초 동안 [0000, 0000] ~ [1111, 1111]까지
30번을 순회하며 픽셀 값을 경신하는
스캔(SCAN) 과정을 진행한다고 볼 수 있다.
< 모니터 스캔 (SCAN) >
"컴퓨터(CPU)는 모니터에게 어떠한 신호를 전달해야 할까?"
이에 대해서 다음과 같은 답을 찾을 수 있었다.
1. 모니터 픽셀의 좌표 [4BIT]
2. 모니터 픽셀의 컬러값 [8BIT]
3. 모니터 갱신 신호
이를 종합해 보면 결국
모니터는 30Hz, 60Hz 등
주기적으로 갱신/스캔하며 새로운 이미지를 출력하면서,
사용자가 원하는 [ Y, X ] 좌표와
해당 좌표의 컬러값을 입력받을 수 있어야 한다.
이를 구현하기 위해서
우선 클럭신호에 맞추어 값이 증가하고 순환하는
x좌표 카운터[0000~1111], y좌표 카운터[0000~1111]
를 구현한다면, 해당 신호를 모니터의 갱신 신호로 사용할 수 있다.
이에 따라 logisim에서 제공하는 카운터를 이용해
16x16 모니터를 SCAN 하는 회로를 구성해 보면 다음과 같다.
해당 회로에 대해 구체적으로 설명하면,
X_Counter의 증가 신호는 클럭(clock) 신호에 맞추어 증가시킨다.
그리고 X_Counter = 1111 이후 다시 0000이 될 때
Y_Counter를 1 증가시켜 다음행으로 넘어가 SCAN을 진행한다.
이에 대해서 X_Coutner = 1111 이후 다시 0000이 될 때
X_Counter에서 자리올림이 발생하므로
해당 자리올림 신호를 Y_Counter의 증가 신호로 배선한다.
※ logisim에서 Y_Counter 트리거 설정을 Falling Edge로 설정해야 x=16 이후에 y가 증가한다. 그렇지 않으면 x=16이 됨과 동시에 y=1이 된다.
그러나 위의 회로만으로는 사용자가 원하는
모니터 좌표를 선택하지 못한다.
또, 그렇기에 각 픽셀에 해당하는 컬러값을
정확한 좌표에 적용하기에 어렵다.
위와 같은 문제를 해결하기 위해 우선,
디스플레이를 위한 메모리(RAM) : 프레임버퍼가 요구된다.
더 정확히는 디스플레이와 같은 크기를 가지는
프레임버퍼가 요구된다.
예를 들어, 16x16 디스플레이는 256개의 픽셀을 가지고,
각 픽셀을 8BIT 컬러값을 가진다.
이를 디스플레이 메모리 : 프레임버퍼에 대응시키면,
256개의 주소를 가지고,
각 주소는 8BIT를 저장하는 RAM회로와 같다.
즉, 프레임 버퍼의 주소는 각 픽셀의 좌표와 같게 된다.
이에 따라 모니터로 출력하고자 하는 시각적 요소를
이진수 픽셀 단위로 쪼개어, 각 픽셀의 컬러값을
프레임 버퍼 [0000 0000] 번지부터 순차적으로 입력해 준다면,
각 픽셀의 컬러 값이 정확한 좌표에 입력되어
출력하고자 했던 시각적 요소를 모니터로 확인할 수 있다.
ex)
< 디스플레이 인터페이스(어댑터) >
“그렇다면 컴퓨터(CPU)는 컬러 값을 어떻게 프레임버퍼로 전송할까?”
필자는 위의 질문을 해결하고자 하는데
다음과 같은 문제들이 떠올랐다.
문제 1 : 컴퓨터(CPU)로부터 오는 픽셀주소:프레임버퍼 주소와 SCAN을 위한 픽셀 좌표값과 겹치는 문제
문제 2 : I/O 장치별 주소에 대한 문제
문제 3 : 컴퓨터(CPU)로부터 안정적으로 값을 받기 위한 타이밍 문제
따라서 위의 문제를 해결하기 위해
결론적으로 다음과 같은 회로를 구현하게 되었다.
[문제 1]을 해결하기 위해 RAM:프레임버퍼의
A(Address) 부분에 "MAR [입력]"과, "MAR [출력]"을 연결했다.
※ MAR : Memory Access Registor 접근하고자 하는 메모리 주소를 저장한다.
※ 특정 메모리 주소 접근하고자 하는 명령어 이후, 해당 주소에 값을 STORE 하거나, 해당 주소의 값을 LOAD 하는 명령어가 진행되기 때문에, MAR이 없다면 STORE, LOAD 명령어를 진행할 시 접근하고자 하는 특정 메모리 주소에 대한 데이터는 사라진다.
이때, "MAR [입력]"은 컴퓨터(CPU)로부터
주소 또는 컬러값들이 전달되었을 때 활성화되도록 하고,
"MAR [출력]"은 스캔(SCAN) 용도로
"MAR [입력]"이 활성화될 때를 제외하고서는
항상 활성화되도록 하여,
계속 스캔(SCAN)할 수 있도록 한다.
< MAR [출력] 활성화 : 평상시 스캔(SCAN) >
< MAR [입력] 활성화 : 컴퓨터(CPU)가 프레임버퍼 참조 >
ex) I/O, Output, Address, RB (RB : 0000 0000)
: 프레임버퍼 [0000 0000] 주소에 접근
위의 이미지와 같이 클럭이 계속 활성화되어 있음에도,
Tri-state Buffer를 활용하여 X좌표, Y좌표 카운터는
증가/변화하지 않도록 한다.
이를 통해 컴퓨터(CPU)가
프레임버퍼에 안정적으로 참조할 수 있도록 한다.
※ 하지만 컴퓨터(CPU)가 프레임버퍼에 참조하는 동안 SCAN을 하지 못하는 것은 비효율적이다. 이에 따라 이후 더블버퍼링 기법(2개의 프레임버퍼 사용) 등의 기술을 통해 컴퓨터가 프레임버퍼에 접근하는 동안에도 스캔(SCAN)할 수 있도록 발전되었다.
다음 [문제 2] : I/O 장치별 주소 문제에 대해서
이전 글을 통해 알 수 있듯이 I/O 장치는 각자
고유의 주소가 부여된다.
ex) 키보드 : 0000 0001
따라서 우선 컴퓨터(CPU)는 접근하고자 하는
장치의 주소를 I/O 명령어를 통해 모든 장치로 출력한다.
그러면, 각 장치의 인터페이스(어댑터)는 해당 주소를
입력받았을 경우 채널이 활성화되도록 설계되어
이후 해당 장치와 통신할 수 있게 된다.
이에 따라 필자도 처음에는 다음과 같이
모니터에 0000 0010이라는 주소를 배정하여
모니터 인터페이스(어댑터)를 설계하고자 하였다.
하지만 컴퓨터(CPU) 기준 I/O 장치의 주소값은
Data/Address 타입 중에 "Address" 형태라 볼 수 있는데,
프레임버퍼의 주소값 역시 "Address" 형태여서
이전 글들을 통해 구현한
8BIT 컴퓨터와 키보드를 따라 적합하게 설계하면
두 데이터를 회로적으로 구분할 수 없다는
문제가 발생했다.
이는 다음과 같이 컴퓨터(CPU)가 모니터와
채널을 연결하고자 0000 0010 값을 Address 형태로
출력할 경우, 해당 값이 [입력] MAR까지 전달되는
문제를 초래한다.
ex) I/O, Output, Address, RB (RB : 0000 0010)
더 큰 문제는 채널 연결 이후
다른 주소를 참조하고자 하면, 채널 연결이 끊기는
문제가 발생한다는 것이다.
그 이유는 위에서 설명하였듯이 채널 연결을 위한
I/O 장치의 주소와, 프레임버퍼의 주소를
물리적으로 구분할 수 없기 때문이다.
ex) 채널 연결 이후 프레임버퍼 0000 1000 주소에 접근
: I/O, Output, Address, RB (RB : 0000 1000)
※ 채널 연결 이후 BUS값이 변환되는 순간에는 MAR [입력] 값이 변경될 수 있지만, 해당 첫 순간에만 변경되고, 이후 채널 연결 여부 역시 변경되어, 이후에는 다른 프레임버퍼 주소에 접근하지 못한다.
따라서 위와 같은 문제를 해결하기 위해서
컴퓨터를 16BIT로 확장하여,
I/O 장치 주소와 프레임버퍼 주소를 구분할 수 있는 비트를
추가하거나,
또는, I/O 장치 주소 범위와 프레임버퍼 주소 범위에 대해
서로 다른 주소 범위를 가지도록 배정하는 등의
방식이 존재하지만,
필자는 이전 글들을 통해 구현한
8BIT 컴퓨터와 키보드를 유지하고자,
모니터 장치에 주소를 부여하지 않는 방법을 도출해 냈다.
즉,
모니터의 주소는 0000 0000과 같으며,
이는 본래 컴퓨터(PC)로부터
I/O장치가 연결되지 않았다는 의미이지만,
해당 의미가
모니터 장치가 연결되었다는 의미가 되도록 구현하였다.
그래서 최종적으로 다음과 같은
모니터 인터페이스(어댑터)를 구현할 수 있었다.
이어서 마지막 [문제 3]
컴퓨터(CPU)로부터 안정적으로 값을 받기 위한 타이밍 문제
에 대해서
컴퓨터(CPU)로부터 전송된 컬러 값과 프레임버퍼 주소 값을
안정적으로 받기 위해 키보드와 같이
I/O clk_s, I/O clk_e를 이용한다.
I/O clk_s : I/O 전용 클럭 신호로, CPU 기준 출력, I/O장치 기준 입력
I/O clk_e : I/O 전용 클럭 신호로, CPU 기준 입력, I/O장치 기준 출력
※자세한 배선 과정은 다음 <컴퓨터와 모니터의 결합> 부분에서 설명한다.
< 컴퓨터와 모니터의 결합 >
이어서 이전 글들을 통해 구현한 8BIT 컴퓨터와 키보드를
앞서 구현한 모니터와 결합한 결과는 다음과 같다.
결합 및 배선 과정을 STEP에 따라 좀 더 세부적으로
살펴보면 다음과 같이 키보드와 동일하다.
[STEP 4]
RB 출력 제어 신호
: (([0] Non-ALU && [111] OPcode) && [1] Output(:IR [3]) && STEP 4 || 다른 ALU/Non-ALU 연산) && clk_e
I/O clk_s 신호
: ([0] Non-ALU && [111] OPcode) && [1] Output(:IR [3]) && STEP 4) && clk_s
※ 외부 I/O장치에 입력클럭신호를 제공함으로써, 특정 타이밍에만 안정적으로 CPU(RB)가 출력한 값을 외부 I/O장치가 입력받을 수 있도록 한다.
Input/Ouput 신호
: IR [3]
Data/Address 신호
: IR [4]
[STEP 5]
I/O clk_e
: ([0] Non-ALU && [111] OPcode) && [0] Input(:NOT(IR [3])) && STEP 5) && clk_e
※ 외부 I/O장치에 출력클럭신호를 제공함으로써, 특정 타이밍에만 안정적으로 외부 I/O장치가 값을 출력하여 CPU(RB)가 입력받을 수 있도록 한다.
RB 입력 제어 신호
: (([0] Non-ALU && [111] OPcode) && [0] Input(:NOT(IR [3])) && STEP 5 || 다른 ALU/Non-ALU 연산) && clk_s
Input/Ouput 신호
: IR [3]
Data/Address 신호
: IR [4]
이번 글을 끝으로,
8BIT 컴퓨터
입력장치 : 키보드
출력장치 : 모니터
를 Logisim 회로 시뮬레이션 프로그램을 통해
구현할 수 있었다.
그러면서 컴퓨터를 구성하는 기본적인
하드웨어/장치의 원리를 파악할 수 있었다.
다음 주제부터는 하드웨어 분야를 넘어
소프트웨어분야에 대해 탐구할 예정이다.
이에 따라 근본이 되는 소프트웨어인
BIOS, 운영체제, 컴파일러 등 새로운 영역을 탐구할 예정이다.
※ 해당 게시글은 주제를 탐구하면서 주관적인 생각을 정리 한 글입니다.
※ 해당 게시글은 J. 클라크 스코트, 『그래서 컴퓨터는 어떻게 동작하나요?』, 지유록, 인사이트(2019) 책을 참고하였습니다.