1. 提升Flash模块

This commit is contained in:
coffee 2026-03-26 13:49:08 +08:00
parent fd76d9f346
commit 599e255653
6 changed files with 863 additions and 286 deletions

View File

@ -33,6 +33,11 @@
#define USE_RTOS_LOG_LOCK (0)
#endif
// 是否使用HShell
#ifndef HDLOG_USE_HSHELL
#define HDLOG_USE_HSHELL (1)
#endif
// 标准日志输出
#define _LogDExtLevel(level, format, ...) HDLogOut(1, level, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)
#define _LogDLevel(level, format, ...) HDLogOut(0, level, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)

View File

@ -11,6 +11,11 @@
#include <stdint.h>
// 是否使用 hshell
#ifndef HFLASH_USE_HSHELL
#define HFLASH_USE_HSHELL 1
#endif
// 使用定时器任务ID
#ifndef HFLASH_TIMER_ID
#define HFLASH_TIMER_ID 0
@ -25,13 +30,17 @@ enum eHFlashMemIO
};
///< 内存缓存页, 配置需和 (memSize / 4096) 大小一致
///< 这里不再使用 heat 衰减, 改成 referenced + probation 的二次机会策略
///< 旧方案需要周期性降温, 小缓存且写入频繁时维护成本高, 也更容易把一次性读页长期留在缓存里
typedef struct HFlashMemCache
{
uint32_t addr; ///< 地址
uint16_t offset; ///< 内存偏移
uint16_t eraseStatus : 2; ///< 擦除状态
uint16_t isDirty : 1; ///< 是否脏页
uint16_t heat : 5; ///< 热度优先级
uint16_t referenced : 1; ///< 最近是否访问过, 仅在命中/装载时置1, 淘汰扫描时清0给第二次机会
uint16_t probation : 1; ///< 新进入缓存的试用页, 只有再次命中后才转正式页, 这样能优先淘汰一次性访问
uint16_t reserved : 11;
} HFlashMemCache;
///< 内存操作, 回调返回0表示失败
@ -42,14 +51,16 @@ typedef struct HFlashMemOpts
uint8_t (*ioctl)(uint32_t addr, uint32_t cmd, void *arg); ///< 控制Flash, 需要外部实现以上eHFlashMemIO指令
uint8_t *mem; ///< Flash映射内存, 需要4k倍数
HFlashMemCache *cache; ///< 内存缓存
uint16_t memSize; ///< Flash映射内存大小
uint16_t cacheSize; ///< 内存缓存大小
uint32_t memSize; ///< Flash映射内存大小, 改成32位后可避免映射区超过64KB时页数计算溢出
uint32_t cacheSize; ///< 内存缓存页数量, 需要始终等于 memSize / 4096, 否则 offset 映射会错位
uint32_t flashSize; ///< Flash大小
} HFlashMemOpts;
/**
* @brief Flash内存映射
* @param opts
* mem/cache ,
* cacheSize memSize / 4096 , cache 4K映射页
*/
void HFlashMemInit(HFlashMemOpts *opts);

View File

