본문 바로가기
mobilgene/NvM

[NvM] NvM 데이터를 실제로 어떻게 쓰는가 (초기화 코드로 보는 사용 방법)

by Autosar 2026. 4. 24.
반응형

NvM을 처음 사용할 때 가장 많이 하는 실수가 있다.

“ReadBlock으로 데이터를 읽었으니 바로 사용하면 된다”

하지만 실제 시스템에서는 이 방식이 매우 위험하다.

 

실제 ECU에서는 데이터를 읽기 전에 “사용 가능한 상태인지”를 먼저 판단해야 한다.

NvM 데이터는 다음과 같은 문제가 발생할 수 있기 때문이다.

 

- 이전 소프트웨어 버전 데이터일 수 있음

- 저장 중 전원 OFF로 데이터 일부 손상

- CRC 오류 발생 가능

- 아직 NvM Job이 완료되지 않은 상태일 수 있음

 

그래서 실무에서는 단순히 데이터를 읽는 것이 아니라 “정상 데이터를 확보하는 과정”이 반드시 포함된다.

 

초기화 시 NvM 처리 흐름

 

ECU Power On 시 NvM Block을 사용하는 일반적인 흐름은 다음과 같다.

ReadBlock 요청
          ↓
Job 완료 대기 (Polling 또는 Callback)
          ↓
GetErrorStatus 확인 (CRC 결과 포함)
          ↓
데이터 검증 (Version / Magic Number)
          ↓
정상 → 사용
비정상 → Reset + 재저장

 

※ 본 예제는 NvM ReadAll 또는 이전 ReadBlock이 이미 수행된 상태를 전제로 한다.

이 흐름을 코드로 보면 다음과 같다.

 

실무 코드 예제

 

초기화 판단 함수

void App_NvM_Init_NvMBlockConfig(void)
{
    uint8 syncCall;
    NvM_RequestResultType RequestResultPtr;

    /* 1. NvM 상태 확인 */
    syncCall = Rte_Call_App_NvMBlock_NvMBlockConfig_GetErrorStatus(&RequestResultPtr);

    if ((syncCall == E_OK) && (RequestResultPtr == NVM_REQ_OK))
    {
        /* 2. NvM 데이터 읽기 */
        (void)Rte_Call_App_NvMBlock_NvMBlockConfig_ReadBlock(&AppNvMBlockConfig);

        /* 3. Version 기반 검증 */
        if (AppNvMBlockConfig.headerVersion != NVM_BLOCK_CONFIG_VERSION)
        {
            App_NvM_Init_NvMBlockConfig_Reset();
        }
        else
        {
            AppNvMBlockConfigChange = FALSE;
        }
    }
    else
    {
        /* 4. CRC 오류 또는 상태 이상 */
        App_NvM_Init_NvMBlockConfig_Reset();
    }
}

 

Reset + 재저장 함수

void App_NvM_Init_NvMBlockConfig_Reset(void)
{
    uint8 syncCall;
    NvM_RequestResultType RequestResultPtr;

    syncCall = Rte_Call_App_NvMBlock_NvMBlockConfig_GetErrorStatus(&RequestResultPtr);

    /* NvM Job 진행 중이면 복구 지연 */
    if ((syncCall == E_OK) && (RequestResultPtr != NVM_REQ_PENDING))
    {
        /* 1. Default 값으로 RAM 초기화 */
        NvM_memcpy((uint8*)&AppNvMBlockConfig,
                   (uint8*)&NvMBlockConfig_Default,
                   (uint16)sizeof(AppNvMBlockConfig));

        /* 2. NvM 저장용 버퍼 동기화 */
        NvM_memcpy((uint8*)&AppNvMBlockConfig_Nv,
                   (uint8*)&NvMBlockConfig_Default,
                   (uint16)sizeof(AppNvMBlockConfig_Nv));

        /* 3. NvM 재저장 요청 */
        (void)Rte_Call_App_NvMBlock_NvMBlockConfig_WriteBlock(&AppNvMBlockConfig_Nv);
    }

    AppNvMBlockConfigChange = TRUE;
}

 

단계별 동작 설명

 

이 코드는 단순히 NvM 데이터를 읽는 코드가 아니라

“정상 데이터만 시스템에 반영하기 위한 필터 구조”이다.

 

각 단계의 의미를 보면 구조가 명확해진다.

 

5.1 GetErrorStatus → CRC 기반 1차 검증

syncCall = Rte_Call_App_NvMBlock_NvMBlockConfig_GetErrorStatus(&RequestResultPtr);

 

이 단계는 단순 상태 확인이 아니다.

NvM에서 CRC 옵션이 활성화되어 있으면:

CRC 정상 → NVM_REQ_OK
CRC 실패 → NVM_REQ_INTEGRITY_FAILED

 

즉, 이 단계에서 데이터 무결성에 대한 1차 검증이 이루어진다.

 

