CN / EN
CN / EN

提问

设计支持

您的项目私密技术问题如需获得一对一支持,请点击下方联系我们。

GR5xxx存储系统NVDS使用局限性及替代方案 其它

已解决

185***527

汇顶员工
2023-12-28 15:15

NVDS概述

NVDS(Non-volatile Data Storage)是一个轻量级逻辑数据Tag-Value存储系统,使用Flash硬件抽象层(Flash HAL)提供的Flash读写接口,将数据存储于Flash中,因此,掉电时数据也不会丢失。开发者也可以在custom_config.h中指定NVDS在Flash中的起始地址和扇区数量,但是需要注意不要和其他已规划为其它用途的Flash冲突。另外,NVDS起始地址需要和Flash扇区的大小对齐。NVDS系统适合存储小块,非频繁更新的数据,例如应用程序的配置参数、校准数据、状态和用户信息等。BLE协议栈也会使用NVDS存储设备绑定等参数。

NVDS实现逻辑

存储结构

Flash中的存储结构如下图,占用连续的若干个Sector的Flash空间,其中第一个sector的起始位置会放置特殊magic,用于标识这块区域是否也被初始化为NVDS。NVDS的每个Item是由Item Header和Item Data组成。

Item Read (nvds_get)

如上介绍,每个Item的Header会记录其Data的长度,因此由Item n可查找到Item n+1,即Item n的地址 + Header固定长度 + Item n数据长度,可以理解为“单链表”查找数据的逻辑。因此,每次Read操作从NVDS的起始位置挨个读取Item Header,并确定其tag是否为目标tag,若是则把其数据读出。

Item Write (nvds_put)

当需要写入Item至NVDS时,会确认是否已存在对应tag的Item:

  • - 若存在,并且存放的数据与需写入的数据一致,则不进行任何操作,返回成功;
  • - 若存在,但存放的数据与需写入的数据不一致,则作为新的Item追加在NVDS区域,并将原Item Header中的Tag写为全0,(0x0000为已删除的tag,);
  • - 若不存在,则作为新的Item追加在NVDS区域;

已知Flash数据写入时,只能由1变0,因此Item更新时并不能在原Item位置更新,而是在尾部追加,那么就会产生垃圾/碎片数据(Tag为0x0000的Item),随着Item不断追加直至NVDS End Address,则需要回收垃圾/碎片数据,腾出空闲空间出来。

NVDS GC(回收垃圾/碎片数据)

在进行GC时,依次将NVDS每个Sector数据读至中转区,将此Sector Erase后,把中转区的有效数据写入已Erase的Sector,忽略掉垃圾/碎片数据,GC无需用户主动进行,而是在Write过程中,发现可用空间足够(垃圾/碎片数据会被计算到可用空间中),但是尾部空间不足时会自动触发。

  • Note:GR551x平台的中转区为RAM空间,可以节省Flash空间占用,但是不具备GC过程中掉电保护;此外其他平台的中转区为Flash空间,具备GC过程中掉电保护,但是会额外占用4KB Falsh空间。

NVDS使用限制

NVDS的实现具备以下局限性:

  1. 1. 不可重入,非线程安全;
  2. 2. 所有操作为阻塞式,包括GC过程,因此NVDS分配的区域越大,GC耗时越久;
  3. 3. 在GC过程中会频繁关闭中断,因为需将有效Item依次写回(GR5xxx平台Flash Write和Erase过程会关闭全局中断)

以上介绍最主要的是GC过程对BLE连接稳定性的影响,细节介绍如下:

下图是4组IO时序信号(红、绿、蓝、紫)分别代表NVDS GC、Flash Write、BLE Stack IRQ、BLE Event。

此次配置NVDS GC 4KB空间,供花费~874ms,也可看到在每次Flash Write结束后,BLE Stack IRQ有响应,但BLE Event并没有执行,空中数据包也可看出没有回复手机数据包。

这是因为BLE事件交互是有严格时序要求的,虽然BLE Stack IRQ Pending后有执行,但是对于BLE时机已错过,因此回复不了对方包。若此时BLE连接超时时间小于874ms或者NVDS配置的区域较大,GC耗时更久,那么就会导致BLE产生超时断链。


如上,我们可以总结由于NVDS自身的实现逻辑和限制,推荐NVDS使用原则如下:

1. 不存放高频次更新数据,不存放大量数据;

2. 用于存储如产品PID,SN等仅读的配置信息数据;

3. 线程非安全,使用尽量应用层做保护;

4. 尽量不要分配较大存储空间;

5. 如有其他存储需求,可选择littlefs等存储系统;

存储系统替代方案 - LittleFS

LittleFS是一个为嵌入式系统设计的轻量级文件系统。它专为低内存和闪存设备而设计,提供高效的存储解决方案,具备低资源消耗、掉电保护、磨损均衡等特性。以下着重列举几点与NVDS的区别以及可以规避NVDS局限性的点:

1. 每个Item会有一个revision字段,越大越新,更新Item时不会更新旧Item的信息,即使在更新过程中掉电,根据revision使用次新的Item数据;

2. GC时并不是一次性回收整个存储区域碎片,而是根据存储情况,整理最小需求Sector数,并且不需要中转区,类似乒乓Sector数据搬运;

依此在LittleFS之上抽象了一层Key-Value API:app_fds_init,app_fds_value_write,app_fds_value_read,app_fds_value_delete,app_fds_traverse,可直接替换原NVDS的存储需求实现,可使用int或string Tag。

详细文件见附件。以下是基于GR551x平台的使用示例,如用任何使用问题,欢迎随时本帖反馈。

资源占用:

Code size: ~16kB Ram:~1.8kB

初始化:(至少分配两个sector)

app_fds_init(0x010a0000, 4);

写入数据:

if ((ret = app_fds_value_write(record_id[i], (uint8_t *)&record_val[i], 4)) < 0)
{
    printf("fail [%d] %d\r\n", __LINE__, ret);
}

读取数据

if ((ret = app_fds_value_read(record_id[i], (uint8_t *)&read_val, 4)) < 0)
{
    printf("fail [%d] %d\r\n", __LINE__, ret);
}

删除Item

if ((ret = app_fds_value_delete(record_id[i]) < 0)
{
    printf("fail [%d] %d\r\n", __LINE__, ret);
}

遍历Item

app_fds_traverse(app_fds_traverse_cb)
void app_fds_traverse_cb(uint32_t key, uint32_t length, bool* is_continue)
{
    printf("key: %d, length:%d\r\n", key, length);
    *is_continue = true;
    if (app_fds_value_delete(key))
    {
        while(1);
    }
}

app_fds littleFS

1收藏

0赞成

最佳答案

185***527

汇顶员工
2023-12-28 15:22

如用任何使用问题,欢迎随时本帖反馈

2条评论

0赞成

0收藏

您的评论

登录后可回答问题,请 注册

我们时刻倾听您的声音
联系销售

扫描关注公众号

打开微信,使用“扫一扫”即可关注