1. 完成Flash内存映射
This commit is contained in:
parent
176709e37d
commit
151d1565d6
80
include/HFlashMem.h
Normal file
80
include/HFlashMem.h
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 日期: 2025-12-05
|
||||
* 作者: coffee
|
||||
* 描述: Flash内存映射, 使用脏页机制加速Flash
|
||||
*/
|
||||
|
||||
#ifndef __H_FLASH_MEM_H__
|
||||
#define __H_FLASH_MEM_H__
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// 使用定时器任务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__
|
||||
309
src/HFlashMem.c
Normal file
309
src/HFlashMem.c
Normal file
@ -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();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user