티스토리 뷰

https://github.com/kcal2845/Logisim-16bit-CPU

1. 소개

<시연 영상. CPU가 프로그램을 입출력하는 모습을 볼 수 있다.>


이번 글에서는 예전에 직접 설계했던 Logisim 16bit CPU를 소개하겠다. 이 CPU의 워드의 길이는 16비트이고, 접근 가능 메모리 용량은 2kb이다. (성능 되게 구림....ㅋㅋ) CPU를 설계해보고자 하는 분들에게 좋은 정보가 되었으면 좋겠다. 


<소스 코드 다운로드>


16bit CPU by KCAL2845.circ

Count

init Memory 0 to 1

tty hello world!!

Input and Output


Github 링크 : https://github.com/kcal2845/Logisim-16bit-CPU


<CPU와 주변 회로> 

CPU위를 보면 데이터 버스가 있다. 그 위에 위의 흰색 긴 장치는 입출력 드라이버이다. 그 위에 조금 짧은 흰색 장치는 입력장치이다. 여기다 문자를 입력하고 EN(엔터)버튼을 누르면 CPU로 값이 입력되고, C를 누르면 값이 지워진다. 그 위에는 7세그먼트(숫자 표시기)와 TTY 장치(아스키 코드 문자를 출력하는 일종의 모니터라고 보면 됨. 사진의 회색장치)를 볼 수 있다. 그 왼쪽에 있는 숫자가 채워져있는 장치는 메모리다.


<CPU의 내부 모습>

사진를 보면 장치들이 한 버스에 모두 물려있는 공통 버스 구조라는 것을 알 수 있다.


<CU(제어장치)의 내부 모습>

CPU의 핵심 부분이라고 할 수 있는 CU이다. 이 부분은 설계하는데도, 만드는데도 오래 걸렸다. 만들때도 잘못 연결해서 디버그한적이 한두번이 아니었다. 단순 로직 게이트 묶음으로 만드는 방식인 하드 와이어드 방식으로 구현되어있다.


2. 명령어

명령어의 길이는 16비트이다. 0~9번 비트 까지는 주소 및 값을 지정하는 OPERAND(오퍼랜드)이다. 10번~11번 비트는 추가적인 OPERAND(오퍼랜드)이다. 12~14비트 총 4비트는 OPCODE(오피코드)이다. 


실행할 명령어(4비트) + 설정(2비트)

값 또는 주소 (10비트)

[OPCODE(4bit)]   [OPERAND(2bit)] [OPERAND(10bit) 값 & 주소]


명령어 세트는 다음과 같다.

종류

니모닉

번호

오퍼랜드

설명

기타

NOP

0000

X

다음 명령어 인출

입출력

LOAD

0001

O

O

메모리에서 값을 CPU로 불러오기

오퍼랜드 00 : 직접주소지정 / 01 : 즉시주소지정 / 10 : 간접주소지정

입출력

STOR

0010

O

X

메모리에 값을 저장

OR0(주소지정 방식) : 직접 주소 지정/간접 주소 지정

분기

CMP

0011

O

상태 레지스터를 비교해서 1일경우 CMP비트 1로 세트, 아닐경우 CMP비트 0으로 세트

OR0~1 : 비교할 연산자 (00:ZF, 01:SF, 10:CF, 11:OF)

분기

JUMP

0100

O

O

오퍼랜드 조건에 따라 주소 위치로 분기

OR0 : 0-무조건 분기 1-조건 분기

OR1 : 0-CMP비트가 0이면 분기 1-CMP비트가 1이면 분기

분기

CALL

0101

O

O

현재 주소를 스택에 저장한 후 분기

오퍼랜드는 JUMP와 같음

분기

RET

0110

X

스택에 저장된 주소로 복귀

연산

NEG

0111

O

O

보수화 연산

연산

ADD

1000

O

O

덧셈 연산

연산

SUB

1001

O

O

뺄셈 연산

연산

MUL

1010

O

O

곱셈 연산

연산

DIV

1011

O

O

나눗셈 연산

연산

LOG

1100

O

논리 연산

OR(0~1) : 논리 연산자 (0:NOT, 1:AND, 2:OR, 3:XOR)

연산

SHFL

1101

O

x

10 : 오른쪽 시프트 01 : 왼쪽 시프트

연산

SHFA

1110

O

x


연산

SHFR

1111

O

x



불러오기/저장 명령어:

