这颗芯片有 5 路 CAN-FD。但 QEMU 8.2 里没有任何 M_CAN 模型——只有 SJA1000、CTU CAN-FD、Xilinx CAN 这几种,都不是 Bosch 的 M_CAN。
所以这部分是完整的自研实现。
消息 RAM 是核心
M_CAN 和普通 CAN 控制器不一样,它的收发缓冲区不在寄存器里,而在一块独立的内存里。
设备树里给了两块地址:
reg = <0xe0828000 0x200>, <0x100000 0x7800>; reg-names = "m_can", "message_ram";
第一块是寄存器,第二块就是消息 RAM,0x7800 字节。驱动直接 ioremap 过去,往里面读写帧。
模型里给这块内存单独建了一个 MemoryRegion,CPU 和内部逻辑共用同一块 buffer。
【10_01_消息RAM】
一个省事的发现
本来以为要按 RXESC / TXESC 寄存器算出每个元素的尺寸,再算偏移。翻驱动代码发现不用:
#define RXF0_ELEMENT_SIZE 72 #define TXB_ELEMENT_SIZE 72 #define TXE_ELEMENT_SIZE 8
驱动里是硬编码的 72 字节。 不管配的是什么数据长度,元素都按 72 字节排。
72 正好是 8 字节头 + 64 字节数据,对应 CAN-FD 的满载荷。
这一下省掉一大块解析逻辑——模型只要按 72 字节搬运就行。
回环:把 TX 的帧搬进 RX
第一个目标是让帧能发出去、能收回来。M_CAN 自带 loopback 模式,TEST 寄存器的 bit 4。
流程:
写 TXBAR 触发发送 → 从 TXB 取第 i 个元素(72 字节) → 复制到 RX FIFO0 的 put 位置 → 更新 FFL / FGI / FPI → 拉 RF0N 中断
驱动那边收到中断后,读 RXF0S 拿到填充数和 get 索引,从消息 RAM 读出元素,再写 RXF0A 确认,硬件把读指针往前推一格。
RXF0S 的字段位置是:填充数 [6:0]、get 索引 [13:8]、put 索引 [21:16]。
测试
先把工具准备好。core-image-minimal 只有 busybox,它的 ip 不支持 type can,得单独编 iproute2 和 can-utils 塞进去。
配接口:
~ # ip link set can0 type can bitrate 500000 loopback on ~ # ip link set can0 up ~ # ip -details link show can0 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 … can <LOOPBACK> state ERROR-ACTIVE (berr-counter tx 0 rx 0) bitrate 500000 sample-point 0.875 clock 40000000 parentdev e0828000.can
位时序被驱动接受了,链路起来了。
然后发帧:
【10_02_CAN回环】
~ # candump can0 & ~ # cansend can0 123#11223344 can0 123 [4] 11 22 33 44 ~ # cansend can0 12345678#AABBCC can0 12345678 [3] AA BB CC ~ # cansend can0 7FF#DEADBEEF can0 7FF [4] DE AD BE EF
标准帧、扩展帧、边界 ID,全部原样回来。
FD 帧发不出去
试 FD 长帧,cansend 直接拒绝:
CAN interface is not CAN FD capable - sorry.
看 ip -details 的输出,mtu 16——这是经典 CAN 的 MTU,FD 应该是 72。
驱动其实支持 FD,ctrlmode_supported 里一直有 CAN_CTRLMODE_FD,只是必须显式打开:
~ # ip link set can0 down ~ # ip link set can0 type can bitrate 500000 dbitrate 2000000 fd on loopback on ~ # ip link set can0 up
加了 fd on 和 dbitrate,MTU 变成 72,再发:
~ # cansend can0 456##3112233445566778899AABBCCDDEEFF can0 456 [16] 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 ~ # cansend can0 789##30011…EEFF # 64 字节满载荷 can0 789 [64] 00 11 22 33 … EE FF
16 字节和 64 字节都通了。
边界
这个模型验证的是驱动路径和数据通路:消息 RAM 布局对了、元素搬运对了、中断和 FIFO 指针握手对了。
没做的是对外通信——要真正挂到 CAN 总线上,得把模型接到 QEMU 的 CAN bus 上,再桥到宿主机的 SocketCAN。那是另一层的事,跟模型正确性无关。
我要赚赏金
