2. VM 掉电存储详细说明

AW31N SDK提供了VM 掉电存储模块。

下面分为7个部分来介绍VM功能:
  1. VM 掉电存储作用和数据存储

  2. VM 掉电存储使用说明

  3. VM 掉电存储版本简单说明

  4. 新旧VM文件组成及版本选择

  5. VM相关参数介绍

  6. VM掉电记忆存储接口

  7. VM常见问题介绍

2.1. VM 掉电存储作用和数据存储

SDK提供了 vm掉电记忆存储功能 ,该功能用于在 内置flash里保存断电之后还需要保存的信息。

使用VM存储的数据会记忆在FLASH的”EEPROM”区域,该区域大小可由下载目录的”isd_config.ini”文件配置,使用VM功能时EEPROM空间不得低于8K字节,且需为8K字节的倍数(即8K,16K,24K 以此类推)。

EEPROM_ADR=AUTO;
EEPROM_LEN=24K;
EEPROM_OPT=1;

2.2. VM 掉电存储使用说明

VM记忆存储使用的步骤分为三步:
  • 第一步:系统初始化时,调用函数vm_init_api()初始化VM功能。

  • 第二步:使用sysmem_write_api()接口把需要保存的数据写入FLASH。

  • 第三步:使用sysmem_read_api()接口读取写入的数据。

可参考”VM简单使用demo:


2.3. VM 掉电存储版本简单说明

目前SDK中VM有新旧两个版本可以选择, 默认选择新版本

Note

旧版VM:

  1. 可存储id号数量:128个;

  2. 单次写入最大数据长度:128个byte;

  3. 没有cache管理;

  4. 没有预擦除功能;

Note

新版VM

  1. 可存储id号数量:512个;

  2. 单次写入最大数据长度:4096个byte;(若EEPROM分配给VM区域只有8K,则单次写入最大数据长度为:4092个byte);

  3. 增加cache管理,提升读取速度;

  4. 增加预擦除功能。

Note

新旧版本VM功能对比:

  1. 新版VM id号数量增多;

  2. 新版VM 单次写入最大数据长度增多;

  3. 新版VM 增加cache管理,提升VM读取速度;

  4. 新版VM 增加预擦除功能,可以在系统空闲时进行预擦除操作,可尽可能避免VM整理时关闭flash时间过长导致的卡音问题;

  5. 新版ID号 与 旧版index句柄 含义相同;


2.4. 新旧VM文件组成及版本选择

vm模块由vm_api.c和vm_api.h文件构成API层,包含vm_api.c和nvm_api.c文件;

Note

用户可在 board_awxxx_cfg.h 文件里选择使用新 / 旧版VM.

#define USE_NEW_VM                                              1
#define USE_OLD_VM                                              2
#define SYS_MEMORY_SELECT                               USE_NEW_VM

2.5. VM相关参数介绍

对于用户,我们需要关心的参数分别为:

  1. ID号(vm_api.h)

  2. 读/写入数据长度

  3. vm存储区域大小

对于新版VM

2.5.1. ID号(vm_api.h)

"sysmem_write_api 参数 id“

sysmem_write_api 参数

Important

旧版VM ID号最多128个,新版VM ID号最多512个;(旧版VM index号在新版vm仍可通用)

新版VM ID号最多支持512个,且不再需要自定义对应ID的数据长度。

用户可在sys_memory.h的VM_INDEX里添加自己的id号。

注意: 1~32号ID为系统使用,用户不可修改 33~99号ID为 开发者使用 ,用户可修改,在user_cfg_id.h可添加 100~号ID为 sdk保留配置项 ,用户不可修改


2.5.2. 读/写入数据长度

"sysmem_write_api 参数 length"

sysmem_write_api 参数length

Important

旧版VM读/写长度最多支持128个byte;

新版VM读/写长度最多支持4096个byte;


2.5.3. vm存储区域大小

Important

VM记忆的数据 是 EEPROM区划分出一定空间大小给VM使用,该区域大小可由下载目录里的isd_config.ini文件配置。 (该配置参数详细说明可参考” isd_config.ini文件介绍 “)

注意:

VM存储区域大小分配的EEPROM空间不得低于8K字节,且需为8K字节的倍速,即8K,16K,24K等等。

补充说明:因为要做擦除备份,实际可用存储数据的空间大小只能是配置的一半,例如配置了8K,则可用存储空间即是4K。

"VM区域大小设置"

VM区域大小设置”


2.5.4. 新版VM格式整理时缓存空间设置new_vm_buff(new_vm.h)