입출력 명령어에는 LOAD, STOR 두가지가 있다. 즉시주소지정, 직접주소지정, 간접주소지정 세가지를 지원한다. 오퍼랜드가 00일때는 직접, 01일때는 즉시주소지정, 10일때는 간접주소지정이다.


예시1)

즉시주소지정 방식으로 숫자 2를 CPU를 불러오기 ->

0001(LOAD 명령어) + 01(즉시주소지정) + 0000000010(값 2) = 0001010000000010


직접주소지정 방식으로 메모리 주소 0xf번지에 CPU의 값을 저장하기 -> 

0010(STOR 명령어) + 00(직접주소지정) + 0000001111(0xf) = 0010000000001111


예시2)

1과 2를 더하는 간단한 프로그램을 만들어 보겠다. 알고리즘은 다음과 같다.

1. 0xf번지에 1 저장

2. 0x10번지에 2 저장

3. 0xf번지 로드하고 0x10번지와 더하기(ADD명령어)

4. 결과 0x11번지에 저장

프로그램이 끝나면 0x11번지에 3이 저장 될 것이다.


그럼 기계어 코드는 다음과 같다.


1번 과정

0001(LOAD 명령어) + 01(즉시주소지정) + 0000000001(값 1) = 0001010000000001 =0x1401

0010(STOR 명령어) + 00(직접주소지정) + 0000001111(0xf번지) = 0010000000001111 = 0x200f

2번 과정

0001(LOAD 명령어) + 01(즉시주소지정) + 0000000001(값 1) = 0001010000000001 = 0x1401

0010(STOR 명령어) + 00(직접주소지정) + 0000010000(0x10번지) = 0010000000010000 = 0x2010

3번 과정    

0001(LOAD 명령어) + 00(직접주소지정) + 0000001111(0xf번지) = 0001000000001111 = 0x100f

1000(ADD 명령어) + 00(일반적인 연산) + 0000010000(0x10번지) = 1000000000010000 = 0x8010

4번 과정

0010(STOR 명령어) + 00(직접주소지정) + 0000010001(0x11번지) = 0010000000010001 = 0x2011


16진수 기계어 코드

1401    200f    1401    2010    100f    8010    2011


조건 분기 명령어:

이제 조건 분기에 대해서 설명하겠다. 이 CPU는 조건 분기를 할 때 CMP,JUMP,CALL,RET 등을 사용한다. 오퍼랜드에 한계가 있어 JUMP 명령어 하나로 조건 검사와 분기까지 구현하지는 못했다. 그래서 CMP 명령어로 조건 검사를 하고, 그 다음에 JUMP 명령어로 분기한다. 과정은 아래와 같다. 


if(A == B)의 구현 : 

1. LOAD 명령어로 A를 불러온다.

2. SUB 명령어로 A에서 B를 뺀다. 

3. CMP 명령어로 ZF를 비교한다. ZF는 ZeroFlag의 약자로, 계산 결과가 0이면 참(1), 아니면 거짓(0)이 저장된다. 그러므로 ZF가 1이라면 뺸 결과가 0이라는 뜻이므로 두 값이 같다는 뜻이 된다. 명령어는 아래와 같이 된다. 

0011(CMP) + 00(ZF) + 0000000000(CMP 명령어는 값 또는 주소가 필요 없다. 그러므로 0으로 둔다) = 0011000000000000

4. JUMP 명령어로 분기를 한다. 이 상황에서는 조건 분기를 하고, CMP결과가 1이면 분기를 해야 한다. 그러므로 명령어는 다음과 같이 된다.

명령어 : 0100(JUMP) + 11 + 분기할 주소값

오퍼랜드 11에서 앞의 1은 조건 분기를 하겠다는 뜻, 뒤의 1은 결과가 1일때 분기를 하겠다는 뜻이다.


if(A < B) 구현 :

A<B도 구현하기 쉽다. A-B의 계산 결과가 양수면 A가 크고, 음수면 B가 클 것이다. 그러므로 계산 결과 음수면 점프하도록 하면 된다.

1. LOAD 명령어로 A를 불러온다.

2. SUB 명령어로 A에서 B를 뺸다.

3. CMP 명령어로 이번에는 SF를 비교한다. SF는 계산 결과의 부호와 관련된 플래그이다. 계산 결과 음수면 1, 양수면 0이 설정된다.

명령어 : 0011(CMP) + 01(SF) + 0000000000

4. JUMP 명령어로 최종 분기해주면 구현이 완료된다. 음수일때 점프해야 하므로 이번에도 앞과 똑같이 오퍼랜드가 11이 될 것이다.

