AUTOSAR中的NvM看起来挺难的,特别是在配置的时候,一堆参数,都不知道是干什么用的。想去研究它,却不知如何下手。
其实吧,AUTOSAR的官方文档讲的挺详细的,但是内容很多,网上有很多文章对其进行解读,很多人没时间也不想细读它。
本文打算换个讨论,不再累述其内容细节了,而通过图解的方式,将NvM重要特点提取出来讲解,方便理解,也方便记忆。
AUTOSAR中有个Memory的处理,在架构中是这样的

分类三层,分别是Memory Service(NvM)、Memory Hardware Abstraction(Memory If和Fee/Ea)和Memory Driver(Fls/Eep驱动),单独拎出来就是这样的

这部分在架构中有这样的接口关系

回到Nvm,这个NVM即Non-VolatileRAM Manager (NVRAM Manager),用于管理存储于各类non-volatile memory(如EEPROM/Flash等)的数据。
NVM在AUTOSAR中是独立于硬件的,通过底层接口访问外部EEPROM或模拟成EEPROM的DataFlash。为了适用于所有的车载系统,其具有高度可扩展性和可靠性。
AUTOSAR中的Memory是怎么寻址的呢?
其实,MemoryAbstraction Interface为底层EEPROM/FEE提供32位地址空间,像这样

这个虚拟的逻辑地址是包含着一些信息的,如NV Block number和Dataset index,还有Block address offset等,他们的关系可以通过以下公式来理解
FEE/EA_BLOCK_NUMBER=(NvMNvBlockBaseNumber << NvMDatasetSelectionBits) + DataIndex
举个例子,假如NVM_DATASET_SELECTION_BITS配置为4bits,那么就有14bits留给blocknumber,因此可以分最大有16个dataset。

以上提到的Dataset、NV Block等概念,是不是容易混淆,也不清楚是干什么用的?好了,下面做个汇总和介绍。
| 名称 | 描述 |
| Basic Storage Object | Basic Storage Object是一个最小的NVRAM block,多个Basic Storage Objects可以组成一个NVRAM Block。 |
| NVRAM Block | NVRAM Block是管理和存储 NV Block所需的整个结构 |
| NV data | 要存储在Non-Volatile memory的数据 |
| Block Management Type | 这个是NVRAM Block的类型,它取决于强制/可选Basic Storage Object中 NVRAM Block的(可配置的)单独组成以及该 NVRAM 块的后续处理。 |
| RAM Block | 属于Basic Storage Object,它是NVRAM Block的一部分,常驻于RAM空间。 |
| ROM Block | 属于Basic Storage Object,它是NVRAM Block的一部分(可选的),常驻于ROM空间。 |
| NV Block | 属于Basic Storage Object,它是NVRAM Block的一部分(必选的),常驻于NV空间。 |
| NV Block Header | 如果Static Block ID是enable的,这个作为附加信息包含在NV Block中。 |
| Administrative Block | 属于Basic Storage Object,它常驻于RAM,是NVRAM Block必选部分。 |
用一个图来表达就是这样的

图上得这几个block,用图示来看可能会更容易理解

NVM在AUTOSAR中的功能是挺完善的,要满足车载系统的各种需求,例如数据块错了怎么办等等,都考虑周全。
NvM支持三种类型实现:Native、Redundant和Dataset。那么这三种类型是怎样的呢?
我做了个表,他们包含了哪几种block
| NV Block | RAM Block | ROM Block | Administrator Block | |
| Native | 1 | 1 | 0..1 | 1 |
| Redundant | 2 | 1 | 0..1 | 1 |
| Dataset | 1..255 | 1 | 0..n | 1 |
Native
这种最简单,平时大多情况下,我们用的就是这种
它包含就简单的NV Block,其他如Header和CRC都是随意可选配置。

Redundant
这种就复杂一点点,可以简单理解为双备份,意思是,如有一份数据错了,可以用备份的那块。

Dataset
Dataset呢,这个不是多备份哈,而是针对不同配置做了对应。例如一个车系统平台有很多种配置,但有想集成在同一个软件种,那么就有很多个对应的配置数据,在运行的时候根据配置参数选择哪份数据使用。

