小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE

小猫爪:S32K3学习笔记08-S32K3之MCAL的FEE模块

  • 1 前言
  • 2 Fls模块
  • 3 Fee模块
    • 3.1 存储结构
    • 3.2 常见操作步骤
      • 3.2.1 Cluster Scan
      • 3.2.2 Block Update
      • 3.2.3 Read Block Data
      • 3.2.4 Cluter swap
    • 3.3 一些小细节
      • 3.3.1 Fee Block Always Available
      • 3.3.2 Fee Swap Foreign Blocks Enabled
      • 3.3.3 Fee Enable Sector Retirement
      • 3.3.4 Fee Immediate Data
      • 3.3.5 Fee Virtual Page Size
  • 4 发生ECC错误导致的后果
  • 5 ECC错误的处理
    • 5.1 Disable Data Flash的ECC中断
    • 5.2 Hardfault中断处理
    • 5.3 OS任务处理
  • END

1 前言

  今天来简单说说MCAL中的FEE模块,因为S32K3的FEE模块在MCAL层应该是比较特殊的存在,因为它本身牵扯不到任何的硬件模块,而是一屁股坐在了Fls模块上,它负责发号施令,而Fls层负责累死累活。

2 Fls模块

  在了解Fee模块之前得最好先了解一下Fls模块,关于Fls模块的介绍,大家请参照一位资深大佬的文章,文章链接如下:
《AUTOSAR MCAL详解:FLS》,请仔细参考。

3 Fee模块

3.1 存储结构

  首先来看一下Cluster group, Cluster, Block, Sector之间的关系:
小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第1张图片

  从图中可以看出,Cluster Group可以理解成是一块大的存储块,然后它又包含若干个Cluster,一个Cluster Group最少有两个Cluster, 其中Cluster可以理解成是模拟EE的滚动大单元,而Block为最小滚动单元。众所周知,FEE就是损失空间来增大使用周期的应用,当我们存数据时,Block为最小存储单元,也为最小滚动单元,当在一个Cluster已经没有空间来创建新的Block时,就会触发Cluster的swap操作,将有效Block复制到下一个Cluster继续滚动,而当前Cluster则会无效。一个Cluster会由多个Flash物理扇区Sector组成,而组成一个Cluster的扇区必须是连续的。

  下图显示了一个Cluster的数据结构:
小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第2张图片
  在图中可以看到,一个Cluster由Cluster头,Block头和Block Data组成;其中Cluster头表明了其ID,状态,起始地址和大小;而Block头表明了其ID,状态,长度,起始地址偏移,而每个Block则是指向了其对应的data存储区。这里需要注意的是Invalid Flag,这一部分是没有被使用的,是保留的。至于checksum是用来检测头的数据正确性的,而Block Assignment是给下面的Fee Swap Foreign Blocks功能使用的。

还有一点很容易让人忽略,就是在Fee更新数据的时候,Block头的增长方向是由上而下的,而Data的更新方向其实是自下而上的,最终两者会在中间相遇。

  最后总结一下FEE存储数据的原理,首先就是Fee_init会完成Cluster的初始化(如果读取到有效Cluster,则会扫描Block并记录最后一条有效Block;如果没有有效Cluster,则会新建);当需要写数据时,则会新建一条Block来完成数据的更新,此为Block的滚动。随着Block的累加,当Cluster没有空间创建Block时,则会进行Cluster swap操作,此为Cluster的滚动。

3.2 常见操作步骤

3.2.1 Cluster Scan

  在使用Fee模块之前,Fee在初始化操作中会启动Cluster Scan操作,而这个操作就是为了找到有效Cluster,并且找到当前有效Block的地址指针,以及写操作的指针。

  1. 扫描有效的Cluster,如果扫描到两个及以上的有效Cluster,则取Cluster ID大的那个区域为有效Cluster。
  2. 依次扫描Block头,同时记录最后一个有效Block头。直到扫描到无Block头的区域,标记写指针地址,并结束扫描。

3.2.2 Block Update

  Block Update操作即为Fee_Write操作,它的流程如下:

  1. 新建Block头
  2. 写数据到目标地址
  3. 标记Blcok头为有效

3.2.3 Read Block Data

  Read Block Data操作即为Fee_Read操作,它的流程如下:

  1. 读取Block头
  2. 读取Block头中的目标地址处的数据
  3. 读取该Block头的Valid Flag,如果为0x81,则数据有效

