eFlash: MCU 내장 플래시의 셀 구조와 신뢰성 설계

MCU·SoC에 내장되는 eFlash의 셀 구조, ISPP 프로그래밍, ECC·OTP 보호, 내구성·보존 특성을 정리하고 파티셔닝·전원 보호 실무 포인트를 다룬다.

2026-08-12 · 최초 발행 2025-12-09

MCU에 펌웨어를 올리는 방법은 여러 가지지만, 전원이 꺼져도 코드가 살아남아야 한다는 요구는 피할 수 없다. eFlash(embedded Flash)는 이 요구를 다이 안에서 해결하는 표준적인 방법이다. MCU·SoC 내부에 집적된 NOR 계열 플래시로, 펌웨어 저장·보안 키 보관·로그 기록까지 떠맡는다. 외장 NOR나 EEPROM을 붙이는 것보다 지연이 낮고 통합 제어가 쉬워 임베디드 시스템의 전력·비용·보안 측면에서 유리하다.

셀 구조와 프로그래밍 메커니즘

eFlash는 온칩 버스(AXI/AHB/APB 등)에 직접 연결되는 구조로, floating-gate 또는 charge-trap 기반 셀에 전하를 주입·방출해 0/1 상태를 저장한다. 프로그래밍은 ISPP(Incremental Step Pulse Programming) 방식으로 미세한 펄스를 누적 인가하고, 소거는 블록 단위 FN 터널링으로 이뤄진다. 제어 경로는 CPU/DMA가 요청을 넣으면 플래시 컨트롤러 → 차지펌프 → 셀 어레이 → 검증(ECC/프로그램-베리파이) 순서로 흐르는 파이프라인이다.

메모리 맵 측면에서는 코드 실행(XIP)을 지원하고, 버스 대기 사이클 조절과 캐시·프리패치 엔진(ART/IFC 등)으로 실행 성능을 보완한다. 단일 뱅크 구성도 있지만 듀얼 뱅크로 만들면 런타임 중 한쪽 영역만 업데이트할 수 있다. 섹터 크기는 보통 16256KB, 프로그램 단위는 8256B 선에서 결정된다.

내부적으로는 차지펌프가 고전압을 만들고, ISPP로 미세 펄스를 누적한 뒤 베리파이 루프를 돈다 — 과도전압이나 과소주입을 막는 로직이 여기 포함된다. 소거는 항상 섹터 전역 단위라, 전원이 떨어지면 도중에 중단될 위험이 있다. 그래서 전압 모니터(BOR)와 인터럽트 관리가 필수다.

신뢰성은 ECC와 보호 비트에서 나온다

eFlash는 SECDED ECC를 내장해 1비트 오류는 자동 정정하고 2비트 오류는 검출한다. 리텐션 감쇠나 소프트 에러에 대응하는 것도 이 ECC의 역할이다. 여기에 읽기/쓰기/소거 보호 비트, RDP(Readout Protection), OTP(One-Time Programmable) 영역이 더해지는데, 이 조합이 보안 부트의 루트키를 저장하기에 적합한 이유다.

수명·품질 특성을 보면 내구성(endurance)은 10k100k P/E 사이클, 데이터 보존(retention)은 1020년(85°C 기준 10년 수준)이다. 고온·고전압 스트레스에는 민감하다. 프로그램 처리량은 0.12 MB/s, 섹터 소거는 50500 ms 정도이며 디바이스와 전압·온도 조건에 따라 변동한다. 운영 중에는 에러/상태 플래그, 타임아웃, 버스 오류를 반환하므로 실패 시 재시도나 블록 격리 전략을 세워야 하고, 공장 캘리브레이션 데이터·트림 값은 별도 전용 영역에 보관한다.

쓰기 요청이 들어오면 벌어지는 일

사용 가능보호 위반/바쁨검증 성공검증 실패임계 초과입력: CPU/DMA 쓰기요청(주소, 데이터, 길이)플래시 컨트롤러 상태확인(바쁨 여부, 잠금/보호비트)차지펌프 활성화 프로그램시퀀스(ISPP)에러 반환(바쁨, 보호 위반),재시도/대기프로그램-베리파이 루프(ECC포함)상태 플래그 클리어, 완료이벤트재시도 N회, 섹터 격리/마킹에러 리포트리맵(웨어레벨링/저널링 계층)출력: 성공 상태 통계(지연,재시도 횟수)

실제로 어디에 쓰이나

펌웨어 저장과 OTA 업데이트가 가장 흔한 용도다. 이중 뱅크·슬롯 구조로 활성 이미지와 대기 이미지를 분리해두면, 업데이트 도중 전원이 나가도 롤백 부트로더로 복구할 수 있다. 이미지 무결성은 해시·서명 검증으로, 읽기 보호는 부트락·RDP로 보장한다.

보안 키·OTP 데이터 관리도 중요한 축이다. 루트 오브 트러스트, 디바이스 ID, 라이선스 토큰을 OTP나 보호 섹터에 저장하고 접근 제어·디버그 락과 연계한다. 키 롤오버는 버전드 슬롯과 폐기 리스트(CRL)로 처리한다.

데이터 로깅·EEPROM 에뮬레이션 용도로는 littlefs·TFS 같은 저널링 NVM 파일시스템이나 키-값 로그 구조를 써서 쓰기 증폭을 최소화하고 웨어 레벨링으로 핫스팟을 분산시킨다. 원자성은 CRC+태그+버전 메타데이터로 보장하고, 전원 장애에 대비한 커밋 레코드를 둔다.

기대되는 효과