讲到NvM的API,其实NvM API分三类:
Class3:所有指定的API调用都可用,最多支持功能。
Class2:有一组中间API调用可用。
Class1:特别是对于硬件资源非常有限的匹配系统,此API配置类仅提供最少的一组
Class 3 包含下面这些API:Type 1:- NvM_SetDataIndex(...)- NvM_GetDataIndex(...)- NvM_SetBlockProtection(...)- NvM_GetErrorStatus(...)- NvM_SetRamBlockStatus(...)- NvM_SetBlockLockStatusType 2:- NvM_ReadBlock(...)- NvM_WriteBlock(...)- NvM_RestoreBlockDefaults(...)- NvM_EraseNvBlock(...)- NvM_InvalidateNvBlock(...)- NvM_CancelJobs(…)- NvM_ReadPRAMBlock(...)- NvM_WritePRAMBlock(...)- NvM_RestorePRAMBlockDefaults(...)Type 3:- NvM_ReadAll(...)- NvM_WriteAll(...)- NvM_CancelWriteAll(...)- NvM_ValidateAll(...)Type 4:- NvM_Init(...)Class 2 包含下面这些API:Type 1:- NvM_SetDataIndex(...)- NvM_GetDataIndex(...)- NvM_GetErrorStatus(...)- NvM_SetRamBlockStatus(...)- NvM_SetBlockLockStatusType 2:- NvM_ReadBlock(...)- NvM_WriteBlock(...)- NvM_RestoreBlockDefaults(...)- NvM_CancelJobs(…)- NvM_ReadPRAMBlock(...)- NvM_WritePRAMBlock(...)- NvM_RestorePRAMBlockDefaults(...)Type 3:- NvM_ReadAll(...)- NvM_WriteAll(...)- NvM_CancelWriteAll(...)- NvM_ValidatedAll(...)Type 4:- NvM_Init(...)Class 1 包含下面这些API:Type 1:- NvM_GetErrorStatus(...)- NvM_SetRamBlockStatus(...)- NvM_SetBlockLockStatusType 2:- --Type 3:- NvM_ReadAll(...)- NvM_WriteAll(...)- NvM_CancelWriteAll(...)Type 4:- NvM_Init(...)
也许你会好奇,上面提到的Type是啥?
其实是根据功能分类而已,可以参考下表内容
| Request Types | Characteristics of Request Types |
| Type 1: - NvM_SetDataIndex (...) - NvM_GetDataIndex (...) - NvM_SetBlockProtection (...) - NvM_GetErrorStatus(...) - NvM_SetRamBlockStatus(...) | n同步请求 n作用于一个RAM Block n适用于所有SWC |
| Type 2: - NvM_ReadBlock(...) - NvM_WriteBlock(...) - NvM_RestoreBlockDefaults(...) - NvM_EraseNvBlock(...) - NvM_InvalidateNvBlock(...) - NvM_CancelJobs(…) - NvM_ReadPRAMBlock(...) - NvM_WritePRAMBlock(...) - NvM_RestorePRAMBlockDefaults(...) | n异步请求(通过callback或polling返回结果) n作用于一个NVRAM Block n通过NVRAM manager task处理 n适用于所有SWC
|
| Type 3: - NvM_ReadAll(...) - NvM_WriteAll(...) - NvM_CancelWriteAll(...) - NvM_ValidateAll(...) | n异步请求(通过callback或polling返回结果 n作用于具有常驻RAM data的NVRAM Block
|
| Type 4: - NvM_Init(...) | n同步请求 n基本初始化 n通过函数本身内部的命令接口向任务发出成功信号 |
以上讲的都是一些原理或者配置上的东西,那么在软件代码上是怎么用的呢,其实用到了一个描述符表(Descriptor Table),即将所有的配置信息映射或囊括到这里面来,那么这个表是怎样的?
AUTOSAR的配置策略,要将所有和NVRAM 描述符相关的内容在配置期间生成,生成的内容包含以下信息。
| SWS Item | Items | Type |
| ECUC_NvM_00476 | NvMBlockCrcType | EcucEnumerationParamDef |
| ECUC_NvM_00554 | NvMBlockHeaderInclude | EcucStringParamDef |
| ECUC_NvM_00477 | NvMBlockJobPriority | EcucIntegerParamDef |
| ECUC_NvM_00062 | NvMBlockManagementType | EcucEnumerationParamDef |
| ECUC_NvM_00557 | NvMBlockUseAutoValidation | EcucBooleanParamDef |
| ECUC_NvM_00556 | NvMBlockUseCRCCompMechanism | EcucBooleanParamDef |
| ECUC_NvM_00036 | NvMBlockUseCrc | EcucBooleanParamDef |
| ECUC_NvM_00552 | NvMBlockUseSetRamBlockStatus | EcucBooleanParamDef |
| ECUC_NvM_00519 | NvMBlockUseSyncMechanism | EcucBooleanParamDef |
| ECUC_NvM_00033 | NvMBlockWriteProt | EcucBooleanParamDef |
| ECUC_NvM_00551 | NvMBswMBlockStatusInformation | EcucBooleanParamDef |
| ECUC_NvM_00119 | NvMCalcRamBlockCrc | EcucBooleanParamDef |
| ECUC_NvM_00116 | NvMInitBlockCallback | EcucFunctionNameDef |
| ECUC_NvM_00533 | NvMMaxNumOfReadRetries | EcucIntegerParamDef |
| ECUC_NvM_00499 | NvMMaxNumOfWriteRetries | EcucIntegerParamDef |
| ECUC_NvM_00478 | NvMNvBlockBaseNumber | EcucIntegerParamDef |
| ECUC_NvM_00479 | NvMNvBlockLength | EcucIntegerParamDef |
| ECUC_NvM_00480 | NvMNvBlockNum | EcucIntegerParamDef |
| ECUC_NvM_00481 | NvMNvramBlockIdentifier | EcucIntegerParamDef |
| ECUC_NvM_00035 | NvMNvramDeviceId | EcucIntegerParamDef |
| ECUC_NvM_00482 | NvMRamBlockDataAddress | EcucStringParamDef |
| ECUC_NvM_00521 | NvMReadRamBlockFromNvCallback | EcucFunctionNameDef |
| ECUC_NvM_00483 | NvMResistantToChangedSw | EcucBooleanParamDef |
| ECUC_NvM_00484 | NvMRomBlockDataAddress | EcucStringParamDef |
| ECUC_NvM_00485 | NvMRomBlockNum | EcucIntegerParamDef |
| ECUC_NvM_00117 | NvMSelectBlockForReadAll | EcucBooleanParamDef |
| ECUC_NvM_00549 | NvMSelectBlockForWriteAll | EcucBooleanParamDef |
| ECUC_NvM_00506 | NvMSingleBlockCallback | EcucFunctionNameDef |
| ECUC_NvM_00532 | NvMStaticBlockIDCheck | EcucBooleanParamDef |
| ECUC_NvM_00072 | NvMWriteBlockOnce | EcucBooleanParamDef |
| ECUC_NvM_00520 | NvMWriteRamBlockToNvCallback | EcucFunctionNameDef |
| ECUC_NvM_00534 | NvMWriteVerification | EcucBooleanParamDef |
| ECUC_NvM_00538 | NvMWriteVerificationDataSize | EcucIntegerParamDef |
这个最好对着代码去看更好理解。
以上讲得内容都不是很深入,适合刚入手的小伙伴学习,我将以上内容做成了一份备查手册,方便以后看代码或者阅读文档的时候,可以直接查看,不用费神看那么多鸡肠文英语。

这图内容比较多,看起来不是非常清晰,如果有需要,请关注公众号“嵌入式软件实战派”,对话框回复NVM,可获得高清PDF版内容。
参考文档:
AUTOSAR_SWS_NVRAMManager.pdf
AUTOSAR_SRS_MemoryServices.pdf

关注公众号“嵌入式软件实战派”回复“AUTOSAR”可获得整套实战教程
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or
我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
我有一个Controller,我想为这个Controller创建一个助手,我可以在不包含它的情况下使用它。我尝试像这样创建一个与Controller同名的助手classCars::EnginesController我创建的助手是moduleCars::EnginesHelperdefcheck_fuellogger.debug("chekingfuel")endend我得到的错误是undefinedlocalvariableormethod`check_fuel'for#有没有我遗漏的约定? 最佳答案 如果你真的想在Controll
我有一个模块stat存在于目录结构中:lib/stat_creator/stat/在lib/stat_creator/stat.rb中,我在lib/stat_creator/stat/目录中有我需要的文件,以及:moduleStatCreatormoduleStatendend当我使用该模块时,我将这些类称为StatCreator::Stat::Foo.new现在我想要一个存在于应用程序中的根Stat类。我在app/models中制作了我的Stat类,并在routes.rb中进行了设置。但是,如果我转到Rails控制台并尝试在应用程序/模型中使用Stat类,例如:Stat.by_use
这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Testingmodulesinrspec目前我正在使用rspec成功测试我的模块,如下所示:require'spec_helper'moduleServicesmoduleAppServicedescribeAppServicedodescribe"authenticate"doit"shouldauthenticatetheuser"dopending"authenticatetheuser"endendendendend我的模块位于应用程序/服务/services.rb应用程序/服务/app_servi