第一次接触“架构性需求”,大约在六年前,当时一位大佬指导我们说,在前期产品规划时,最重要的就是找到“架构性需求”。本人就一头的问号,“架构性需求”是什么?我没有听错吧?当时也没怎么放在心上,直到近年架构设计经验增多,才领悟这句话的正确性。
什么是?
首先,什么是需求?
需求是一个多义词,它的准确所指往往取决于你所处的位置。在汽车行业我们往往会利用ASPICE的V模型来找到自己需求的来源。比如做详细设计,其需求来源就是架构设计。
但是这个理解是不准确的,下图的表述会更加准确,也就是说本层次的需求往往有三个来源,以Domain level - Functional architecture步骤为例:
Vehicle level的Functional architecture
Vehicle level的Logical architecture
Domain level的Requirements
RFLP: MBSE背后的方法论
什么是架构性需求?
架构性需求是整个系统的最重要的那些需求,它可能出现在产品开发的各个层次和各个阶段,对产品的形态、功能、开发周期等等往往起着决定性的作用。它们最大的特征是:只能在项目早期出现,在项目中期出现往往会导致巨大的工作量,甚至需要将整个项目推倒重来。
典型的架构性需求一般有这么三类(但也有不少不在其中):
系统的主要功能需求,它们确定了这个系统必须拥有的功能。比如:汽车必须能在路上开、SOA通信框架必须能跨MPU和MCU通信等等。
质量需求和非功能性需求,一般是指这个系统的那些功能要完成的有多好。比如刹车系统必须达到ASIL-D标准、最差通信时延必须在1ms以内等等。
决定了解决方案和项目的各种约束条件。比如X项目必须在1年之内完成、某ECU的内存只有100KB等等。
那么接下来以汽车行业的典型“架构性需求”为例,从不同的层次依次展开讲讲。
2. 整车级别
整车级别的架构性需求会特别多,就会有下面这些:
车辆的售价:定价20万区间的车,如果中途说改成要卖40万,几乎就是不可能完成的任务。
上市时间:一辆新车的正向开发时间普遍要3年左右,如果说要1年内上市一款新车,那整体的开发策略会完全不一样。
这个环节离用户会比较接近,大家一看就明白,我就不多讲了。
3. 域(子系统)级别域指的是功能域,一般乘用车会分为“座舱域”、“智驾域”、“车辆控制域”、“动力底盘域”等等。这里以座舱域的“3秒内倒车影像启动”展开讲。
种类:非功能性需求
这个需求是由整车级别的R和F同时作用下,才决定了是否需要这个需求: - 车辆的售价决定了是否需要高品质
- 车辆的功能拆解决定了是否有倒车影像
自从车载倒车影像出现以来,在车辆启动之后3秒内,倒车影像就可以正常工作就是个基础需求。否则的话,司机系好安全带后若要倒车,就只能傻坐着,用户体验非常之差。 这个需求对于Linux时代的车载多媒体系统不是问题,因为Linux本身启动就比较快。但是当车载多媒体系统进入Android时代,就要了命了。
下图是Android的启动顺序,Android的界面要能显示图像,需要至少到下面的Normal Mode处。当前行业的优秀水平是15秒以内完全启动。那怎么办呢?
http://www.ryantzj.com/boot-sequence-in-android.html
行业一般有几种办法:
驼鸟大法:忽略这个需求,15秒就让用户等吧,反正我这车也便宜。
深度改造法:对Android系统进行深度定制,比如将倒车影像的启动顺序直接提前到跟Zygote并行启动。但这个的研发成本很高,需要引入一套新的GUI系统,因为这时Android的UI系统还没启动完毕呢。
Hypervisor法:近年来不少芯片都支持Hypervisor,就直接将倒车影像的精简版本部署到启动较快的系统上,如Linux或QNX。在快启阶段使用精简版本的功能,等到Android完全启动后再切换至完整版本。
休眠法:某些芯片支持在关机时将当前系统状态保存至Flash上,这样下次开机就能更快了。但支持这个功能代价是很高的,首先是这种芯片就不多,然后还得浪费几个G的宝贵空间用于保存状态。
不下电法:对于纯电车,干脆就不让车机下电,后果就是每天要多掉个几公里续航,并且对系统的稳定性提出了更高的要求。毕竟每次关机都是清除系统垃圾的机会,不关机就没有这个时机了。
不管哪个解决办法,这个看似简单的“3秒启动”的需求,搞出了这么多解决方案,也是让各大厂商的工程师掉了好多头发。
4. ECU级别
各个域的功能设计做进一步的拆解,就落到了ECU里面。这里就以MCU的“极少的内存(以KB为单位)”做展开。
种类:约束条件
这个需求的来源大致如下:
为了实现车内的各种功能,如无线钥匙、远程解锁、防盗报警等等,车内的电子器件现在已经是不可或缺的了 (Vehicle Level - Functional)
受限于成本考虑,能由MCU完成的,绝不用MPU (Domain Level - Functional)
说到“极少的内存”,没接触过MCU编程的工程师都会想,那又怎么样?那我就少实现点功能呗。但其实对你的编程模式发生极大的影响。
MCU的一大特点是内存极小,在车载行业较常见的Renesas V850系列,其内存最小8K,最大也就192K。
这么一点内存,在MPU端,往往启动一个进程就可以耗光了,根本就不够用。启动一个线程,至少需要给它分配两部分内存,TCB和Stack,TCB还好,但Stack会较大,比如Linux的默认配置就是8MB。那么怎么办呢?这就引出了一条软件架构设计阶段的“架构性需求”。
基于RTE运行
很多针对MCU的软件架构都会包含RTE这一角色,其核心的功能就是大部分的线程/Task由RTE创建,并由各个应用共享,这样就可以节省很多Stack的内存开销。
最著名的就是AUTOSAR了,你的程序(应用)被拆成了一个个的Runnables,多个Runnable可以运行在同一个Task中,共享同一个栈。
而一旦你的程序需要基于RTE运行,那么随之而来的,就是要做几件基础的事情:
对你的程序进行配置,主要是配置有哪些Runnable。
配置触发这些Runnable对应的事件Event。
实现这些Runnable。
下图为Mathworks对Runnable的配置界面:
支持的Event类型
https://www.mathworks.com/help/autosar/ug/configure-runnables-events-irvs.html
这种开发方式就跟MPU端的灵活开发有非常大的不同。一切都是由事件触发,没有main()函数,并且需要时刻牢记一点:不能block当前的task,否则整个ECU可能都会卡死。
5. 模块级别
各个ECU确定了选型后,做了整体的系统设计和软件架构设计后,最终的实现会落到软件模块级别。我们以“禁止动态分配内存”为例展开这部分。
种类:约束条件
这个需求的来源大致如下:
车辆的动力底盘域要绝对可靠 (Domain Level - Requirement)
动力底盘域的某ECU上的软件的可靠性要达到ASIL-D级别 (ECU Level - Requirement)
想达到ASIL-D级别,是必然不允许动态分配内存的。(Module Level - Requirement)
动态分配内存有很多好处,但是坏处也不少
内存利用率相对较低
需要内存管理程序,会额外占用内存
产生内存碎片,需要定期清理
不确定性较大
虚拟内存往往会大于物理内存,不可避免的引发缺页中断,使得分配内存的时间不确定性较大
分配内存时,查找可用内存也会引入一定的不确定性
分配内存可能会失败,为避免这种情况往往需要较为复杂的内存回收机制(如Android的OOM killer),而不定时的触发会进一步导致不确定性)
所以,像SafeRTOS这种对可靠性要求极高的OS干脆就禁用了动态分配内存。
这又意味着什么呢?这就意味着本来在系统层面上的资源分配会前移到代码编写阶段,对编程模式是个重大的改变。
比如AUTOSAR中的Component配置就包含了这些东西:
提供哪些接口,类型是啥?
需要使用哪些接口,类型是啥?
6. 如何识别架构性需求
既然架构性需求是如此的关键,那么如何才能在项目前期将绝大部分的架构性需求识别出来呢?个人觉得主要有两大类方法:
用常见的架构性需求分类来帮助梳理,就如文章开头所讲的:
系统的主要功能需求
质量需求和非功能性需求
决定了解决方案和项目的各种约束条件
进行行业对标和调研,对于行业中各个领域的解决方案中特殊的点要知其然且知其所然,而不是简单的想当然,因为这些特殊的点往往就是为了满足特定的“架构性需求”而来的。
结语
三年前,有位大佬问我,架构设计那么多环节,哪个环节最重要?我回答说:“把需求弄清楚最重要”。现在想来,确实如此,万一你漏了一条“架构性需求”,可该怎么办?
全部0条评论
快来发表一下你的评论吧 !