From 151d1565d6b2fd0f66235d2bb3bbea106dd5f980 Mon Sep 17 00:00:00 2001 From: coffee Date: Sat, 6 Dec 2025 17:36:52 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=AE=8C=E6=88=90Flash=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/HFlashMem.h | 80 ++++++++++++ src/HFlashMem.c | 309 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 389 insertions(+) create mode 100644 include/HFlashMem.h create mode 100644 src/HFlashMem.c diff --git a/include/HFlashMem.h b/include/HFlashMem.h new file mode 100644 index 0000000..9bb1f91 --- /dev/null +++ b/include/HFlashMem.h @@ -0,0 +1,80 @@ +/** + * 日期: 2025-12-05 + * 作者: coffee + * 描述: Flash内存映射, 使用脏页机制加速Flash + */ + +#ifndef __H_FLASH_MEM_H__ +#define __H_FLASH_MEM_H__ + + +#include + + +// 使用定时器任务ID +#ifndef HFLASH_TIMER_ID +#define HFLASH_TIMER_ID 0 +#endif + +// Flash内存大小, 需要4k倍数 +#ifndef HFLASH_MEM_SIZE +#define HFLASH_MEM_SIZE (4096 * 3) +#endif + +///< 以下IO指令必须实现 +enum eHFlashMemIO +{ + kHFlashMemIOErase, ///< 请求该地址擦除, 异步非阻塞(addr, cmd) + kHFlashMemIOCheckErase, ///< 检查是否已擦除完成(cmd, uint8_t *status) status设置为0:未擦除, 1:已擦除 + kHFlashMemIOSyncErase, ///< 请求该地址擦除, 同步阻塞到完成(addr, cmd) +}; + +///< 内存缓存页, 配置需和 (memSize / 4096) 大小一致 +typedef struct HFlashMemCache +{ + uint32_t addr; ///< 地址 + uint16_t offset; ///< 内存偏移 + uint16_t eraseStatus : 2; ///< 擦除状态 + uint16_t isDirty : 1; ///< 是否脏页 +} HFlashMemCache; + +///< 内存操作, 回调返回0表示失败 +typedef struct HFlashMemOpts +{ + uint8_t (*read)(uint32_t addr, void *buf, uint32_t len); ///< 读取Flash + uint8_t (*write)(uint32_t addr, const void *buf, uint32_t len); ///< 写入Flash + uint8_t (*ioctl)(uint32_t aadr, uint32_t cmd, void *arg); ///< 控制Flash + uint8_t *mem; ///< Flash映射内存, 需要4k倍数 + HFlashMemCache *cache; ///< 内存缓存 + uint16_t memSize; ///< Flash映射内存大小 + uint16_t cacheSize; ///< 内存缓存大小 +} HFlashMemOpts; + +/** + * @brief 初始化Flash内存映射 + * @param opts 内存操作信息 + */ +void HFlashMemInit(HFlashMemOpts *opts); + +/** + * @brief 读取Flash + * @param addr 读取地址 + * @param buf 读取数据写入缓存的内存 + * @param len 读取长度 + */ +uint8_t HFlashMemRead(uint32_t addr, void *buf, uint32_t len); + +/** + * @brief 写入Flash + * @param addr 写入地址 + * @param buf 写入数据 + * @param len 写入长度 + */ +uint8_t HFlashMemWrite(uint32_t addr, const void *buf, uint32_t len); + +/** + * @brief 同步脏页写入Flash + */ +void HFlashMemSync(); + +#endif // __H_FLASH_MEM_H__ diff --git a/src/HFlashMem.c b/src/HFlashMem.c new file mode 100644 index 0000000..8984656 --- /dev/null +++ b/src/HFlashMem.c @@ -0,0 +1,309 @@ + +#include "HFlashMem.h" +#include "HDLog.h" +#include "HTimer.h" + + +#ifdef USE_STD_MEM +#define FATAL_ERROR(format, ...) LogE(format, ##__VA_ARGS__); +#else +#define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); }; +#endif + +// flash块大小 +#define HFLASH_BLOCK_SIZE (4096) + +// 是否对齐页 +#define IS_4K(size) ((size & (HFLASH_BLOCK_SIZE - 1)) == 0) + +enum eErase +{ + kEraseWait, ///< 等待擦除 + kEraseStart, ///< 擦除开始 + kWaitWrite, ///< 擦除结束, 等待写入数据 +}; + +struct Info +{ + uint16_t useNum; ///< 缓存已使用数量 + uint16_t needWaitErase : 1; ///< 用于判断是否在擦除 +}; + +// 内存操作 +static HFlashMemOpts *sOpts; + +// 信息 +static struct Info sInfo; + +// 检查定时器 +static HTimer_t sCheckTimer = HTIMER_INVALID; + +// 对齐页地址, 向下取整 +static inline uint32_t Align4K(uint32_t addr) { + return addr & ~(HFLASH_BLOCK_SIZE - 1); +} + +// 查找映射表 +static inline uint8_t *GetMMap(uint16_t offset) { + return (uint8_t *)sOpts->mem + offset * HFLASH_BLOCK_SIZE; +} + +static void SyncCache() +{ + // 如果存在异步等待擦除的, 先等擦除完成 + if (sInfo.needWaitErase) { + uint8_t status = 0; + for (;;) { + sOpts->ioctl(0, kHFlashMemIOCheckErase, &status); + if (status) { + break; + } + } + + sInfo.needWaitErase = 0; + } + + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (sOpts->cache[i].isDirty == 0) { + continue; + } + + LogD("Sync[%d], addr[0x%08x], offset[%d]", i, sOpts->cache[i].addr, sOpts->cache[i].offset); + sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOSyncErase, NULL); + sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE); + sOpts->cache[i].isDirty = 0; + sOpts->cache[i].eraseStatus = kEraseWait; + } +} + +static void CheckSyncCache() +{ + // 检查擦除是否完成 + uint8_t status = 0; + if (sInfo.needWaitErase) { + sOpts->ioctl(0, kHFlashMemIOCheckErase, &status); + if (status == 0) { + return ; + } + + sInfo.needWaitErase = 0; + } + + uint16_t needErase = sInfo.useNum; + for (int32_t i = sInfo.useNum - 1; i >= 0; --i) { + // 检查哪个页的异步擦除, 现在擦除完成 + if (sOpts->cache[i].eraseStatus == kEraseStart) { + sOpts->cache[i].eraseStatus = kWaitWrite; + } + + if (needErase == sInfo.useNum) { + // 寻找需要擦除的页 + if (sOpts->cache[i].isDirty && sOpts->cache[i].eraseStatus == kEraseWait) { + needErase = i; + } + } + + // 如果不是脏页就跳过 + if (sOpts->cache[i].isDirty == 0) { + continue; + } + + // 检查Flash是否已经擦除完成 + if (sOpts->cache[i].eraseStatus != kWaitWrite) { + continue; + } + + sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE); + sOpts->cache[i].isDirty = 0; + sOpts->cache[i].eraseStatus = kEraseWait; + } + + if (needErase != sInfo.useNum) { + sInfo.needWaitErase = 1; + sOpts->cache[needErase].eraseStatus = kEraseStart; + sOpts->ioctl(sOpts->cache[needErase].addr, kHFlashMemIOErase, NULL); + } + + if (sInfo.needWaitErase == 0) { + if (sCheckTimer != HTIMER_INVALID) { + HTimerRemove(sCheckTimer); + sCheckTimer = HTIMER_INVALID; + } + } +} + +static void StartSyncCache() +{ + if (sCheckTimer != HTIMER_INVALID) { + HTimerRemove(sCheckTimer); + sCheckTimer = HTIMER_INVALID; + } + + sCheckTimer = HTimerAdd(HFLASH_TIMER_ID, 1, CheckSyncCache, kHTimerLoop); +} + +static HFlashMemCache *FindCache(uint32_t addr, uint8_t create) +{ + addr = Align4K(addr); + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (addr != sOpts->cache[i].addr) { + continue; + } + + return sOpts->cache + i; + } + + if (create == 0) { + return NULL; + } + + // 如果缓存没有满, 直接增加 + if (sInfo.useNum < sOpts->cacheSize) { + const uint16_t index = sInfo.useNum; + ++sInfo.useNum; + if (sOpts->read(addr, GetMMap(index), HFLASH_BLOCK_SIZE) == 0) { + LogE("addr[0x%08x], read faild", addr); + return NULL; + } + + memset(sOpts->cache + index, 0, sizeof(HFlashMemCache)); + sOpts->cache[index].addr = addr; + sOpts->cache[index].offset = index; + return sOpts->cache + index; + } + + // 从尾巴开始找旧的并且已经不是脏页的 + for (int32_t i = sInfo.useNum - 1; i > 0; --i) { + if (sOpts->cache[i].eraseStatus || sOpts->cache[i].isDirty) { + continue; + } + + if (sOpts->read(addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) { + LogE("addr[0x%08x], read faild", addr); + return NULL; + } + + memset(sOpts->cache + i, 0, sizeof(HFlashMemCache)); + sOpts->cache[i].addr = addr; + sOpts->cache[i].offset = i; + return sOpts->cache + i; + } + + // 如果在创建模式下, 没有一个空闲的缓存, 就需要先同步脏页, 再获取一个 + HFlashMemSync(); + return FindCache(addr, create); +} + + +static uint8_t WriteData(uint32_t addr, const void *buf, uint32_t len) +{ + uint32_t adjustAddr = Align4K(addr); + HFlashMemCache *cache = FindCache(adjustAddr, 1); + if (cache == NULL) { + LogE("addr[0x%08x], write len[%d] faild", addr, len); + return 0; + } + + // 开启逐步同步 + StartSyncCache(); + + // 找到映射表, 先处理首页 + const uint8_t *bufPtr = (const uint8_t *)buf; + const uint32_t offset = addr - cache->addr; + const uint32_t remain = offset + len > HFLASH_BLOCK_SIZE ? HFLASH_BLOCK_SIZE - offset : len; + + // 先处理首页 + cache->isDirty = 1; + memcpy(GetMMap(cache->offset) + offset, bufPtr, remain); + len -= remain; + bufPtr += remain; + adjustAddr += HFLASH_BLOCK_SIZE; + + for (; len >= HFLASH_BLOCK_SIZE; adjustAddr += HFLASH_BLOCK_SIZE, len -= HFLASH_BLOCK_SIZE, bufPtr += HFLASH_BLOCK_SIZE) { + cache = FindCache(adjustAddr, 1); + if (cache == NULL) { + LogE("addr[0x%08x], write len[%d] faild", adjustAddr, len); + return 0; + } + + cache->isDirty = 1; + memcpy(GetMMap(cache->offset), bufPtr, HFLASH_BLOCK_SIZE); + } + + if (len) { + cache = FindCache(adjustAddr, 1); + if (cache == NULL) { + LogE("addr[0x%08x], write len[%d] faild", adjustAddr, len); + return 0; + } + + cache->isDirty = 1; + memcpy(GetMMap(cache->offset), bufPtr, len); + } + + return 1; +} + +void HFlashMemInit(HFlashMemOpts *opts) +{ + sOpts = opts; + if (sOpts == NULL || sOpts->read == NULL || sOpts->write == NULL || sOpts->ioctl == NULL) { + FATAL_ERROR("opts is null"); + return ; + } + + if (sOpts->mem == NULL || IS_4K(sOpts->memSize) == 0) { + FATAL_ERROR("not 4k align"); + return ; + } + + if (sOpts->cache == NULL || sOpts->cacheSize != sOpts->memSize / HFLASH_BLOCK_SIZE) { + FATAL_ERROR("cache is null"); + return ; + } +} + +uint8_t HFlashMemRead(uint32_t addr, void *buf, uint32_t len) +{ + if (sOpts == NULL) { + return 0; + } + + uint8_t *p = (uint8_t *)buf; + while (len > 0) { + const uint32_t pageAddr = Align4K(addr); + const uint32_t offset = addr - pageAddr; + const uint32_t remain = HFLASH_BLOCK_SIZE - offset; + const uint32_t copyLen = len > remain ? remain : len; + HFlashMemCache *cache = FindCache(pageAddr, 0); + + if (cache) { + memcpy(p, GetMMap(cache->offset) + offset, copyLen); + } else { + if (sOpts->read(addr, p, copyLen) == 0) { + LogE("addr[0x%08x], read faild", addr); + return 0; + } + } + + addr += copyLen; + p += copyLen; + len -= copyLen; + } + + return 1; +} + +uint8_t HFlashMemWrite(uint32_t addr, const void *buf, uint32_t len) +{ + if (sOpts == NULL) { + return 0; + } + + return WriteData(addr, buf, len); +} + +void HFlashMemSync() +{ + SyncCache(); +}