본문 바로가기
Embedded SW/Embedded SW Introduction

Flash Driver 포팅 및 Erase, Write 동작 확인 정리 (TC275 인피니언 MCU)

by 방구석 임베디드 2022. 9. 16.
반응형

안녕하세요.

오늘은 인피니언 MCU TC275를 이용하여 Flash를 Erase, Write 하는 동작을 수행해 보도록 하겠습니다.

먼저 인피니언에서 제공해주는 TC2x Flash Demo 코드를 포팅하여 동작시켜 볼게요!

관심있으신 분들은 추후 TC275보드를 구매하셔서

직접 따라해 보셔도 좋을 것 같습니다.

우선 Demo Code는 아래와 같습니다.

그럼 먼저 포팅을 해볼게요!

휴우 드디어 포팅이 완료 되었습니다.

인피니언 예제는 쉽게 포팅하여 동작시켜볼수 있도록 잘되어 있네요!

그러면 인피니언에서 제공하는 IDE (Aurix Development Studio)를 실행하여

Tasking 컴파일러로 컴파일 해보도록 하겠습니다. 

다행이 Error랑 Warning이 없네요.

그러면 이제 포팅한 Pflash Demo Code를 살펴보겠습니다.

먼저, 초기화 코드를 보도록 하겠습니다.

구조체에 값을 넣어주고 있습니다.

void IfxFlashDemo_init(void)
{
    /* Initialise the P Flash global data */
    g_Pflash.sector        = FLASHDEMO_PFLASH_SECTOR_NO;
    g_Pflash.startPage     = FLASHDEMO_PFLASH_START_PAGE;
    g_Pflash.numberOfPages = FLASHDEMO_PFLASH_NO_OF_PAGES;
    g_Pflash.flashType     = IfxFlash_FlashType_P0;

    /* Initialise the D Flash global data */
    g_Dflash.sector        = FLASHDEMO_DFLASH_SECTOR_NO;
    g_Dflash.startPage     = FLASHDEMO_DFLASH_START_PAGE;
    g_Dflash.numberOfPages = FLASHDEMO_DFLASH_NO_OF_PAGES;
    g_Dflash.flashType     = IfxFlash_FlashType_D0;
}

그리고 copyFlashroutinesToPspr 이라는 함수를 실행시키네요.

그후 Erase, Write를수행하네요.

copyFlashRoutimesToPspr는 딱 한번만 실행하면 될 함수네요!!

