임베디드 면접 용어 사전
메모리·아키텍처write-back · Write-Back

write-back 캐시

쓰기를 캐시에만 반영하고 나중에 메모리에 내리는 방식. DMA와 함께 쓰면 일관성 문제가 생긴다.

쓰기를 일단 캐시에만 하고 메인 메모리에는 나중에 반영하는 방식.

write-through와의 대비

write-through
  CPU → 캐시 + 메모리 (동시에)
  ├ 항상 일치 → 단순, 안전
  └ 매 쓰기가 메모리 속도에 묶인다 → 느리다

write-back
  CPU → 캐시만 (dirty 표시)
       → 그 캐시 라인이 쫓겨날 때 메모리에 기록
  ├ 같은 곳에 여러 번 쓰면 마지막 한 번만 메모리로 → 빠르다
  └ 메모리와 일시적으로 다르다 → 관리 필요

dirty 비트가 핵심이다. 캐시 라인마다 "이 라인은 수정됐다"는 표시를 두고, 교체될 때 dirty면 메모리에 쓴다(write-back).

얼마나 이득인가

for (int i = 0; i < 1000; i++) sum += arr[i];   // sum 에 1000번 쓰기

write-through : 메모리 쓰기 1000번
write-back    : 캐시에서 1000번, 메모리 쓰기 1번   ← 쓰기 병합

루프 변수나 누적 변수처럼 같은 위치를 반복해 쓰는 경우 차이가 극적이다.

임베디드에서 무엇이 문제인가 — DMA와의 충돌

이것이 실무에서 가장 자주 겪는 캐시 문제다.

① CPU → DMA 방향 (송신)

uint8_t tx[64];
fill(tx);                        // 캐시에만 기록됨 (dirty)
HAL_UART_Transmit_DMA(&h, tx, 64);
// DMA는 메모리를 직접 읽는다 → 옛 데이터를 전송!  ✗

해결 — 전송 전에 캐시를 비운다(clean)

SCB_CleanDCache_by_Addr((uint32_t *)tx, 64);

② DMA → CPU 방향 (수신)

uint8_t rx[64];
HAL_UART_Receive_DMA(&h, rx, 64);   // DMA가 메모리에 직접 씀
// 완료 후
process(rx);                         // 캐시에 옛 값이 남아 있으면 그것을 읽는다  ✗

해결 — 읽기 전에 캐시를 무효화한다(invalidate)

SCB_InvalidateDCache_by_Addr((uint32_t *)rx, 64);

외우는 법

  • 보내기 전 → Clean — (캐시의 내용을 메모리로 밀어낸다)
  • 받은 후 — → Invalidate (캐시를 버리고 메모리에서 다시 읽게 한다)

캐시 라인 정렬 함정

캐시 연산은 라인 단위(보통 32바이트) 로 동작한다.

uint8_t a[10];
uint8_t dma_buf[10];    // a 와 같은 캐시 라인에 있을 수 있다

SCB_InvalidateDCache_by_Addr(dma_buf, 10);
// → 같은 라인의 a 까지 무효화된다 → a의 수정 내용이 사라진다!  ✗

DMA 버퍼는 캐시 라인 크기로 정렬하고 크기도 배수로 맞춘다.

__attribute__((aligned(32))) uint8_t dma_buf[64];

이 정렬을 빠뜨려 간헐적으로만 데이터가 깨지는 버그가 실제로 흔하다. 재현이 어려워 원인 파악에 오래 걸린다.

더 나은 해법 — MPU로 non-cacheable 영역

  • MPU 로 DMA 버퍼 영역을 "캐시 안 함"으로 설정
  • clean/invalidate 를 신경 쓸 필요가 없어진다
  • 그 영역 접근은 느려지지만 버그 위험이 사라진다

실무에서는 이 방식을 선호한다. 사람이 매번 캐시 연산을 정확히 호출하는 것보다 안전하기 때문이다. 링커 스크립트에 전용 섹션을 만들어 DMA 버퍼를 모아 두고, 그 구역만 MPU로 설정한다.

Cortex-M에서 언제 신경 쓰나

  • Cortex-M0/M3/M4 — → 데이터 캐시 없음. 이 문제가 아예 없다
  • Cortex-M7 — → L1 데이터 캐시 있음. 반드시 관리해야 한다

M4에서 M7으로 포팅했더니 DMA가 이상하게 동작한다는 것이 전형적인 시나리오다. 코드는 그대로인데 캐시가 생겼기 때문이다.

함께 보면 좋은 용어

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