임베디드 면접 용어 사전
메모리·아키텍처

DMA

CPU를 거치지 않고 주변장치와 메모리가 직접 데이터를 옮기는 기능. 전송 전후 캐시 유지·무효화가 필수다.

CPU를 거치지 않고 주변장치와 메모리가 직접 데이터를 주고받는 기능 (Direct Memory Access).

무엇을 해결하나

CPU가 직접 옮기는 경우 (polling) for (i = 0; i < 1000; i++) {

  • while (!(UART->SR & TXE)) { } // 준비될 때까지 대기

  • UART->DR = buf[i]; }

  • CPU가 1000번 반복하며 대부분 기다리기만 한다

DMA DMA_Start(buf, &UART->DR, 1000);

  • 설정만 하고 CPU는 다른 일을 한다. 끝나면 인터럽트로 알려 준다

CPU 사이클을 데이터 복사에 쓰지 않는 것이 핵심 이득이다.

구성 요소

  • 소스 주소 — : 어디서 (메모리 또는 주변장치 레지스터)
  • 목적지 주소 — : 어디로
  • 전송 크기 — : 몇 개
  • 데이터 폭 — : 바이트/하프워드/워드
  • 주소 증가 — : 소스·목적지 각각 증가할지 고정할지
  • 트리거 — : 무엇이 전송을 유발하나 (UART 수신, 타이머, 소프트웨어)
  • 모드 — : 일반(1회) / 순환(circular)

주소 증가 설정이 중요하다. 메모리 → UART : 소스 증가 O, 목적지 증가 X (레지스터는 고정) 메모리 → 메모리 : 둘 다 증가

  • ADC → 메모리 — : 소스 고정, 목적지 증가

순환 모드 — 임베디드에서 매우 유용

// ADC 값을 링 버퍼에 계속 채운다. 끝에 도달하면 처음으로
HAL_ADC_Start_DMA(&hadc, buf, 128);   // circular 설정 시

CPU 개입 없이 무한히 데이터가 수집된다. 절반 완료(half-transfer)와 완료 인터럽트를 이용하면 한쪽을 처리하는 동안 다른 쪽을 채우는 더블 버퍼링이 된다.

[───── 앞 절반 ─────][───── 뒤 절반 ─────]
        ↑ DMA가 채우는 중        ↑ CPU가 처리 중

오디오·ADC 스트리밍의 표준 패턴이다.

반드시 알아야 할 함정

① 버스 경합 — CPU가 느려진다

DMA와 CPU가 같은 버스·같은 메모리를 쓰면 서로 기다린다. "DMA를 켰더니 오히려 전체가 느려졌다"는 경우가 있다.

→ 서로 다른 SRAM 블록이나 다른 버스 매트릭스 경로를 쓰도록 배치한다.

② 캐시 일관성 (Cortex-M7 등)

송신 : CPU가 캐시에만 쓴 데이터를 DMA가 못 본다 → Clean 필요 수신 : DMA가 메모리에 쓴 데이터를 CPU가 캐시에서 옛것을 읽는다 → Invalidate 필요

③ 접근 불가 영역

  • CCM(Core Coupled Memory)에 버퍼를 두면 DMA가 접근하지 못한다
  • 전송이 아예 안 되거나 버스 폴트 "DMA가 동작하지 않는다"의 가장 흔한 원인 중 하나다. 링커 스크립트에서 버퍼 배치를 확인해야 한다.

④ 버퍼 수명

void send(void) {
    uint8_t buf[64];              // ✗ 지역 변수 — 함수를 나가면 스택이 재사용된다
    HAL_UART_Transmit_DMA(&h, buf, 64);
}   // DMA는 아직 전송 중인데 buf 영역이 다른 데이터로 덮인다

static 또는 전역으로 두어야 한다.

⑤ volatile

DMA가 채우는 버퍼는 CPU가 모르게 바뀌므로 volatile이 필요하다. 다만 큰 배열 전체를 volatile로 하면 최적화가 다 막히므로, 완료 플래그만 volatile로 두고 완료 후 일반 접근하는 편이 낫다.

인터럽트와의 관계 — 비교

인터럽트 방식DMA
바이트당 CPU 개입매번없음
오버헤드진입·복귀 비용 × 개수설정 1회 + 완료 1회
적합소량, 불규칙대량, 연속
지연낮음설정 비용이 있어 소량엔 손해

한두 바이트 전송에 DMA를 쓰면 설정 비용이 더 크다. UART로 1바이트씩 명령을 받는다면 인터럽트가 낫고, 1KB 로그를 보낸다면 DMA가 낫다.

디버깅

  • 전송이 안 된다 — → 클럭 인가? 스트림/채널 매핑이 맞나? 버퍼가 접근 가능 영역인가?
  • 값이 깨진다 — → 캐시? 정렬? 데이터 폭 설정?
  • 중간에 멈춘다 — → 전송 에러 인터럽트(TE) 확인. 버스 폴트일 수 있다 DMA 상태 레지스터의 에러 플래그를 반드시 확인하는 습관이 중요하다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 메모리·아키텍처 — 메모리맵·MMU/MPU·캐시·DMA·엔디안·정렬