Does sEMMC support wear leveling when accessing flash blocks directly by block number?

I tried writing read and write code for sEMMC based on information obtained from an AI assistant. The resulting code is shown below:

// Read function
void emmc_read(uint32_t block_address, uint32_t num_blocks)
{
    s_cmd_desc.cmd       = NRF_SEMMC_CMD18_READ_MULTIPLE;
    s_cmd_desc.arg       = block_address;   // Change this to read from a different position
    s_cmd_desc.resp_type = NRF_SEMMC_RESP_R1;

    s_transfer.buffer     = s_read_buf;
    s_transfer.block_size = 512;
    s_transfer.num_blocks = num_blocks;     // Variable

    s_config.process_response = NRF_EMMC_RESPONSE_PROC_PROCESS;
    s_config.clk_freq_hz      = 32000000;
    s_config.bus_width        = NRF_SEMMC_BUS_WIDTH_4;

    nrf_semmc_cmd(&m_semmc, &s_cmd_desc, &s_config, &s_transfer, 1, 0);
}

// Write function
void emmc_write(uint32_t block_address, uint32_t num_blocks)
{
    s_cmd_desc.cmd       = NRF_SEMMC_CMD25_WRITE_MULTIPLE;
    s_cmd_desc.arg       = block_address;
    s_cmd_desc.resp_type = NRF_SEMMC_RESP_R1;

    s_transfer.buffer     = s_write_buf;
    s_transfer.block_size = 512;
    s_transfer.num_blocks = num_blocks;

    s_config.process_response = NRF_EMMC_RESPONSE_PROC_PROCESS;
    s_config.clk_freq_hz      = 32000000;
    s_config.bus_width        = NRF_SEMMC_BUS_WIDTH_4;

    nrf_semmc_cmd(&m_semmc, &s_cmd_desc, &s_config, &s_transfer, 1, 0);
}

As you can see, the block address is passed directly as the arg field of the command descriptor (CMD18 for reads, CMD25 for writes). This means every read/write goes to the same specified physical block.

My understanding is that wear leveling typically requires a layer that abstracts away direct block addressing — such as a filesystem (e.g., LittleFS) or a key-value storage system (e.g., ZMS using 32-bit integer IDs, or settings_save_one() using hash keys). These approaches avoid repeatedly writing to the same physical block.

With this direct block addressing approach, it seems like wear leveling would not be functioning. Is this correct? Does sEMMC itself provide any built-in wear leveling, or is it necessary to use a higher-level abstraction layer (such as LittleFS or ZMS) on top of sEMMC to achieve wear leveling?

Any clarification would be appreciated. Thank you.

  • I was able to resolve this myself before receiving a reply.

    The key point I was missing is that the block address specified in the arg field of the sEMMC command descriptor (e.g., CMD18 for reads, CMD25 for writes) is a Logical Block Address (LBA), not a physical address.

    eMMC devices have an internal Flash Translation Layer (FTL) that maintains a mapping table between logical and physical addresses. This FTL transparently handles wear leveling on the device side — so even though the host (MCU) always specifies the same logical block address, the eMMC controller internally distributes writes across different physical blocks.

    This is fundamentally different from internal MCU flash (e.g., NOR flash on nRF52 Series), where wear leveling must be handled explicitly by a software layer such as ZMS or NVS. With eMMC, the device itself takes care of it transparently.

    I hope this helps anyone who has the same question when starting out with sEMMC!

Related