개발/개발 공부

[밑바닥부터] 2일차 - 1장 불 논리: 논리 게이트와 HDL 소개

codesparkling 2025. 4. 7. 18:25

논리 게이트와 HDL 소개

논리 게이트

게이트(gate)는 간단한 불 함수를 구현한 물리적 장치를 의미한다.
현재 우리 레벨(소프트웨어를 위해 간단하게 이해하는)에서는 칩과 게이트를 하나의 개념으로 봐도 좋다. 엄밀하게는 게이트는 칩을 구성하는 기본 요소다.


오늘날 일반적인 경우는 실리콘에 식각(etching, 화학 반응을 이용하여 회로를 깎아내는 것)하여 칩, 게이트를 생산한다.


논리 게이트를 구현하기 위해서는 스위치 기술을 적용할 수 있다면 어떤 물리적 기술을 사용해도 상관이 없다. 공기, 물, 전기 등 0/1이나 true/false 같은 2진수를 표현할 수 있다면 기술에 구애받지 않는다.


우리 같은 컴공 소프트웨어 개발을 하는 사람은 하드웨어의 물리적 구현에 대해 추상화하고 논리와 불 대수에 대한 관심사만 살려두면 된다.
그래서 우리는 블랙박스(내부 구현이 몰라도 원인과 결과만 아는)로 두고 기본 논리 연산인 and, or, not을 활용할 수 있다.



출처: https://computerengineeringforbabies.com/blogs/engineering/and-or-not-gates

기본 게이트와 조합 게이트

모든 논리 게이트의 입출력은 0, 1로 이루어지기에 서로를 이어서 복잡한 조합 게이트를 생성할 수도 있다.



출처: https://www.nand2tetris.org/course


논리 게이트는 외부와 내부를 바라보는 관점으로 나눌 수 있다.


위 예시 그림의 왼쪽은 논리 게이트 외부, 인터페이스를 바라보는 시점으로 나타내는 게이트이다.


오른쪽은 논리 게이트의 내부 아키텍처, 구현이 어떻게 되어 있는지에 초점이 맞춰져 있다.


우리 같이 추상화 레벨에서 사용한다면 인터페이스에 집중하면 되고, 회로를 설계하고 제작하는 사람들은 실제 구현을 들여다 봐야 할 것이다.
인터페이스는 유일하지만 구현은 여러 방법을 사용할 수 있기 때문에 더 효율적인 방법을 찾아가야 한다.

하드웨어 구성

and 게이트, or 게이트, not 게이트로 여러 조합을 만들어 논리 게이트를 생성할 수 있다. 그러나 그 설계가 맞는지 등 테스트할 환경이 필요하다.
직접 하나하나 회로를 만들어서 테스트할 수도 있지만 HDL(Hardware Description Language, 하드웨어 기술 언어)을 사용해서 소프트웨어로 하드웨어를 검사할 수 있다.


HDL과 하드웨어 시뮬레이터를 적절히 사용하면 회로를 직접 만들지 않고도 최적화할 수 있다.


HDL 온라인 IDE 링크

XOR 게이트 구현

HDL의 사용법에 친숙해지자는 파트이다.



출처: https://www.nand2tetris.org/course


위 그림은 XOR 게이트를 구현한 것이다.


HDL로 칩을 정의하는 것은 HDL program 부분을 확인하면 된다.
HDL 정의는 헤더와 파트로 나뉘는데, 헤더는 칩의 인터페이스를 정의한다.
칩 이름과 입출력 핀이름이 나열되어 있다. (IN a,b 나 OUT out 등)


파트는 같은 코드 부분의 PARTS: 이후의 내용으로 실제 칩 내부 구현을 정의한다.


HDL 코드를 작성하면 내부 핀은 자동 생성된다.
내부 핀은 nota, notb, w1과 같은 것들을 말한다. 이런 녀석들은 별도 선언이 필요없이 사용할 때 자동으로 생성된다.


또한 하나의 입력 신호는 여러 게이트에 동시에 입력으로 사용될 수 있다.
예를 들어, a라는 입력 신호는 NOT 게이트와 AND 게이트 2개에 연결되어 있다.

테스트

칩의 품질을 검사하기 위해서 테스트 코드가 사용된다.
테스트 코드는 테스트 스크립트를 작성하고 하드웨어 시뮬레이터를 통해서 그 스크립트를 실행 시킬 수 있다.

