前言
NVM這塊還只停留在按需求配置階段,遇到問題不能深入分析。本系列文章將從遇到的問題或者提出的疑問著手,一起來更深入學習AUTOSAR架構下的存儲協議棧。目前遇到的問題和疑問如下:
1.通過RTE接口連續讀寫NVMBlock會影響其他Block的讀寫嗎?
2.沒有配置NVMRom Block的default value且沒有寫過NVM Block,通過RTE接口去讀NVMBlock讀到的是什么值?
3.Fee模塊中的Block的長度更改會造成NVM Block在Flash中重排嗎?中間插入NVMBlock了?
本文使用的AUTOSAR配置工具為:Vector公司的Davinci工具
正文
1.應用SWC讀寫NVMBlock的兩種方式
入下圖所示,Vector的AUTOAR配置工具Davinci Developer提供兩種方式配置SWC使用NVM的服務。
方式一:SWC和NVM直連。NVM提供5個標準接口供SWC使用,SWC可以直接通過C-S接口直接讀寫NVMBlock。這種方式適用于NVMBlock僅被一個SWC訪問的場景。
圖1:SWC和NVM直連
如下圖所示,在Developer中直接配置SWC中的Service Need的NvM_Block_NeeDs即可配置NVM服務。
圖2:配置NvMBlockNeed
最后在Rte_SWC.h生成的讀寫NvM_Block的接口如下所示,Rte接口直接封裝了NvM的標準接口(NvM_ReadBlock, NvM_WriteBlock, NvM_GetErrorStates)。
# define Rte_Call_NvMService_AC3_SRBS_NvBlockNeed_ PortName _GetErrorStatus(arg1) (NvM_GetErrorStatus((NvM_BlockIdType)57, arg1)) # define Rte_Call_NvMService_AC3_SRBS_NvBlockNeed_ PortName _ReadBlock(arg1) (NvM_ReadBlock((NvM_BlockIdType)57, arg1)) # define Rte_Call_NvMService_AC3_SRBS_NvBlockNeed_ PortName _WriteBlock(arg1) (NvM_WriteBlock((NvM_BlockIdType)57, arg1))
方式二:SWC通過NonvolatileMemoryBlock訪問NVM。Davinci提供一個類似NvM User的NonvolatileMemoryBlock的中間模塊,SWC配置普通的S-R接口和NonvolatileMemoryBlock連接,NonvolatileMemoryBlock模塊通過標準的S-R接口和NvM連接。SWC通過NonvolatileMemoryBloc來讀寫NvM模塊。這種方式適用于多個SWC訪問同一個NvM Block的場景。
圖3:SWC通過NonvolatileMemoryBlock訪問NVM
如下圖所示,使用方式二需要配置中間NonvolatileMemoryBlock模塊,然后在SWC和NonvolatileMemoryBlock分別配置S-R的讀寫接口,然后進行連接即可。
圖4:配置NoVolatileMemoryBlock
如下所示,生成的Rte_SWC.h中SWC讀寫NvM Block的接口僅僅是讀寫一個名為Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName全局變量。
FUNC(Std_ReturnType, RTE_CODE) Rte_Read_SWC_Pp_NvData_PortName_Rx_Element_NvData_PortName(P2VAR(uint8, AUTOMATIC, RTE_SWC_APPL_VAR) data) /* PRQA S 1505, 3206 */ /* MD_MSR_Rule8.7, MD_Rte_3206 */ { Std_ReturnType ret = RTE_E_OK; /* PRQA S 2981 */ /* MD_MSR_RetVal */ Rte_DisableOSInterrupts(); /* PRQA S 1881, 4558 */ /* MD_Rte_Os, MD_Rte_Os */ Rte_MemCpy(data, Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName, sizeof(NvData_PortName)); /* PRQA S 0314, 0315, 0316 */ /* MD_Rte_0314, MD_Rte_0315, MD_Rte_0316 */ Rte_EnableOSInterrupts(); /* PRQA S 1881, 4558, 2983 */ /* MD_Rte_Os, MD_Rte_Os, MD_Rte_2983 */ return ret; } FUNC(Std_ReturnType, RTE_CODE) Rte_Write_SWC_Pp_NvData_PortName_Tx_Element_NvData_PortName(P2CONST(uint8, AUTOMATIC, RTE_SWC_APPL_DATA) data) /* PRQA S 1505, 2982 */ /* MD_MSR_Rule8.7, MD_Rte_2982 */ { Std_ReturnType ret = RTE_E_OK; /* PRQA S 2981 */ /* MD_MSR_RetVal */ Rte_DisableOSInterrupts(); /* PRQA S 1881, 4558 */ /* MD_Rte_Os, MD_Rte_Os */ Rte_MemCpy(Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName, data, sizeof(NvData_PortName)); /* PRQA S 0314, 0315, 0316 */ /* MD_Rte_0314, MD_Rte_0315, MD_Rte_0316 */ Rte_OsApplication_QM_Core0_DirtyFlags.Rte_DirtyFlag_NonVolatileMemoryBlock_NVBlockDescriptor_PortName = 1U; Rte_EnableOSInterrupts(); /* PRQA S 1881, 4558, 2983 */ /* MD_Rte_Os, MD_Rte_Os, MD_Rte_2983 */ /* scheduled trigger for runnables: NonVolatileMemoryBlockTriggerRunnable */ (void)SetEvent(AplTask_10ms_Core0, Rte_Ev_Run_NonVolatileMemoryBlock_NonVolatileMemoryBlockTriggerRunnable); /* PRQA S 3417 */ /* MD_Rte_Os */ return ret; }
然后NvM也通過Rte_SetMirror_xxx/Rte_GetMirror_xxx接口來更新這個Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName全局變量。
FUNC(Std_ReturnType, RTE_CODE) Rte_SetMirror_NonVolatileMemoryBlock_NVBlockDescriptor_PortName(P2CONST(void, AUTOMATIC, RTE_APPL_DATA) NVMBuffer) /* PRQA S 3112 */ /* MD_Rte_3112 */ { Std_ReturnType ret = E_NOT_OK; /* PRQA S 2981 */ /* MD_MSR_RetVal */ CONST(uint16_least, RTE_CONST) size = sizeof(NvData_PortName); if (size <= 100U) /* PRQA S 2991, 2995 */ /* MD_Rte_2991, MD_Rte_2995 */ /* COV_RTE_NVMBUFFER_SIZE */ { Rte_DisableOSInterrupts(); /* PRQA S 1881, 4558 */ /* MD_Rte_Os, MD_Rte_Os */ Rte_MemCpy32(Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName, NVMBuffer, size); /* PRQA S 0314, 0315, 0316 */ /* MD_Rte_0314, MD_Rte_0315, MD_Rte_0316 */ Rte_EnableOSInterrupts(); /* PRQA S 1881, 4558, 2983 */ /* MD_Rte_Os, MD_Rte_Os, MD_Rte_2983 */ ret = E_OK; } return ret; } FUNC(Std_ReturnType, RTE_CODE) Rte_GetMirror_NonVolatileMemoryBlock_NVBlockDescriptor_PortName(P2VAR(void, AUTOMATIC, RTE_APPL_VAR) NVMBuffer) /* PRQA S 3112 */ /* MD_Rte_3112 */ { ???Std_ReturnType?ret?=?E_NOT_OK;?/*?PRQA?S?2981?*/?/*?MD_MSR_RetVal?*/ CONST(uint16_least, RTE_CONST) size = sizeof(NvData_PortName); if (size <= 100U) /* PRQA S 2991, 2995 */ /* MD_Rte_2991, MD_Rte_2995 */ /* COV_RTE_NVMBUFFER_SIZE */ { Rte_DisableOSInterrupts(); /* PRQA S 1881, 4558 */ /* MD_Rte_Os, MD_Rte_Os */ Rte_MemCpy32(NVMBuffer, Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName, size); /* PRQA S 0314, 0315, 0316 */ /* MD_Rte_0314, MD_Rte_0315, MD_Rte_0316 */ Rte_EnableOSInterrupts(); /* PRQA S 1881, 4558, 2983 */ /* MD_Rte_Os, MD_Rte_Os, MD_Rte_2983 */ ret = E_OK; } return ret; }
2.RAMBlock和ROMBlock
RAMBlock最后在代碼中體現就是一個全局變量(存放在棧Ram中),ROMBlock在代碼中體現就是一個const 全局變量(存放在Data Flash中)。RamBlock是一定會有的,Rom Block是可配置的。其中最主要的就是default value的配置。
方式1配置RAMBlock和ROMBlock:
方式1是SWC通過NvM Block Needs直接訪問NvM,如果配置了Default Value就配置了ROMBlock。
圖5:NvM Block Needs配置RAM和ROM Block
方式2配置RAMBlock和ROMBlock:
方式2是SWC通過NonvolatileMemoryBlock間接訪問NvM,Rom Block通過勾選來配置,InitValue也就是Default value也可配置。
圖5:NonvolatileMemoryBlock配置RAM和ROM Block
RAMBlock對應的代碼:
VAR(Nvdata_PortName, RTE_VAR_INIT) Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName;
ROMBlock對應的代碼:
CONST(Nvdata_PortName, RTE_CONST_DEFAULT_RTE_CDATA_GROUP) Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName_ROM_NVBlockDescriptor_PortName = { 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U };
關于Default Value:
如SWS_NvM_00388所訴,在ECU啟動階段,如果配置了ROMBlock,NvM_ReadAll會把ROMblock中的default value拷貝到RAMBlock。
如SWS_NvM_00085所述,如果沒有配置ROMBlock,則RAMBlock的值需要應用來保證。在Davinci Developer中可以配置RAM Block的Init Value,也可以讓RAMBlock有一個初始值。
如果,我們既沒有配置RAMBlock的Initvalue,也沒有配置ROMBlock,也從沒有寫過NvBlock,那么我們第一次去讀的RAMBlock是.BSS段的一個全局變量(未初始化的全局變量在.BSS段),而MCU上電后會對.BSS段作清零的操作,也就是說我們會讀到0。
3. 應用SWC讀寫NVM過程
SWC通過NonvolatileMemoryBlock讀NvM Ram Block:SWC就是讀一個名為Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName的全局變量,也就是RAMBlock。
FUNC(Std_ReturnType, RTE_CODE) Rte_Read_SWC_Pp_NvData_PortName_Rx_Element_NvData_PortName(P2VAR(uint8, AUTOMATIC, RTE_SWC_APPL_VAR) data) /* PRQA S 1505, 3206 */ /* MD_MSR_Rule8.7, MD_Rte_3206 */ { Std_ReturnType ret = RTE_E_OK; /* PRQA S 2981 */ /* MD_MSR_RetVal */ Rte_DisableOSInterrupts(); /* PRQA S 1881, 4558 */ /* MD_Rte_Os, MD_Rte_Os */ Rte_MemCpy(data, Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName, sizeof(NvData_PortName)); /* PRQA S 0314, 0315, 0316 */ /* MD_Rte_0314, MD_Rte_0315, MD_Rte_0316 */ Rte_EnableOSInterrupts(); /* PRQA S 1881, 4558, 2983 */ /* MD_Rte_Os, MD_Rte_Os, MD_Rte_2983 */ return ret; }
SWC通過NonvolatileMemoryBlock寫NvM Ram Block:SWC就是寫一個名為Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName的全局變量,也就是RAMBlock。同時會設置一個更新Flag,SetEvent觸發一個OSTask事件。
FUNC(Std_ReturnType, RTE_CODE) Rte_Write_SWC_Pp_NvData_PortName_Tx_Element_NvData_PortName(P2CONST(uint8, AUTOMATIC, RTE_SWC_APPL_DATA) data) /* PRQA S 1505, 2982 */ /* MD_MSR_Rule8.7, MD_Rte_2982 */ { Std_ReturnType ret = RTE_E_OK; /* PRQA S 2981 */ /* MD_MSR_RetVal */ Rte_DisableOSInterrupts(); /* PRQA S 1881, 4558 */ /* MD_Rte_Os, MD_Rte_Os */ Rte_MemCpy(Rte_NonVolatileMemoryBlock_NVBlockDescriptor_PortName, data, sizeof(NvData_PortName)); /* PRQA S 0314, 0315, 0316 */ /* MD_Rte_0314, MD_Rte_0315, MD_Rte_0316 */ Rte_OsApplication_QM_Core0_DirtyFlags.Rte_DirtyFlag_NonVolatileMemoryBlock_NVBlockDescriptor_PortName = 1U; Rte_EnableOSInterrupts(); /* PRQA S 1881, 4558, 2983 */ /* MD_Rte_Os, MD_Rte_Os, MD_Rte_2983 */ /* scheduled trigger for runnables: NonVolatileMemoryBlockTriggerRunnable */ (void)SetEvent(AplTask_10ms_Core0, Rte_Ev_Run_NonVolatileMemoryBlock_NonVolatileMemoryBlockTriggerRunnable); /* PRQA S 3417 */ /* MD_Rte_Os */ return ret; }
NonvolatileMemoryBlock的Runnable被Event事件激活調用NvM_WriteBlock:
RTE_LOCAL FUNC(void, RTE_CODE) NonVolatileMemoryBlockTriggerRunnable(void) { if (Rte_OsApplication_QM_Core0_DirtyFlags.Rte_DirtyFlag_NonVolatileMemoryBlock_NVBlockDescriptor_PortName == 1U) { NvM_RequestResultType NvM_ErrorStatus = 0; (void)NvM_GetErrorStatus(NvMConf_NvMBlockDescriptor_NonVolatileMemoryBlockNVBlockDescriptor_PortName, &NvM_ErrorStatus); if (NvM_ErrorStatus != NVM_REQ_PENDING) { Rte_DisableOSInterrupts(); /* PRQA S 1881, 4558 */ /* MD_Rte_Os, MD_Rte_Os */ Rte_OsApplication_QM_Core0_DirtyFlags.Rte_DirtyFlag_NonVolatileMemoryBlock_NVBlockDescriptor_PortName = 0; Rte_EnableOSInterrupts(); /* PRQA S 1881, 4558, 2983 */ /* MD_Rte_Os, MD_Rte_Os, MD_Rte_2983 */ (void)NvM_WriteBlock(NvMConf_NvMBlockDescriptor_NonVolatileMemoryBlockNVBlockDescriptor_PortName, NULL_PTR); } else { Rte_DisableOSInterrupts(); /* PRQA S 1881, 4558 */ /* MD_Rte_Os, MD_Rte_Os */ Rte_NvBlockPendingFlags.Rte_NvBlockPendingFlag_NonVolatileMemoryBlock_NVBlockDescriptor_PortName = 1U; Rte_EnableOSInterrupts(); /* PRQA S 1881, 4558, 2983 */ /* MD_Rte_Os, MD_Rte_Os, MD_Rte_2983 */ } } }
NvM_WriteBlock調用NvM_QueueJob將Job排隊:
FUNC(Std_ReturnType, NVM_PUBLIC_CODE) NvM_WriteBlock(NvM_BlockIdType BlockId, P2CONST(void, AUTOMATIC, NVM_APPL_DATA) NvM_SrcPtr) { Std_ReturnType returnValue = E_NOT_OK; uint8 detErrorId = NVM_E_NO_ERROR; const NvM_RamMngmtPtrType NvM_RamMngmt_ptloc = NvM_GetMngmtAreaPtr(BlockId); if(NvM_WriteProtectionChecks(NvM_RamMngmt_ptloc) == TRUE) /* SBSW_NvM_FuncCall_PtrParam_BlockMngmtArea */ { /* PRQA S 0311, 0316 1 */ /* MD_NvM_11.5_CastLossOfConst, MD_NvM_11.5_JobQueue_CastVoidPtrToObjPtr */ if (NvM_QueueJob(BlockId, NVM_INT_FID_WRITE_BLOCK, (NvM_RamAddressType)NvM_SrcPtr)) /* SBSW_NvM_FuncCall_PtrParam_QueueJob */ { if(NvM_SrcPtr==NULL_PTR) { NvM_EnterCriticalSection(); NvM_RamMngmt_ptloc->NvRamAttributes_u8|=(NVM_STATE_VALID_SET|NVM_STATE_CHANGED_SET);/*SBSW_NvM_AccessBlockManagementArea*/ NvM_ExitCriticalSection(); } returnValue = E_OK; } } return returnValue; }
NvM_QueueJob會判斷隊列是否已經Full,如果Full則Job被丟棄,如果沒有Full,則Job入隊列:
FUNC(boolean, NVM_PRIVATE_CODE) NvM_QueueJob(NvM_BlockIdType BlockId, NvM_InternalServiceIdType ServiceId, NvM_RamAddressType RamAddress ) { boolean retVal = FALSE; boolean queueFull; boolean blockAlreadyPending; /* get block management area */ const NvM_RamMngmtPtrType ramMngmtPtr = ((BlockId & NVM_DCM_BLOCK_OFFSET) != 0u) ? (&NvM_DcmBlockMngmt_t) : (&NvM_BlockMngmtArea_at[BlockId]); #if(NVM_JOB_PRIORISATION == STD_ON) const uint8 priority = (uint8)NvM_BlockDescriptorTable_at[NVM_BLOCK_FROM_DCM_ID(BlockId)].BlockPrio_u8; /* NvM_HighPrioQueue is only the right queue if block has an immediate priority, current job is a write-job and requested block is not a DCM-Block. Otherwise NvM_NormalPrioQueue is right queue. */ P2VAR(NvM_JobQueueType, AUTOMATIC, NVM_PRIVATE_DATA) usedQueue = ((priority == 0u) && (ServiceId == NVM_INT_FID_WRITE_BLOCK) && ((BlockId & NVM_DCM_BLOCK_OFFSET) == 0u)) ? (&NvM_HighPrioQueue) : (&NvM_NormalPrioQueue); #else P2VAR(NvM_JobQueueType, AUTOMATIC, NVM_PRIVATE_DATA) usedQueue = &NvM_NormalPrioQueue; #endif /* #200 critical section (Reason: During accessing the job queue, it shall not be possible to access it from another task) */ NvM_EnterCriticalSection(); /* check queue fill status before queuing the block! */ queueFull = (usedQueue->EmptyList == NVM_LIST_END); blockAlreadyPending = (ramMngmtPtr->NvRamErrorStatus_u8 == NVM_REQ_PENDING); /* #210 queue is not full and the requested block isn't already pending */ if((queueFull == FALSE) && (blockAlreadyPending == FALSE)) { /* #211 find next free element in queue */ const NvM_QueueEntryRefType elem = NvM_QueuePop(&usedQueue->EmptyList); /* SBSW_NvM_FuncCall_PtrParam_Queue */ CONSTP2VAR(NvM_QueueEntryType, AUTOMATIC, NVM_PRIVATE_DATA) elemPtr = &NvM_JobQueue_at[elem]; /* #212 setup and queue NvM job */ elemPtr->BlockId = BlockId; /* SBSW_NvM_AccessJobQueue */ elemPtr->RamAddr_t = RamAddress; /* SBSW_NvM_AccessJobQueue */ elemPtr->ServiceId = ServiceId; /* SBSW_NvM_AccessJobQueue */ #if(NVM_JOB_PRIORISATION == STD_ON) elemPtr->JobPrio = priority; /* SBSW_NvM_AccessJobQueue */ #endif NvM_QueuePush(&usedQueue->SrvList, elem); /* SBSW_NvM_FuncCall_PtrParam_Queue */ /* #213 set the block status to NVM_REQ_PENDING */ ramMngmtPtr->NvRamErrorStatus_u8 = NVM_REQ_PENDING; /* SBSW_NvM_AccessBlockManagementArea */ /* block queued and pending, return successfully */ retVal = TRUE; } return retVal; }
綜上所述,SWC讀寫NvM接口的時候,其實讀寫的是一個Ram Block的全局變量。調用寫NvM的接口時會觸發NonvolatileMemoryBlock的NonVolatileMemoryBlockTriggerRunnable被Os調用,NonVolatileMemoryBlockTriggerRunnable根據Block的寫Flag調用NvM_WriteBlock,NvM_WriteBlock中調用NvM_QueueJob,NvM_QueueJob根據隊列是否Full來將Job入隊列。
也就是說,如果有SWC一直在頻繁的調用寫NvM的接口,就會導致NvM的隊列Full,導致其他的SWC的寫NvM Block的請求被丟棄,最終對外表現是NvM存不進去。
Ps: 本人就遇到過隊列大小配的太小,導致SWC的NvM寫請求被丟棄的問題。。。
4. 問題回答
問題1:通過RTE接口連續讀寫NVMBlock會影響其他Block的讀寫嗎?
答:會。如果SWC連續寫NvM Block,且NvM模塊的隊列大小配置太小,就會導致NvM模塊的隊列Full后SWC的寫請求被丟棄。
問題2:沒有配置NVMRom Block的default value且沒有寫過NVM Block,通過RTE接口去讀NVMBlock讀到的是什么值?
答:要看有沒有配置NvM Ram Block的InitValue。如果配置NvM Ram Block的InitValue,那么讀到的就是NvM Ram Block的InitValue,如果沒有配置,讀到的就是0。
問題3:Fee模塊中的Block的長度更改會是的NVM Block在Flash中重排嗎?中間插入NVMBlock了?
答:還沒研究透徹,請關注本公眾號的后續文章。
5.總結
搞清楚了NVM的基本概念后,配置都比較容易。難的是出現問題后怎么查問題,因為NvM和Fee/EA,Fls/EEProm模塊的實現還是很復雜的,常見的問題如下:
nSWC開發者抱怨NvM數據存不進去
nSWC開發者抱怨NvM數據在ECU復位變成了默認值
nSWC開發者抱怨NvM數據在ECU復位后改成了異常值
如果,存儲協議棧配置本身沒有問題,常見的原因如下:
nNvM隊列配置太小,以及SWC在頻繁的請求寫NvM
nSWC在下電前把自己的Block寫成了默認值,這個時候,我們可以通過修改NvM Block的Default Value為另一個值來證明不是NvM本身的問題,是SWC自己寫了個Default value。
n用來接收NvM讀數據的SWC的全局變量復位后并不是直接從NvM讀取數據,而是從Back Ram(復位后內容不清除)中讀取數據,Back Ram異常后導致接收NvM數據的全局變量內容錯亂。
如果不是以上的問題,那就可能是NvM本身或者ECU系統出問題了,這就需要深入分析NvM源碼加調試了。
審核編輯:劉清
-
RAM
+關注
關注
8文章
1386瀏覽量
116516 -
AUTOSAR
+關注
關注
10文章
370瀏覽量
22328 -
ecu
+關注
關注
14文章
911瀏覽量
55349 -
NVM
+關注
關注
1文章
42瀏覽量
19314 -
Flash單片機
+關注
關注
0文章
111瀏覽量
9640
原文標題:AUTOSAR架構下NVM Block連續寫及Default Value問題分析
文章出處:【微信號:汽車電子嵌入式,微信公眾號:汽車電子嵌入式】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
NVRAM Manager(NVM)模塊詳細介紹
錯誤7發生于Get LV Class Default Value.vi
DEFAULT強制IDELAY_VALUE為0的FIXED和DEFAULT之間的區別是什么?
怎么使用程序存儲器作為NVM?
如何將NVM示例的讀取/寫入區域從32KB更改為4KB?
怎么讀寫NVM?
怎么寫一個文件保存在內部NVM?
IE Default Behaviors Handbook
什么是NVM
如何使用新型NVM存儲系統支持高并發訪問提高處理速度

盡可能限制NVM寫操作的數據庫日志方案NVRC

評論