static void PFlashDemo(void)
{
    uint32  errors      = 0;
    uint32  offset;
    uint32  page        = 0;
    uint32  flash       = 0;
    uint32  sector_addr = IfxFlash_pFlashTableLog[g_Pflash.sector].start;

    /* disable interrupts */
    boolean interruptState = IfxCpu_disableInterrupts();

    /* Copy the erase and program routines to PSPR memory */
    copyFlashRoutinesToPspr();

    /* erase & program flash (execute from relocated memory)*/
    g_Pflash.command.pFlashErase(flash, sector_addr);
    g_Pflash.command.pFlashProgram(flash, sector_addr, g_Pflash.startPage, g_Pflash.numberOfPages);

    /* enable interrupts again */
    IfxCpu_restoreInterrupts(interruptState);

 

copyFlashRoutimesToPspr 함수를 조금더 살펴 보도록 하겠습니다.

RAM Address에 함수를 복사하고 있습니다.

Flash에 위치한 함수들을 RAM으로 복사하고 

RAM에서 Erase, Write 함수를 부르고 있는것이 보입니다.

우선 한번만 동작하면 되는 함수 복사의 속성을 가지고 있네요!

static void copyFlashRoutinesToPspr(void)
{
    memcpy((void *)FLASHDEMO_ERASESECTOR_ADDR, (const void *)IfxFlash_eraseSector, FLASHDEMO_ERASESECTOR_LEN);
    g_Pflash.command.eraseSector = (void *)FLASHDEMO_RELOC_START_ADDR;

    memcpy((void *)FLASHDEMO_WAITUNBUSY_ADDR, (const void *)IfxFlash_waitUnbusy, FLASHDEMO_WAITUNBUSY_LEN);
    g_Pflash.command.waitUnbusy = (void *)FLASHDEMO_WAITUNBUSY_ADDR;

    memcpy((void *)FLASHDEMO_ENTERPAGEMODE_ADDR, (const void *)IfxFlash_enterPageMode, FLASHDEMO_ENTERPAGEMODE_LEN);
    g_Pflash.command.enterPageMode = (void *)FLASHDEMO_ENTERPAGEMODE_ADDR;

    memcpy((void *)FLASHDEMO_LOADPAGE2X32_ADDR, (const void *)IfxFlash_loadPage2X32, FLASHDEMO_LOADPAGE2X32_LEN);
    g_Pflash.command.loadPage2X32 = (void *)FLASHDEMO_LOADPAGE2X32_ADDR;

    memcpy((void *)FLASHDEMO_WRITEPAGE_ADDR, (const void *)IfxFlash_writePage, FLASHDEMO_WRITEPAGE_LEN);
    g_Pflash.command.writePage = (void *)FLASHDEMO_WRITEPAGE_ADDR;

    memcpy((void *)FLASHDEMO_PFLASHERASE_ADDR, (const void *)PFlashErase, FLASHDEMO_PFLASHERASE_LEN);
    g_Pflash.command.pFlashErase = (void *)FLASHDEMO_PFLASHERASE_ADDR;

    memcpy((void *)FLASHDEMO_PFLASHPROGRAM_ADDR, (const void *)PFlashProgram, FLASHDEMO_PFLASHPROGRAM_LEN);
    g_Pflash.command.pFlashProgram = (void *)FLASHDEMO_PFLASHPROGRAM_ADDR;
}

혹시 이 구조가 보이시나요?

Driver 코드를 포팅하는 과정에서 아주 중요한 구조를 얻었습니다.

바로 구조체안에 함수를 등록하여 사용하는 Atomic한 구조 입니다.

이 구조를 추후, Application에서도 사용해 보도록 하겠습니다.

하지만 무엇보다도,

왜 RAM에 복사를 했는지 아는것이 중요할것 같습니다.

이유는 나중에 말씀드릴게요!

typedef struct
{
/** \brief Function pointers of Erase & program routines which are relocated to different memory (PSPR).
 *  The function pointers are used to execute the relocated routines
 */
    struct
    {
        void (*eraseSector)(uint32);
        uint8 (*waitUnbusy)(uint32, IfxFlash_FlashType);
        uint8 (*enterPageMode)(uint32);
        void (*loadPage2X32)(uint32, uint32, uint32);
        void (*writePage)(uint32);
        void (*pFlashErase)(uint32, uint32);
        void (*pFlashProgram)(uint32, uint32, uint32, uint32);
    }                  command;

    uint32             sector;        /**< \brief Flash sector to be erased & programmed */
    uint32             startPage;     /**< \brief Page from which programming starts */
    uint32             numberOfPages; /**< \brief No of pages to be programmed */
    IfxFlash_FlashType flashType;     /**< \brief Flash type ussed for the erase/program */
} App_Pflash;

이제 어느정도 순서가 보이고 있습니다.

1) 전역변수안에 Flash를 Write하기 위한 설정값들을 넣는다.

2) 구조체안의 함수포인터에 기본적인 Flash Driver (ILLD) 함수를 등록한다.

(copyFlashRoutinesToPspr 함수를 통해 수행)

이 함수는 RAM에 위치해 있다.

3) Demo Api를 이용하여 Erase, Write를 수행한다.

그러면 한번 실행 시켜 보도록 하겠습니다.

Pflash가 어떻게 쓰여지는지 확인해 보도록 하겠습니다.

