CAN-FIFO 确认处理(FIFO Acknowledge Handling)
FIFO 确认处理
(FIFO Acknowledge Handling)
概述
本文基于Bosch的《M_CAN Controller Area Network User’s Manual》围绕 M_CAN 中 FIFO 确认处理机制展开,明确该机制仅适用于 Rx FIFO 0、Rx FIFO 1 和 Tx Event FIFO 这三类 FIFO,其核心是通过主机向确认索引寄存器写入值(AckIdx),触发读指针(Get Index)和填充量(Fill Level)的同步更新,以此实现主机对已读取元素的显式确认,避免因读指针自动更新导致的数据重复读取或丢失问题。
1.作用范围
仅针对 3 类 FIFO
-
FIFO 确认处理并非适用于所有 M_CAN 的 FIFO
-
仅管控以下 3 类 FIFO 的 “读指针更新”
-
其读指针(Get Index)不会自动前进,必须通过 “写确认索引” 触发更新
Rx FIFO 0:
-
对应确认索引寄存器RXF0A.F0AI
- 参考 2.3.29
Rx FIFO 1:
-
对应确认索引寄存器RXF1A.F1AI
- 参考 2.3.33
Tx Event FIFO:
-
对应确认索引寄存器TXEFA.EFAI
- 参考 2.3.47
特点
-
需主机明确确认已读取元素
-
避免因误判 “未读 / 已读” 导致数据重复读取或丢失
2.核心机制
FIFO 确认处理的核心逻辑是
-
通过单一写确认索引(Acknowledge Index)操作
-
同时触发读指针(Get Index)和填充量(Fill Level)的更新
更新规则:
-
当主机向 “确认索引寄存器” 写入一个值(记为AckIdx)后,M_CAN 会自动执行两步操作
-
读指针更新:
-
将该 FIFO 的Get Index设为AckIdx + 1
-
表示已读取到AckIdx对应的元素,下一次从AckIdx + 1开始读
-
-
填充量更新:
-
根据 Get Index 的变化方向同步更新 FIFO 的 Fill Level
-
正常场景(AckIdx ≥ 当前 Get Index):
-
Get Index 前进,Fill Level 减少
-
减少量 = (AckIdx + 1) - 原 Get Index
-
-
非法场景(AckIdx <当前 Get Index):
-
Get Index 回退,Fill Level 增加
-
增加量 = 原 Get Index - (AckIdx + 1)
-
-
示例:
-
正常
-
Get Index 从 3 跳到 6(AckIdx=5)
- → Fill Level 减少 3
-
-
非法
-
Get Index 从 4 回退到 3(AckIdx=2)
- → Fill Level 增加 1
-
-
-
本质目的:
-
通过 “显式确认” 确保主机确实读完元素后,才标记 “该元素已读”
-
避免 FIFO 自动更新读指针导致未读数据被覆盖(尤其 Rx FIFO 接收新数据时)或重复读取旧数据
3.使用场景
单元素读取与多元素批量读取
-
根据 “读取元素数量”,确认索引的操作方式分为两种
-
核心是 “批量读取时无需逐次确认,提升效率”
1:读取单个元素(按读指针顺序读)
-
当主机仅读取 FIFO 中 “当前读指针(Get Index)指向的元素” 时:
- 如Get Index=2,只读元素 2
-
操作步骤:
- 将当前Get Index的值(即 2)写入 “确认索引寄存器”
-
结果:
-
Get Index更新为2 + 1 = 3,Fill Level减少 1
-
表示元素 2 已读,填充量从原数值减 1
-
-
示例:
-
Rx FIFO 0 的Get Index=3、Fill Level=5
-
读元素 3 后写RXF0A.F0AI=3,最终Get Index=4、Fill Level=4
-
2:读取连续多个元素(批量读)
-
当主机连续读取多个元素时:
- 如从Get Index=2开始,读元素 2、3、4
-
操作步骤:
-
无需逐次写确认索引,仅在读完最后一个元素(如元素 4)后
-
将 “最后一个元素的索引(4)” 写入 “确认索引寄存器”
-
-
结果:
-
Get Index更新为4 + 1 = 5
-
Fill Level减少 3
- 一次性标记元素 2、3、4 已读
-
-
优势:
- 避免多次写寄存器的开销,尤其在批量处理大量接收数据(如 Rx FIFO 满时)时提升效率
4.风险提示
禁止乱序读取后写确认索引
-
场景:
-
CPU 可自由访问 M_CAN 的 Message RAM
- 无需通过 FIFO 读指针
-
常见于从 Rx FIFO 读取高优先级消息(High Priority Message)时
- 为快速处理高优先级消息,可能跳过读指针指向的低优先级元素,采用任意顺序读取
-
-
风险:
-
若在任意顺序读(不考虑 Get Index)后写确认索引,会导致:
-
1.Get Index 跳至错误位置(AckIdx + 1)
-
2.FIFO Fill Level 被错误修改
-
3.未读取的旧元素(如被跳过的低优先级元素)被标记为 “已读
- 后续新数据写入时会覆盖,导致数据丢失
-
原因
-
假设场景:
-
Rx FIFO 0 的Get Index=2(应读元素 2)
-
但主机跳过元素 2、3,直接读取元素 4
-
-
错误操作:
-
若此时写 “确认索引 = 4”
-
Get Index会更新为4 + 1 = 5,Fill Level减少 3
-
-
数据丢失:
-
M_CAN 会认为元素 2、3、4 已读
-
但实际元素 2、3 未被读取,后续新数据写入时会覆盖这些未读元素,导致旧数据丢失
-
典型示例
-
Rx FIFO 0 的 Get Index=2(应读低优先级元素 2)
-
主机为优先处理元素 4(高优先级),跳过元素 2、3 直接读 4
-
错误操作:
- 写确认索引 = 4 → Get Index 更新为 5,Fill Level 减少 3
-
结果:
-
M_CAN 误判元素 2、3、4 已读,元素 2、3 未读
-
即被覆盖,数据丢失
-
结论:
-
仅当 “按读指针顺序读取元素” 时,才能写确认索引
-
乱序读取(如高优先级消息优先)时,需通过其他方式标记 “已读 / 未读”,禁止操作确认索引
5.注意事项
应用层需保证确认索引的合法性
-
M_CAN 不会检查 “确认索引” 的值是否合法
-
如是否超过当前 FIFO 的最大有效索引
-
是否小于当前Get Index
-
-
应用层必须自行确保写入的值有效,否则会导致 FIFO 读指针异常
非法场景 1:
-
写入的AckIdx大于当前Fill Level对应的最大索引
- 如Fill Level=5,写AckIdx=10
-
会导致Get Index跳至 11,后续读取会访问无效的 FIFO 地址
非法场景 2:
-
写入的AckIdx小于当前Get Index
- 如Get Index=4,写AckIdx=2
-
会导致Get Index回退至 3,可能重复读取已读元素
小结
主机说了算:读完哪条标哪条,没读完不瞎标
- 避免 FIFO 自动更新指针带来的不确定性
FIFO 确认处理是 M_CAN 为 “精准管控 FIFO 读状态” 设计的显式确认机制
-
通过 “确认索引” 解耦 “读操作” 与 “读指针更新”
-
支持单元素精细控制和多元素批量优化
-
但需遵循 “顺序读才确认” 规则,应用层要保证确认索引合法性