3.2.4 Cluter swap

  以下四种情况会触发Swap操作:

  1. 当前Cluster没有空余空间
  2. FEE初始化操作失败
  3. Scan发现Header不合法(Checksum有误,未知的Block ID,Blcok与配置的不匹配
  4. 立即数检查到有已有立即数操作

  Swap操作的流程如下:

  1. 擦除下一个Cluster所在的扇区 (ERASING stage 1)
  2. 在下一个Cluster所在的扇区中新建Cluster头,不包括Valid Flag(FORMATTING stage 2)
  3. 将当前Cluster中的最后一个有效Blcok头和所对应的数据复制到新Cluster中(COPYING stage 3)
  4. 写新Cluster头中的Valid Flag,标记新Cluster为有效 (ACTIVE stage 4)
  5. 在新的Cluster完成Fee读写操作(UPDATING stage 5)

  下表显示了在swap的5个阶段,新老Cluster的状态变化:
在这里插入图片描述

3.3 一些小细节

  在配置MCAL的时候,会有一些选项让人很迷惑,接下来看看有哪些选项需要注意。

3.3.1 Fee Block Always Available

在这里插入图片描述
  这个选项是什么意思呢?如果你在更新数据的时候,一些出乎意料的破坏性事件,比如掉电,异常等,导致最后一步的valid flag没有标记成功,那么当上电去扫描Blcok的时候,则会受到这个选项影响。

  1. 如果OFF,当扫描到无效Block头时,即该Block头Valid flag无效,则Fee会认定该数据无效,会重新初始化该数据。
  2. 如果ON,当扫描到无效Block头时,即该Block头Valid flag无效,则Fee会查找历史Block,找到历史数据。

3.3.2 Fee Swap Foreign Blocks Enabled

小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第3张图片
  这个功能是为了两个应用会访问同一个Cluster的场景而设计的,比如Bootloader和APP都得访问一个Cluster,那么就可以通过这个来实现。

  如果使能这个功能,就可以在每个Block的配置中配置其的归属权限,其中SHARD即为Bootloader和APP都可以访问。如下图:
小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第4张图片
  这个功能会增加Cluster Swap的时间。

3.3.3 Fee Enable Sector Retirement

在这里插入图片描述
  这个功能则是实现了Fee的坏块管理功能,而Fee Sector Erase Retries (0 -> 255) 则可指定尝试读取的次数,一当读取失败的次数超过了这个值,则Fee会标记这个Sector为坏块。下次读写Block时,则会自动跳过坏块。

3.3.4 Fee Immediate Data

在这里插入图片描述
  这个功能是为了避免一种情况而设计的。就是在更新Block的时候,发现当前Cluster没有空间了,这个时候就会触发Cluster Swap功能,这就导致了整个更新Block的时间过长。而这个功能其实就是在Cluster中预留一块空间,而这块空间能够容纳一条所有的Immediate Block。 写block时,当发现当前Cluster的空间不够时,则会将新的Block写到这个预留空间里,随后调用一个检测函数(Fee_EraseImmediateBlock)去检测,如果一旦检测到预留空间被使用了,则触发Cluster Swap。这样做的意义就是将Fee Write操作与Fee Swap操作分离,让Fee Write操作快速完成。

  下图中的这个选项,暂时无用,不勾选。
在这里插入图片描述

3.3.5 Fee Virtual Page Size

在这里插入图片描述
  这个选项非常重要,决定了Block data的最小操作单元,举个例子,如果将一个Block的大小设置为8个字节,那么在flash中,一个Block的data部分不只是会占用8个字节,而是会占32个字节。默认最小设置成32个字节,因为这跟ECC检测字长有关。

4 发生ECC错误导致的后果

  因为Fee操作的对象是FLASH, 所以如果Fee在写flash的时候被事件打断则很容易造成FLASH出现ECC错误。那么一旦Fee操作时被打断(如断电),Flash发生了ECC错误,那么在下次重启,会对系统造成什么样的影响呢?

  1. ECC错误发生在Cluster头处
      如果Fee在写Cluster头时,发生了ECC错误,则会导致整个Custer无效,当Fee扫描Cluster时,会直接擦除整个Custer区域。

  2. ECC错误发生在Block头和Valid Flag处
      如果Fee在写Block头或者Valid Flag时,发生了ECC错误,使得Block头或者Valid Flag数据错误,则会在上电初始化的时候,则会标记当前Cluster无效,在下次进行Block写操作的时候,直接触发Swap操作,直接抛弃当前的Cluster。不仅仅是ECC错误,如果Block不合法(Checksum有误,未知的Block ID,Blcok与配置的不匹配),同样会触发Swap。

  3. ECC错误发生在Data区域处
      如果ECC错误出现在data区,而其对应的Block头是有效的,这对整个Fee状态机没有任何影响。唯一的影响只是会丢失当前的这一个数据,而历史数据是可以保留的。

5 ECC错误的处理

  在Fee操作Data Flash的期间,如果发生了ECC错误,那么针对这种情况,MCU如何去救急呢?下面列出三种解决方案。

5.1 Disable Data Flash的ECC中断

  S32K3片上是有Code flash和Data flash两块flash的,而它们两者如果出现ECC错误的话,都可以导致S32K3进入Hardfault中断,但是这个中间过程有个区别,如下图:
小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第5张图片
  Code flash发生ECC错误导致HardFault只需要经过一个开关,而Data flash发生ECC错误导致HardFault则需要经过两个开关。

  这第一个解决方法就是断开其中一个开关Gate-PFCR4[DERR_SUP],让Data flash即使发生了ECC错误也不会导致K3进入HardFault,当Fee触发初始化或者swap操作时,就会触发擦操作,从而消除ECC错误。

  而在MCAL的配置中实现这一点,只需要在Fls模块中使能Fls Data Error Suppression这个选项就可以了。
小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第6张图片

5.2 Hardfault中断处理

  这种方法是建立在打开HardFault中断的基础上的,基本思路就是,当CPU执行操作Flash的指令时触发了HardFault中断,然后在中断中进行相关判断后,强行让CPU跳过这条会触发ECC错误的指令,让CPU从HardFault返回后能继续运行下去。

  基本流程图如下:
小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第7张图片
  而在MCAL的配置中实现这一个方法,则需要在Fls模块使能Fls ECC Handling HardfaultHandler这个选项。
小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第8张图片
  另外还需要在HardFault中实现处理代码,除了实现CPU指令的强行跳转外,还需要调用调用函数**Fls_DsiHandler()**来更新Fls的状态机。

下面贴出HardFault处理代码:
(**注:**在代码中,需要获得data_pt ,syndrome_u32 这两个参数,这两个参数可通过K3的ERM模块去获取,我这里为了简单,就直接简单化了,后期通过ERM获取该参数待更新。)

void HardFault_Handler(void)
{
	register uint32_t Reg;

	uint32_t *LR_Pointer = NULL;
	Fls_ExceptionDetailsType  ExceptionDetails;
	Fls_CompHandlerReturnType res;

	__asm volatile ("mov %0, r14\n" : "=r" (Reg) );

	if((Re2TM_from_FPS_PSP_to_use_PSP!=Reg)&&(Re2TM_from_nFPS_PSP_to_use_PSP!=Reg))
	{
		__asm volatile ("MRS %0, msp\n" : "=r" (Reg) );
	}
	else
	{
		__asm volatile ("MRS %0, psp\n" : "=r" (Reg) );
	}
	LR_Pointer =(uint32_t *) (Reg + 0x18 + 0x18);
	//第一个0x18指的是在栈内PC与R1之间的偏移,这是固定的
	//第二个0x18指的是Hardfault函数为保护现场压栈的大小,可通过观察汇编代码得到PUSH的大小

	if(S32_SCB->HFSR)
	{
		S32_SCB->HFSR = S32_SCB->HFSR;
	}
	
	//data_pt 可由ERM得到,这里为了简单
	ExceptionDetails.data_pt = S32_SCB->BFAR;
	//syndrome 可由ERM得到,这里为了简单
	ExceptionDetails.syndrome_u32 = C40_DSI_EXC_SYNDROME;

	res = Fls_DsiHandler(&ExceptionDetails);
	if(FLS_HANDLED_SKIP == res)
	{
		*LR_Pointer += 4;
		return;
	}
	else
	{
		while(1);
	}
}

5.3 OS任务处理

  这种方法的本质其实就是利用OS的多任务管理功能来处理。基本思路如下图所示:
小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第9张图片
  基本流程为:

  1. Main Task发起Fee操作
  2. Fee底层会直接调用一个Callout函数,而这个Callout函数就会调用OS API创建一个新任务Small Task,而在Small Task中完成Fls层的Flash操作,让Main Task与操作Flash相关的代码隔离。
  3. 如果Small Task在操作Flash时没有发生ECC错误,则操作成功后自动删除Small Task,如果发生了ECC错误,则Small Task被打断并删除,再回到Main Task中进行错误处理。

  而在MCAL的配置中实现这一个方法,则需要在Fls模块使能Fls ECC Handling ProtectionHook这个选项。
小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第10张图片
  然后需要定义Fls Read Function Callout函数,如下:
小猫爪:S32K3学习笔记08-S32K3之MCAL中的FEE_第11张图片
  而这个函数是需要用户自己去实现的,其需要实现的功能主要是调用OS API创建一个新任务。而在这个新任务中需要去调用Fls模块的底层函数Fls_ReadEachBlock来完成与Flash相关的操作。

  如果在执行Fls_ReadEachBlock发生ECC错误,则客户需要自己调用ProtectionHook()来处理,ProtectionHook()也是需要用户自己去实现的,其中必须要做的事情就是结束Small Task。而用户对于错误的处理,必须要调用函数Fls_DsiHandler()来更新Fls的状态机。

END

你可能感兴趣的:(S32K3学习笔记,学习,S32K3,FEE)