g_Pflash.sector = FLASHDEMO_PFLASH_SECTOR_NO;

g_Pflash.startPage = FLASHDEMO_PFLASH_START_PAGE;

g_Pflash.numberOfPages = FLASHDEMO_PFLASH_NO_OF_PAGES;

g_Pflash.flashType = IfxFlash_FlashType_P0;

FLASHDEMO_PFLASH_SECTOR_NO : 22

지금 22번 섹터를 Erase Write 하려고 하려고 합니다.

uint32 sector_addr = IfxFlash_pFlashTableLog[g_Pflash.sector].start;

이것을 찾아서 들어가니,

22번 sector는 0xa00e0000을 의미하고 있습니다.

Flash 메모리 주소입니다.

그리고 동작시켜 보도록 하겠습니다.

Trap에 빠졌습니다.

Trap은 exception을 의미하는데요.

 

빠진 위치를 찾아보니 Flash에 Write를 수행하고

나중에 제대로 잘 쓰여졌는지 확인하는 코드에서 나오고 있습니다.

우선 중요한 부분이 아니니, 잠시 막도록 하겠습니다.

#if 0
    /* Verify the programmed data */
    for (page = g_Pflash.startPage; page < g_Pflash.numberOfPages; ++page)
    {
        uint32          pageAddr = sector_addr + page * FLASHDEMO_PFLASH_PAGE_LENGTH;
        volatile uint8 *addr     = (uint8 *)pageAddr;

        for (offset = 0; offset < FLASHDEMO_PFLASH_PAGE_LENGTH; offset += 8)
        {
            if (!((pageAddr == *((uint32 *)(addr + offset))) &&
                  (addr[offset + 4] == offset)))
            {
                errors++;
            }
        }
    }
    #endif

잠시 아래 함수에서 API 부분의 일부분을 수정하도록 하겠습니다.

수정 포인트는uint32_t* pDataAddress를 추가하여서,

From 주소에서 To 주소로 Write를 하도록 조금더 명시적으로 써준 것입니다.

 

From 주소가 보이지 않아서 조금 편하게 API를 사용하려고 수정했습니다.

ㅎㅎ

static void PFlashProgram(uint32 flash, uint32 sector_addr, uint32 start_page, uint32 no_of_pages, uint32_t* pDataAddress)
{
    uint32 offset;
    uint32 page;
    uint16 endinitSfty_pw;
    uint32_t temp = 0u;

    endinitSfty_pw = IfxScuWdt_getSafetyWatchdogPasswordInline();

    temp = 0u;

    /* program the given no of pages */
    for (page = start_page; page < no_of_pages; ++page)
    {
        uint32 pageAddr = sector_addr + page * FLASHDEMO_PFLASH_PAGE_LENGTH;
        g_Pflash.command.enterPageMode(pageAddr);

        /* wait until unbusy */
        g_Pflash.command.waitUnbusy(flash, g_Pflash.flashType);

        /* write 32 bytes (8 doublewords) into assembly buffer */
        for (offset = 0; offset < FLASHDEMO_PFLASH_PAGE_LENGTH; offset += 8)
        {
            g_Pflash.command.loadPage2X32(pageAddr, pDataAddress[temp], pDataAddress[temp+1]);
            temp += 2u;
        }

        /* write page */
        IfxScuWdt_clearSafetyEndinitInline(endinitSfty_pw);
        g_Pflash.command.writePage(pageAddr);
        IfxScuWdt_setSafetyEndinitInline(endinitSfty_pw);

        /* wait until unbusy */
        g_Pflash.command.waitUnbusy(flash, g_Pflash.flashType);
    }
}

위의 함수에서 4번에 걸쳐서 8바이트씩 logPage2x32 함수를 타네요!

그렇다면 32바이트가 써지겠군요!