하드웨어 시뮬레이션

이쪽 파트는 하드웨어 시뮬레이터 UI가 소개되어 있다.
이 부분은 프로젝트 실습을 해보면서 숙달될 것 같아 가볍게 읽고 넘어갔다.

명세

이제 칩을 만드는 데 필요한 논리 게이트를 정의를 살펴본다.

NAND

a b NAND(a,b)
0 0 1
0 1 1
1 0 1
1 1 0
//API 형식
칩 이름: NAND
입력: a, b
출력: out
기능: if((a==1)and(b==1)) then out = 0, else out = 1


모든 게이트의 기초가 되며, 컴퓨터 아키텍처의 시작점이다. 앞으로 책을 읽으며 정리할 내용에서 칩들은 위와 같은 API 형식으로 되어 있다.

기본 논리 게이트

기본 논리 게이트는 더 복잡한 칩을 만드는 데 중요한 역할을한다.
not, and, or, xor 게이트는 기본적인 논리 연산자를 구현한 게이트이며,
멀티플렉서와 디멀티플렉서는 정보의 흐름을 제어하는 게이트이다.

  1. NOT
    • 인버터라고도 부르며 입력 값의 반대 값을 출력한다.
    • 게이트 API
    • 칩 이름: NOT 입력: a 출력: out 기능: if(a == 1) then out = 0, else out = 1
  2. AND
    • 입력 값이 둘 다 1인 경우는 1, 아니라면 0을 반환한다.
    • 게이트 API
    • 칩 이름: AND 입력: a, b 출력: out 기능: if((a == 1) and (b == 1)) then out = 1, else out = 0
  3. OR
    • 입력 값 둘 중 하나 이상이 1, 그 외에는 0을 반환한다.
    • 게이트 API
    • 칩 이름: OR 입력: a, b 출력: out 기능: if((a == 0) and (b == 0)) then out = 0, else out = 1
  4. XOR
    • exclusive or(배타적 논리합)라고 하며, 두 입력 값 중 하나만 1일 때 1을, 그 외에는 0을 반환한다.
    • 게이트 API
    • 칩 이름: XOR 입력: a, b 출력: out 기능: if(a != b) then out = 1, else out = 0
  5. 멀티플렉서(multiplexer)
    • 멀티플렉서는 3-입력 게이트이다.(입력이 3개라는 뜻)
      두 개의 입력 비트 a,b와 선택 비트 sel을 입력받는다.
      (a, b는 데이터 비트, sel은 선택 비트라 한다.)
      sel의 값에 따라 a, b 중 출력할 값을 선택한다.
    • 이름의 기원은 하나의 통신 채널에서 여러 입력 신호를 직렬화(serialize)하는 데 사용되는 통신 기기 이름이다.
    • 진리표
      a b sel out
      0 0 0 0
      0 1 0 0
      1 0 0 1
      1 1 0 1
      0 0 1 0
      0 1 1 1
      1 0 1 0
      1 1 1 1
      sel out
      0 a
      1 b
    • 인터페이스

    • 출처: https://www.nand2tetris.org/course
    • 게이트 API
    • 칩 이름: MUX 입력: a, b, sel 출력: out 기능: if(sel == 0) then out = a, else out = b
  6. 디멀티플렉서(demultiplexer)
    • 입력 신호를 하나 받고 선택 비트에 따라 두개의 출력 중 하나로 내보낸다. 그리고 다른 출력은 0으로 설정된다.
      멀티플렉서가 여러 입력 신호를 단일 출력으로 바꿔준다면,
      디멀티플렉서는 단일 입력 신호를 여러 출력 라인 중 하나로 전달해준다.
    • 진리표
      sel a b
      0 in 0
      1 0 in
    • 인터페이스

      출처: https://www.nand2tetris.org/course
    • 게이트 API
    • 칩 이름: DMUX 입력: in, sel 출력: a, b 기능: if(sel == 0) then {a, b} = {in, 0}, else {a, b} = {0, in}

 

기본 게이트의 멀티비트 버전

