[왕초보 임베디드-C 공부 일기] 13+. 비트 연산자와 논리 회로

C언어의 비트 연산자(AND, OR, XOR, NOT)는 단순히 숫자를 계산하는 문법을 넘어, MCU 내부의 물리적 하드웨어 그 자체를 의미한다. 그래서 이번 글에서는 소프트웨어의 논리 기호가 실제 하드웨어 부품으로 어떻게 전환되는지 간단한 예시와 함께 알아본다.

1. NAND : 원자 단위

C언어 문법에는 단일 NAND 연산 기호가 존재하지 않는다. 따라서 수학적 정의에 따라 ~( A & B ) 형태로 AND와 NOT을 조합하여 구현한다. 하지만 하드웨어의 관점에서는 이야기가 다르다.

가장 경제적인, 단순한 건축 자재

칩 설계 엔지니어들은 AND, OR게이트를 물리적으로 각각 설계하지 않는다. 물리적 구조가 가장 단순하고 공간을 적게 차지하는(다양한 이유가 있지만 여기서 설명하지는 않겠다.) 'NAND 트랜지스터'를 수억 개 찍어낸 뒤, 이들을 조합하여 AND와 OR 연산 회로를 만든다. (이를 범용 게이트, Universal Gate라 부른다.)

NAND 플래시 메모리

우리가 작성한 C 코드를 개발 보드에 업로드하면 전원이 꺼져도 데이터가 유지되는 공간(ROM, Flash Memory)에 기록된다. 이 메모리 셀의 물리적 배선 구조가 NAND 논리 게이트의 직렬 형태를 띠고 있어 'NAND 플래시'라는 이름이 붙었다.

2. 논리 연산자로 조립하는 MCU의 핵심 부품

소프트웨어의 논리 연산자만으로 칩 내부의 모든 하드웨어 구조를 100% 설명할 수 있다. 수억 개의 논리 게이트가 모여 만들어진 4개의 핵심 부품은 다음과 같다.

1. CPU의 계산기 : 가산기 (Adder)

CPU 내부에는 덧셈(+)기호를 이해하는 부품이 없다. 오직 XOR(AND, OR, NOT의 조합)과 AND연산의 조합만 존재한다.

예시 : 반가산기(Half Adder)의 1 + 1 연산 추적

2진수 1과 1을 더할 때 하드웨어는 다음과 같이 동작한다.
  1. 합(Sum) : 1 XOR 1 = 0. 현재 자리의 결과는 0이 된다.
  2. 자리 올림수(Carry) : 1 AND 1 = 1. 다음 자리로 넘겨줄 값이 1이 된다.
  3. 결과 : 이 두 게이트를 통과한 전기 신호는 자연스럽게 10(십진수 2)이라는 이진수 결과를 만들어낸다. 이 반가산기를 32개 이어 붙이면 32비트 연산을 수행하는 산술논리연산장치(ALU)가 된다.

2. 메모리와 레지스터의 정체 : Flip-Flop(플립플롭)

전기가 끊기기 전까지 상태를 기억하는 레지스터의 원리이다.

예시 : 상태의 무한 루프

2개의 NAND 게이트의 출력을 서로 상대방의 입력으로 꼬리 물기(Feedback)시킨다. 우리가 C언어 코드를 통해 특정 핀에 전기를 통하게 하면, 전기 신호가 두 게이트 사이를 무한히 맴돌며 꽉 맞물리게 된다. 전원(VCC)이 공급되는 한 이 상태는 절대 풀리지 않으며, 이것이 CPU의 가장 빠른 메모리인 SRAM(레지스터)의 물리적 실체이다.

3. 데이터의 선로 스위치 : Multiplexer, MUX(멀티플렉서)

특정 센서 핀의 데이터만 읽어오고 싶을 때 사용하는 하드웨어 신호등이다.

예시 : STM32의 선택적 기능 변경

STM32 보드의 PA9 핀은 단순 입출력(GPIO)으로도 쓸 수 있고, 통신용(UART TX)으로도 쓸 수 있다. CPU가 "지금으로 PA9를 통신용으로 사용"이라고 레지스터에 값을 쓰면, 이 값이 MUX의 선택 핀(Selector)으로 들어간다. 내부의 AND, OR 게이트들이 길을 조합하여 GPIO 쪽 회로는 전기적으로 차단(AND 0)하고, UART 통신 회로 쪽 길만 활성화(AND 1)하여 연결해 준다.

4. 포인터(주소)의 물리적 실체 : Decoder(디코더)

C언어에서 포인터 주소(0x40020000)를 전송한 뒤, 하드웨어가 정확한 메모리 위치를 찾아가는 원리이다.

예시 : 메모리 깨우기

32비트의 거대한 2진수 주소 신호가 디코더 회로로 들어온다. 디코더 내부의 수많은 NOT 게이트와 AND 게이트들은 이 입력값을 스캐닝한다. 만약 신호가 정확히 0100 0000 0000 0010 ...과 일치하면, 수만 개의 출력선 중 오직 해당 레지스터와 연결된 단 하나의 선에만 HIGH(1)신호를 뿜어내어 해당 하드웨어를 깨운다(Chip Select). 포인터는 결국 이 디코더 회로에 던져주는 암호 키인 것이다.

3. 모든 논리를 지휘하는 심장 박동 : 클럭(Clock)

아무리 정교한 논리 게이트를 조립해 두어도, 기준이 없다면 데이터가 뒤죽박죽 섞여버린다.

예시 : 전파 지연을 해결하기 위한 동기화

실리콘 내부에서 전기가 이동하는 데는 아주 미세한 시간(전파 지연, Propagation Delay)이 걸린다. 어떤 비트는 빨리 도착하고 어떤 비트는 늦게 도착하기 때문에, 중간 과정에서 엉뚱한 값(Glitch)이 발생하기도 한다. 이때 CPU가 값을 읽어버리면 시스템이 오작동할 수 있다. 이를 해결하기 위해 동기화(SYnchronization) 절차를 수행하는 부품이 MCU 내에 존재한다. MCU내부에는 일정한 주기로 전기 진동을 일으키는 오실레이터가 있다. 이를 클럭(Clock) 이라고 부르며, 가신기, 플립플롭 등 모든 하드웨어 게이트들을 전기 신호가 완전히 안정화될 때까지 기다렸다가, 오직 클럭 신호가 켜지는 순간에만 논리 연산을 수행한다. C언어로 작성한 코드가 하드웨어에서 완벽한 원자성(Atomicity) 을 가지며 실행될 수 있는 이유가 이 덕분이다.