DEV Community

hk0816
hk0816

Posted on

BiCS FLASH memory controller wear leveling function

This is sample code for wear-leveling functionality for BiCS FLASH (3D NAND) controllers, along with automated test code using Unity. It complies with the Ministry of Economy, Trade and Industry's IPA's "ESCR [C language version] Ver. 3.0 (Code Practice Guide for Embedded Software)" and incorporates CERT C coding conventions and defensive programming (strict NULL checks, thorough use of const modifiers, elimination of magic numbers, and use of fixed-length integer types).

Here, we implement a dynamic wear leveling mechanism that selects the free block with the fewest erase cycles (P/E cycle count) (the least worn) to prevent uneven distribution during writing and erasing.


1. Sample code (implementation of BiCS wear leveling function)

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

/* Constant definition (elimination of magic numbers) */
#define BICS_WL_TOTAL_BLOCKS       (8U)
#define BICS_WL_INVALID_BLOCK_ID   (0xFFFFFFFFU)

/* Block wear status metadata */
typedef struct {
    uint32_t erase_count; /* Erasure count (P/E cycle counter) */
    bool is_free;         /* free block flag */
} bics_block_wear_t;

/* Wear leveling management context */
typedef struct {
    bics_block_wear_t blocks[BICS_WL_TOTAL_BLOCKS];
} bics_wear_leveling_t;

/**
 * @brief  Initialization of the wear leveling management module
 * @note   ESCR compliant: NULL checking for pointer arguments, fixed-length integer types, and elimination of magic numbers.
 */
bool bics_wl_init(bics_wear_leveling_t* const wl) {
    if (wl == NULL) {
        return false;
    }

    for (uint32_t i = 0U; i < BICS_WL_TOTAL_BLOCKS; i++) {
        wl->blocks[i].erase_count = 10U; /* Initial number of erase cycles (example) */
        /* For example, let's use indices 6 and 7 as initial free blocks. */
        wl->blocks[i].is_free = (i >= 6U); 
    }
    return true;
}

/**
 * @brief  Select the empty block with the fewest erases (the least worn).
 *         (Core processing of dynamic wear leveling)
 * @note   ESCR compliant: Robust input validation and safe scanning.
 */
uint32_t bics_wl_select_free_block(const bics_wear_leveling_t* const wl) {
    if (wl == NULL) {
        return BICS_WL_INVALID_BLOCK_ID;
    }

    uint32_t selected_block = BICS_WL_INVALID_BLOCK_ID;
    uint32_t min_erase_count = 0xFFFFFFFFU;

    for (uint32_t i = 0U; i < BICS_WL_TOTAL_BLOCKS; i++) {
        if (wl->blocks[i].is_free) {
            if (wl->blocks[i].erase_count < min_erase_count) {
                min_erase_count = wl->blocks[i].erase_count;
                selected_block = i;
            }
        }
    }

    return selected_block;
}

/**
 * @brief  The erase count (P/E counter) is incremented when block erasure is complete.
 * @note   ESCR compliant: Boundary value checking and argument validation
 */
bool bics_wl_increment_erase_count(bics_wear_leveling_t* const wl, uint32_t block_id) {
    if (wl == NULL) {
        return false;
    }
    if (block_id >= BICS_WL_TOTAL_BLOCKS) {
        return false;
    }

    wl->blocks[block_id].erase_count++;
    return true;
}

/**
 * @brief  Change the block's empty state.
 */
bool bics_wl_set_block_free_status(bics_wear_leveling_t* const wl, uint32_t block_id, bool is_free) {
    if (wl == NULL) {
        return false;
    }
    if (block_id >= BICS_WL_TOTAL_BLOCKS) {
        return false;
    }

    wl->blocks[block_id].is_free = is_free;
    return true;
}

Enter fullscreen mode Exit fullscreen mode

2. Automated test code using Unity

#include "unity.h"

static bics_wear_leveling_t wl_inst;

/* Unity standard setup */
void setUp(void) {
    bool init_res = bics_wl_init(&wl_inst);
    TEST_ASSERT_TRUE(init_res);
}

void tearDown(void) {
    /* Cleanup as needed */
}

/* Test Case 1: Verification that the empty block with the fewest erase counts is correctly selected (Dynamic Wear Leveling) */
void test_bics_wl_select_free_block_min_wear(void) {
    /* Leave blocks 6 and 7 empty to create a difference in the number of times they are cleared. */
    wl_inst.blocks[6].is_free = true;
    wl_inst.blocks[6].erase_count = 50U; /* A lot of wear */

    wl_inst.blocks[7].is_free = true;
    wl_inst.blocks[7].erase_count = 10U; /* Low wear (this is the one to choose) */

    uint32_t selected = bics_wl_select_free_block(&wl_inst);

    TEST_ASSERT_EQUAL_UINT32(7U, selected);
}

/* Test Case 2: Boundary Value Test in the Case Where No Empty Blocks Exist */
void test_bics_wl_select_free_block_no_free(void) {
    /* Make all blocks empty. */
    for (uint32_t i = 0U; i < BICS_WL_TOTAL_BLOCKS; i++) {
        wl_inst.blocks[i].is_free = false;
    }

    uint32_t selected = bics_wl_select_free_block(&wl_inst);

    TEST_ASSERT_EQUAL_UINT32(BICS_WL_INVALID_BLOCK_ID, selected);
}

/* Test Case 3: Verification of ESCR-compliant defensive programming (NULL safety and out-of-range indexes) and erasure counters. */
void test_bics_wl_safety_and_increment(void) {
    /* Checking the safety of NULL pointers */
    TEST_ASSERT_EQUAL_UINT32(BICS_WL_INVALID_BLOCK_ID, bics_wl_select_free_block(NULL));
    TEST_ASSERT_FALSE(bics_wl_increment_erase_count(NULL, 0U));
    TEST_ASSERT_FALSE(bics_wl_set_block_free_status(NULL, 0U, true));

    /* Checking for guards against out-of-range indexes */
    TEST_ASSERT_FALSE(bics_wl_increment_erase_count(&wl_inst, BICS_WL_TOTAL_BLOCKS));
    TEST_ASSERT_FALSE(bics_wl_set_block_free_status(&wl_inst, BICS_WL_TOTAL_BLOCKS, true));

    /* Verification of normal increment process */
    uint32_t initial_count = wl_inst.blocks[0].erase_count;
    bool inc_res = bics_wl_increment_erase_count(&wl_inst, 0U);

    TEST_ASSERT_TRUE(inc_res);
    TEST_ASSERT_EQUAL_UINT32(initial_count + 1U, wl_inst.blocks[0].erase_count);
}

/* test runner */
int main(void) {
    UNITY_BEGIN();

    RUN_TEST(test_bics_wl_select_free_block_min_wear);
    RUN_TEST(test_bics_wl_select_free_block_no_free);
    RUN_TEST(test_bics_wl_safety_and_increment);

    return UNITY_END();
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)