Important

new_vm_buff,该buff用于vm格式整理时数据的缓存。

该buff由 用户自定义最大id数量(BIT_MAP_SIZE) + 单个id在整理时数据缓存的最大长度(BIT_MAX_LEN) 两部分组成。

BIT_MAP_SIZE :即BIT_MAP:vm最多可以存储的id数量,固定为512个。

BIT_MAX_LEN :即VM格式整理时数据缓存的大小,该值越大,则整理速度越快,该值越小,则整理速度越慢。(注意:该buf与读/写数据最大长度为两个概念)

"VM掉电存储记忆项的定义(nvm_api.c和new_vm.h)“

VM掉电存储记忆项的定义(nvm_api.c和new_vm.h)

2.5.5. 新版vm格式整理后是否立即擦除旧半区区域功能

"config_vm_erasure_after_format_en“

config_vm_erasure_after_format_en

config_vm_erasure_after_format_en 用于控制新版vm格式整理后是否立即擦除旧半区区域;
0 : 不立即擦除(默认值)
1 : 立即擦除

而默认关闭该功能时,系统在vm操作时,进行VM格式整理却不会进行擦除动作,而是等到系统空闲时进行擦除动作。

2.5.6. 新版vm是否支持多段读功能

"config_vm_multiple_read_en“

config_vm_multiple_read_en

config_vm_multiple_read_en 用于控制新版vm是否支持多段读功能;
0 : 不支持多段读(默认值)
1 : 支持多段读

2.6. VM掉电记忆存储接口

函数接口在sys_memory.c

2.6.1. 函数u32 syscfg_vm_init(u32 mem_addr, u32 mem_size)

此函数用来初始化vm虚拟存储系统,其中参数:

1. eeprom_saddr:vm区域的起始地址。
2. eeprom_size:vm区域的空间长度。
3. 返回值:
        成功:0;
        失败:错误号,可参考errno-base.h;

2.6.2. 函数u32 sysmem_write_api(u32 id,u8 *data_buf, u32 len)

此函数用来把对应hdl的数据存储到vm虚拟存储系统,其中参数:

1. hdl:访问vm的id。
2. buf:vm读数据buffer。
3. len:数据长度。
4. 返回值:
        成功:成功写入VM数据长度;
        失败:错误号,可参考errno-base.h;

2.6.3. 函数u32 sysmem_read_api(u32 id ,u8 *data_buf, u32 len)

此函数用来把对应hdl的数据从vm虚拟存储系统读出来,其中参数:

1. id:访问vm的id。
2. buf:vm读数据buffer。
3. len:数据长度。
4. 返回值:
        成功:成功获取到的数据长度;
        失败:错误号,可参考errno-base.h;

2.6.4. 函数 u32 sysmem_pre_erase_api(void)

此函数用于预擦除设备,在系统空闲时主动调用提前擦除flash,可尽量避免VM做整理时关flash时间过长导致卡音问题;
该函数只有在新版VM处生效,旧版VM没有该功能。
示例:音乐模式下,500MS消息来时若系统处于空闲状态,则会进行预擦除动作:
"图2.1 系统空闲预擦除动作“

图2.1 系统空闲预擦除动作

2.6.5. 函数void nvm_pre_erasure_next(NEW_VM_OBJ *p_nvm, u16 using_next, u16 idle_next)

此函数为NEW VM中独有接口,由2.4 函数 u32 vm_pre_erase() 调用,用于系统空闲时选择擦除当前VM已存储的数据往后的flash扇区,以及空闲VM需要往后擦除的flash扇区数,用户可自行修改需要擦除的flash扇区数,其中参数:

1、p_nvm:NEW VM句柄指针;
2、using_next:当前VM存储块需要往后擦除的flash扇区数;
3、idle_next:空闲VM存储块需要往后擦除的flash扇区数;
4、返回值:无

2.7. VM常见问题介绍

2.7.1. VM简单使用demo:

用户可在app/bsp/start/flash_init.c里的vm_init_api()函数找到demo示例。

"vm_demo“

vm_demo

2.7.2. 调整VM区域大小设置:

节省VM占用系统空间可以从两个地方入手:

  1. 减少VM区域大小。

    具体参考上面”vm存储区域大小”章节的 vm存储区域大小 部分

  2. 新版VM可以减少VM格式整理时的缓存new_vm_buff

    可以适当减少BIT_MAX_LEN的长度。

    具体参考上面”新版VM格式整理时缓存空间设置new_vm_buff(new_vm.h)”章节的 新版VM格式整理时缓存空间设置 部分。