컴퓨터 하드웨어는 멀티비트 값을 처리하도록 설계된다. n 비트 게이트의 논리적 구조는 16비트, 32비트, 이런 실제 n 값과는 무관하게 동일하므로 이 책에서는 16비트 값을 기준으로 설명을 한다.
멀티비트 값은 개별비트에 접근하기 위해 인덱스가 설정되어 있다는 점(우리가 자료구조에서 배우는 배열과 같다.)을 제외하면 단일비트 값과 비슷하게 취급한다.
맨 오른쪽 비트는 0번째 비트, 가장 왼쪽 비트는 15번째 비트가 된다.(리틀 엔디안, little endian)

  1. 멀티비트 NOT
    • n 비트 NOT 게이트는 n비트 입력의 모든 비트마다 NOT 불 연산을 수행한다.
    • 게이트 API
    • 칩 이름: NOT16 입력: in[16] 출력: out[16] 기능: for i = 0..15 out[i] = NOT(in[i])
  2. 멀티비트 AND
    • n비트 AND 게이트는 두 개의 n비트 입력 쌍마다 AND 불 연산을 수행한다.
    • 게이트 API
    • 칩 이름: AND16 입력: a[16], b[16] 출력: out[16] 기능: for i = 0..15 out[i] = AND(a[i], b[i])
  3. 멀티비트 OR
    • n비트 OR 게이트는 두 개의 n비트 입력 쌍마다 OR 불 연산을 수행한다.
    • 게이트 API
    • 칩 이름: OR16 입력: a[16], b[16] 출력: out[16] 기능: for i = 0..15 out[i] = OR(a[i], b[i])
  4. 멀티비트 멀티플렉서
    • 입력과 출력이 n비트임을 제외하면 기본 멀티플렉서와 같은 기능을 한다.
    • 게이트 API
    • 칩 이름: MUX16 입력: a[16], b[16], sel 출력: out[16] 기능: if (sel == 0) then for i = 0..15 out[i] = a[i], else for i = 0..15 out[i] = b[i]
  5. 멀티비트 디멀티플렉서
    • sel 비트에 따라 입력 비트를 가능한 데이터 비트로 분배하여 출력한다.
    • 게이트 API기본 게이트의 다입력 버전
    • 칩 이름: DMUX16 입력: in, sel 출력: a[16], b[16] 기능: if (sel == 0) then for i = 0..15 a[i] = in[i], b[i] = 0 else for i = 0..15 a[i] = 0, b[i] = in[i]

입력이 하나 혹은 두 개인 논리 게이트는 입력이 2개 이상인 다입력(multi-way) 게이트로 일반화 할 수 있다.

  1. 다입력 OR
    • m-입력 OR 게이트는 m개의 입력 비트 중 적어도 하나가 1이면 1을 출력하고 그 외에는 0을 출력한다.
    • 게이트 API (8비트 입력 게이트 예시) 
    • 칩 이름: OR8WAY 입력: in[8] 출력: out 기능: out = OR(in[0], ..., in[7])
  2. 다입력/멀티비트 멀티플렉서
    • m-입력 n비트 멀티플렉서는 m개의 n비트 입력 중 하나를 선택해서 n비트 출력으로 내보낸다.
      선택 입력(sel)은 k개의 제어 비트로 구성되고, k는 log m(밑이 2)이다.
    • 4-입력 멀티플렉서 예시
      • 진리표
        sel[1] sel[0] out
        0 0 a
        0 1 b
        1 0 c
        1 1 d
      • 인터페이스

      • 출처: https://www.nand2tetris.org/course
      • 게이트 API
      • 칩 이름: MUX4WAY16 입력: a[16], b[16], c[16], d[16], sel[2] 출력: out[16] 기능: if(sel == 00, 01, 10 or 11) then out = a, b, c, or d 설명: 할당은 16비트 연산이다. 예를 들어 'out = a'는 'for i = 0..15 out[i] = a[i]'라는 의미이다.
    • 8-입력 멀티플렉서 게이트 API
    • 칩 이름: MUX8WAY16 입력: a[16], b[16], c[16], d[16], e[16], f[16], g[16], h[16], sel[3] 출력: out[16] 기능: if(sel == 000, 001, 010, ... or 111) then out = a, b, c, ... , h 설명: 할당은 16비트 연산이다. 예를 들어 'out = a'는 'for i = 0..15 out[i] = a[i]'라는 의미이다.
  3. 다입력/멀티비트 디멀티플렉서
    • m-출력 n비트 디멀티플렉서는 n비트 입력을 하나 받아 m개의 n비트 출력 중 하나로 내보낸다.
      다른 출력은 0으로 설정된다.
      선택 입력은 다입력/멀티비트 멀티플렉서와 비슷하게 log m의 k 값을 가진다.
    • 4-입력 멀티플렉서 예시
      • 진리표
        sel[1] sel[0] a b c d
        0 0 in 0 0 0
        0 1 0 in 0 0
        1 0 0 0 in 0
        1 1 0 0 0 in
      • 인터페이스

      • 출처: https://www.nand2tetris.org/course
      • 게이트 API
      • 칩 이름: DMUX4WAY16 입력: in, sel[2] 출력: a, b, c, d 기능: if(sel == 00) then {a, b, c, d} = {in, 0, 0, 0}, else **if(sel == 01) then {a, b, c, d} = {0, in, 0, 0}, else if(sel == 10) then {a, b, c, d} = {0, 0, in, 0}, else if(sel == 11) then {a, b, c, d} = {0, 0, 0, in}**
    • 8-입력 멀티플렉서 게이트 API
    • 칩 이름: DMUX8WAY16 입력: in, sel[3] 출력: a, b, c, d, e, f, g, h 기능: if(sel == 000) then {a, b, c, d, e, f, g, h} = {in, 0, 0, 0, 0, 0, 0, 0}, else if(sel == 001) then {a, b, c, d, e, f, g, h} = {0, in, 0, 0, 0, 0, 0, 0}, ... else if(sel == 111) then {a, b, c, d, e, f, g, h} = {0, 0, 0, 0, 0, 0, 0, in},

