十几年前,我从学校毕业,入职某汽车电子从事汽车电子的产品研发。

当时第一份工作就是收集整理并翻译理解汽车电子的软硬件设计规范。

并且负责每天固定一小时向全部门的工程师介绍规范要求。

这份工作对我之后的研发工作影响巨大。在之后的从业生涯中,我严格遵循该规范设计软件和硬件,并不断总结完善,形成了自己独有的一套嵌入式软件设计经验和模块。

1. 软件时序-计算指令

不能将计算指令数用于计时目的,因为这种做法会使系统对软件、硬件的变动相当敏感。指令计算可用于底层的硬件接口。该例外情况的原因应有明确的记录,并对代码进行完整注释。必须对任一例外情况列详细的清单,在设计审查中指明时序计算方法、单片机类型、时钟速率及时钟的预分频、PLL倍频。

2. MCU设置-控制寄存器的刷新

影响模块正常操作的MCU控制寄存器必须以<=5S的间隔定时刷新。每次复位时,这些寄存器都必须重新初始化。

如果在睡眠状态下采用轮询模式, 那么在睡眠状态下,这些寄存器<=5S的间隔定时刷新。如果没有采用轮询模式,在MCU被唤醒时,寄存器必须刷新。

对于MCU的所有输出,在设置方向寄存器之前必须正确设置数据寄存器。

对于写入数值会造成非预期副效应的寄存器或者一次性写入寄存器,程序必须以相同的刷新周期监控寄存器的数值,一旦寄存器数值改变,程序必须强制MCU复位。

因为半导体工艺的缺陷,寄存器长期的电荷泄漏会导致其数值的改变。该要求是为了增加软件的鲁棒性,减少被误操作的风险。这条要求对于长期上电的模块尤其重要。

3. CPU负荷

对于新开发的模块,在CP阶段,最坏情况下的CPU负荷不得超过CPU时80%,在JOB1阶段,不得超过90%,任何模块,最坏情况下的CPU负荷不得超过95%。

最坏情况下的CPU中断负荷不能超过30%的CPU带宽。

4. 未编写的程序存储器

运行未编写的程序存储器应对MCU进行复位。一旦PC指针超出程序存储器的地址范围,复位是PC指针清零,以及寄存器重新初始化的最安全、有效的方式。

5. 非易失存储器的数据完整性

如果使用了非易性存储器,必须通过一些方法来验证数据的完整性(比如checksum)。

在数据初始化的过程中验证数据的完整性,初始化之后也应定期验证。

一旦验证出数据完整性被破坏,必须记录错误信息,并在随后采取错误抑制策略。

错误抑制策略必须要解决下述问题:

如何重新初始化存储器(新的数值从何而来)何时重新初始化存储器。何时执行复位操作。

6. 动态内存分配

不要使用内存分配函数(比如C语言中的malloc)来定义动态数据结构(甚至最简单的原子数据类型)。

内存分配功能仅限于静态的数据结构。

由编译器定义及管理的局部变量不受此限制。

该要求的目的在于通过消除内存碎片,内存泄露及内存与堆的冲突来增强软件的鲁棒性。

7. 汇编语言

应用程序应该用高级语言编写,而不能使用汇编语言。

特例:汇编语言可用于底层的硬件访问(关闭中断,使能中断,空操作,寄存器访问)以及中断服务程序。

如果使用了汇编语言,必须用函数或宏定义隔离出来,明确记录;而且代码必须完整注释。软件设计审查必须提供汇编语言使用情况的完整清单并得到批准。

目的在于增加软件的可靠性,可读性和可维护性。另一方面,自动的静态分析工具以及复杂性度量工具一般不支持汇编语言。

8. 面向对象的语言

由于嵌入式的C++使用较少,而且缺乏公开而且广为接受软件标准,不允许使用面向对象的语言。一般而言,BCM不需要这些语言的复杂性和功能。

9. 单片机的选择-客户定制的MCU

按照以往经验,和标准MCU相比,定制的MCU的升级路径有限。MCU产商对标准的MCU在制造和工程上将给予更大的支持。另一方面,一些工具对定制MCU的支持更具局限性。这包括:编译器,连接器,仿真设备。

因此,不能使用定制的MCU。定制的MCU是指购买量超过厂商最销售50%的MCU。

10. MCU存储器空间

应用和诊断内存的分配中,应保留备用资源,如下:

RAM 程序代码空间 EEPROM

报价阶段 30% 30% 30%

样车确认阶段 20% 20% 20%

整车投产阶段 10% 10% 10%

备注:作为以上备用资源分配的替代方案为,有一个MCU机的升级路径,这个升级路径(有更大的RAM, Flash 和 EEPROM)必须是使用相同的封装。

11. 软件循环

等待某一事件的循环,除了看门狗复位以外,必须有替代的退出机制。

12. 实时的边界检查

关键的输入和数据必须经过合法边界值的校验。

13. 非易失存储器的数据更新

修改的NVRAM数据应在获取数据后的25ms内完成存储工作。

要经常修改的数据同样允许在积累多次变更之后再写入内存,避免损耗NVRAM。然而,只有具有最小更新频率规定的数据项目才适用。

多字节的数据据可能超过25ms,但是至少以25ms/字节的速度更新。

14. 非易失存储器-可靠的写入时序

系统设计必须要保证非易失存储器写入时序的完整性。甚至在模块掉电时也应如此。

15. 非易失存储器的使用寿命

在NVRAM整个使用寿命中所写入NVRAM任何一个存取单元的读写次数都不能超过NVRAM制造商所规定的NVRAM最大允许读写入次数。

工作温度对NVRAM的使用寿命有一定的不良影响。

16. 非易失存储器的初始化

所有模块在出厂前必须对非易失存储器进行初始化。

17. 看门狗

必须使用看门狗防止程序跑飞。

最坏情况下的看门狗计数器周期必须至少超过最坏情况下喂狗周期20%,包括公差累积以及最坏情况下的中断。

只能在固定的软件存储单元内喂狗,而且不应在中断服务程序中进行。喂狗必须依靠计时器的正确操作。因此要求直接通过计时器控制喂狗时间,Watchdog 允许在其它的一些存储单元内进行清理,例如复位初始化,一些很长和低功耗模式存储单位。

备注:复位初始化和唤醒初始化不同。

18. 外部中断

不允许外部的输入造成CPU的中断。

如果确实需要外部中断:

a. 输入端口必须使用硬件的低通滤波器

b. 削波频率必须至少比预计的最差情况下的频率高5%

c. 具有削波频率的中断不会超过最坏情况的CPU负荷

19. 时钟预分频及PLL

只有在下述情况下改变MCU的时钟预分频及PLL:

a. 在复位初始化时

b. 在进入低功耗(睡眠)模式之前

c. 在退出低功耗(睡眠)模式之后

只要MCU的时钟预分频及PLL有改变,那么代码应该考虑在MCU规格书中要求的稳定时间。

20. 数值比较

a. 整型变量(非枚举或布尔)只能使用不等号进行比较。

b. 循环中止语句必须用不等号进行比较。

c. 不要比较浮点型变量是否相等。因为浮点型变量本身就不准确。

下述情况例外:

申明为整数,却用于枚举类型的变量。将一个整数与其代表的最大值和最小值进行比较。将一个整数和零做比较。