上一章节,我们介绍了Classic AUTOSAR三层架构中的Application层,主要讲解了Application中涉及到的相关元素(SWC,Port,Runnable等),下面我们来讲一下AUTOSAR三层架构中的RTE层。
RTE是AUTOSAR中的一个重要的层次,在AUTOSAR中,RTE相当于1个虚拟功能总线一样,如上图,主要实现2种数据交互:
① 不同SWC之间的交互;
在不同SWC之间的交互过程中,RTE作为中间层,为SWC提供相应的接口,SWC之间不直接进行交互,当SWC需要发送相应的数据给其他SWC的时候,只需要调用RTE相应的接口,将数据发送给RTE即可,其他SWC读取数据时,从RTE里直接读取数据即可。
在不同SWC之间交互时,SWC只需要通过Port接口对RTE负责即可,即RTE是作为不同SWC之间连接的桥梁;故在开发过程中,不同SWC只要约定好了和RTE之间的接口内容,不同SWC内部功能开发可以是独立的,相当于RTE解耦了不同SWC之间的关系。
② SWC和BSW之间的交互;
在SWC和BSW之间的交互过程中,RTE作为中间层,分别为BSW和SWC提供接口,SWC和BSW只需要通过Port分别对RTE负责即可,即SWC和BSW内部开发内容是相互独立的,相当于RTE解耦了SWC和BSW之间的关系。
在SWC与BSW的通信中,如BSW向上层提供的服务调用的时候,那么RTE会将BSW的服务以接口的形式提供给SWC,SWC直接调用相应的接口即可完成相应的服务的调用。
1. RTE架构实现
如下图,RTE是处于SWC与BSW之间的层次,主要的作用就是让SWC的设计和实现不依赖于底层BSW的实现,RTE为SWC和BSW提供了相应的接口,供他们进行使用和交互,这个交互可以是ECU内部的,也可以是ECU之间的。
再进一步看,BSW层次里面也遵循了模块化、分层的原则,简单分成3层,分别为Service(服务层)、ECU Abstraction Layer(ECU抽象层)、Microcontroller Abstraction Layer(简称MCAL);
Service层主要包括OS, Memory, ComM, CANSM, NM等的一些Service模块;
ECU抽象层主要包括一些Memory的硬件抽象层MemIf, 通信的硬件抽象层CanIf/LinIf/EthIf, 其他服务的一些抽象层等等,除此之外,还包含板载驱动和IOHWAB硬件抽象层;
MCAL驱动层主要包含各种Driver驱动。
对于APP、RTE、BSW三层架构而言,RTE解耦了APP和BSW,使得APP和BSW的设计和开发相关独立,为APP和BSW的并行开发提供了架构上的可能性。
2. RTE作为通信接口
RTE作为AUTOSAR软件架构下 “不同SWC之间” 以及 “SWC与BSW” 之间的通信媒介,它可以作为VFB(虚拟功能总线)的接口,提供了对应通信接口。
在ECU内部,即 “不同SWC之间” ,可以走 “Sender/Receiver Port数据交互接口” 或 “Client/Server Port服务交互接口” 完成通信数据或服务交互,比如对应Rte_Read**/Read_Write**函数接口或Read_Call**接口;在ECU之间,即 “SWC和BSW之间”,也同样可以走这两种交互接口完成交互。
RTE可以基于COM模块,比如CAN总线有数据接收更新时,COM模块会触发1个Callback函数,该Callback可以由RTE实现,然后SWC通过Port从RTE中获取最新的数据。
除此之外,RTE作为通信接口,还可以支持如下特点;
① 支持简单和复杂数据结构;
Rte作为通信接口进行数据或服务交互时,传输的数据结构可以是基本数据类型,也可以是根据需求定义的各种复杂数据类型。
② 保证数据完整性;
比如,在使用Sender/Receive Port接口实现不同SWC之间数据交互时,如下图,SWC1在10ms的周期内调用接口写入数据,SWC2在5ms的周期内调用接口读取数据,如果数据类型是复杂数据类型,那么,当SWC2调用接口读取数据时,很可能出现SWC1也正在调用写入数据,这个时候,很容易出现数据一致性问题;针对这种情况,RTE层做了保护机制处理,即当调用Rte_Read或Rte_Write接口时,如果从设计调度上有数据不一致的风险,RTE会自动生成保护代码,避免数据不一致的情况。具体的细节保护内容,主要有终端锁等机制,后续章节中再进行详细介绍。
③ SWC支持1种类型的多个多实例化处理;
对于有些功能组件SWC的设计而言,存在多个相同SWC组件多实例化的需求,比如说车身的左门和右门,除了方位的区别,在功能层面上是一摸一样的;在设计阶段,如果左门和右门作为2个SWC,相关的Port/Runnable等的设计都应该完全一样,RTE生成的相关内容也应该差不多。
针对这种情况,存在1个SWC的多实例化处理选择,可以在创建SWC的时候选择多实例化选项,在实例化的时候,就可以创建多个一样操作的SWC了,当在1个SWC上面做相关设计操作时,其余SWC也会做同样设计操作。
2.1 Send/Receive Port
对于Send/Receive Port接口,在数据传输时,如下图,主要分为 “SWC与SWC之间” 和 “SWC与BSW” 之间这2种交互方式。
① SWC与SWC之间的接口交互
SWC与SWC之间的接口交互,主要应用于ECU内部的功能组件之间,如SWC1与SWC2之间的数据交互,SWC1或SWC2只需要对RTE进行负责即可。SWC1调用Rte_Write_<Port>_<Data>接口将数据写入到RTE的Buffer中,SWC2调用Rte_Read_<Port>_<Data>接口从RTE的Buffer中读取数据。
② SWC与BSW之间的接口交互
SWC与BSW之间的接口交互,主要应用于ECU之间,如ECU1的SWC2与ECU2的SWC3之间的数据交互,SWC2或SWC3也只需要对RTE进行负责即可。ECU1的SWC2调用Rte_Write_<Port>_<Data>接口将数据写入到RTE的Buffer中,RTE进一步调用Com_SendSignal函数将数据写到Com的PDU中,再通过BSW将数据发送到总线上去;ECU2收到总线上的数据时,数据通过BSW传递到COM层的PDU中,ECU2的SWC3通过调用Rte_Read_<Port>_<Data>接口读取数据,RTE会调用Com_ReceiveSignal接口将数据从COM层的PDU读取出来。
2.1.1 Explict显性接口
Send/Receive Port在做数据传输时,通信双方只需要对RTE负责,对应的数据在RTE中会有对应的GlobalBuffer,如下图,在调用Send Port接口或Receive Port接口去写入或读取RTE GlobaoBuffer内容时,如果配置为 “Explict显性接口”,那么访问的GlobalBuffer内容始终是最新的,对应接口为Rte_Read_<Port>_<Data>或Rte_Write_<Port>_<Data>。
比如说,数据发送方Send Port以1ms周期发送数据,数据接收方Receive Port以10ms周期接收数据,如下图,如果是 “Explict显性接口”,那么,数据接收方Receive Port始终获取的是RTE中GlobalBuffer的最新内容。
2.1.2 Implict隐性接口
Send/Receive Port在做数据传输时,当发送方和接收方数据收发周期不一致时,收发数据方可能希望在1个任务周期内访问的数据是一致的,这种需求,就需要 “Implict隐性接口” 来实现了,对应接口为Rte_IRead_<Port>_<Data>或Rte_IWrite_<Port>_<Data>。
如下图,在Runnable执行期间调用Receive Port接口获取数据,在这种情况下,Runnable执行之前,该Runnable中需要访问的Receive Port对应的RTE GlobalBuffer会先把内容拷贝到CopyBuffer中,在Runnable运行期间,无论何时调用Receive Port访问数据,都是获取的是CopyBuffer中的数据,保证该Runnable运行期间,得到的数据都是一致的。
2.1.3 Queue队列机制处理
基于Send/Receive Port中的 “Explict显性接口”,当接收双方周期不一致时,比如说Send Port发送方周期为1ms,Receive Port接收方周期为5ms时,当Receive Port接收方读取数据时,只能够读取到RTE中GlobalBuffer的最新值,在上个5ms周期间隔期间,至少会丢失4次数据,但实际需求可能是需要所有接收到的数据。
基于这种需求,RTE提供了Queue队列机制,如下图,GlobalBuffer中存在多个Buffer,可以起到缓存的作用,Receive Port接收方可以获取Queue里面所有缓存的值。
2.1.4 无效值处理
在Send/Receive Port传输数据过程中,可能存在传输数据内容超出设定范围等情况,针对这种无效值的情况处理,RTE会生成RTE_Invalidate_...或Rte_IInvalidate_...的接口,在发送到Invalid数据时,可以调用这种接口来进行Invalid无效值/非法值的设置。
如果接收端接收到的数据是无效Invalid的,对于Explict显性接口,通过Rte_Read函数可得到函数返回值RTE_E_INVALID,否则是RTE_E_OK;对于Implict隐性接口,通过Rte_IStatus_...返回无效的结果RTE_E_INVALID;同时,当有无效值/非法值得时候,这个错误事件本身可以作为1个DataReceiveErrorEvent,触发Runnable的执行。
在Send/Receive Port接口创建的时候可以进行无效值范围的设置,一般可通过InvalidData数据限制(DataConstraint)进行配置,HandleInvalid是Reaction的配置。
2.2 Client/Server Port
Client/Server Port的交互主要侧重于服务调用,也包含数据传输的特性,在进行服务的请求调用时,ClientServer支持的是n:1的方式,就是你n个client可以调用一个服务。
Sever的实现操作在SWC中是以ServerRunnable的形式去实现,相当于1个功能服务函数,Client端调用执行时,会调用Server的Operation来执行相应的操作,Server端正常作为一个SWC的一个ServerRunnable来进行实现。
Client/Server的调用方式支持同步和异步两种调用的方式,对于ServerRunnable,它主要运行在两个环境,一种是在客户端的逻辑中,即直接的函数调用,当Client端调用时,Server端直接嵌套执行完成;另一种是在Task任务中,当Client端调用时,对Server端下发一个触发执行请求,Server端一般运行在Task任务中,接收到这个触发请求之后,再触发执行,而Client端下发完成请求之后,继续直接执行Client端的后续内容的执行。
2.2.1 Synchronous同步处理接口
同步调用的处理,当Client端调用Port的时候(Rte_Call_**),触发Server端ServerRunnable的执行,Client端会Block在那里,等待ServerRunnable执行完成并返回结果给Client端。
ServerRunnable在执行的时候,Client也不会一直Block在那里,会有一个Timeout的超时计时;如果在Timeout超时之前,Server端正常执行结束,结果会正常返回给Client端;如果Server端在Timeout超时之后还未执行完,发生超时,那么Timeout的Error会报出来。
Client/Server Port一般用的是同步的,也可以做异步的;同步的CSPort相当于嵌套函数的调用;异步的CSPort,需要将ServerRunnable Map到对应的Task上,可用于不同ASIL等级的OSApplication上,也可用于不同Core上的Task(不同Core上的CSPort需要采用IOC机制实现数据交互);当然,CSPort中的ServerRunnable也可Map到Task上,此时,Client端调用相当于发送1个Request请求(相当于给ServerRunable设置1个SetEvent),ServerRunnable被RTE调度。
2.2.2 Asynchronous异步处理接口
异步调用的处理,Client端调用服务的客户端不会被BLOCK掉,不会等待执行结果的到来;异步调用中Client端可以采用 “Polling” 或者 “Waiting” 的方式来进行结果的检查(RTE会提供相应的接口给SWC来调用)。“Polling” 方式,Client端采用轮询访问的方式去查询结果;“Waiting” 方式,Client端在调用接口查询结果时,会一直Hold在这边,等待Server端执行完成,这种有点类似于同步调用的机制了,区别在于Client端可以决定何时查询结果。
CSPort的Asynchronous调用,分为4种方式;
如果配置成None的方式,Rte_Result**函数接口不生成(返回结果只返回Client端请求的结果,RTE_E_OK代表请求成功,RTE_E_LIMIT代表请求未成功,但不确定Server端是否执行正常),Client端与Server端Operation的执行,通过几个标志位实现(Complete,Active等,比如Server端Operation执行完之后,CompleteFlag重新置为True,然后Client端根据判断CompleteFlag是否True进行再次调用);
如果配置成Polling方式,Rte_Result**函数接口生成,需要Client端自己周期性(周期自己决定)去调用Rte_Result**函数去Polling请求结果,Client端、Server端Operation和Rte_Result**函数之间的顺序调用,通过在Polling方式几个标志位的基础上再添加ClientFlag标志位;Client端根据判断ClientFlag标志位为1,执行进一步的调用操作,将ClientFlag标志位置为0、CompleteFlag置为0、Active置为1;Server端Operation执行完之后,Active置为0;Rte_Result**函数判断ClientFlag标志位为0和CompleteFlag为1,将ClientFlag置为1并返回函数结果(结果为RTE_E_OK/RTE_E_NO_DATA);
如果配置成Waiting方式,Rte_Result**函数接口生成,Client端一旦调用Rte_Result**函数去请求结果时,会WaitEvent一直等Server端执行完,ServerRunnable执行完之后会SetEvent,然后Rte_Result**函数可以继续执行返回结果(结果为RTE_E_OK/RTE_E_TIMEOUT);Wait过程相当于进入IdleTask状态,故Waiting方式下Rte_Result**函数最好放在单独的Task或该Task中其他任务不重要;Wait的Timeout机制代码会自动生成(Timeout为0时,相当于没配置Timeout,对应的Timeout机制不会生成);
如果配置成以上三种方式,再重新配置1个Runnable,选择OnOperationCallReturn的方式,在ServeRunnable执行完会SetEvent触发配置的Runnable的执行,相当于1个Callback函数,在该Runnable中可以手动调用Rte_Result**函数去请求结果(结果为RTE_E_OK/RTE_E_NO_DATA)。
3. RTE作为运行时调度环境
RTE是Runnable运行实体的运行时环境,它可以为Runnable执行调度提供触发机制,主要有以下特点:
① RTE主要是配置,没有静态代码,代码一般都是配置生成代码;对于不同的ECU,RTE的最终实现是不同的,因为每个ECU它实现的功能都是不同的,它的SWC不同,那么相对于的各种配置就是不同;
② RTE对OS和BSW进行了抽象,在设计上层SWC时,不需要关心底软BSW的服务机制和OS的运行机制,即SWC的设计只需要对RTE负责;
③ Runnable运行实体的调度是由RTE的事件来触发执行的,根据 “SWC的Runnable和TASK的映射关系” 配置生成RTE;
那么,RTE作为运行时调度环境,是如何通过RTE的事件触发调度SWC的Runnable运行的呢?下图,OS作为底层调度,在满足RTE Event事件时,触发调度Runnable的执行,实际上来说,Runnable的触发和运行,是依赖于OS和RTE这两者一起的。
除此之外,RTE还可以提供Rte_Start和Rte_Stop的接口,一般来说,Rte_Start在程序启动初始阶段Active相关的OS Task或Set相关的OS Alarm等,Rte_Stop在程序结束阶段Deactive相关的OS Task或Cancel相关的OS Alarm等。
RTE Event触发事件支持的类型如下:
① Init,初始化触发事件,对应触发初始化Init Runnable,初始化执行一次;
② TimingEvent,时间触发事件,一般触发周期执行Runnable;
③ DataReceiveEvent,接收数据触发事件,一般接收到数据之后触发执行Runnable;
④ DataReceiveErrorEvent,接收数据错误事件,一般接收数据发生错误时触发执行Runnable;
还有其他的Event类型,比如Background事件、Com模块的Notification事件等等,这里就不逐个列出了,后面具体应用时再逐渐体会。
4. 总结
以上内容即为 “AUTOSAR RTE” 的整体内容了,后面我们将会持续更新AUTOSAR BSW相关内容、AUTOSAR方法论等内容,包含BSW层架构、典型协议栈模块的功能原理和开发内容示例。
敬请期待......