2.7.3. 新版VM在关机或者掉电后可能存在数据丢失BUG:

该问题主要原因为系统在关机前格式整理后没有做预擦除动作。

具体原因及解决办法说明可以参考以下链接:

FAQ:AD14/15/17/AC104 new_vm功能在关机或者掉电后可能存在数据丢失BUG

2.7.4. VM格式整理原理:

VM格式整理原理:

VM功能将EEPROM区域划分为A、B两块,A块记录VM数据,B块处于空闲状态,
当A块数据接近饱和时,会擦除B块并将A块的数据整合到B块,后续B块记录VM数据,A块处于空闲状态,以此往复。

2.7.5. VM卡音问题原因和解决办法:

问题原因:

VM格式整理时 数据整合过程需要关中断并擦除空闲块的flash扇区,时间较长可能会导致解码卡音/蓝牙无法收发等问题;

解决办法:

NEW VM可以在系统空闲状态时调用sysmem_pre_erase_api()函数;
该函数作用是提前擦除当前已存储数据往后的flash扇区,以及空闲块的flash扇区数(内部会限制不超出EEPROM区域扇区数),即把格式整理的VM擦除提前处理。

SDK上常见操作如:

(一)切换模式时做擦除动作;

(二)500ms时,系统空闲会做擦除动作;

(三)系统关机/睡眠前会做擦除动作;

2.7.6. VM删除id功能:

AW31的新版VM提供了可以删除ID功能,目前暂未提供应用接口,用户可以直接调用底层接口进行删除。 添加接口形式如下图:

"vm_delete_id“

vm_delete_id“

使用原理:输入位图(ignore_map),bit几对应id号,置1表示删除该id。

使用参数:
1. ignore_map:位图,bit几对应id号
2. ignore_len:位图长度(要删除的id号不应超过位图长度)
"vm_delete_id_demo

vm_delete_id_demo

使用示例代码:

#include "new_vm.h"
void test()
{
    log_info("test\n");
    u8 buf1[5] = {1,2,3,4,5};
    u8 buf2[5] = {0};
    u8 buf3[5] = {0};
    u8 buf4[5] = {0};
    u8 buf5[5] = {0};
    u32 ignore_bit_map[BIT_MAP_SIZE / 4] = {0};

    log_info("write 0 1 77 99\n");
    nvm_write_api(0, buf1, 5);          //往id 0 写入{1,2,3,4,5}
    nvm_write_api(1, buf1, 5);
    nvm_write_api(77, buf1, 5);
    nvm_write_api(99, buf1, 5);

    log_info("read 0 1 77 99\n");
    nvm_read_api(0, buf2, 5);           //读取是否写入成功
    nvm_read_api(1, buf3, 5);
    nvm_read_api(77, buf4, 5);
    nvm_read_api(99, buf5, 5);

    log_info("read first 0 1 77 99\n");
    log_info_hexdump((u8*)buf2 ,sizeof(buf2));  //打印读到的数据
    log_info_hexdump((u8*)buf3 ,sizeof(buf3));
    log_info_hexdump((u8*)buf4 ,sizeof(buf4));
    log_info_hexdump((u8*)buf5 ,sizeof(buf5));

    //在位图置上想要删除的id对应的bit位
    ignore_bit_map[0] |= BIT(0);                //如:删除id 0
    ignore_bit_map[2] |= BIT(13);               //如:删除id 77
    ignore_bit_map[3] |= BIT(3);                //如:删除id 99
    log_info("delete 0 77 99\n");
    nvm_delete_id(ignore_bit_map, BIT_MAP);     //调用删除接口

    log_info("buff reset\n");
    memset(buf2, 0, 5);
    memset(buf3, 0, 5);
    memset(buf4, 0, 5);
    memset(buf5, 0, 5);

    log_info("read 0 1 77 99\n");
    nvm_read_api(0, buf2, 5);           //再次读取数据,确认是否删除成功
    nvm_read_api(1, buf3, 5);
    nvm_read_api(77, buf4, 5);
    nvm_read_api(99, buf5, 5);

    log_info("read twice 0 1 77 99\n");
    log_info_hexdump((u8*)buf2 ,sizeof(buf2));  //打印读到的数据
    log_info_hexdump((u8*)buf3 ,sizeof(buf3));
    log_info_hexdump((u8*)buf4 ,sizeof(buf4));
    log_info_hexdump((u8*)buf5 ,sizeof(buf5));
}