MUNIK解读ISO26262:软件开发阶段(二)-软件架构设计和详细设计
—原创文章,文章内所有内容文字资料,版权均属本公众号所有,任何媒体、网站、公众号或个人未经本公众号授权不得转载、转贴、引用或以其他方式复制发布 、发表。已经本公众号授权的媒体、网站、公众号、个人,在下载使用时必须注明来源,违者本公众号将依法追究相关责任.
回到本文的源头文章“ISO26262标准解读”,请点击链接:ISO26262 Functional Safety of Automotive汽车功能安全服务
回到知识分享,请点击链接:MUNIK中国-技术分享
功能安全软件开发阶段-软件架构设计和详细设计
引言:
随着软件系统规模的增加,软件架构设计的主要性越来越突出。因为它可以帮我们解决与软件架构设计相关的一系列问题。比如:
系统规模庞大,内部耦合严重,开发效率低;
系统耦合严重,牵一发动全身,后续修改和扩展困难;
系统逻辑复杂,容易出问题,出现问题很难排查和修复……
软件架构设计并不是功能安全独有的要求,对于功能安全软件设计而言,软件架构设计的一个重要的目标是使软件需求的实现过程以一种完整的、正确的同时尽可能简单、可理解和可验证的方式展现,从而在软件详细设计实现过程中,尽可能降低设计错误造成违反功能安全需求的可能性。本文主要讲述功能安全软件开发阶段中的软件架构设计和详细设计子阶段。
1、相互依赖关系
软件架构设计以层次结构的形式表示软件架构要素以及他们的交互方式,开发满足软件安全要求的软件架构设计,并支持软件的实现与验证,验证其架构设计满足软件安全要求。同时实现安全相关和非安全相关的软件安全要求。因此软件架构设计依赖关系如下图所示:
图一:软件架构设计依赖关系图
软件详细设计或者软件单元设计主要任务就是基于软件架构,对软件安全需求进一步的实现。详细设计可以是模型化的形式,源代码层面的实现可以从与软件开发环境保持一致的设计中手动或自动生产。软件详细设计依赖关系如下图所示:
图二:软件单元设计依赖关系图
2、软件架构设计要求和建议
软件架构设计提供了实现软件需求和ASIL等级所需的软件安全要求的方法,也提供了管理软件详细设计和实现复杂度的方法。静态方面,如所有软件组件间的接口和数据路径;动态方面,如进程顺序和定时行为,都得到了描述。ISO 26262-6:2018中对软件架构设计的具体要求体现在以下几个方面:
2.1 软件架构设计标记法
软件架构设计的描述应随着ASIL等级的提升满足一定的标记法,使其具备以下属性特征:可理解性、一致性、简单性、可验证性、模块化、抽象化、封装性、可维护性。软件设计标记法如下图二所示:
图三、软件架构设计标记法
其中:
- 1a自然语言:可以补充标记法的使用,例如,一些主题更容易用自然语言表达,或为使用此标记法提供解释和理由,使抽象的设计更容易被理解;
- 1b非形式记发:通常不作为标准的标记方法,描述上比较模糊;
- 1c半形式记法:包含伪代码或使用UML,SysML,Simulink或Statefiow的建模;
- 1d形式记法:包括数学或物理学表达式,通常是被证明过的严谨的表达式。
2.2 软件架构设计基本原则
为了避免系统性故障,软件架构设计提出了一些设计原则如下图所示:
图四、软件架构设计原则
其中:
- 1a软件组件的适当分层结构,不仅仅提出了分层设计的理念,也明确了可以根据产品具体功能,实现的难易程度等确定分层结构,只要确保架构层清晰明了,简单易懂即可;
- 1b 和1c 针对软件架构中每个模块定义的复杂度提出了指导性要求,要求模块定义基于合理的颗粒度,避免模块复杂度过高以及模块与模块之间的交互过于复杂,尽可能减少相互之间的调用和耦合;
- 1d和1e 从模块化设计以及耦合度这个维度提出要求,每个软件模块无法做到完全独立,但是在设计中尽可能要求空间相对独立,如果有共享资源的情况,也需要制定严格的规则,保证贡献资源的双方互不影响操作安全性;
- If和1g 从软件动态设计设计的鲁棒性这个角度提出要求,尤其底层软件开发中断使用中要注意中断相关规则和优先级,避免太高的调度负载和过多的中断请求造成系统对负载变化的裕量变小、实时性不稳定、鲁棒性变差;
- 1h和1i 从防止冲突和串扰的角度,需要避免不同模块软件模块之间对于数据读取写操作的冲突以及调度机制的冲突,如任务之间的死锁、活锁等而造成相互干扰。在设计中软件模块之间做好合理的数据存储资源和任务调度资源分区和隔离,是常用的一种安全机制。
2.3 软件架构设计描述
软件架构的描述主要从静态和动态设计方面提现出来的,这两方面特性描述越全面,越利于后续软件详细设计。软件架构的静态和动态设计方面内容如下图所示:
图五、软件架构的静态和动态设计方面内容
2.4 软件架构设计安全机制
安全机制是检测/避免/控制失效或者减轻其有害影响的技术解决方案,是由E/E功能、元件或其他技术实现的。在软件架构设计中主要采用错误探测和错误处理的安全机制。
2.4.1用于错误探测的安全机制
用于错误探测的安全机制如下图所示:
其中:数据的范围检查指输入输出数据的范围进行检查,例如:在程序中我们可以通过数据范围来排除数据的正确性;合理性检查 例如:使用参考模式导致期望行为和实际进行比较来实现程序执行的正确定;数据流监控指外部要素监控程序执行,例如:看门狗时刻监控程序的执行,避免程序进入死循环中等等。
2.4.2用于错误处理的安全机制
用于错误处理的安全机制如下图所示:
图七、错误处理安全机制
其中:功能关闭机制指为了达到和维持安全状态的功能关闭,比如:当汽车上的空调制冷系统故障后,为了不影响车辆行驶将制冷功能关闭;平稳降级机制指通过划分功能的优先级进行平稳降级,从而最小化潜在失效对功能安全的不利影响。
具体来讲,在功能监控层架构设计过程中,可以根据具体监控的功能,进行相应的异构冗余计算,对其输入,输出,进行范围及合理性检查,异构冗余计算程序执行过程进行时内部或外部要素的时间或逻辑监控,一旦发现错误,通过错误处理机制,将系统导入安全状态。
2.5 软件架构的验证方法
为了验证软件架构设计符合软件安全需求规范、兼容目标硬件和设计指南保持一致,需要按照图七推荐的方法进行软件架构设计的验证。
图八、软件架构设计验证方法
此外,在整个架构设计过程中,还需要关注以下几个方面:
1、软件架构设计中每个软件模块与对应的软件需求之间的关系;
2、软件架构中与同一条或者一组需求的相关软件模块组合在一起是否完整、正确地执行了
对应的软件需求,这些模块之间有没有内在矛盾、重复、需求执行的遗漏等。
3、软件所需资源进行评估(其中评估包括:执行时间、存贮空间和通讯资源);
4、软件架构层级安全分析及相关失效分析(由于篇幅原因这里不多介绍了,详细内容可参看后边分享的技术文稿”软件开发阶段-软件安全分析FMEA and DFA”)
3、常见软件架构示例
3.1 AUTOSAR分层架构
功能安全软件开发常用汽车开放系统(AUTOSAR,AUTomotive Open System Architecture),AUTOSAR通过中间层RTE(Runtime Environment)作为虚拟总线,成功的实现上层应用层软件层(Application Software Layer)和下层基于硬件的基础软件层(Basic Software)的隔离。在进行应用层软件开发时,可以不受硬件ECU的限制,摆脱了对硬件的依赖,这样车厂或者Tier1可以专心进行与产品功能直接相关的应用层开发,在应用层上建立起自身软件独有的特征,对于中间层和底层,可以交由专业的供应商来完成。AUTOSAR分层机构如下图:
图九、AUTOSAR软件架构框图
AUTOSAR架构中共分六层:
1、 应用层软件(Application Layer:企业主要开发及测试的软件部分)
2、 运行环境(Runtime Environment:软件及软件之间的交互接口定义,可以理解为虚拟总线)
3、 服务层(Services Layer:包括操作系统、存储、诊断管理等)
4、 ECU抽象层(ECU Abstraction Layer:与微控制抽象层进行对接。直接对接控制器)
5、 微控制器抽象层(Microcontroller Abstraction Layer:直接对接微控制器,用来对微控制器内部设备和映射了微控制器外部设备的内存进行访问)
6、 复杂驱动(Complex Device:用来进行非标准模块代码编写的,例如大功率的电流驱动器、逆变器等)
3.2 EGAS分层结构
-
EGAS分三层安全结构,整体设计分为Leverl1、Leverl2、Leverl3,如下图所示为一个带锁步核的安全架构框图示例。
-
Leverl1:功能层,这里包括对各输入信号的处理、扭矩的解析、功能的诊断,最直接的理解就是能够实现设计基本的软件及相关硬件资源的组合。
-
Leverl2:功能监控层,负责监控功能层的输出结论,简单理解就是软件的冗余效验,但是,由于不想消耗太多资源及避免算法共因,所以基于功能结果的监控;同时对所有诊断发现的故障也需要在这一层进行处理,可能涉及到了故障分类、处理优先级仲裁等。
-
Leverl3:硬件监控层,负责确保Leverl1和Leverl2的运行环境是正常工作的,异常的运行环境会导致Leverl2的设计起不到很好效果,因此Leverl3在整体的监控架构中作用是不可替代的。主要涉及监控硬件相关的潜在故障和一些安全机制等。比如微控制器硬件相关检测包括内存、ADC、定时器、CPU的运行和通讯模块等。内存和CPU相关检测机制是对所有产品通用的,而其他检测可根据产品应用而定,对于没有使用的模块,可以不进行其相应的安全机制的检测。常用的检测机制包括程序效验、数据效验、内存保护、寄存器保护等。对于多核系统,很重要的一个机制是CPU优先级机制。
图十、EGAS三层安全架构(带锁步核)
4 软件详细设计要求和建议
完成软件架构设计后,需要进一步实现架构设计中的模块开始进行软件详细设计,即软件单元设计。软件单元设计主要分为2个步骤,分别为:
1、 按照软件架构设计和相关的软件安全要求定义软件单元,即详细设计的定义;
2、 按照定义,实现软件单元,即代码实现(依据详细设计按照建模或编码指南,以模型或直接以源代码的形式实现)。
在开发每一个软件单元设计时,软件安全要求和非安全相关的要求都要实现。但是对于软件单元是安全相关要素时,除了实现软件安全需求外,最主要的区别就是要考虑软件安全需求ASIL等级带来的开发要求和约束条件,其主要包括:软件单元设计的标记法、软件单元设计和实现的设计原则。
4.1 软件单元标记法
为了避免系统故障并确保软件单元设计实现一致性、可理解性、可维护性、可验证性,应遵循下图中的要求。
图十一、软件单元设计的标记法
其中:
标记法的方法与软件架构设计标记法相同,只是应于于不同的子阶段,这里不单独介绍了。自然语言和标记法的结合使用使设计更容易理解,自然语言常用语对单元设计的描述和解释;使用一些常用的建模语言有利后期单元设计的维护和验证,并可以使用基于模型的验证方法;使用一些形式记法有时可以使复杂的表述更简单化,更容易被接受,比如常用的一些公式计算之类的。
这里需要注意:在具有自动代码生成的基于模型的开发的情况下,表示软件单元设计的方法被应于作为代码生成的基础的模型。
4.2 软件单元设计和实现的原则
源代码层面软件单元设计和实现应满足如下图的设计原则,应具有以下特征:
1、 基于软件架构设计,软件单元内的子程序和函数按照正确次序执行;
2、 软件单元接口的一致性;
3、 软件单元内和软件单元间的数据流及控制流的正确定;
4、 简单性;
5、 可读性和可理解性;
6、 鲁棒性;
7、 软件修改的适用性;
8、 可验证性。
图十二、软件单元设计和实现的设计原则
其中:
1a避免程序或函数出现多个入口和出口,集成时单元与单元之间数据调用太多,不利于程序的简单性,和可理解和可维护性;
1b 强调单元设计实现过程中内部动态对象和变量的使用,因动态数据类型不是在编译阶段确定的,而是把类型绑定延后到了运行阶段,侧重运行代码期间数据类型的变化。常用的动态类型语言有:Python、Ruby、Erlang、JavaScript、swift、PHP、Perl;
1c 对于变化都要进行初始化,即使初始值是0。避免因编译环境和平台,给变量随机赋值,造成非预期时间的发生;
1d 一般情况下重复使用变量名称,编译环境都会自动报错或者警告。重复使用变量名称也不利于代码的可阅读性和可维护性,容易混淆;
1e 在程序编写过程因使用全局变量有关的问题比较多,例如:调用时无意间的修改;初始化的顺序;因变量造成的干扰等等。所以尽量要避免全局变量的使用,非万不得已不使用全局变量。当然改使用时也可以使用,因权衡使用和不使用的利弊,使用应明确全局变量规范;不要用全局变量存放中间结果等;
1f 都说指针是C语言的灵魂,但是误用或滥用指针会引发非常严重的问题,例如变量值破坏,程序崩溃;
1g 隐式转换数据类型转换一般编译器都会警告,多参看warning信息。因为隐式转换影响代码的可读性和可维护性,而且容易出现数据精度丢失;
1h 隐藏的数据流和控制流使代码更难以理解和维护,也使程序更为复杂。
1i 无条件跳转使程序的逻辑思路不清晰明确,加大了代码的理解性和分析难度。
1j 递归算法功能很强大,但是它会使代码复杂化,难以理解和验证。
软件单元设计和实现可以通过模块化的软件自动生成源代码,也可手动编写具有独特风格的软件代码,一般情况下底层驱动软件代码多使用标准的库文件,应用层软件习惯在根据软件环境生成源代码的基础上手动添加和修改代码。
关于软件架构设计和详细设计的分享就到这里了,其中软件架构设计是一个很复杂的主题,它还包含了软件安全分析(SW-FMEA和SW-DFA),实际设计中我们会通过安全分析的结果不断地完善软件架构设计,这里就引出了安全机制。软件安全分析和软件安全机制的研究又是一个个独立的主题,本文里仅仅提了一点点,具体的技术细节,怎么落实,会有专栏去详细拆解分析。