부품 구성이 단순해지는 게 첫 번째 이점이다. 외장 메모리 부품을 제거하면 BOM이 12개 줄고 PCB 면적도 520% 수준 감소한다. 성능 면에서는 XIP로 코드 접근 지연이 최소화되고, 캐시·프리패치를 활용하면 루프 성능이 1.2~2.0배 향상된다. 신뢰성 측면에서는 ECC로 단일 비트 오류가 자동 정정되고, 로그 구조를 적용하면 전원 장애 후에도 데이터 일관성을 99.99% 수준까지 확보할 수 있다. 전력은 외장 인터페이스를 비활성화하는 만큼 유휴 전력이 수 mW 수준 줄어든다.

구현할 때 신경 써야 할 것들

레이아웃은 부트로더, 앱 슬롯 A/B, NVM(로그/설정), OTP/보호 영역을 구분하고 섹터 경계에 맞춰 설계해야 한다. 듀얼 뱅크 디바이스라면 실행-중-프로그램(RWW)이 가능하지만, 단일 뱅크는 IAP 도중 실행할 루틴을 RAM에 미리 확보해둬야 한다.

일관성·무결성은 저널링 파일시스템이나 페이지 로그 구조로, prepare → write → verify → commit 순서의 원자적 커밋 절차를 지켜야 한다. CRC와 ECC를 이중으로 걸고, 전원이 떨어질 때를 대비해 BOR 임계값을 올리고 커밋 전 파워굿 신호를 확인한다.

성능·지연 최적화는 캐시·프리패치 설정과 대기 사이클 튜닝, 대량 프로그래밍 시 DMA 사용으로 처리한다. 섹터 소거를 백그라운드에서 미리 해두면(프리이레이즈) 쓰기 지연 스파이크를 완화할 수 있다.

수명·환경 관리는 P/E 사이클을 웨어 레벨링으로 고르게 분산시키고 핫 섹터를 감시하는 게 기본이다. 85°C를 넘는 고온 환경에서는 리텐션 디레이팅을 적용하고, 장기 보존 데이터는 1~3년 주기로 리프레시하는 정책을 두는 게 좋다.

보안은 RDP·TrustZone·MPU를 결합해 실행/읽기를 보호하고 펌웨어 서명 검증과 디버그 포트 잠금을 병행한다. 키는 OTP나 물리적으로 분리된 보호 섹션에 저장하고, 키 파생(KDF)으로 평문 저장을 피한다.

eFlash vs 외장 NOR vs EEPROM

항목 eFlash(온칩) 외장 NOR(QSPI) EEPROM(I2C/SPI)
성능 XIP 가능, 읽기 지연 최소. 프로그램 0.12 MB/s, 소거 50500 ms/섹터 XIP 가능하나 버스 대역 의존, 페이지 프로그램 수백 KB/s~수 MB/s 바이트/페이지 쓰기, 수십~수백 KB/s 수준
확장성 용량 고정(수백 KB~수십 MB) 대용량 확장 용이(수 MB~수백 MB) 중소용량(수 KB~수 MB)
일관성 큰 소거 단위, 저널링 필요 유사 제약, 파일시스템 요구 바이트 단위 쓰기, 비교적 단순
안정성 ECC 내장, 보호 비트/OTP 제공 ECC 미포함이 일반적, 상위계층 필요 높은 endurance(최대 1M 사이클)
운영 편의 외장 부품 불필요, 단순 통합 핀/전력/드라이버 필요 인터페이스 간단, 드라이버 경량

섹터 소거·프로그래밍·검증을 코드로 보면

ARM Cortex-M 계열(듀얼 뱅크 지원)과 arm-none-eabi-gcc, MCU HAL 드라이버를 전제로 한다. 동일 뱅크에서 실행 중인 코드는 해당 뱅크를 소거·프로그램할 수 없으므로 인터럽트를 마스킹하거나 벡터를 다른 메모리로 재배치해야 한다.

// 환경: C99, MCU HAL 가정(함수명은 실제 MCU에 맞게 조정)
// 책임: 섹터 소거 → 프로그래밍 → ECC 기반 검증 → 오류 처리
#include <stdint.h>
#include <string.h>

#define FLASH_OK 0
#define FLASH_ERR -1

int flash_erase_sector(uint32_t sector_addr);
int flash_program(uint32_t dst, const uint8_t* src, uint32_t len); // len은 프로그래밍 최소 단위 정렬
int flash_read(uint32_t src, uint8_t* dst, uint32_t len);

int flash_write_with_verify(uint32_t addr, const uint8_t* data, uint32_t len) {
    // 1) 섹터 정렬/범위 확인
    // 2) 필요 시 사전 소거
    if (flash_erase_sector(addr) != FLASH_OK) return FLASH_ERR;

    // 3) 프로그램
    if (flash_program(addr, data, len) != FLASH_OK) return FLASH_ERR;

    // 4) 검증(읽기 비교)
    uint8_t buf[256];
    uint32_t off = 0;
    while (off < len) {
        uint32_t chunk = (len - off) > sizeof(buf) ? sizeof(buf) : (len - off);
        if (flash_read(addr + off, buf, chunk) != FLASH_OK) return FLASH_ERR;
        if (memcmp(buf, data + off, chunk) != 0) return FLASH_ERR;
        off += chunk;
    }
    return FLASH_OK;
}

프로그램 최소 단위(예: 16B/32B)와 주소 정렬을 지켜야 하고, 섹터 소거는 반드시 섹터 경계에서만 수행한다. IAP 중에는 ISR을 RAM에 배치하거나 인터럽트를 일시 비활성화하고, 전원 강하에 대비해 BOR 임계값을 올려둔다. 실패하면 재시도 횟수를 제한하고, 불량 섹터는 마킹 후 리맵한다.

eFlash임베디드 메모리MCU비휘발성 메모리ECC