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

캐시 일관성

캐시와 실제 메모리 내용이 어긋나지 않게 유지하는 문제. DMA 버퍼에서 clean/invalidate로 다룬다.

같은 메모리 위치에 대해 캐시와 메모리, 또는 여러 캐시가 서로 다른 값을 갖게 되는 문제와 그것을 맞추는 일(cache coherency).

두 가지 층위 — 구분해야 한다

① 단일 코어 + DMA (임베디드에서 흔한 경우)

CPU ──► [D-Cache] ──► 메모리 ◄── DMA
              ↑                    ↑
        여기에 최신값이         여기만 보는
        있을 수 있다            DMA는 옛값을 본다

하드웨어가 자동으로 맞춰 주지 않는다. 소프트웨어가 직접 관리해야 한다.

② 멀티코어 (Cortex-A, SMP)

코어0 [캐시] ─┐
코어1 [캐시] ─┼── 버스 ── 메모리
코어2 [캐시] ─┘

같은 주소를 여러 캐시가 갖고 있으면 하나가 수정했을 때 나머지는?

여기서는 하드웨어 프로토콜(MESI 등)이 자동으로 처리한다.

MESI 프로토콜 — 멀티코어의 자동 해결

각 캐시 라인이 네 상태 중 하나를 갖는다.

상태
Modified나만 갖고 있고 수정했다. 메모리와 다르다
Exclusive나만 갖고 있고 수정 안 했다
Shared여러 캐시가 같은 값을 갖고 있다
Invalid무효. 쓸 수 없다
코어0이 Shared 라인에 쓰려고 한다
→ 버스에 "무효화" 신호를 보낸다
→ 다른 코어들의 그 라인이 Invalid 가 된다
→ 코어0의 라인이 Modified 가 된다

캐시들이 버스를 엿들으며(snooping) 서로 조율하는 구조다.

False Sharing — MESI의 부작용

struct { int a; int b; } data;    // a와 b가 같은 캐시 라인(64바이트)에 있다

코어0: data.a++ 반복
코어1: data.b++ 반복

서로 다른 변수인데도 같은 라인이라 매번 무효화가 오간다. 논리적으로는 공유가 없는데 하드웨어적으로 경쟁하는 것이다.

캐시 라인 크기로 패딩해 분리한다.

struct { int a; char pad[60]; int b; } data;

성능이 이유 없이 나쁠 때 의심해 볼 항목이다.

임베디드(Cortex-M7)에서의 실전 관리

// 송신: CPU가 쓴 것을 DMA가 읽어야 한다
memcpy(tx_buf, data, len);
SCB_CleanDCache_by_Addr((uint32_t *)tx_buf, len);   // 캐시 → 메모리
HAL_SPI_Transmit_DMA(&hspi, tx_buf, len);

// 수신: DMA가 쓴 것을 CPU가 읽어야 한다
HAL_SPI_Receive_DMA(&hspi, rx_buf, len);
// … 완료 콜백에서
SCB_InvalidateDCache_by_Addr((uint32_t *)rx_buf, len);  // 캐시 버리기
process(rx_buf);
함수동작언제
Clean캐시의 dirty 데이터를 메모리로 밀어냄DMA 송신 전
Invalidate캐시 내용을 버림 (다음 접근 시 메모리에서 다시)DMA 수신 후
Clean&Invalidate둘 다양방향 버퍼

정렬이 반드시 필요한 이유

캐시 연산은 라인 단위로 동작하므로, 버퍼가 라인 경계에 맞지 않으면 이웃 데이터까지 영향을 받는다.

__attribute__((aligned(32))) uint8_t dma_buf[64];   // 32바이트 라인 기준

정렬을 안 하면 간헐적으로만 재현되는 데이터 손상이 생겨 디버깅이 매우 어렵다.

더 안전한 접근 — 처음부터 캐시를 끄기

  • MPU로 DMA 버퍼 영역을 non-cacheable, shareable 로 설정
  • 캐시 연산이 아예 필요 없다
  • 그 영역 접근은 느려지지만 실수의 여지가 사라진다

실무에서는 이쪽을 권장한다. 링커 스크립트에 .dma_buffer 섹션을 만들어 모든 DMA 버퍼를 모으고, 그 구역만 MPU로 설정하는 것이 정석이다.

메모리 배리어와의 관계

캐시 일관성과 메모리 순서는 다른 문제다.

캐시 일관성 : 값이 서로 다른 문제 메모리 순서 : 쓰기가 보이는 순서가 뒤바뀌는 문제

  • __DSB() — — 이전 메모리 접근이 완료될 때까지 대기
  • __DMB() — — 이전과 이후 메모리 접근의 순서를 보장
  • __ISB() — — 명령 파이프라인을 비움 (설정 변경 후 필수)
SCB->VTOR = new_vector_table;
__DSB();     // 쓰기 완료 보장
__ISB();     // 파이프라인 갱신

시스템 레지스터를 바꾼 뒤에는 배리어가 필요하다 — 안 넣으면 파이프라인에 남은 옛 설정으로 실행되는 일이 생긴다.

함께 보면 좋은 용어

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