구현

이번 파트부터는 논리 게이트를 구현하는 두 가지의 일반적인 방법론을 살펴본다.
하나는 행동 시뮬레이션(behavioral simulation), 다른 하나는 하드웨어 구현(hardware implementation)이다.

행동 시뮬레이션

앞서 설명했던 게이트, 칩 설명들은 매우 추상적이었고 HDL로 만들어보기 전 추상화 개념을 직접 실험해볼 수 있으면 좋을 것 같다.


칩의 작동만 생각하면 굳이 HDL로 칩을 만들 필요가 없다.
그냥 JAVA 같은 언어 하나를 골라서 클래스를 정의하고 eval 메서드를 만들고 고수준의 칩들이 저수준의 칩들로 정의될 수 있도록 구현하면 된다.
거기에 사용자를 위한 인터페이스를 추가하거나 출력을 관찰할 수 있도록 하는 등 편의성을 제공할 수도 있다.


이런 소프트웨어 기반 기법을 행동 시뮬레이션이라 한다.
HDL로 칩을 만들기 전에 칩 인터페이스를 실험할 수 있기 때문이다.

하드웨어 구현

이번 파트에서는 15개의 논리 게이트를 실제로 구현하는 방법에 대해 알아본다.

  1. NAND
    • NAND 게이트를 하드웨어의 기본 구성 블록으로 삼기 때문에, NAND 게이트 기능은 외부에서 제공된다고 가정한다.
  2. NOT
    • NAND 게이트 하나로 구현할 수 있다.
    • NAND(a, a)
  3. AND
    • NAND와 NOT을 사용해서 구현할 수 있다.
    • NOT(NAND(a, b))
  4. OR
    • AND와 NOT으로 구현할 수 있다.
    • NOT(AND(NOT(a), NOT(b)))
  5. XOR
    • AND, NOT, OR로 구현할 수 있다.
    • (A AND NOT B) OR (NOT A AND B)
  6. 멀티플렉서/디멀티플렉서
    • 멀티플렉서: NOT, AND, OR을 조합한다.
    • 디멀티플렉서: NOT과 AND를 조합한다.
  7. 멀티비트 NOT, AND, OR
    • 각각 단일비트 NOT, AND, OR을 멀티비트 개수만큼 반복해주면 된다.
  8. 멀티비트 멀티플렉서
    • 마찬가지로 단일비트 멀티플렉서를 멀티비트 개수만큼 반복한다.
  9. 다입력 게이트
    • 입력을 2개씩 묶어서 연산한 후 그 결과를 다시 2개씩 묶어서 사용하면 된다.
    • 그래서 효율적으로 코드를 작성했다면 m개의 입력에서 log m의 효율을 보일 것이다.