/* * 模块名称:NAND FTL 适配层(dhara + FatFS diskio 胶水) * 模块功能:实现 dhara NAND HAL 和 FatFS 磁盘 I/O,连接 GD5F2GQ5UE 底层驱动 * 适用平台:STM32F407ZGT6 * 作者:王建锋 * 创建日期:2026-07-20 * 修改记录: * 2026-07-20 王建锋 创建初始版本 */ /* 头文件包含区 */ #include #include "gd5f2gq5ue.h" #include "ff.h" #include "diskio.h" #include "nand.h" #include "map.h" /* FTL 分区起始地址(块 0,偏移 0,占用全部 256MB) */ #define FTL_FATFS_OFFSET 0U /* 调试输出配置 */ #define DBG_TAG "[NAND_FTL]" #include "dbg_log.h" /* 私有宏定义区 */ #define FTL_PAGE_SIZE GD5F_PAGE_SIZE #define FTL_SECTORS_PER_PAGE (FTL_PAGE_SIZE / 512) #define FTL_START_BLOCK (FTL_FATFS_OFFSET / GD5F_BLOCK_SIZE) #define FTL_NUM_BLOCKS (GD5F_TOTAL_BLOCKS - FTL_START_BLOCK) #define FTL_START_PAGE (FTL_START_BLOCK * GD5F_PAGES_PER_BLOCK) /* dhara 逻辑地址到物理地址转换 */ static inline uint32_t ftl_phy_block(dhara_block_t b) { return b + FTL_START_BLOCK; } static inline uint32_t ftl_phy_page(dhara_page_t p) { return p + FTL_START_PAGE; } /* ======================== 私有变量 ======================== */ /* dhara 核心数据结构 */ static struct dhara_nand s_nand; static struct dhara_map s_map; static uint8_t s_page_buf[FTL_PAGE_SIZE]; /* dhara 内部页缓冲区 */ /* 页面缓存(FatFS 读写缓存) */ static uint8_t s_cache_buf[FTL_PAGE_SIZE]; static dhara_sector_t s_cached_lpn; static uint8_t s_cache_dirty; /* 拷贝临时缓冲区 */ static uint8_t s_copy_buf[FTL_PAGE_SIZE]; /* 初始化状态 */ static uint8_t s_initialized; /* ======================== 私有函数声明 ======================== */ static int ftl_flush_cache(void); static int ftl_read_page(dhara_sector_t lpn); /* SPI 原语从 gd5f2gq5ue.h 获取 */ /* ======================== 页面缓存管理 ======================== */ static int ftl_flush_cache(void) { if (!s_cache_dirty) { return GD5F_OK; } dhara_error_t err; if (dhara_map_write(&s_map, s_cached_lpn, s_cache_buf, &err) < 0) { DBG_ERROR("Cache flush failed: LPN=%lu err=%d", s_cached_lpn, err); return GD5F_ERROR; } s_cache_dirty = 0; return GD5F_OK; } static int ftl_read_page(dhara_sector_t lpn) { dhara_error_t err; if (dhara_map_read(&s_map, lpn, s_cache_buf, &err) < 0) { DBG_ERROR("Page read failed: LPN=%lu err=%d", lpn, err); return GD5F_ERROR; } s_cached_lpn = lpn; s_cache_dirty = 0; return GD5F_OK; } /* ======================== dhara NAND HAL ======================== */ int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b) { (void)n; return gd5f2gq5ue_is_block_bad(ftl_phy_block(b)); } void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b) { (void)n; gd5f2gq5ue_mark_block_bad(ftl_phy_block(b)); } int dhara_nand_erase(const struct dhara_nand *n, dhara_block_t b, dhara_error_t *err) { (void)n; int ret = gd5f_block_erase(ftl_phy_block(b)); if (ret == GD5F_ERASE_FAIL || ret != GD5F_OK) { dhara_nand_mark_bad(n, b); dhara_set_error(err, DHARA_E_BAD_BLOCK); return -1; } return 0; } int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p, const uint8_t *data, dhara_error_t *err) { int ret; uint32_t phy_pg = ftl_phy_page(p); gd5f_write_enable(); ret = gd5f_program_load(0, data, FTL_PAGE_SIZE); if (ret != GD5F_OK) { dhara_nand_mark_bad(n, p >> n->log2_ppb); dhara_set_error(err, DHARA_E_BAD_BLOCK); return -1; } ret = gd5f_program_exec(phy_pg); if (ret == GD5F_PROGRAM_FAIL || ret != GD5F_OK) { dhara_nand_mark_bad(n, p >> n->log2_ppb); dhara_set_error(err, DHARA_E_BAD_BLOCK); return -1; } return 0; } int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p) { (void)n; uint32_t i; uint8_t buf[64]; uint32_t phy_pg = ftl_phy_page(p); if (gd5f_page_read(phy_pg) != GD5F_OK) { return 0; } gd5f_read_from_cache(0, buf, sizeof(buf)); for (i = 0; i < sizeof(buf); i++) { if (buf[i] != 0xFF) { return 0; } } return 1; } int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, size_t length, uint8_t *data, dhara_error_t *err) { (void)n; uint32_t phy_pg = ftl_phy_page(p); if (gd5f_page_read(phy_pg) != GD5F_OK) { dhara_set_error(err, DHARA_E_ECC); return -1; } if (gd5f_check_ecc() != GD5F_OK) { dhara_set_error(err, DHARA_E_ECC); return -1; } gd5f_read_from_cache((uint16_t)offset, data, length); return 0; } int dhara_nand_copy(const struct dhara_nand *n, dhara_page_t src, dhara_page_t dst, dhara_error_t *err) { (void)n; if (dhara_nand_read(n, src, 0, FTL_PAGE_SIZE, s_copy_buf, err) < 0) { return -1; } if (dhara_nand_prog(n, dst, s_copy_buf, err) < 0) { return -1; } return 0; } /* ======================== FatFS 磁盘 I/O ======================== */ DSTATUS disk_initialize(BYTE pdrv) { if (pdrv != 0) { return STA_NOINIT; } if (s_initialized) { return 0; } DBG_INFO("Initializing NAND FTL..."); s_nand.log2_page_size = 11; s_nand.log2_ppb = 6; /* dhara 可见块数 = 总块数 - 保留块池(BBT 存储区不交给 dhara,避免被擦写) */ s_nand.num_blocks = gd5f_get_usable_blocks(); dhara_map_init(&s_map, &s_nand, s_page_buf, 4); memset(s_cache_buf, 0, FTL_PAGE_SIZE); s_cached_lpn = (dhara_sector_t)-1; /* -1 表示缓存未加载;LPN 0 合法,不能用 0 作哨兵 */ s_cache_dirty = 0; { dhara_error_t err; if (dhara_map_resume(&s_map, &err) < 0) { DBG_INFO("No valid map found, creating fresh (err=%d)", err); dhara_map_clear(&s_map); } else { DBG_INFO("Map resumed: %lu/%lu sectors used", dhara_map_size(&s_map), dhara_map_capacity(&s_map)); } } s_initialized = 1; DBG_INFO("NAND FTL ready (capacity: %lu pages = %lu MB)", dhara_map_capacity(&s_map), (dhara_map_capacity(&s_map) * FTL_PAGE_SIZE) / (1024 * 1024)); return 0; } int nand_ftl_init(void) { DSTATUS sta = disk_initialize(0); return (sta == 0) ? 0 : -1; } void nand_ftl_deinit(void) { /* 复位 FTL 状态,使下次 nand_ftl_init() 真正重新走 dhara_map_resume, 用于测试"掉电恢复"与"坏块注入后重建"场景(模拟设备重启) */ s_initialized = 0; s_cached_lpn = (dhara_sector_t)-1; s_cache_dirty = 0; } DSTATUS disk_status(BYTE pdrv) { if (pdrv != 0) { return STA_NOINIT; } if (!s_initialized) { return STA_NOINIT; } return 0; } DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) { if (pdrv != 0 || !buff) { return RES_PARERR; } if (!s_initialized && disk_initialize(pdrv) != 0) { return RES_NOTRDY; } while (count > 0) { dhara_sector_t lpn = (dhara_sector_t)(sector / FTL_SECTORS_PER_PAGE); uint32_t offset = (uint32_t)(sector % FTL_SECTORS_PER_PAGE) * 512; uint32_t batch = FTL_SECTORS_PER_PAGE - (sector % FTL_SECTORS_PER_PAGE); if (batch > count) { batch = count; } if (lpn != s_cached_lpn) { if (ftl_flush_cache() != GD5F_OK) { return RES_ERROR; } if (ftl_read_page(lpn) != GD5F_OK) { return RES_ERROR; } } memcpy(buff, s_cache_buf + offset, batch * 512); buff += batch * 512; sector += batch; count -= (UINT)batch; } return RES_OK; } DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) { if (pdrv != 0 || !buff) { return RES_PARERR; } if (!s_initialized && disk_initialize(pdrv) != 0) { return RES_NOTRDY; } while (count > 0) { dhara_sector_t lpn = (dhara_sector_t)(sector / FTL_SECTORS_PER_PAGE); uint32_t offset = (uint32_t)(sector % FTL_SECTORS_PER_PAGE) * 512; uint32_t batch = FTL_SECTORS_PER_PAGE - (sector % FTL_SECTORS_PER_PAGE); if (batch > count) { batch = count; } if (lpn != s_cached_lpn) { if (ftl_flush_cache() != GD5F_OK) { return RES_ERROR; } if (ftl_read_page(lpn) != GD5F_OK) { return RES_ERROR; } } memcpy(s_cache_buf + offset, buff, batch * 512); s_cache_dirty = 1; if (offset == 0 && batch == FTL_SECTORS_PER_PAGE) { if (ftl_flush_cache() != GD5F_OK) { return RES_ERROR; } } buff += batch * 512; sector += batch; count -= (UINT)batch; } return RES_OK; } DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) { if (pdrv != 0) { return RES_PARERR; } if (!s_initialized) { return RES_NOTRDY; } switch (cmd) { case CTRL_SYNC: if (ftl_flush_cache() != GD5F_OK) { return RES_ERROR; } { dhara_error_t err; if (dhara_map_sync(&s_map, &err) < 0) { DBG_ERROR("Map sync failed: %d", err); return RES_ERROR; } } return RES_OK; case GET_SECTOR_COUNT: { LBA_t *p_sectors = (LBA_t *)buff; dhara_sector_t pages = dhara_map_capacity(&s_map); *p_sectors = pages * FTL_SECTORS_PER_PAGE; return RES_OK; } case GET_SECTOR_SIZE: *(WORD *)buff = 512; return RES_OK; case GET_BLOCK_SIZE: *(DWORD *)buff = 1; return RES_OK; default: return RES_PARERR; } } /* ======================== 公共函数定义 ======================== */ int nand_ftl_format(void) { dhara_error_t err; if (!s_initialized) { if (disk_initialize(0) != 0) { return GD5F_ERROR; } } if (ftl_flush_cache() != GD5F_OK) { return GD5F_ERROR; } dhara_map_clear(&s_map); if (dhara_map_sync(&s_map, &err) < 0) { DBG_ERROR("Map sync after clear failed: %d", err); return GD5F_ERROR; } s_cache_dirty = 0; s_cached_lpn = (dhara_sector_t)-1; DBG_INFO("NAND FTL formatted, capacity: %lu pages", dhara_map_capacity(&s_map)); return GD5F_OK; }