DMA 기초 정리: CPU 대신 데이터를 옮기는 방식

카드뉴스

DMA는 CPU가 매번 복사하지 않게 해주는 데이터 이동 하드웨어입니다

임베디드 시스템에서는 데이터가 한 번에 조금씩 들어오는 상황이 많습니다. ADC가 일정 주기로 샘플을 만들고, UART로 바이트가 계속 들어오고, SPI나 I2C로 센서 데이터가 반복 전송됩니다. 이때 CPU가 매번 레지스터를 읽고 메모리에 저장하는 방식만 쓰면 다른 작업을 처리할 시간이 줄어들 수 있습니다. DMA는 이런 주변장치와 메모리 사이의 데이터 이동을 CPU 대신 맡아주는 하드웨어 블록입니다.

DMA를 쓴다고 CPU가 완전히 사라지는 것은 아닙니다. CPU는 전송 방향, 시작 주소, 버퍼 크기, 데이터 폭, 주소 증가 여부 같은 조건을 먼저 설정합니다. 그 다음 DMA가 peripheral register와 memory buffer 사이에서 데이터를 옮기고, half complete나 transfer complete 이벤트로 처리 시점을 알려줍니다. CPU는 매 바이트를 옮기는 대신 채워진 버퍼를 해석하거나 다음 동작을 준비하는 쪽에 집중할 수 있습니다.

ADC, UART, SPI에서 DMA가 자주 나오는 이유

ADC 샘플링에서는 정해진 주기로 변환된 값을 배열에 쌓아야 할 때가 많습니다. DMA를 사용하면 변환이 끝날 때마다 CPU가 값을 읽지 않아도 버퍼에 순서대로 저장할 수 있습니다. UART 수신에서도 바이트 단위 인터럽트가 너무 자주 들어오면 코드가 복잡해질 수 있는데, DMA 수신 버퍼를 쓰면 일정 구간 단위로 데이터를 처리하는 구조를 만들 수 있습니다.

SPI 송수신이나 DAC 출력처럼 연속 전송이 필요한 곳에서도 DMA가 유용합니다. 다만 메시지의 시작과 끝, 패킷 길이, 오류 처리까지 DMA가 자동으로 해결해주는 것은 아닙니다. UART에서 idle interrupt를 함께 쓰거나, ring buffer 구조를 설계하거나, ADC 버퍼를 반씩 나누어 처리하는 방식처럼 데이터 해석 로직은 별도로 필요합니다.

DMA 디버깅은 버퍼와 전송 조건을 같이 봐야 합니다

DMA가 동작하지 않을 때는 peripheral clock, DMA channel 또는 stream 매핑, 전송 방향, 데이터 폭, 메모리 주소 증가 설정을 먼저 확인해야 합니다. peripheral-to-memory인데 방향을 반대로 잡거나, 16비트 ADC 값을 8비트 폭으로 받으면 결과가 이상해질 수 있습니다. circular mode를 쓰는 경우 버퍼가 계속 덮어써지므로 CPU가 데이터를 처리하는 속도도 고려해야 합니다.

캐시가 있는 MCU에서는 DMA가 쓴 메모리와 CPU가 읽는 캐시 내용이 달라지는 문제도 생길 수 있습니다. 모든 보드에서 바로 겪는 문제는 아니지만, 고성능 MCU로 갈수록 cache invalidate, memory alignment, bus contention 같은 조건을 확인해야 합니다. DMA의 장점은 CPU 부하를 줄이는 데 있지만, 안정적으로 쓰려면 버퍼 설계와 완료 이벤트 처리까지 함께 설계해야 합니다.

완료 인터럽트만으로는 데이터 의미를 알 수 없습니다

DMA 완료 이벤트는 정해진 개수의 전송이 끝났다는 신호일 뿐, 그 데이터가 완전한 메시지인지까지 판단해주지는 않습니다. UART처럼 길이가 변하는 데이터는 idle line, delimiter, header length 같은 기준을 따로 둬야 하고, ADC 연속 샘플은 half buffer와 full buffer 처리 시간이 다음 전송보다 짧은지 확인해야 합니다. DMA를 잘 쓰려면 하드웨어 전송과 프로토콜 해석을 분리해서 설계하는 감각이 필요합니다.

댓글 남기기