/* write 32 bytes (8 doublewords) into assembly buffer */
        for (offset = 0; offset < FLASHDEMO_PFLASH_PAGE_LENGTH; offset += 8)
        {
            g_Pflash.command.loadPage2X32(pageAddr, pDataAddress[temp], pDataAddress[temp+1]);
            temp += 2u;
        }

그리고 아래와같이 32바이트가 써지는 것을 확인가능하네요!

참고로 0x800E0000과 0xA00E0000 영역은 동일하게 써지는 영역입니다.

쓸때는 0xA00E0000 영역만 쓰더라도 0x800E0000영역도 mirror로 동일하게 써지게 됩니다.

이건 인피니언 MCU의 구조인데요

0x800E0000영역은 캐시를 사용할때 CPU가 접근하는 영역이고

0xA00E0000 영역은 캐시를 사용하지 않을때 CPU가 접근하는 영역입니다.

레퍼런스 메뉴얼을 참고하시면 될 것 같습니다.

참고로 처음에 Wirte를 하시면

아마도 써지지 않을 것입니다.

지금 RAM 아까 함수를 복사하는 과정에서

Offset 주소가 작아서 그렇습니다.

그래서 영역을 2배로 늘려서 동작시켜보도록 하겠습니다.

RAM으로 복사를 하는 과정에서

충분한 크기의 memcopy가 필요하네요.

 

하지만 컴파일 최적화 옵션에 따라서

ILLD Driver의 크기가 달라지기 때문에

지금 우리가 사용하는 환경에서는 크기가 너무 작게 복사가 되어

함수가 온전히 복사가 되지 않았던것 같습니다.

#define FLASHDEMO_ERASESECTOR_LEN    (200)
#define FLASHDEMO_WAITUNBUSY_LEN     (200)
#define FLASHDEMO_ENTERPAGEMODE_LEN  (200)
#define FLASHDEMO_LOADPAGE2X32_LEN   (200)
#define FLASHDEMO_WRITEPAGE_LEN      (200)
#define FLASHDEMO_PFLASHERASE_LEN    (420)
#define FLASHDEMO_PFLASHPROGRAM_LEN  (600)

그리고 #define FLASHDEMO_RELOC_START_ADDR (0xC0000000U) /**< \brief Program & Erase routines relocation address */

이러한 설정이 있는데

저는 지금 이 함수를 PRAM(program ram)에 올리려고 하고

Program Ram의 위치는 0x70100000 입니다.

이 RAM의 영역은 함수를 복사하여 사용하는 곳으로

이 RAM영역에서 코드를 돌리면 조금더 빠르게 함수를 동작시킬수 있는 영역이라고 하네요!

그런데 보면

0xC0000000영역에 쓰고 있습니다.

하지만 실제로 이것도 mirror 영역이여서

0x70100000 에 써지는 것을 확인하실 수 있습니다.

조금더 자세한 내용은 초기화코드 분석에서 다루도록 하겠습니다.

우선 포팅을 하고 동작하는 과정에서 필요한 추가 코드를 한번 넣어보았습니다.

만일 32바이트 + 32바이트

이렇게 64바이트를 쓰려면 어떻게 해야할까요?

여기 부분을 2로 수정하면

2번 써지게 됩니다.

그런데 API가 많이 불편하네요.

이제 초기화 코드를 조금더 자세히 분석해 보고,

조금더 편하게 사용할 수 있도록

한번 수정을 해보아야 겠습니다!

해당 포팅 코드는 우선 아래 링크에서 다운로드 가능합니다.

https://cafe.naver.com/binaryembedded/146

 

53. Flash Erase Write 포팅 및 동작 확인 정리 (Program Flash)

우선 인피니언 TC2x Flash Demo 를 한번 포팅해 보도록 하겠습니다. 예제는 아래 FlashDemo를 사용하였습니다. 휴우 우선 포팅을 하였습니다. 인피니언 예제는 ...

cafe.naver.com

오늘 포스팅은 이것으로 마무리 하도록 하겠습니다.

반응형

댓글