第四章 过程实施(ENG.1):需求获取与管理、需求分析、软件架构设计、软件详细设计与单元构建
各位工程师,咱们今天聊聊ASPICE里最核心的干活环节——过程实施(ENG.1)。说白了,就是从客户嘴里那句“我想要个能自动泊车的功能”,到最终变成芯片里跑的一行行代码,这中间所有步骤该怎么管、怎么做。
我个人习惯把ENG.1拆成四个阶段来看:需求获取与管理、需求分析、软件架构设计、软件详细设计与单元构建。这四个阶段不是串行的,而是层层递进、反复迭代的。你想想看,如果需求没搞清楚就画架构图,那后面返工的代价可就大了。
一、需求获取与管理:别让“我以为”变成“我后悔”
需求获取,听起来简单吧?不就是跟客户聊聊天、记记笔记吗?我在项目中遇到过太多次,客户说“我要一个安全的功能”,结果交付时才发现他想要的是ISO 26262 ASIL D级别的安全,而我们只做了QM等级。嗯,这就是需求获取没做到位。
ASPICE对需求获取与管理的要求,核心就三点:
- 来源明确:每个需求必须能追溯到具体干系人(客户、系统工程师、法规部门等)
- 唯一标识:每个需求有独立ID,方便追踪和变更管理
- 双向追溯:从客户需求→系统需求→软件需求,每一层都能上下追溯
关键点:需求管理工具(比如DOORS、Polarion、Jama)不是摆设。我建议团队在项目启动第一天就把需求管理流程跑起来,哪怕只有10条需求,也要用工具管理。别等到需求膨胀到1000条时再补课,那会累死人的。
这里有个避坑指南:我曾经接手过一个项目,需求文档里写着“系统应支持OTA升级”,但没写清楚是SOTA(静默升级)还是FOTA(全量升级)。结果开发团队按SOTA做了,客户验收时发现升级过程会中断用户操作,直接打回重做。所以,需求获取时一定要把模糊词(如“支持”、“优化”、“增强”)变成可验证的指标。
二、需求分析:把“要什么”变成“怎么做”
需求分析阶段,说白了就是翻译——把客户的语言翻译成工程师的语言。比如客户说“倒车影像要清晰”,你得分析出:分辨率至少720p、帧率不低于30fps、延迟小于100ms、在夜间光照低于1 lux时仍能识别障碍物。
ASPICE对需求分析的要求,我总结成一张表:
| 活动 | 输入 | 输出 | 关键检查点 |
|---|---|---|---|
| 需求澄清 | 客户原始需求 | 澄清后的需求列表 | 每个需求是否可测试? |
| 需求分类 | 澄清后的需求 | 功能/非功能/接口/安全需求 | 分类是否覆盖所有维度? |
| 需求优先级 | 分类后的需求 | 优先级矩阵(P1/P2/P3) | P1需求是否与核心功能强相关? |
| 需求评审 | 需求规格说明书 | 评审记录+修改后的需求 | 评审参与方是否包含测试人员? |
我个人习惯在需求分析阶段做一件事:写测试用例草稿。没错,在还没开始设计代码之前,我就先想好怎么测它。这样做的好处是,能提前发现需求里的矛盾点。比如客户要求“系统启动时间小于2秒”,同时又要求“启动时完成全量自检”,这两个需求放在一起,你想想看,2秒内能完成全量自检吗?大概率不行。这时候就要跟客户协商,要么放宽启动时间,要么减少自检项。
小技巧:需求分析阶段,建议用状态机图或时序图来可视化需求。一张图胜过千言万语,尤其是跟客户确认时,画个图比读文档高效得多。
三、软件架构设计:搭好房子的骨架
软件架构设计,就是给软件搭骨架。你想想看,盖房子如果地基没打好、承重墙位置不对,后面装修得再好也没用。软件架构也一样,如果模块划分不合理、接口定义不清晰,后面写代码时就会各种“拧巴”。
ASPICE对软件架构设计的要求,核心是分层和模块化。我一般推荐使用分层架构,比如:
- 应用层:业务逻辑,与硬件无关
- 中间件层:通信、诊断、存储等公共服务
- 抽象层:硬件抽象(HAL),隔离硬件变化
- 驱动层:直接操作寄存器、外设
这里有个常见的坑:我曾经见过一个团队,架构设计时把“CAN通信”模块放在应用层,结果换了个CAN控制器芯片,整个应用层代码都要改。正确的做法是把CAN驱动放在驱动层,中间件层提供统一的CAN接口,应用层只管调用接口,不管底层是哪个芯片。
下面这张图展示了软件架构的核心分层关系:
四、软件详细设计与单元构建:把骨架填上血肉
到了详细设计阶段,就要把架构里的每个模块拆成具体的函数、数据结构、状态机。我习惯用伪代码或流程图来描述每个函数的行为,然后再写真正的代码。
ASPICE对详细设计的要求,我特别强调两点:
- 每个函数都要有明确的输入、输出、前置条件、后置条件
- 复杂逻辑必须用状态机或决策表描述
举个例子,一个简单的“车窗防夹”功能,详细设计可以这样写:
// 函数:车窗防夹检测
// 输入:current_position(当前车窗位置,单位mm)
// motor_current(电机电流,单位mA)
// 输出:stop_command(是否发送停止指令,布尔值)
// 前置条件:车窗正在上升
// 后置条件:如果stop_command为true,车窗停止并下降10mm
bool anti_pinch_detect(int current_position, int motor_current) {
// 防夹阈值:电机电流超过500mA且位置在50mm-200mm之间
if (motor_current > 500 && current_position > 50 && current_position < 200) {
return true; // 触发防夹
}
return false;
}
注意:详细设计阶段最容易犯的错误是过度设计。我曾经见过一个团队,把简单的“LED闪烁”功能设计成了包含状态机、回调函数、事件队列的复杂模块。结果代码量翻了3倍,测试覆盖率反而下降了。记住:够用就好,别炫技。
单元构建阶段,就是写代码、做单元测试。ASPICE要求单元测试覆盖率达到100%的语句覆盖和100%的分支覆盖。我建议使用TDD(测试驱动开发)的方式:先写测试用例,再写实现代码。这样做的好处是,代码写出来就是可测试的,不会出现“这个函数没法测”的尴尬局面。
最后,我把这四个阶段的关键交付物整理成一张清单:
| 阶段 | 关键交付物 | 评审要点 |
|---|---|---|
| 需求获取与管理 | 需求规格说明书、需求追溯矩阵 | 需求是否完整?是否可测试? |
| 需求分析 | 软件需求规格说明书、测试用例草稿 | 需求是否分解到可实现的粒度? |
| 软件架构设计 | 架构设计文档、接口定义、模块划分图 | 架构是否满足非功能需求(性能、安全)? |
| 详细设计与单元构建 | 详细设计文档、源代码、单元测试报告 | 代码是否遵循编码规范?测试覆盖率是否达标? |
嗯,以上就是ENG.1过程实施的四个核心阶段。每个阶段都有它的难点和坑,但只要按ASPICE的要求一步步走,就能避免后期的大规模返工。记住:需求搞不清楚,后面全是白干。