@ -12,6 +12,11 @@
#include <stdint.h>
// 使用HShell
#ifndef HFLASH_USE_HSHELL
#define HFLASH_USE_HSHELL 1
#endif
///< 使用Flash内存映射功能
#ifndef HFLASH_NOT_USE_FLASH_MEM
#define HFLASH_USE_FLASH_MEM
@ -85,6 +90,12 @@ typedef union HFlashPageInfo {
};
} HFlashPageInfo;
typedef struct HFlashProtectInfo {
HFlashAddr_t addr; ///< 保护区地址
uint32_t size; ///< 保护区大小
HFlashAddr_t backupAddr; ///< 保护区备份地址
} HFlashProtectInfo;
typedef struct HFlashCacheInfo {
HFlashPageInfo info; ///< 页表信息
uint32_t pos : 19; ///< 存储页表信息的位置
@ -149,6 +160,14 @@ uint16_t HFlashGetVersion(HFlashAddr_t addr, HFlashVersionInfo *info);
*/
void HFlashSetPageCache(HFlashCacheInfo *pageInfo, uint16_t size);
/**
* @brief HFlashSetProtectCache , ,
*
* @param protectInfo
* @param size
*/
void HFlashSetProtectCache(HFlashProtectInfo *protectInfo, uint16_t size);
/**
* @brief HFlashSetPageAddr
* @param addr
@ -158,24 +177,31 @@ void HFlashSetPageAddr(HFlashAddr_t addr, uint32_t size);
/**
* @brief HFlashSetPageBackupAddr ,
* ,
* @param addr
* @param size ,
* @param size , 4K对齐
*/
void HFlashSetPageBackupAddr(HFlashAddr_t addr, uint32_t size);
/**
* @brief HFlashSetProtectAddr , 使
* @param addr
* @param size
* @brief HFlashClearProtectAddr
*/
void HFlashSetProtectAddr(HFlashAddr_t addr, uint32_t size);
void HFlashClearProtectAddr();
/**
* @brief HFlashSetProtectBackupAddr ,
* @brief HFlashAddProtectAddr ,
* HFlashSetProtectCache
* @param addr
* @param size ,
* @param size
* @param backupAddr ,
*/
void HFlashSetProtectBackupAddr(HFlashAddr_t addr, uint32_t size);
void HFlashAddProtectAddr(HFlashAddr_t addr, uint32_t size, HFlashAddr_t backupAddr);
/**
* @brief HFlashDelProtectAddr
* @param addr
*/
void HFlashDelProtectAddr(HFlashAddr_t addr);
/**
* @brief HFlashInitCheck HFlashRegister前需要初始化检查, ,

View File

@ -2,12 +2,15 @@
#include "HDLog.h"
#include "HBit.h"
#include "HShellLex.h"
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#if HDLOG_USE_HSHELL
#include "HShellLex.h"
#endif
#if USE_RTOS_LOG_LOCK
#include "FreeRTOS.h"
#include "semphr.h"
@ -39,6 +42,7 @@ typedef struct HDLogFpgaCheckInfo
#define CHECK_FPGA_INFO_ID (100)
#endif
#if HDLOG_USE_HSHELL
static HShellMatch sLogMatch[] = {
HSHELL_MATCH_ITEM(kLogLevelSwitch, "log"),
HSHELL_MATCH_ITEM(kLogLevelColor, "logColor"),
@ -54,6 +58,7 @@ static HShellMatch sLogMatch[] = {
HSHELL_MATCH_ITEM(CHECK_FPGA_INFO_ID + 1, "clearCheckFpga"),
#endif
};
#endif
///< 日志级别
static HBIT_DEFINE(sLogItem, kLogLevelMax);
@ -184,6 +189,7 @@ static void LogHex(const uint8_t *data, int len, uint8_t checkPrint, uint8_t log
}
}
#if HDLOG_USE_HSHELL
static void LogShell(HSHELL_FUNC_ARGS)
{
uint32_t arg1 = 0;
@ -211,6 +217,7 @@ static void LogShell(HSHELL_FUNC_ARGS)
sFlashCall(1, key, arg1);
}
}
#endif
void HDLogSetOptFlashCall(HDLogFlashOpt_t call)
{
@ -234,7 +241,9 @@ void HDLogOptFlashInit()
void HDLogInit(uint32_t (*getTime)())
{
sGetTime = getTime;
#if HDLOG_USE_HSHELL
HSHellRegister(sLogMatch, sizeof(sLogMatch) / sizeof(HShellMatch), LogShell, 1);
#endif
if (sFlashCall == NULL)
{
HBitSet(sLogItem, kLogLevelSwitch, 0);

View File

@ -3,34 +3,31 @@
#include "HDLog.h"
#include "HTimer.h"
#ifndef USE_STD_MEM
#if HFLASH_USE_HSHELL
#include "HShellLex.h"
#endif
#endif
#ifdef USE_STD_MEM
#define FATAL_ERROR(format, ...) LogE(format, ##__VA_ARGS__);
#else
#include "HShellLex.h"
#elif HFLASH_USE_HSHELL
#define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); HShellRun(); };
#else
#define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); };
#endif
// flash块大小
#define HFLASH_BLOCK_SIZE (4096)
// 调度热度时间
#define HEAT_TIME_MS (1000)
// 是否对齐页
#define IS_4K(size) ((size & (HFLASH_BLOCK_SIZE - 1)) == 0)
// 最大热度
#define MAX_HEAT ((1 << 5) - 1)
// 写转读增加热度
#define WRITE_CONV_READ_ADD_HEAT (2)
// 读最小热度
#define READ_MIN_HEAT (0)
// 快速最小值
#define FAST_MIN(type, a, b) ((b) ^ (((a) ^ (b)) & (-(type)((a) < (b)))))
// 这里允许少量读 miss 直接绕过缓存
// 当缓存已被脏页占满时, 如果每次只读 miss 都强制抢占缓存, 会触发同步刷写, 小缓存场景抖动很大
// 因此只有同一页连续被直读多次, 才认为它值得挤进缓存
// 全脏页下, 同一页连续直读达到该次数后才提升进缓存
#define BYPASS_PROMOTE_THRESHOLD (3)
enum eErase
{
@ -41,9 +38,13 @@ enum eErase
struct Info
{
uint32_t currHeatTime; ///< 当前热度时间, 用于计算下一次热度下降
uint32_t bypassAddr; ///< 全脏页读 miss 时, 最近一次直读页地址
uint16_t useNum; ///< 缓存已使用数量
uint16_t readClockHand; ///< 读页淘汰时的时钟指针
uint16_t dirtyClockHand; ///< 脏页回收时的时钟指针
uint8_t bypassCount; ///< 当前直读页连续命中次数
uint16_t needWaitErase : 1; ///< 用于判断是否在擦除
uint16_t bypassValid : 1; ///< 最近一次直读页地址是否有效
};
// 内存操作
@ -55,6 +56,9 @@ static struct Info sInfo;
// 检查定时器
static HTimer_t sCheckTimer = HTIMER_INVALID;
static void WatiErase();
static void CheckSyncCache();
// 对齐页地址, 向下取整
static inline uint32_t Align4K(uint32_t addr) {
return addr & ~(HFLASH_BLOCK_SIZE - 1);
@ -65,6 +69,110 @@ static inline uint8_t *GetMMap(uint16_t offset) {
return (uint8_t *)sOpts->mem + offset * HFLASH_BLOCK_SIZE;
}
static inline uint8_t IsReusableCleanCache(const HFlashMemCache *cache)
{
return cache->isDirty == 0 && cache->eraseStatus == kEraseWait;
}
static void TouchCache(HFlashMemCache *cache)
{
// 这里同时清 probation
// 新页首次装入时先标记为“试用页”, 只有再次命中才认为它真的是热点, 避免一次性读页长期占坑
cache->referenced = 1;
cache->probation = 0;
}
static void InitCache(HFlashMemCache *cache, uint16_t index, uint32_t addr, uint8_t probation)
{
memset(cache, 0, sizeof(HFlashMemCache));
cache->addr = addr;
cache->offset = index;
cache->referenced = 1;
cache->probation = probation;
}
static void MarkCleanCache(HFlashMemCache *cache)
{
// 脏页刷回后会被重新当作试用页
// 刚完成回写只代表它“被写过”, 不代表后续仍然高频访问, 立即降为 probation 能避免旧脏页长期压制新页
cache->isDirty = 0;
cache->eraseStatus = kEraseWait;
cache->referenced = 0;
cache->probation = 1;
}
static void ClearBypassAddr(uint32_t addr)
{
if (sInfo.bypassValid && sInfo.bypassAddr == addr) {
sInfo.bypassValid = 0;
sInfo.bypassCount = 0;
}
}
static void RecordBypassAddr(uint32_t addr)
{
if (sInfo.bypassValid && sInfo.bypassAddr == addr) {
if (sInfo.bypassCount < BYPASS_PROMOTE_THRESHOLD) {
++sInfo.bypassCount;
}
return;
}
sInfo.bypassAddr = addr;
sInfo.bypassValid = 1;
sInfo.bypassCount = 1;
}
static uint8_t HasDirtyCache()
{
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (sOpts->cache[i].isDirty) {
return 1;
}
}
return 0;
}
static HFlashMemCache *FindCacheByAddr(uint32_t addr)
{
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (addr == sOpts->cache[i].addr) {
return sOpts->cache + i;
}
}
return NULL;
}
static uint8_t LoadCachePage(HFlashMemCache *cache, uint32_t addr)
{
WatiErase();
if (sOpts->read(addr, GetMMap(cache->offset), HFLASH_BLOCK_SIZE) == 0) {
LogE("addr[0x%08x], read faild", addr);
return 0;
}
return 1;
}
static uint8_t SyncDirtyCache(HFlashMemCache *cache)
{
if (cache == NULL || cache->isDirty == 0) {
return cache != NULL;
}
WatiErase();
sOpts->ioctl(cache->addr, kHFlashMemIOSyncErase, NULL);
if (sOpts->write(cache->addr, GetMMap(cache->offset), HFLASH_BLOCK_SIZE) == 0) {
LogE("addr[0x%08x], write faild", cache->addr);
return 0;
}
MarkCleanCache(cache);
return 1;
}
static void WatiErase()
{
// 如果存在异步等待擦除的, 先等擦除完成
@ -90,66 +198,145 @@ static void SyncCache()
}
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);
if (sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) {
LogD("Sync[%d], addr[0x%08x], offset[%d] faild", i, sOpts->cache[i].addr, sOpts->cache[i].offset);
--i;
continue;
if (SyncDirtyCache(sOpts->cache + i) == 0) {
LogE("Sync[%d], addr[0x%08x], offset[%d] faild", i, sOpts->cache[i].addr, sOpts->cache[i].offset);
}
}
}
static HFlashMemCache *FindCleanVictim()
{
HFlashMemCache *fallback = NULL;
if (sInfo.useNum == 0) {
return NULL;
}
// 两轮时钟扫描:
// 1. referenced=1 的页先清位, 给第二次机会, 不立刻淘汰
// 2. 优先淘汰 probation 页, 因为它们还没被二次命中过, 更可能只是一次性访问
// 3. 如果没有 probation 页, 再回退到普通 clean 页
for (uint8_t round = 0; round < 2; ++round) {
for (uint16_t count = 0; count < sInfo.useNum; ++count) {
const uint16_t index = sInfo.readClockHand;
HFlashMemCache *cache = sOpts->cache + index;
sInfo.readClockHand = (index + 1) % sInfo.useNum;
if (IsReusableCleanCache(cache) == 0) {
continue;
}
if (cache->referenced) {
cache->referenced = 0;
continue;
}
if (cache->probation) {
return cache;
}
fallback = cache;
}
sOpts->cache[i].isDirty = 0;
sOpts->cache[i].eraseStatus = kEraseWait;
// 写入页转换空闲页, 热度持续增加
sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + WRITE_CONV_READ_ADD_HEAT, MAX_HEAT);
if (fallback) {
return fallback;
}
}
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (IsReusableCleanCache(sOpts->cache + i)) {
return sOpts->cache + i;
}
}
return NULL;
}
static HFlashMemCache *FindDirtyVictim()
{
HFlashMemCache *fallback = NULL;
if (sInfo.useNum == 0) {
return NULL;
}
// 脏页选择也走 clock, 不再依赖 heat
// 这里只挑 eraseStatus=kEraseWait 的脏页, 避免和正在擦除或等待写回的页并发冲突
// 当前底层 ioctl 的擦除状态是全局查询模型, 同一时刻只维护一个异步擦除流程更安全
for (uint8_t round = 0; round < 2; ++round) {
HFlashMemCache *candidate = NULL;
for (uint16_t count = 0; count < sInfo.useNum; ++count) {
const uint16_t index = sInfo.dirtyClockHand;
HFlashMemCache *cache = sOpts->cache + index;
sInfo.dirtyClockHand = (index + 1) % sInfo.useNum;
if (cache->isDirty == 0 || cache->eraseStatus != kEraseWait) {
continue;
}
fallback = cache;
if (cache->referenced) {
cache->referenced = 0;
continue;
}
candidate = cache;
}
if (candidate) {
return candidate;
}
}
return fallback;
}
static void NotifyASyncErase()
{
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (sOpts->cache[i].isDirty == 0) {
continue;
}
HFlashMemCache *cache = FindDirtyVictim();
if (cache) {
// 这里只发起一个异步擦除请求
// CheckSyncCache 依赖 needWaitErase + eraseStatus 顺序推进状态机, 多个并发擦除会让状态归属变复杂
sInfo.needWaitErase = 1;
sOpts->cache[i].eraseStatus = kEraseStart;
sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOErase, NULL);
break;
cache->eraseStatus = kEraseStart;
sOpts->ioctl(cache->addr, kHFlashMemIOErase, NULL);
}
}
static void FastSyncCache()
static void StartSyncCache()
{
WatiErase();
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
// 存在空闲页, 可以立刻解除
if (sOpts->cache[i].isDirty == 0) {
break;
}
LogD("FastSync[%d], addr[0x%08x], offset[%d]", i, sOpts->cache[i].addr, sOpts->cache[i].offset);
sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOSyncErase, NULL);
if (sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) {
LogD("FastSync[%d], addr[0x%08x], offset[%d] faild", i, sOpts->cache[i].addr, sOpts->cache[i].offset);
--i;
continue;
}
sOpts->cache[i].isDirty = 0;
sOpts->cache[i].eraseStatus = kEraseWait;
// 写入页转换空闲页, 热度持续增加
sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + WRITE_CONV_READ_ADD_HEAT, MAX_HEAT);
break;
if (HasDirtyCache() == 0) {
return;
}
NotifyASyncErase();
if (sCheckTimer != HTIMER_INVALID) {
return;
}
sCheckTimer = HTimerAdd(HFLASH_TIMER_ID, 0, CheckSyncCache, kHTimerLoop);
}
static HFlashMemCache *ReclaimOneCleanCache()
{
HFlashMemCache *cache = FindCleanVictim();
if (cache) {
return cache;
}
cache = FindDirtyVictim();
if (cache == NULL) {
return NULL;
}
// 这里退化成同步回收一个脏页
// 写路径必须拿到可用 cache, 如果此时全是脏页, 只能牺牲一次阻塞把一个页刷回, 否则无法继续分配
LogD("Reclaim addr[0x%08x], offset[%d]", cache->addr, cache->offset);
if (SyncDirtyCache(cache) == 0) {
return NULL;
}
return cache;
}
static void CheckSyncCache()
{
// 检查擦除是否完成
uint8_t status = 0;
if (sInfo.needWaitErase) {
sOpts->ioctl(0, kHFlashMemIOCheckErase, &status);
@ -158,63 +345,44 @@ static void CheckSyncCache()
}
sInfo.needWaitErase = 0;
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (sOpts->cache[i].eraseStatus == kEraseStart) {
sOpts->cache[i].eraseStatus = kWaitWrite;
break;
}
}
}
uint16_t needErase = sInfo.useNum;
uint8_t heat = MAX_HEAT;
uint8_t waitWrite = 0;
// 这里先处理 kWaitWrite, 再决定是否继续提交新的擦除
// 写回完成后才能把页重新放回 clean 集合, 否则 read/write 侧会一直看不到可回收页
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
// 检查哪个页的异步擦除, 现在擦除完成
if (sOpts->cache[i].eraseStatus == kEraseStart) {
sOpts->cache[i].eraseStatus = kWaitWrite;
}
waitWrite |= sOpts->cache[i].isDirty;
// 寻找需要擦除的页
if (sOpts->cache[i].isDirty && sOpts->cache[i].eraseStatus == kEraseWait) {
// 查找最低热度的优先擦除, 方便低热度页转换空闲
if (sOpts->cache[i].heat <= heat) {
heat = sOpts->cache[i].heat;
needErase = i;
}
}
// 如果不是脏页就跳过
if (sOpts->cache[i].isDirty == 0) {
HFlashMemCache *cache = sOpts->cache + i;
if (cache->isDirty == 0 || cache->eraseStatus != kWaitWrite) {
continue;
}
// 检查Flash是否已经擦除完成
if (sOpts->cache[i].eraseStatus != kWaitWrite) {
if (sOpts->write(cache->addr, GetMMap(cache->offset), HFLASH_BLOCK_SIZE) == 0) {
LogE("addr[0x%08x], write faild", cache->addr);
cache->eraseStatus = kEraseWait;
continue;
}
// 如果写入失败, 则需要再次擦除重新写入
if (sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) {
LogE("addr[0x%08x], write faild", sOpts->cache[i].addr);
sInfo.needWaitErase = 1;
sOpts->cache[i].eraseStatus = kEraseWait;
if (needErase == sInfo.useNum) {
heat = sOpts->cache[i].heat;
needErase = i;
}
continue;
}
sOpts->cache[i].isDirty = 0;
sOpts->cache[i].eraseStatus = kEraseWait;
// 写入页转换空闲页, 热度持续增加
sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + WRITE_CONV_READ_ADD_HEAT, MAX_HEAT);
MarkCleanCache(cache);
}
if (needErase != sInfo.useNum) {
sInfo.needWaitErase = 1;
sOpts->cache[needErase].eraseStatus = kEraseStart;
sOpts->ioctl(sOpts->cache[needErase].addr, kHFlashMemIOErase, NULL);
uint8_t hasPendingWrite = 0;
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (sOpts->cache[i].isDirty && sOpts->cache[i].eraseStatus == kWaitWrite) {
hasPendingWrite = 1;
break;
}
}
if (sInfo.needWaitErase == 0 && waitWrite == 0) {
if (sInfo.needWaitErase == 0 && hasPendingWrite == 0 && HasDirtyCache()) {
NotifyASyncErase();
}
if (sInfo.needWaitErase == 0 && hasPendingWrite == 0 && HasDirtyCache() == 0) {
if (sCheckTimer != HTIMER_INVALID) {
HTimerRemove(sCheckTimer);
sCheckTimer = HTIMER_INVALID;
@ -222,67 +390,7 @@ static void CheckSyncCache()
}
}
static void StartSyncCache()
{
if (sCheckTimer != HTIMER_INVALID) {
HTimerRemove(sCheckTimer);
sCheckTimer = HTIMER_INVALID;
}
sCheckTimer = HTimerAdd(HFLASH_TIMER_ID, 0, CheckSyncCache, kHTimerLoop);
}
static void ScheduleHeat()
{
if (HDLogGetTime() - sInfo.currHeatTime < HEAT_TIME_MS) {
return;
}
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (sOpts->cache[i].eraseStatus || sOpts->cache[i].isDirty) {
continue;
}
// 仅对空闲页降温
sOpts->cache[i].heat >>= 1;
}
sInfo.currHeatTime = HDLogGetTime();
}
static HFlashMemCache *FindMinHeatCache()
{
int16_t index = -1;
uint8_t minHeat = MAX_HEAT;
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (sOpts->cache[i].eraseStatus || sOpts->cache[i].isDirty) {
continue;
}
// 寻找最小热度
const uint8_t currHeat = sOpts->cache[i].heat;
if (index == -1) {
index = i;
minHeat = currHeat;
continue;
}
if (currHeat > minHeat) {
continue;
}
index = i;
minHeat = currHeat;
}
if (index == -1) {
return NULL;
}
return sOpts->cache + index;
}
static HFlashMemCache *FindReadCache(uint32_t addr, uint8_t needRead)
static HFlashMemCache *FindReadCache(uint32_t addr, uint8_t needRead, uint8_t allowBypass)
{
addr = Align4K(addr);
if (addr + HFLASH_BLOCK_SIZE > sOpts->flashSize) {
@ -290,54 +398,56 @@ static HFlashMemCache *FindReadCache(uint32_t addr, uint8_t needRead)
return NULL;
}
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (addr != sOpts->cache[i].addr) {
continue;
}
sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + 1, MAX_HEAT);
return sOpts->cache + i;
HFlashMemCache *cache = FindCacheByAddr(addr);
if (cache) {
ClearBypassAddr(addr);
TouchCache(cache);
return cache;
}
// 如果缓存没有满, 直接增加
if (sInfo.useNum < sOpts->cacheSize) {
const uint16_t index = sInfo.useNum;
++sInfo.useNum;
cache = sOpts->cache + index;
InitCache(cache, index, addr, 1);
if (needRead) {
WatiErase();
if (sOpts->read(addr, GetMMap(index), HFLASH_BLOCK_SIZE) == 0) {
LogE("addr[0x%08x], read faild", addr);
if (LoadCachePage(cache, addr) == 0) {
return NULL;
}
}
memset(sOpts->cache + index, 0, sizeof(HFlashMemCache));
sOpts->cache[index].addr = addr;
sOpts->cache[index].offset = index;
sOpts->cache[index].heat = FAST_MIN(uint8_t, sOpts->cache[index].heat + 1, MAX_HEAT);
return sOpts->cache + index;
ClearBypassAddr(addr);
++sInfo.useNum;
return cache;
}
// 查找空闲的最低热度页
HFlashMemCache *cache = FindMinHeatCache();
if (cache == NULL || cache->heat > READ_MIN_HEAT) {
return NULL;
}
const uint16_t index = cache->offset;
memset(cache, 0, sizeof(HFlashMemCache));
cache->addr = addr;
cache->offset = index;
cache->heat = READ_MIN_HEAT;
if (needRead) {
WatiErase();
if (sOpts->read(addr, GetMMap(index), HFLASH_BLOCK_SIZE) == 0) {
LogE("addr[0x%08x], read faild", addr);
cache = FindCleanVictim();
if (cache == NULL && allowBypass && needRead) {
// bypass 只允许真正的读 miss
// 普通读接口可以接受本次直接读 flash, 但像 mmap/callback 这种需要稳定页指针的场景必须强制拿到 cache
RecordBypassAddr(addr);
if (sInfo.bypassCount < BYPASS_PROMOTE_THRESHOLD) {
return NULL;
}
}
if (cache == NULL) {
cache = ReclaimOneCleanCache();
}
if (cache == NULL) {
return NULL;
}
const uint16_t index = cache->offset;
InitCache(cache, index, addr, 1);
if (needRead) {
if (LoadCachePage(cache, addr) == 0) {
return NULL;
}
}
ClearBypassAddr(addr);
return cache;
}
@ -349,38 +459,47 @@ static HFlashMemCache *FindWriteCache(uint32_t addr, uint8_t needRead)
return NULL;
}
HFlashMemCache *cache = FindReadCache(addr, needRead);
HFlashMemCache *cache = FindCacheByAddr(addr);
if (cache) {
ClearBypassAddr(addr);
TouchCache(cache);
return cache;
}
for (;;) {
// 写入模式下, 需要抢占缓存页
cache = FindMinHeatCache();
if (cache == NULL) {
// 如果在创建模式下, 没有一个空闲的缓存, 就需要先同步脏页, 再获取一个
FastSyncCache();
continue;
}
const uint16_t index = cache->offset;
memset(cache, 0, sizeof(HFlashMemCache));
cache->addr = addr;
cache->offset = index;
cache->heat = READ_MIN_HEAT;
if (sInfo.useNum < sOpts->cacheSize) {
const uint16_t index = sInfo.useNum;
cache = sOpts->cache + index;
InitCache(cache, index, addr, 0);
if (needRead) {
WatiErase();
// 找到一个空闲且最低温度的
if (sOpts->read(addr, GetMMap(index), HFLASH_BLOCK_SIZE) == 0) {
LogE("addr[0x%08x], read faild", addr);
if (LoadCachePage(cache, addr) == 0) {
return NULL;
}
}
ClearBypassAddr(addr);
++sInfo.useNum;
return cache;
}
return NULL;
cache = FindCleanVictim();
if (cache == NULL) {
cache = ReclaimOneCleanCache();
}
if (cache == NULL) {
return NULL;
}
{
const uint16_t index = cache->offset;
InitCache(cache, index, addr, 0);
if (needRead) {
if (LoadCachePage(cache, addr) == 0) {
return NULL;
}
}
ClearBypassAddr(addr);
}
return cache;
}
@ -393,9 +512,6 @@ static uint8_t WriteData(uint32_t addr, const void *buf, uint32_t len, void (*cp
return 0;
}
// 开启逐步同步
StartSyncCache();
// 找到映射表, 先处理首页
const uint8_t *bufPtr = (const uint8_t *)buf;
const uint32_t offset = addr - cache->addr;
@ -430,6 +546,8 @@ static uint8_t WriteData(uint32_t addr, const void *buf, uint32_t len, void (*cp
cpCall(GetMMap(cache->offset), bufPtr, len, userData);
}
// 写路径只负责标脏, 后台异步回收逐步推进, 避免小缓存下同步冲刷
StartSyncCache();
return 1;
}
@ -461,7 +579,7 @@ static uint8_t FindReadPageCopy(uint32_t addr, void *buf, uint32_t len)
return 0;
}
HFlashMemCache *cache = FindReadCache(pageAddr, 1);
HFlashMemCache *cache = FindReadCache(pageAddr, 1, 1);
if (cache) {
memcpy(p, GetMMap(cache->offset) + offset, copyLen);
} else {
@ -501,6 +619,14 @@ void HFlashMemInit(HFlashMemOpts *opts)
FATAL_ERROR("flash Size not 4k align");
return ;
}
// 重新初始化时会把运行态和定时器一并清掉
// 这些状态记录的是上一次 cache 使用轨迹和异步擦除进度, 沿用会把新配置带进旧状态机
memset(&sInfo, 0, sizeof(sInfo));
if (sCheckTimer != HTIMER_INVALID) {
HTimerRemove(sCheckTimer);
sCheckTimer = HTIMER_INVALID;
}
}
const HFlashMemOpts *HFlashMemGetOpt()
@ -518,7 +644,6 @@ uint8_t HFlashMemRead(uint32_t addr, void *buf, uint32_t len)
return 1;
}
ScheduleHeat();
uint8_t *p = (uint8_t *)buf;
while (len > 0) {
const uint32_t pageAddr = Align4K(addr);
@ -530,7 +655,7 @@ uint8_t HFlashMemRead(uint32_t addr, void *buf, uint32_t len)
return 0;
}
HFlashMemCache *cache = FindReadCache(pageAddr, 1);
HFlashMemCache *cache = FindReadCache(pageAddr, 1, 1);
if (cache) {
memcpy(p, GetMMap(cache->offset) + offset, copyLen);
} else {
@ -565,7 +690,6 @@ uint8_t HFlashMemWrite(uint32_t addr, const void *buf, uint32_t len)
return 1;
}
ScheduleHeat();
return WriteData(addr, buf, len, _MemCpyHelper, NULL);
}
@ -581,7 +705,6 @@ uint8_t HFlashMemSetValue(uint32_t addr, uint8_t value, uint32_t len)
return 0;
}
ScheduleHeat();
return WriteData(addr, NULL, len, _MemSetValueHelper, &value);
}
@ -597,7 +720,6 @@ uint8_t HFlashMemAddrCopy(uint32_t srcAddr, uint32_t destAddr, uint32_t len)
return 0;
}
ScheduleHeat();
return WriteData(destAddr, NULL, len, _MemAddrCopy, &srcAddr);
}
@ -607,15 +729,15 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t
return 0;
}
ScheduleHeat();
uint32_t offset = 0;
HFlashMemCache *cache = NULL;
uint16_t len = 0;
uint8_t result = 0;
// 先处理未对齐的部分
if (addr != Align4K(addr)) {
cache = FindWriteCache(addr + offset, 1);
// 这里显式禁止 bypass
// 回调拿到的是 cache 内存指针, 如果退化成直读 flash, 本接口就无法提供连续且可复用的页视图
HFlashMemCache *cache = FindReadCache(addr + offset, 1, 0);
if (cache == NULL) {
LogE("addr[0x%08x], find cache faild", addr);
return 0;
@ -632,7 +754,7 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t
// 剩余处理对齐部分
while (result == 0) {
cache = FindWriteCache(addr + offset, 1);
HFlashMemCache *cache = FindReadCache(addr + offset, 1, 0);
if (cache == NULL) {
LogE("addr[0x%08x], find cache faild", addr + offset);
return 0;
@ -651,14 +773,19 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t
void HFlashMemSync()
{
ScheduleHeat();
if (sCheckTimer != HTIMER_INVALID) {
HTimerRemove(sCheckTimer);
sCheckTimer = HTIMER_INVALID;
}
SyncCache();
}
void HFlashMemFreeCache()
{
HFlashMemSync();
sInfo.useNum = 0;
sInfo.currHeatTime = HDLogGetTime();
if (sCheckTimer != HTIMER_INVALID) {
HTimerRemove(sCheckTimer);
sCheckTimer = HTIMER_INVALID;
}
memset(&sInfo, 0, sizeof(sInfo));
}

View File

@ -4,7 +4,9 @@
#include "HDLog.h"
#include "HVector.h"
#include "HTimer.h"
#if HFLASH_USE_HSHELL
#include "HShellLex.h"
#endif
#include <stdio.h>
#ifdef HFLASH_USE_FLASH_MEM
@ -49,7 +51,9 @@ __attribute__((weak))
void HDMainLoopCallOverride(uint8_t event)
{
(void)(event);
#if HFLASH_USE_HSHELL
HShellRun();
#endif
}
#define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); HDMainLoopCallOverride(0); };
#endif
@ -60,7 +64,9 @@ void HDMainLoopCallOverride(uint8_t event)
// 检查是不是4对齐
#define IS_NOT_4(size) ((size & (4 - 1)) != 0)
#define IS_NOT_4K(size) ((size & (HFLASH_BLOCK_SIZE - 1)) != 0)
#define ALIGN_4K(size) (((size) + HFLASH_BLOCK_SIZE - 1) & ~(HFLASH_BLOCK_SIZE - 1))
#if HFLASH_USE_HSHELL
enum eShell
{
kShellPageInfo, ///< 查看页表信息
@ -84,6 +90,7 @@ static HShellMatch sShellMatch[] = {
HSHELL_MATCH_ITEM(kShellFlashMemInfo, "flashMemInfo"),
#endif
};
#endif
union PageInfo {
@ -104,9 +111,9 @@ struct Info {
HFlashAddr_t pageAddr; ///< 页表地址
HFlashAddr_t pageBackupAddr; ///< 页表备份地址
uint32_t pageSize; ///< 页大小
HFlashAddr_t protectAddr; ///< 保护区地址
HFlashAddr_t protectBackupAddr; ///< 保护区备份地址
uint32_t protectSize; ///< 保护区大小
HFlashProtectInfo *protectInfo; ///< 保护区描述缓存
uint16_t protectCacheSize; ///< 保护区描述缓存大小
uint16_t protectUseNum; ///< 保护区使用数量
HFlashCacheInfo *pageCache; ///< 页表缓存
uint16_t pageCacheSize; ///< 页表缓存大小
uint16_t pageCacheUseNum; ///< 页表缓存使用页
@ -130,30 +137,193 @@ static HTimer_t sSyncCacheTimer = HTIMER_INVALID;
static HVECTOR_DEFINE32(sNeedBackupOffset, 10);
static uint8_t RestoreBackup(HFlashAddr_t srcAddr, HFlashAddr_t dstAddr, uint32_t size);
static uint8_t RestoreBackupRange(HFlashAddr_t addr, uint32_t size);
static uint8_t SyncBackupRange(HFlashAddr_t addr, uint32_t size);
static void StartSyncBackupTimer();
static uint8_t ReadFlashCall(HFlashAddr_t addr, void *data, uint32_t size);
static int16_t FindProtectIndex(HFlashAddr_t addr, uint32_t size);
static int16_t FindProtectBackupIndex(HFlashAddr_t addr, uint32_t size);
static int16_t FindProtectAddrIndex(HFlashAddr_t addr);
static HFlashCacheInfo *FindCache(HFlashAddr_t addr);
static void WriteCachePage(HFlashCacheInfo *info);
static uint32_t GetFlashCrc32(HFlashAddr_t addr, uint32_t size);
static uint8_t ReadBackup(HFlashAddr_t addr, void *data, uint32_t size);
static uint32_t GetBackupCrc32(HFlashAddr_t addr, uint32_t size);
static uint8_t GetBackupAddr(HFlashAddr_t addr, uint32_t size, HFlashAddr_t *backupAddr);
static uint8_t GetProtectBackupRange(HFlashAddr_t addr, uint32_t size, HFlashAddr_t *backupStartAddr, uint32_t *backupSize);
static inline uint32_t AdjustProtectBackupSize(HFlashAddr_t addr, uint32_t size);
static void ResetRuntimeState()
{
if (sBackupTimer != HTIMER_INVALID) {
HTimerRemove(sBackupTimer);
sBackupTimer = HTIMER_INVALID;
}
#ifndef HFLASH_USE_FLASH_MEM
if (sSyncPageTimer != HTIMER_INVALID) {
HTimerRemove(sSyncPageTimer);
sSyncPageTimer = HTIMER_INVALID;
}
#endif
if (sSyncCacheTimer != HTIMER_INVALID) {
HTimerRemove(sSyncCacheTimer);
sSyncCacheTimer = HTIMER_INVALID;
}
HVectorClear(sNeedBackupOffset);
}
///< 检查地址是否在双备份保护区内
static uint8_t IsProtect(HFlashAddr_t addr, uint32_t size)
{
return FindProtectIndex(addr, size) >= 0;
}
static uint8_t GetProtectBackupRange(HFlashAddr_t addr, uint32_t size, HFlashAddr_t *backupStartAddr, uint32_t *backupSize)
{
const uint64_t startAddr = addr & ~((uint64_t)HFLASH_BLOCK_SIZE - 1u);
const uint64_t endAddr = (uint64_t)addr + size;
const uint64_t alignMask = (uint64_t)HFLASH_BLOCK_SIZE - 1u;
uint64_t backupEnd = endAddr;
if (size == 0) {
*backupStartAddr = (HFlashAddr_t)startAddr;
*backupSize = 0;
return 1;
}
if (endAddr > UINT32_MAX) {
FATAL_ERROR("protect addr[0x%08x] size[%d] overflow", addr, size);
return 0;
}
if ((backupEnd & alignMask) != 0) {
backupEnd = (backupEnd + alignMask) & ~alignMask;
}
if (backupEnd > UINT32_MAX) {
FATAL_ERROR("protect addr[0x%08x] size[%d] align overflow", addr, size);
return 0;
}
*backupStartAddr = (HFlashAddr_t)startAddr;
*backupSize = (uint32_t)(backupEnd - startAddr);
return 1;
}
static int16_t FindProtectIndexByRange(HFlashAddr_t addr, uint32_t size, uint8_t useBackupRange)
{
const HFlashAddr_t addrEnd = addr + size;
if (addrEnd < addr) {
FATAL_ERROR("addr[0x%08x] size[%d] overflow", addr, size);
return -1;
}
for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) {
HFlashAddr_t protectAddr = sInfo.protectInfo[i].addr;
uint32_t protectSize = sInfo.protectInfo[i].size;
if (useBackupRange && GetProtectBackupRange(protectAddr, protectSize, &protectAddr, &protectSize) == 0) {
return -1;
}
const HFlashAddr_t protectEnd = protectAddr + protectSize;
if (protectEnd < protectAddr) {
FATAL_ERROR("protect addr[0x%08x] size[%d] overflow", protectAddr, protectSize);
return -1;
}
if (addr < protectAddr || addr >= protectEnd) {
continue;
}
if (addrEnd > protectEnd) {
FATAL_ERROR("addr[0x%08x][0x%08x] size[%d][%d] overflow protect%s",
addr,
protectAddr,
size,
protectSize,
useBackupRange ? " backup" : "");
return -1;
}
return (int16_t)i;
}
return -1;
}
static int16_t FindProtectIndex(HFlashAddr_t addr, uint32_t size)
{
return FindProtectIndexByRange(addr, size, 0);
}
static int16_t FindProtectBackupIndex(HFlashAddr_t addr, uint32_t size)
{
return FindProtectIndexByRange(addr, size, 1);
}
static int16_t FindProtectAddrIndex(HFlashAddr_t addr)
{
for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) {
if (sInfo.protectInfo[i].addr == addr) {
return (int16_t)i;
}
}
return -1;
}
static uint8_t GetBackupAddr(HFlashAddr_t addr, uint32_t size, HFlashAddr_t *backupAddr)
{
const HFlashAddr_t addrEnd = addr + size;
if (addrEnd < addr) {
FATAL_ERROR("backup addr[0x%08x] size[%d] overflow", addr, size);
return 0;
}
if (addr < sInfo.protectAddr || addr >= sInfo.protectAddr + sInfo.protectSize) {
const HFlashAddr_t pageEnd = sInfo.pageAddr + sInfo.pageSize;
if (pageEnd >= sInfo.pageAddr && addr >= sInfo.pageAddr && addrEnd <= pageEnd) {
*backupAddr = sInfo.pageBackupAddr + (addr - sInfo.pageAddr);
return 1;
}
const int16_t index = FindProtectBackupIndex(addr, size);
if (index >= 0) {
HFlashProtectInfo *info = &sInfo.protectInfo[index];
HFlashAddr_t protectAddr = 0;
uint32_t protectSize = 0;
if (GetProtectBackupRange(info->addr, info->size, &protectAddr, &protectSize) == 0) {
return 0;
}
(void)protectSize;
*backupAddr = info->backupAddr + (addr - protectAddr);
return 1;
}
return 0;
}
static uint8_t ReadBackup(HFlashAddr_t addr, void *data, uint32_t size)
{
if (size == 0) {
return 1;
}
if (data == NULL) {
return 0;
}
if (addrEnd > sInfo.protectAddr + sInfo.protectSize) {
FATAL_ERROR("addr[0x%08x][0x%08x] size[%d][%d] overflow protect", addr, sInfo.protectAddr, size, sInfo.protectSize);
HFlashAddr_t backupAddr = 0;
if (GetBackupAddr(addr, size, &backupAddr) == 0) {
LogE("backup addr[0x%08x] not config", addr);
return 0;
}
return 1;
return ReadFlashCall(backupAddr, data, size);
}
static uint8_t ReadFlashCall(HFlashAddr_t addr, void *data, uint32_t size)
@ -330,6 +500,19 @@ static inline uint32_t AdjustPageByte(uint32_t index)
return index * sizeof(HFlashPageInfo);
}
static inline uint32_t AdjustProtectBackupSize(HFlashAddr_t addr, uint32_t size)
{
HFlashAddr_t backupStartAddr = 0;
uint32_t backupSize = 0;
if (GetProtectBackupRange(addr, size, &backupStartAddr, &backupSize) == 0) {
return 0;
}
(void)backupStartAddr;
return backupSize;
}
static void SyncBackup()
{
LogD("Start sync Backup");
@ -340,8 +523,7 @@ static void SyncBackup()
for (uint16_t i = 0; i < HVectorGetUseLen(sNeedBackupOffset); ++i) {
const uint32_t addr = HVectorGetData(sNeedBackupOffset, i);
const uint32_t destAddr = sInfo.protectBackupAddr + (addr - sInfo.protectAddr);
if (RestoreBackup(addr, destAddr, HFLASH_BLOCK_SIZE) == 0) {
if (SyncBackupRange(addr, HFLASH_BLOCK_SIZE) == 0) {
LogE("RestorePage faild, addr[0x%08x]", addr);
StartSyncBackupTimer();
return ;
@ -351,9 +533,9 @@ static void SyncBackup()
HVectorClear(sNeedBackupOffset);
// 备份页表数据
const uint32_t crcValue = GetFlashCrc32(AdjustPageAddr(sInfo.pageBackupAddr), AdjustPageByte(sPageInfo.useNum));
const uint32_t crcValue = GetBackupCrc32(AdjustPageAddr(sInfo.pageAddr), AdjustPageByte(sPageInfo.useNum));
if (crcValue != sPageInfo.crc32) {
RestoreBackup(sInfo.pageAddr, sInfo.pageBackupAddr, AdjustPageByte(sPageInfo.useNum + 1));
SyncBackupRange(sInfo.pageAddr, AdjustPageByte(sPageInfo.useNum + 1));
}
LogD("Sync Backup End");
}
@ -466,6 +648,21 @@ static uint32_t GetFlashCrc32(HFlashAddr_t addr, uint32_t size)
return HSCrc32Get();
}
static uint32_t GetBackupCrc32(HFlashAddr_t addr, uint32_t size)
{
if (size == 0) {
return 0xFFFFFFFF;
}
HFlashAddr_t backupAddr = 0;
if (GetBackupAddr(addr, size, &backupAddr) == 0) {
LogE("backup addr[0x%08x] not config", addr);
return 0xFFFFFFFF;
}
return GetFlashCrc32(backupAddr, size);
}
static uint8_t RestoreBackup(HFlashAddr_t srcAddr, HFlashAddr_t dstAddr, uint32_t copySize)
{
if (copySize == 0) {
@ -541,6 +738,31 @@ _FlashError:
#endif
}
static uint8_t CopyBackupRange(HFlashAddr_t addr, uint32_t size, uint8_t isRestore)
{
HFlashAddr_t backupAddr = 0;
if (GetBackupAddr(addr, size, &backupAddr) == 0) {
LogE("backup addr[0x%08x] not config", addr);
return 0;
}
if (isRestore) {
return RestoreBackup(backupAddr, addr, size);
}
return RestoreBackup(addr, backupAddr, size);
}
static uint8_t RestoreBackupRange(HFlashAddr_t addr, uint32_t size)
{
return CopyBackupRange(addr, size, 1);
}
static uint8_t SyncBackupRange(HFlashAddr_t addr, uint32_t size)
{
return CopyBackupRange(addr, size, 0);
}
///< 恢复指定页表
static uint8_t RestorePage(uint32_t index)
{
@ -549,9 +771,7 @@ static uint8_t RestorePage(uint32_t index)
return 0;
}
const HFlashAddr_t srcAddr = AdjustPageAddrOffset(sInfo.pageBackupAddr, index);
const HFlashAddr_t dstAddr = AdjustPageAddrOffset(sInfo.pageAddr, index);
return RestoreBackup(srcAddr, dstAddr, sizeof(HFlashPageInfo));
return RestoreBackupRange(AdjustPageAddrOffset(sInfo.pageAddr, index), sizeof(HFlashPageInfo));
}
static void SyncPageInfo()
@ -944,6 +1164,18 @@ void HFlashSetPageCache(HFlashCacheInfo *pageInfo, uint16_t size)
sInfo.pageCacheSize = size;
}
void HFlashSetProtectCache(HFlashProtectInfo *protectInfo, uint16_t size)
{
ResetRuntimeState();
sInfo.protectInfo = protectInfo;
sInfo.protectCacheSize = size;
sInfo.protectUseNum = 0;
if (protectInfo != NULL && size) {
memset(protectInfo, 0, size * sizeof(HFlashProtectInfo));
}
}
void HFlashSetPageAddr(HFlashAddr_t addr, uint32_t size)
{
if (IS_NOT_4K(addr) || IS_NOT_4K(size)) {
@ -956,42 +1188,200 @@ void HFlashSetPageAddr(HFlashAddr_t addr, uint32_t size)
void HFlashSetPageBackupAddr(HFlashAddr_t addr, uint32_t size)
{
if (IS_NOT_4K(addr) || IS_NOT_4K(size)) {
if (IS_NOT_4K(addr) || IS_NOT_4K(size) || size != sInfo.pageSize) {
FATAL_ERROR("not align page size[0x%08x], size[%x]", addr, size);
return ;
}
if (sInfo.pageSize != size) {
FATAL_ERROR("page size not match[%d][%d]", sInfo.pageSize, size);
if (IsOverLap(sInfo.pageAddr, sInfo.pageSize, addr, size)) {
FATAL_ERROR("page backup overlap src/dst[0x%08x][0x%08x]", sInfo.pageAddr, addr);
return ;
}
for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) {
HFlashProtectInfo *info = &sInfo.protectInfo[i];
if (IsOverLap(addr, size, info->backupAddr, AdjustProtectBackupSize(info->addr, info->size))) {
FATAL_ERROR("page backup overlap protect backup[0x%08x][0x%08x]", addr, info->backupAddr);
return ;
}
}
sInfo.pageBackupAddr = addr;
sInfo.pageSize = size;
}
void HFlashSetProtectAddr(HFlashAddr_t addr, uint32_t size)
static uint8_t CheckProtectConflict(HFlashAddr_t addr, uint32_t size, HFlashAddr_t backupAddr)
{
if (IS_NOT_4K(addr) || IS_NOT_4K(size)) {
FATAL_ERROR("not align page size[0x%08x], size[%x]", addr, size);
HFlashAddr_t adjustAddr = 0;
uint32_t backupSize = 0;
if (GetProtectBackupRange(addr, size, &adjustAddr, &backupSize) == 0) {
return 0;
}
sInfo.protectAddr = addr;
sInfo.protectSize = size;
if (IsOverLap(adjustAddr, backupSize, sInfo.pageAddr, sInfo.pageSize)) {
FATAL_ERROR("protect area overlap page[0x%08x][0x%08x]", addr, sInfo.pageAddr);
return 0;
}
if (sInfo.pageBackupAddr && IsOverLap(backupAddr, backupSize, sInfo.pageBackupAddr, sInfo.pageSize)) {
FATAL_ERROR("protect backup overlap page backup[0x%08x][0x%08x]", backupAddr, sInfo.pageBackupAddr);
return 0;
}
if (IsOverLap(adjustAddr, backupSize, backupAddr, backupSize)) {
FATAL_ERROR("protect backup overlap src/dst[0x%08x][0x%08x]", addr, backupAddr);
return 0;
}
for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) {
HFlashProtectInfo *info = &sInfo.protectInfo[i];
HFlashAddr_t infoAdjustAddr = 0;
uint32_t infoBackupSize = 0;
if (GetProtectBackupRange(info->addr, info->size, &infoAdjustAddr, &infoBackupSize) == 0) {
return 0;
}
if (info->addr == addr) {
FATAL_ERROR("protect addr[0x%08x] duplicate", addr);
return 0;
}
if (IsOverLap(infoAdjustAddr, infoBackupSize, adjustAddr, backupSize)) {
FATAL_ERROR("protect area overlap[0x%08x][0x%08x]", info->addr, addr);
return 0;
}
if (info->backupAddr == backupAddr) {
FATAL_ERROR("protect backup addr[0x%08x] duplicate", backupAddr);
return 0;
}
if (IsOverLap(info->backupAddr, infoBackupSize, backupAddr, backupSize)) {
FATAL_ERROR("protect backup overlap[0x%08x][0x%08x]", info->backupAddr, backupAddr);
return 0;
}
}
return 1;
}
void HFlashSetProtectBackupAddr(HFlashAddr_t addr, uint32_t size)
void HFlashClearProtectAddr()
{
if (IS_NOT_4K(addr) || IS_NOT_4K(size)) {
FATAL_ERROR("not align page size[0x%08x], size[%x]", addr, size);
ResetRuntimeState();
sInfo.protectUseNum = 0;
if (sInfo.protectInfo != NULL && sInfo.protectCacheSize) {
memset(sInfo.protectInfo, 0, sInfo.protectCacheSize * sizeof(HFlashProtectInfo));
}
}
if (sInfo.protectSize != size) {
FATAL_ERROR("protect size not match[%d][%d]", sInfo.protectSize, size);
void HFlashAddProtectAddr(HFlashAddr_t addr, uint32_t size, HFlashAddr_t backupAddr)
{
if (size == 0) {
FATAL_ERROR("protect size is 0 addr[0x%08x]", addr);
return ;
}
sInfo.protectBackupAddr = addr;
sInfo.protectSize = size;
if (IS_NOT_4K(backupAddr)) {
FATAL_ERROR("not align backup addr[0x%08x]", backupAddr);
return ;
}
if (sInfo.protectInfo == NULL || sInfo.protectCacheSize == 0) {
FATAL_ERROR("protectCache is null");
return ;
}
if (sInfo.protectUseNum >= sInfo.protectCacheSize) {
FATAL_ERROR("protect is full");
return ;
}
if (CheckProtectConflict(addr, size, backupAddr) == 0) {
LogE("protect addr[0x%08x] conflict", addr);
return ;
}
HFlashProtectInfo *info = &sInfo.protectInfo[sInfo.protectUseNum];
info->addr = addr;
info->size = size;
info->backupAddr = backupAddr;
++sInfo.protectUseNum;
}
void HFlashDelProtectAddr(HFlashAddr_t addr)
{
const int16_t index = FindProtectAddrIndex(addr);
if (index < 0) {
FATAL_ERROR("protect addr[0x%08x] not exist", addr);
return ;
}
ResetRuntimeState();
for (uint16_t i = (uint16_t)index; i + 1 < sInfo.protectUseNum; ++i) {
sInfo.protectInfo[i] = sInfo.protectInfo[i + 1];
}
--sInfo.protectUseNum;
memset(&sInfo.protectInfo[sInfo.protectUseNum], 0, sizeof(HFlashProtectInfo));
}
static uint8_t CheckBackupArea()
{
if (sInfo.pageBackupAddr == 0) {
FATAL_ERROR("page backup addr is null");
return 0;
}
if (IsOverLap(sInfo.pageAddr, sInfo.pageSize, sInfo.pageBackupAddr, sInfo.pageSize)) {
FATAL_ERROR("page backup overlap src/dst[0x%08x][0x%08x]", sInfo.pageAddr, sInfo.pageBackupAddr);
return 0;
}
for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) {
HFlashProtectInfo *info = &sInfo.protectInfo[i];
HFlashAddr_t adjustAddr = 0;
uint32_t backupSize = 0;
if (GetProtectBackupRange(info->addr, info->size, &adjustAddr, &backupSize) == 0) {
return 0;
}
if (IsOverLap(adjustAddr, backupSize, info->backupAddr, backupSize)) {
FATAL_ERROR("protect backup overlap src/dst[0x%08x][0x%08x]", info->addr, info->backupAddr);
return 0;
}
if (IsOverLap(info->backupAddr, backupSize, sInfo.pageBackupAddr, sInfo.pageSize)) {
FATAL_ERROR("protect backup overlap page backup[0x%08x][0x%08x]", info->backupAddr, sInfo.pageBackupAddr);
return 0;
}
}
for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) {
for (uint16_t j = i + 1; j < sInfo.protectUseNum; ++j) {
HFlashAddr_t adjustAddrI = 0;
HFlashAddr_t adjustAddrJ = 0;
uint32_t backupSizeI = 0;
uint32_t backupSizeJ = 0;
if (GetProtectBackupRange(sInfo.protectInfo[i].addr, sInfo.protectInfo[i].size, &adjustAddrI, &backupSizeI) == 0 ||
GetProtectBackupRange(sInfo.protectInfo[j].addr, sInfo.protectInfo[j].size, &adjustAddrJ, &backupSizeJ) == 0) {
return 0;
}
if (IsOverLap(adjustAddrI, backupSizeI, adjustAddrJ, backupSizeJ)) {
FATAL_ERROR("protect area overlap[0x%08x][0x%08x]", sInfo.protectInfo[i].addr, sInfo.protectInfo[j].addr);
return 0;
}
if (IsOverLap(sInfo.protectInfo[i].backupAddr, backupSizeI, sInfo.protectInfo[j].backupAddr, backupSizeJ)) {
FATAL_ERROR("protect backup overlap[0x%08x][0x%08x]", sInfo.protectInfo[i].backupAddr, sInfo.protectInfo[j].backupAddr);
return 0;
}
}
}
return 1;
}
static uint8_t _Print(uint32_t index, HFlashPageInfo *info, void *userData)
@ -1010,6 +1400,7 @@ static uint8_t _Print(uint32_t index, HFlashPageInfo *info, void *userData)
return 0;
}
#if HFLASH_USE_HSHELL
static void Shell(HSHELL_FUNC_ARGS)
{
uint32_t arg1 = 0;
@ -1048,7 +1439,7 @@ static void Shell(HSHELL_FUNC_ARGS)
HFlashResetPage();
} break;
case kShellRestorePage: {
RestoreBackup(sInfo.pageAddr, sInfo.pageBackupAddr, AdjustPageByte(sPageInfo.pageNum + 1));
RestoreBackupRange(sInfo.pageAddr, AdjustPageByte(sPageInfo.pageNum + 1));
} break;
#ifdef HFLASH_USE_FLASH_MEM
case kShellFlashMemInfo: {
@ -1058,7 +1449,13 @@ static void Shell(HSHELL_FUNC_ARGS)
}
for (uint8_t i = 0; i < opt->cacheSize; ++i) {
HSHELL_PRINTFL("addr[0x%08x], offset[%d], eraseStatus[%d], isDirty[%d], heat[%d]", opt->cache[i].addr, opt->cache[i].offset, opt->cache[i].eraseStatus, opt->cache[i].isDirty, opt->cache[i].heat);
HSHELL_PRINTFL("addr[0x%08x], offset[%d], eraseStatus[%d], isDirty[%d], referenced[%d], probation[%d]",
opt->cache[i].addr,
opt->cache[i].offset,
opt->cache[i].eraseStatus,
opt->cache[i].isDirty,
opt->cache[i].referenced,
opt->cache[i].probation);
}
} break;
#endif
@ -1066,18 +1463,20 @@ static void Shell(HSHELL_FUNC_ARGS)
break;
}
}
#endif
void HFlashInitCheck()
{
#if HFLASH_USE_HSHELL
HSHellRegister(sShellMatch, sizeof(sShellMatch) / sizeof(HShellMatch), Shell, 0);
#endif
if (sInfo.pageCache == NULL || sInfo.pageCacheSize == 0) {
FATAL_ERROR("pageCache is null");
return ;
}
// 检查页表地址有没有和备份地址重叠问题
if (IsOverLap(sInfo.pageAddr, sInfo.pageSize, sInfo.pageBackupAddr, sInfo.pageSize)) {
FATAL_ERROR("page table and backup table is overlap");
if (CheckBackupArea() == 0) {
LogE("Backup area check faild");
return ;
}
@ -1101,15 +1500,15 @@ void HFlashInitCheck()
LogD("page table is invalid, check backup table");
// 备份校验
if (ReadFlashCall(sInfo.pageBackupAddr, sPageInfo.reverse, sizeof(sPageInfo)) == 0) {
LogE("Read backup page table[0x%08x] faild", sInfo.pageBackupAddr);
if (ReadBackup(sInfo.pageAddr, sPageInfo.reverse, sizeof(sPageInfo)) == 0) {
LogE("Read backup page table[0x%08x] faild", sInfo.pageAddr);
break;
}
invalid = sPageInfo.pageNum > num;
invalid = invalid || sPageInfo.useNum > num;
invalid = invalid || sPageInfo.useNum > sPageInfo.pageNum;
invalid = invalid || GetFlashCrc32(AdjustPageAddr(sInfo.pageBackupAddr), AdjustPageByte(sPageInfo.useNum)) != sPageInfo.crc32;
invalid = invalid || GetBackupCrc32(AdjustPageAddr(sInfo.pageAddr), AdjustPageByte(sPageInfo.useNum)) != sPageInfo.crc32;
if (invalid) {
// 备份也无效, 首次创建, 去初始化数据
LogD("backup page table is invalid, init data");
@ -1117,7 +1516,7 @@ void HFlashInitCheck()
}
// 备份校验通过, 恢复全备份数据
RestoreBackup(sInfo.pageBackupAddr, sInfo.pageAddr, sInfo.pageSize);
RestoreBackupRange(sInfo.pageAddr, sInfo.pageSize);
return ;
} while (0);
@ -1191,7 +1590,7 @@ void HFlashRegister(HFlashAddr_t addr, uint32_t size)
// 先恢复内容区域
LogD("addr[0x%08x] size[%d] crc32 not match, start restore", addr, size);
RestoreBackup(sInfo.protectBackupAddr + (cache->info.addr - sInfo.protectAddr), cache->info.addr, cache->info.size);
RestoreBackupRange(cache->info.addr, cache->info.size);
// 如果恢复后还校验失败, 那就恢复对应页表
const uint32_t contentCrc = GetFlashCrc32(cache->info.addr, cache->info.useSize);