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;
}
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();
}
Top comments (0)