S32K3学习笔记—S32K3之Fls & MemAcc & Fee模块
1.前言
对于嵌入式开发Flash是及其重要的,本文主要介绍一下S32K3的Flash模拟EE的操作,对于RTD3.0以后的版本相对于之前的版本有一个很大的架构变化,主要是根据最新Autosar的变化,也是一种趋势吧,可能后续我们看到芯片的Flash都是外挂的吧。就使用情况来说,RTD3.0的版本有bug且操作flash的速度也很慢,建议用目前最新的版本(RTD4.0 P12),修复了很多Bug。具体原因可以参考官方给出的变更记录。
2.原理
几个基本术语:
1.sector:一个最小可擦除单元,定义为区
2.Page:一个最小可编程单元,定义为页
3.block: 模拟EE滚动的最小单元,称最小储存单元
4.Cluster: 模拟EE滚动的最大单元,一个Cluster由多个sector组成且必须为连续扇区。由Cluster头(ID、状态、起始地 址、大小)+Block头(ID、状态、起始地址偏移、长度)+Block Data组成
5.ClusterGroup:由多个Cluster组成的空间,出于Flash寿命原因,一般用双扇区算法,一个ClusterGroup两个Cluster
6.Swap:当前Cluster没有空间继续存储时,会进行swap操作,把当前有效的数据拷贝到下一个Cluster
3.EB配置
主要依赖于FLS、MemAcc、Fee、Mcl模块
3.1Fls
相比与之前的FLS模块,此版本Fls是直接操作的物理地址,以及区分了INFLs和EXFLS,支持两种Flash的配置,再加上MemAcc模块对二者进行管理,当然如果用的简单也可以直接不适用MemAcc以及FEE模块,此案例是内部Dflash模拟EE使用Fls+MemAcc+FEE,Pflash直接使用Fls+MemAcc,包含两种情况
1、2.使能需要使用的API的开关
3.在闪存块被擦除之后,擦除空白检查将寻址的存储器区域的内容与擦除的闪存单元的值进行比较,以检查该块是否已被完全擦除
4.在写入闪存块之后,写入验证检查将重新编程的存储器区域的内容与所提供的应用缓冲区的内容进行比较,以检查该块是否已被完全重新编程。
5.每当擦除或写入作业启动时,flash driver应将闪存访问代码加载到RAM,并在该作业完成或取消后卸载(覆盖)该代码
6.允许在将AccessCode加载到RAM后清理缓存,以确保缓存和RAM内存之间的同步
注意:5和6使能需要注意链接文件划分合理的范围
7.使能MemAcc的回调通知
8.启用超时监控
9.超时时间可以自行配置
1.在每次闪存硬件操作后,通过使缓存无效来同步内存。如果使用burst需要打开
MEM驱动程序需要通过三种方法来保持内存一致性:
a.禁用数据缓存
b.将驱动器操作的闪存区域配置为不可缓存
c.启用MemSynchronizeCache功能
1.此处的sector可以按照连续的地址分配一个就行,例如此处:两个DFlash,每个8x8K,主要是用来模拟EE。以及PFlash 8x512K,三个就能将所有内存划分完。
1、2.物理地址和其实地址,相比于之前版本RTD,RTD 3.0之后的版本Fls都是可以直接操作物理地址
3.MemSectorSize和MemPageSize值相同的连续扇区数,此处将64k,在一个block,所以此处设置为8
4.扇区是最小的可擦除单元,所以是0x2000
5、6.读写取页的大小(byte)
7.为存储器设备指定的擦除周期数(通常在设备数据表中给出),默认不使用
8.Burst用于提高性能,使能之后可以大批量的擦除读写,对于提高速度建议使能,如图是支持的最大的硬件参数
剩下的sector都类似的配置就行,注意起始地址就行。
3.2MemAcc
1.是否使用Mem函数指针表API调用Mem驱动程序函数
2.如果使能,则地址类型应以64位实现
3.使能MemAcc_Compare(),允许将存储在缓冲器中的数据与存储在存储器中的数据进行比较
4.指定MemAccmodule在一个Mem比较请求中请求的最大字节数
5.指定MemAcc_MainFunction()的固定调用周期
1.指定AddressArea的ID。以便区分具有相同逻辑地址的多个AddressArea
2.对于每个AddressArea,一次只能处理一个作业。MemAcc基于优先级处理作业。0(最低)-65535(最高),在上层请 求具有更高优先级的作业的情况下,较低优先级的作业被挂起,直到较高优先级的作业完成
3.MemAcc上层模块继承的缓冲区对齐值
4.在MemAcc作业完成后触发,通知上层完成
1.配置所需要的SubAddressArea的相关属性,包括逻辑地址可做偏移、Fls类型等
1.指定SubAddressArea的逻辑起始地址
2.指定子地址区域的扇区偏移量,以防子地址区域不以参考的MemSectorBatch的第一个扇区开始
3.此值指定子地址区域的物理扇区数
4.定义如何访问Mem驱动程序服务,以及如何调度和激活/初始化Mem驱动
a.DIRECT_STATIC:Mem驱动程序与应用程序链接。Mem服务函数由MemAcc直接调用。Mem_Init由EcuM调用,Mem_MainFunction由SchM触发
b.INDIRECT_DYNAMIC:Mem驱动程序作为一个单独的二进制程序链接,并被动态激活。MemAcc将使用Mem驱动程序头表来调用Mem服务功能。Mem_Init和Mem_MainFunction的调用由MemAcc处理。
c.INDIRECT_STATIC:Mem驱动程序与应用程序链接。MemAcc将使用Mem驱动程序头表来调用Mem服务功能。Mem_Init和Mem_MainFunction的调用由MemAcc处理
5.根据MemAccUseMemFuncPtrTable配置,此前缀用于引用Mem驱动程序头结构或相应的Mem API函数。
6.指定失败的擦除、写作业的重试次数
7.如果Mem中使能了Burst,此处三个Burst必须使能
8.对映射到子地址区域的MemSectorBatch的引用
3.3 FEE
1.调用主函数的周期
2.通知上层(NVM)FEE的状态,如果不是能NVM需要周期轮询FEE的状态
3.逻辑块应与之对齐的大小
4.FEE最小读取页是最小页的倍数
5.参数确定费缓冲器需要的起始地址的对齐方式
6.数据缓冲区的大小。当Fee将数据从一个Group复制到另一个Group时,以及当Fee在启动时读取块头信息时,数据缓 冲区用于缓冲数据
7.当写入操作开始时,相应的块被标记为不一致。成功写入后,它被标记为一致。这意味着当写入操作中断(取消、重 置)时,应用程序无法再访问块数据
8.如果启用,Fee_EraseImmediateBlock函数将使已写入的引用块无效。否则,块将保持原样
9.不建议开启
10.如果启用了该参数,Fee将未写入的块标记为INVALID,否则Fee将未写入的块标为INCONSISTENT
1.配置ClusterGroup,此处只配置一个组
1.配置Cluster,此处是配置两个,平分Dflash,每个Cluster64K,双扇区算法。
1.对MemAcc地址的引用,对应MemAcc中的SubAddressArea
2.其值应等于配置的子地址区域在配置的子寻址区域列表中的位置,对应MemAcc中的SubAddressArea Index
1.根据我们的需求,来配置所需要的block
1.选择ClusterGroup ID
2.配置blcok ID
3.配置block的大小(字节)
4.是否支持立即写
5.此块所需的写入周期数。
6.对应MemAcc中的SubAddressArea Index
3.4 Mcl
1.需要使用到cache,因此需要在此使能
至此,配置全部完成
4.代码调试
可以通过写入再读取的方式、结合IDE的界面查看对应内存的值来验证正确性。
const uint8 DataBlock0[4] = {0x00,0x01,0x02,0x03};
uint8 DataReceive[2] = {0x0,0x0};
int main_c0(void)
{
MemIf_StatusType status = MEMIF_IDLE;
Std_ReturnType RetValue = E_NOT_OK;
/* Init MemAcc */
MemAcc_Init(NULL_PTR);
/* Init Fee */
Fee_Init(NULL_PTR);
do
{
Fee_MainFunction();
MemAcc_MainFunction();
status = Fee_GetStatus();
} while (status != MEMIF_IDLE);
/* Init fee success */
/*Write data to block 0*/
RetValue = Fee_Write(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_0, DataBlock0);
/*Perform write data to Block 0*/
do
{
Fee_MainFunction();
MemAcc_MainFunction();
status = Fee_GetStatus();
} while (status != MEMIF_IDLE);
/* Write operation success */
/*Read data block 0, from offset 2 and length is 2*/
RetValue = Fee_Read(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_0, 2, DataReceive, 2);
/*Perform read data form Block 0*/
do
{
Fee_MainFunction();
MemAcc_MainFunction();
status = Fee_GetStatus();
} while (status != MEMIF_IDLE);
Fee_ExampleAssert(MEMIF_JOB_OK == Fee_GetJobResult());
/*Check Data*/
if((DataReceive[0] != DataBlock0[2])|| (DataReceive[1] != DataBlock0[3]))
{
/*error*/;
}
else
{
/*do nothing*/
}
}
5.展望
对于原理处写的很简陋,基本就那几个操作,后面有必要再展开记录一下,或者再后续记录NVM的时候再详细写一下。