Author Topic: Flash Writing Routine Fails Approximately 10% of the Time on CH32V203 MCU  (Read 1201 times)

0 Members and 1 Guest are viewing this topic.

Offline ali_asadzadehTopic starter

  • Super Contributor
  • ***
  • Posts: 2139
  • Country: ca
Hello everyone,
I'm working on a function to write data to the flash memory of a CH32V203 microcontroller, effectively emulating EEPROM behavior. Unfortunately, the routine fails about 10% of the time, resulting in unsuccessful writes or data corruption.
I've included the relevant C code below for reference. Could someone please review it and point out what I might be doing wrong? Any insights into potential issues like timing, interrupt handling, or MCU-specific flash constraints would be greatly appreciated.
Note that I'm using FREERTOS

the write function
Code: [Select]
uint8_t save_to_flash(void *data, size_t size, uint32_t flash_address) {
    const uint8_t *src = (const uint8_t *)data;
    uint8_t erase_ok = 1;
    uint8_t program_ok = 1;
    uint8_t verify_ok = 1;
    // Validate alignment and size
/* ------------------- Parameter validation ------------------- */
    if ((flash_address & 1) || (size & 1) || size == 0) {
        return 0;
    }

    // disables interrupts + preemption
    taskENTER_CRITICAL();
    // Unlock the flash for programming
    FLASH_Unlock();

    /* Wait for any previous operation to finish */
    while (FLASH_GetFlagStatus(FLASH_FLAG_BSY) == SET);

    /* Clear all possible error flags */
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPTERR | FLASH_FLAG_WRPRTERR | FLASH_FLAG_BSY);

/* ------------------- Erase affected pages ------------------- */
    uint32_t first_page = flash_address & ~(FLASH_PAGE_SIZE - 1);
    uint32_t last_page  = (flash_address + size - 1) & ~(FLASH_PAGE_SIZE - 1);


    for (uint32_t page_addr = first_page; page_addr <= last_page; page_addr += FLASH_PAGE_SIZE) {
        FLASH_Status status = FLASH_ErasePage(page_addr);
        if (status != FLASH_COMPLETE) {
            //printf("Flash erase failed at page 0x%08x\n", page_addr);
            erase_ok = 0;
            break;
        }
        /* Extra safety - wait until really finished */
        while (FLASH_GetFlagStatus(FLASH_FLAG_BSY) == SET);
    }
    if (!erase_ok) {
        FLASH_Lock();
        taskEXIT_CRITICAL();
        return 0;
    }

/* ------------------- Program data ------------------- */
    for (size_t i = 0; i < size; i += 2)
    {
        /* Reconstruct original half-word (little-endian correct) */
        uint16_t halfword = src[i] | (src[i + 1] << 8);

        /* Extra safety - wait until really finished */
        while (FLASH_GetFlagStatus(FLASH_FLAG_BSY) == SET);
        /* Clear all possible error flags */
        FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPTERR | FLASH_FLAG_WRPRTERR | FLASH_FLAG_BSY);

        FLASH_Status status = FLASH_ProgramHalfWord(flash_address + i, halfword);
        if (status != FLASH_COMPLETE) {
            program_ok = 0;
            break;
        }
        /* Wait until programming of this half-word is finished */
        while (FLASH_GetFlagStatus(FLASH_FLAG_BSY) == SET);

    }

    if (!program_ok) {
        FLASH_Lock();
        taskEXIT_CRITICAL();
        return 0;
    }

/* ------------------- Verify written data ------------------- */
    /* Direct comparison from flash memory - fastest and safest */
    const uint8_t *flash_ptr = (const uint8_t *)flash_address;
   

    for (size_t i = 0; i < size; i++)
    {
        if (flash_ptr[i] != src[i]) {
            verify_ok = 0;
            break;
        }
    }

    FLASH_Lock();
    taskEXIT_CRITICAL();

    return verify_ok;   // true = everything perfect

}

The read function
Code: [Select]
void read_from_flash(void *data, size_t size, uint32_t flash_address) {
    // Simply copy data from flash to the provided buffer
    memcpy(data, (void *)flash_address, size);
}

ASiDesigner, Stands for Application specific intelligent devices
I'm a Digital Expert from 8-bits to 64-bits
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 637
  • Country: be
Re: Flash Writing Routine Fails Approximately 10% of the Time on CH32V203 MCU
« Reply #1 on: February 09, 2026, 08:54:21 pm »
I am not familiar with CH32V MCUs, but usually you may not erase or program the flash bank you are executing from. For that, the function should be located in RAM or other flash bank, if available.

Code: [Select]
    (src[i + 1] << 8)

You are shifting 8-bit quantity 8 bits. This is not right.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Flash Writing Routine Fails Approximately 10% of the Time on CH32V203 MCU
« Reply #2 on: February 10, 2026, 01:51:48 am »
Code: [Select]
    (src[i + 1] << 8)

You are shifting 8-bit quantity 8 bits. This is not right.

In C it's automatically already a 32 bit int before you shift it (or any other arithmetic).

RISC-V doesn't have any arithmetic instructions on 8 bit values anyway, but even on x86 or m68k etc a byte arithmetic operation (e.g. using AL rather than EAX) will only ever be used if the compiler can prove that the result will be the same as doing a 32 bit operation -- which as you point out, it would not be.

Also partial register updates tend to be bad on modern µarches so compilers avoid them unless required to.

Obviously, substitute 16 bits for 32 on 8086-80286, AVR, 6502, z80, MSP430, some m68k compilers etc.
« Last Edit: February 10, 2026, 02:00:06 am by brucehoult »
 

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1061
Re: Flash Writing Routine Fails Approximately 10% of the Time on CH32V203 MCU
« Reply #3 on: February 10, 2026, 03:59:55 am »
I don't use a CH32, and this may not be related to your problems, but-

unless you are planning to always write to addresses that start at a page boundary (doesn't appear to be so), I would think you would have to backup any preceding partial page before the page erase, then restore the partial page in the process of writing the new data.

8 bytes per page as an example-
write 4 bytes [0,1,2,3] to address 0-
[xxxxxxxx] erase page at address (0 bitand ~(8-1)) = 0
[0123xxxx] write data at address 0

write 4 bytes [4,5,6,7] to address 4-
[xxxxxxxx] erase page at address (4 bitand ~(8-1)) = 0
[xxxx4567] write data at address 4

the previous 4 bytes are now gone



Quote
resulting in unsuccessful writes or data corruption
Since you have multiple reasons for failure, you may want to return more values than 0 or 1 to narrow down what in particular is failing in your function. Enums of course work good for that kind of thing.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

-->