ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

多核架构下功能安全设计:从AMP选型到核间通信与部署踩坑实录

多核架构下功能安全设计:从AMP选型到核间通信与部署踩坑实录 1. 多核架构下功能安全的真实挑战为什么单核经验直接搬过来会翻车做功能安全的人早晚会撞上多核这颗钉子。单核时代那套“一个主循环跑到底、看门狗喂一喂、关键变量加个CRC”的思路放到多核SoC上几乎处处漏风。我最早接触多核功能安全项目时犯过一个典型错误把两个核当成两颗独立的MCU来对待各自跑各自的诊断结果在集成测试阶段被问了一个问题——“如果核A的诊断结果要影响核B的安全反应你怎么保证这个跨核通信本身是安全的”当场哑火。这就是多核软件架构在功能安全语境下的核心矛盾并行带来了算力也带来了共享资源的竞争、核间通信的不可预测性以及故障传播路径的指数级增长。IEC 61508和ISO 26262这类标准并不会因为你用了多核就降低要求反而会追问更多——你的核间通信有没有被当作安全机制来验证共享内存的访问冲突算不算共因失效一个核跑飞了另一个核怎么知道、怎么在安全时间内做出反应先把场景说清楚。多核软件架构在功能安全项目里通常出现在这么几类需求下一是算力不够单核跑不动复杂的控制算法加诊断二是要做安全冗余比如一个核跑控制、一个核跑监控lockstep之外的异构冗余思路三是混合关键度场景安全任务和非安全任务比如通信、日志、HMI要隔离不能让非安全任务拖垮安全任务。这三类需求对应的架构设计思路完全不同后面会逐一拆。关键词里提到的“部署”在多核功能安全里不是简单的“把程序烧进去”。它涉及核间任务分配、内存分区、启动时序、诊断调度、以及安全状态的跨核同步。我见过太多项目架构图画得漂亮一到部署阶段就发现两个核抢同一块DMA、中断优先级配错导致安全任务被饿死、共享内存没做访问保护导致数据撕裂。这些问题在单核上根本不存在在多核上却是家常便饭。所以这篇内容我想按一个真实项目的推进逻辑来写从架构选型的决策依据到核间通信的安全设计再到部署阶段的实操细节和踩坑记录。适合正在做或即将做多核功能安全项目的嵌入式工程师、功能安全经理也适合想了解多核安全架构设计思路的同行。不堆标准条款讲的是标准背后那些“为什么这么设计”的工程逻辑。2. 架构选型SMP、AMP、BMP到底怎么选别被“对称”两个字骗了多核软件架构的第一个决策点是对称多处理SMP还是非对称多处理AMP。很多资料会把BMPBound Multiprocessing单独拎出来但实际项目里BMP更像是AMP的一种变体——核间有绑定关系但任务分配是静态的。功能安全场景下我的经验是绝大多数情况选AMP少数混合关键度场景选BMPSMP基本不碰。为什么SMP的核心特征是操作系统统一调度所有核任务可以在核间迁移。这在通用计算里是优点在功能安全里是灾难。任务迁移意味着执行时间不可预测而功能安全最怕的就是不可预测。你没法向认证机构证明“这个安全任务的WCET最坏执行时间是X”因为它可能被调度到任何核上受其他核负载影响。更麻烦的是SMP下共享资源缓存、总线、内存控制器的竞争是动态的故障传播路径难以静态分析。AMP则是每个核跑独立的OS或裸机程序任务静态绑定。核A跑安全控制核B跑诊断监控核C跑通信。每个核的执行时间可以独立分析核间通过明确的通信机制交互。这种架构的可分析性远高于SMP是功能安全项目的首选。但AMP也有坑。最大的坑是核间通信的安全完整性。你两个核通过共享内存传数据这块内存本身有没有被保护如果核A写了一半被中断核B读到撕裂的数据怎么办如果核A跑飞了往共享内存里乱写核B怎么识别这些问题在SMP下由OS统一管理在AMP下得你自己设计。我参与过一个电机控制项目最初方案是双核AMP核0跑FOC控制核1跑安全监控。共享内存里放控制输出和监控反馈。第一版没做访问保护测试时偶尔出现监控核读到旧数据导致误报警。后来加了双缓冲加序列号机制写方写完一个缓冲区后更新序列号读方读之前和读之后各读一次序列号不一致就重读。这个机制不复杂但没经验的人很容易忽略。再说BMP。BMP适合混合关键度场景安全任务绑定到特定核非安全任务绑定到其他核但共享一个OS实例。这样既有隔离性又比纯AMP省资源不用跑多个OS。但BMP的隔离是“软隔离”依赖OS的调度策略和内存保护单元MPU。如果OS本身有漏洞非安全任务可能突破隔离。所以BMP通常用在QM质量管理和ASIL A/B混合的场景高ASIL等级还是老老实实AMP。选型时还有一个维度核间通信的物理通道。常见的有共享内存、消息队列硬件、核间中断。共享内存带宽高但需要自己做保护硬件消息队列有硬件仲裁但深度有限核间中断适合事件通知不适合大数据传输。实际项目里通常是组合使用共享内存传数据核间中断做通知硬件信号量做互斥。提示选AMP还是SMP不要只看算力需求。先问自己这个安全任务的WCET能不能静态分析如果答案是否定的SMP直接排除。3. 核间通信的安全设计共享内存不是“能用就行”得当成安全机制来验证核间通信在多核功能安全架构里地位很特殊。它既是功能实现的一部分又是安全机制的一部分。标准里有个概念叫**“安全相关的通信”**意思是如果通信失败会导致安全功能失效那这个通信本身就得按安全完整性等级来设计。很多项目在这里栽跟头把核间通信当成普通的数据传递没做完整性保护认证时被开不符合项。核间通信的安全设计我总结为三个层次数据完整性、通信可用性、故障可检测性。数据完整性解决的是“数据有没有被篡改或损坏”。最基础的是CRC校验但CRC只能检测随机错误检测不了系统性错误比如写方逻辑错误一直写错值。更高级的是端到端保护E2E Protection包含CRC、序列号、数据ID、超时计数。AUTOSAR的E2E Profile就是干这个的但很多项目觉得“太重”不用结果认证时补都来不及。我一般建议安全相关的核间通信至少要有CRC32加序列号加超时。CRC32的漏检率在功能安全场景下是可接受的序列号能检测丢包和重复超时能检测通信中断。如果ASIL等级高再加数据ID和计数器冗余。通信可用性解决的是“通信会不会被阻塞”。共享内存方案里如果两个核同时写同一块内存数据就乱了。所以需要互斥机制。硬件信号量是最可靠的但数量有限软件自旋锁在AMP下要小心因为一个核挂死会导致另一个核一直等。我的做法是关键共享数据用双缓冲写方写备用缓冲区写完切换指针读方读当前缓冲区。这样不需要互斥但需要保证指针切换的原子性。指针切换可以用硬件原子指令或者用一个字节的序列号配合内存屏障。故障可检测性解决的是“通信坏了怎么知道”。这需要心跳机制和超时监控。每个核定期往共享内存写心跳计数其他核监控这个计数。如果超时没更新就认为对方核故障触发安全反应。心跳周期要小于安全时间窗口通常取安全时间的1/3到1/2。这里有个容易忽略的点核间通信的初始化时序。多核启动时核间通信通道的初始化顺序很重要。如果核A先启动并开始写共享内存核B还没初始化好数据就丢了。所以需要一个启动同步机制核A写完初始化数据后通过核间中断通知核B核B确认后再开始正常通信。这个握手过程本身也要有超时保护。还有一个坑是缓存一致性。如果两个核有各自的缓存共享内存的数据可能一个核写了另一个核看不到。AMP下通常的做法是共享内存区域配置为非缓存或者用缓存维护指令手动同步。非缓存简单但性能差缓存维护灵活但容易漏。我见过一个项目共享内存忘了配非缓存测试时数据时对时错查了两周才发现是缓存问题。注意核间通信的安全机制不是“加了就行”得验证。CRC多项式选对了吗序列号回绕处理了吗超时阈值算对了吗这些都要有测试用例覆盖。4. 部署阶段的实操细节从启动时序到诊断调度的完整落地架构设计完了部署才是见真章的地方。多核功能安全的部署我按启动、分区、调度、诊断四个环节来说每个环节都有具体的操作和坑。4.1 启动时序谁先谁后握手怎么做多核启动通常有一个**主核Boot Core**负责初始化基础硬件然后释放其他核。功能安全项目里启动时序要保证安全监控核先于被监控核启动或者至少同时启动但监控核先就绪。否则被监控核跑飞了监控核还没起来就漏检了。具体操作上主核初始化时钟、内存、核间通信硬件后通过核间中断或硬件信号量释放从核。从核启动后先做自检RAM、Flash、外设然后向主核发“就绪”信号。主核收到所有从核的就绪信号后才启动安全任务。这个握手过程要有超时如果某个从核超时没就绪主核触发安全状态。我踩过一个坑从核自检时间太长主核等超时了直接进安全状态但其实从核只是慢了点。后来把超时阈值从固定值改成基于自检项数的动态计算并且把自检分成“关键自检”和“非关键自检”关键自检必须通过非关键自检超时只记录不阻塞。4.2 内存分区MPU怎么配才不打架AMP下每个核有独立的内存空间但共享外设和共享内存需要MPU保护。MPU配置的原则是最小权限每个核只能访问自己需要的区域共享区域只读或只写不能读写随意。具体配置时要注意MPU区域的对齐和大小。MPU区域通常要求起始地址和大小按2的幂对齐配置不当会导致保护失效。我一般会把共享内存分成几个区域控制数据区核A写核B读、监控数据区核B写核A读、心跳区双向读写。每个区域单独配MPU属性。还有一个细节栈的保护。每个核的栈要单独配MPU区域并且加栈溢出检测。栈溢出在多核下更危险因为可能踩到其他核的数据。检测方法是在栈顶和栈底放哨兵值定期检查。4.3 任务调度安全任务的时间预算怎么算多核下每个核的任务调度可以独立设计但核间同步点要统一。比如安全控制任务和监控任务需要周期同步那它们的周期要一致或者有明确的相位关系。时间预算的计算我通常按这个流程先确定安全时间窗口FHTI故障处理时间间隔然后倒推每个安全任务的WCET和调度开销留20%余量。多核下还要加上核间通信的延迟这个延迟包括中断响应、数据拷贝、同步等待。实测下来共享内存加核间中断的往返延迟在微秒级但如果是通过消息队列硬件可能到几十微秒。调度策略上安全任务用静态优先级抢占式调度非安全任务用时间片轮转。安全任务的中断优先级要高于非安全任务但核间中断的优先级要仔细配太高会打断安全任务太低会延迟安全反应。我的经验是核间中断优先级设在安全任务中断之下、非安全任务之上。4.4 诊断调度诊断不能只在一个核上跑多核架构的一个优势是诊断可以分布式部署。比如核A跑CPU自检和RAM自检核B跑Flash CRC和通信诊断核C跑外设诊断。但分布式诊断带来一个问题诊断结果的汇总和决策。如果核A发现RAM故障核B发现通信故障谁来决定进安全状态我的做法是设一个安全决策核通常是最可靠的那个核比如带锁步的核。其他核把诊断结果通过安全通信发给决策核决策核综合判断后触发安全反应。决策核本身的诊断由其他核监控形成互相监控的闭环。诊断调度还要注意诊断的执行时机。有些诊断比如Flash CRC执行时间长不能放在安全任务周期里得放在后台。但后台诊断不能影响安全任务的实时性所以要用空闲时间或低优先级任务跑。如果诊断时间太长可以分片执行每次跑一小块多个周期跑完。提示诊断的分布式部署不是简单地把诊断项分到不同核要考虑诊断结果的相关性和故障传播。比如电源故障会影响多个核的诊断结果这种共因故障要单独处理。5. 踩坑实录那些认证前才暴露的架构问题说几个我亲身经历或身边同行踩过的坑都是认证前才暴露、返工代价很大的那种。第一个坑共享内存的初始化竞争。项目里两个核通过共享内存传数据初始化时核A先启动写了初始值核B后启动也写了初始值把核A的覆盖了。结果核A读到的初始值是核B写的逻辑错乱。后来加了启动握手核B启动后先读共享内存的魔数确认核A已初始化才写自己的部分。第二个坑核间中断丢失。核A通过核间中断通知核B处理数据但核B正在处理高优先级中断核间中断被挂起等处理完发现中断标志被清了数据没处理。后来改成中断加轮询中断只做通知核B在任务里轮询标志位确保不丢。第三个坑诊断结果的时间戳不一致。两个核各自打时间戳但两个核的时钟源不同步导致诊断结果的时间顺序错乱安全决策误判。后来统一用主核的时钟从核通过核间通信获取时间戳。第四个坑安全状态跨核同步延迟。核A检测到故障触发安全状态但核B还在跑因为安全状态信号通过共享内存传递有延迟。后来改成硬件信号线加共享内存双通道硬件信号线立即拉低共享内存传详细信息核B收到硬件信号后立即进安全状态再读共享内存确认原因。第五个坑Flash CRC诊断在多核下的重复执行。两个核都跑Flash CRC浪费算力还可能导致总线冲突。后来改成一个核跑CRC另一个核读结果通过安全通信传递。这些坑的共同点是单核下不存在或很简单的问题多核下变成了架构级问题。解决思路都是“显式化”——把隐式的假设变成显式的机制把动态的行为变成静态的设计。6. 从架构到代码一个双核安全监控的落地示例用一个简化但完整的例子收尾双核AMP架构核0跑安全控制核1跑安全监控通过共享内存和核间中断通信。共享内存布局typedef struct { uint32_t magic; // 初始化魔数 uint32_t seq; // 序列号 uint32_t crc; // 数据CRC uint32_t control_output; // 控制输出 uint32_t monitor_feedback;// 监控反馈 uint32_t heartbeat_a; // 核0心跳 uint32_t heartbeat_b; // 核1心跳 } SharedData_t;核0的写流程读当前seq加1写数据算CRC写seq和CRC触发核间中断。核1的读流程读seq读数据和CRC再读seq如果两次seq一致且CRC校验通过使用数据否则重读或报错。心跳机制核0每1ms更新heartbeat_a核1每1ms更新heartbeat_b。核1监控heartbeat_a如果超过3ms没更新触发安全状态。核0同样监控heartbeat_b。MPU配置核0对共享内存有读写权限核1对共享内存有读写权限但核0对核1的私有内存无权限反之亦然。共享内存区域配置为非缓存。这个例子的关键点序列号防撕裂、CRC防篡改、心跳防挂死、MPU防越界。四个机制缺一不可。实际项目里还要加超时计数、故障注入测试、以及认证要求的文档记录。最后分享一个实操心得多核功能安全的调试日志系统要独立于安全任务。我通常用一个核专门跑日志其他核通过低优先级消息把日志发过去。这样即使安全任务出问题日志还能记录现场。但日志核本身不能影响安全任务所以它的优先级最低且日志缓冲区满了就丢弃不阻塞。这个领域没有银弹每个项目都要根据具体的核数、ASIL等级、算力需求来定制架构。但核心逻辑是通的把不确定性变成确定性把隐式假设变成显式机制把单核思维升级成系统思维。做到这三点多核功能安全就没有过不去的坎。
返回列表