5.2 왜 NVM_REQ_OK만 허용하는가

if ((syncCall == E_OK) && (RequestResultPtr == NVM_REQ_OK))

 

이 조건은 꽤 강한 필터다.

Pending → 아직 준비 안됨 → 사용 금지
Integrity Failed → 데이터 깨짐 → 사용 금지
Not OK → Job 실패 → 사용 금지

 

NVM_REQ_OK는 NvM 관점(CRC, Job 결과)에서 정상임을 의미하며,

Application 수준 검증(Version 등)은 별도로 수행해야 한다.

 

5.3 ReadBlock의 역할

Rte_Call_App_NvMBlock_NvMBlockConfig_ReadBlock(&AppNvMBlockConfig);

 

ReadBlock은 NvM 데이터를 RAM으로 복사 요청하는 함수이며,

실제 데이터 반영은 비동기 Job 완료 이후 보장된다.

 

즉, Read 성공 ≠ 데이터 정상

 

5.4 Version 검증 역할

if (AppNvMBlockConfig.headerVersion != NVM_BLOCK_CONFIG_VERSION)

 

이 부분이 실무에서 가장 중요한 검증이다.

CRC는 “데이터가 깨졌는지”만 확인하고 Version은 “이 데이터를 지금 써도 되는지”를 확인한다.

CRC → 데이터 손상 여부 확인
Version → 데이터 구조 호환성 확인

 

예를 들어, SW 업데이트로 구조 변경되거나 필드 추가/삭제가 발생한 경우 CRC는 정상이어도 데이터는 사용할 수 없다.

 

5.5 Reset 처리 의미

App_NvM_Init_NvMBlockConfig_Reset();

 

이 함수는 단순 초기화가 아니라 다음 역할을 한다.

- Default 값으로 RAM 초기화

- NvM WriteBlock 수행

- NV 영역까지 복구

- WriteBlock으로 재저장

 

즉, RAM을 즉시 정상화하고, NV 영역은 WriteBlock을 통해 이후 Job 완료 시 정상 값으로 갱신된다.

 

5.6 else 분기 의미

else
{
    App_NvM_Init_NvMBlockConfig_Reset();
}

 

이 else 분기에는 CRC 오류, Job 실패 등 “데이터를 신뢰할 수 없는 상태”가 포함된다.

단, Pending 상태는 아직 작업이 진행 중인 상태이므로 별도로 대기 처리해야 한다.

 

5.7 상태를 다시 확인

RequestResultPtr != NVM_REQ_PENDING

 

NvM은 Queue 기반 비동기 구조라서 Job 진행 중에 Write 호출하면 충돌할 가능성이 있다.

그래서 Pending 상태에서는 아무 것도 하지 않는다.

 

5.8 RAM 데이터 초기화

NvM_memcpy((uint8*)&AppNvMBlockConfig, (uint8*)&NvMBlockConfig_Default, (uint16)sizeof(AppNvMBlockConfig));

 

현재 실행 중인 ECU에서 사용할 값을 정상화한다.

 

5.9 NV Write용 데이터 준비

NvM_memcpy((uint8*)&AppNvMBlockConfig_Nv, (uint8*)&NvMBlockConfig_Default, (uint16)sizeof(AppNvMBlockConfig_Nv));

 

NvM은 RAM 포인터를 그대로 쓰지 않는 구조가 많고, Write 전용 버퍼를 따로 두는 경우가 일반적이다.
그래서 RAM과 NV 데이터를 분리해서 관리한다.

 

5.10 WriteBlock

Rte_Call_App_NvMBlock_NvMBlockConfig_WriteBlock(&AppNvMBlockConfig_Nv);

 

단순 초기화가 아니라 Flash / EEPROM까지 정상 값으로 덮어쓴다.

 

왜 Write까지 해야 하는가?

RAM만 초기화하면 현재 실행은 정상 동작한다.

하지만 다음 부팅 시 다시 깨진 데이터를 읽게 된다.

따라서 NV까지 재저장해야 다음 부팅에서도 정상 동작이 보장된다.

 

정리

 

NvM은 단순한 저장소가 아니다.

ECU 상태를 유지하는 신뢰성 기반 데이터 관리 시스템이다.

따라서 NvM 데이터를 사용할 때는 다음 원칙을 반드시 지켜야 한다.

 

1. ReadBlock 이후 바로 사용하지 않는다.

2. GetErrorStatus로 CRC 상태를 확인한다.

3. Pending 상태는 대기 처리한다.

4. 데이터를 검증한다.

5. 이상 시 Default 값으로 복구한다.

6. 필요 시 WriteBlock을 통해 NV 영역까지 정상화하여 다음 부팅에서도 일관성을 유지한다.

 

결국 NvM 사용의 핵심은 하나다.

“데이터를 읽는 것이 아니라, 정상 데이터를 확보하는 것”이다.

반응형