명령어 : 0100(JUMP) + 11 + 분기할 주소값


이제 아래와 같은 if else문도 구현할 수 있다.

if(...){...}else if(...){...}else{...}


임의로 A번지~...~B번지~...~C번지~...~D번지가 있다고 생각하자.


A 번지 : if문에 해당하는 코드. 마지막 JUMP문에서 D번지를 주소로 하면 된다. JUMP문 뒤에 실행할 코드를 둔다. 실행할 코드 맨 마지막에는 무조건분기로 D번지로 간다.

B 번지 : if문에 해당하는 코드. 마지막 JUMP문에서 D번지를 주소로 하면 된다. JUMP문 뒤에 실행할 코드를 둔다. 마찬가지로 실행할 코드 맨 마지막에는 무조건분기로 D번지로 간다.

C 번지 : else문에 해당하는 코드. 여기서는 실행 코드 뒤에 무조건 분기로 D번지로 가는 것만 맨 끝에 붙이면 된다.

D 번지 : IF~else if~else 문 뒤에 실행될 코드들 여기에 두면 된다.



3. 프로그램

이제 출력 프로그램을 만들어 보겠다. 출력 프로그램을 만들기 위해서 이 CPU가 어떻게 TTY를 제어하는지 알아보도록 하겠다. 

위 사진은 TTY와 키보드, 7세그먼트를 제어하는 회로이다. CPU는 200, 201, 202, 203, 204, 205번지에 접근해서 값을 저장하거나 로드하면서 제어한다.


0x200번지 : 7세그먼트(카운터) 버퍼. 여기에 출력하고 싶은 값을 저장하면 된다. 

0x201번지 : LED 매트릭스 데이터를 저장하는 버퍼 

0x202번지 : TTY 데이터 버퍼. 여기에 출력하고 싶은 ASCII 코드를 저장하면 된다. 

0x203번지 : TTY 제어 회로. 여기에 1을 저장했다 0을 저장하면 출력을 하고, 2를 저장했다 0을 저장하면 모든 값을 지운다.

0x204번지 : 키보드 버퍼의 가장 왼쪽 끝 글자가 출력된다. (출력 전용)

0x205번지 : 키보드의 상태를 저장하면서 동시에 키보드를 제어하는 주소이다. 205번지의 값을 읽어왔을 때 1이면 키보드의 엔터가 쳐졌다는 뜻이다. 205번지에 1을 저장했다 0을 저장하면 키보드 버퍼의 끝부분을 지운다. (이 부분을 더 자세하게 설명하자면 CPU가 205번지에서 값을 읽어올 때는 키보드의 상태가 읽어와지고, 205번지에 값을 저장할 때는 키보드 제어 버퍼에 값이 저장된다.)


이제 간단한 TTY 출력 프로그램의 알고리즘을 알려주겠다.

1. 202번지에 원하는 아스키 코드를 저장한다.

2. 203번지에 1을 저장한다.

3. 203번지에 0을 저장한다.

4. 1번 부터 3번까지 반복....


키보드로부터 읽어오는 프로그램의 알고리즘은 다음과 같다.

1. 반복문을 계속 돌리면서 205번지를 로드해와서 1이 들어왔는지 확인한다.

2. 205번지에 1이 들어왔다면 3번으로 넘어간다.

3. 204번지로부터 데이터를 읽어온다. (키보드 버퍼의 왼쪽 끝 문자부터 출력될 것이다. (예를 들어 "Hello!" 문장을 키보드에 입력했다면 H부터 들어올 것)

4. 205번지에 1을 저장했다, 0을 저장한다. 이렇게 하면 키보드 버퍼의 왼쪽 끝이 삭제되어 다음 문자을 받을 수 있다.

5. 204번지가 0이 나올때 까지 3~4를 반복한다.


4. 어셈블러

이 CPU는 무려 "전용 어셈블러"까지 있다!...... 거창하게 말하긴 했지만 사실 별거 없다. 단순히 1대1 치환 정도 밖에 안한다. 그래도 ORG기능, 주석 기능과 자동으로 문자열을 16진수 아스키 코드로 변환해서 저장하는 기능도 있다. Logisim에 넣기 편하라고 16진수로 출력하게끔 했다. 그래도 어셈블러 하나 있어야 폼날꺼 같아서 만들어봤다. 구현 언어는 Python이다. 


소스 코드 : https://github.com/kcal2845/kcal-cpu-assembler

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함