{"code":1,"msg":"成功","time":"1790226971","data":{"title":"NanoPi M5（RK3576）linux网络（GMAC0）+ AMP：跑网络（GMAC1 ）","content":"NanoPi M5（RK3576）AMP：跑网络\r\nCortex-M0 那条路走不通,A53 这条走通了\r\nRT-Thread 独占一颗 A53\r\n\r\n\/\/上传不了图片，那就没有图了。。。。。\r\n\r\n一句话结论\r\nRK3576 上把 GMAC1 整个搬到 AMP 的 RT-Thread 侧,ping 通了。\r\n\r\n走通的是 AP+AP(从 Linux 手里剥一颗 A53 给 RT-Thread);\r\nbus_mcu(Cortex-M0)那条路是架构级隔离,走不通 ——这一条如果你也在考虑,可以省下几周。\r\n\r\n三个根因都定位并修复了(第 5 章,含一个长期偶发的堆崩溃);\r\n还有一个架构级的问题只解决了一半,在第 6 章。\r\n\r\n2026 年 9 月\r\n \r\n目  录\r\n第 1 章  背景\t2\r\n1.1\t环境\t3\r\n1.2\t多核异构系统\t4\r\n1.3\t结论摘要\t8\r\n第 2 章  先说否定结论:Cortex-M0 那条路走不通\t8\r\n2.1  最小验证:不写驱动,先看总线通不通\t9\r\n2.2  为什么说这是访问控制,不是时钟或走线问题\t9\r\n2.3  两条外部信息的核实\t9\r\n第 3 章  改走 AP+AP:从 Linux 手里剥一颗 A53\t10\r\n3.1  好消息:不用打内核补丁\t10\r\n3.2  上板定位的几个根因\t10\r\n第 4 章  GMAC1 移植:这份 SDK 里 RK3576 的 GMAC HAL 是空壳\t12\r\n4.1  要补的五项\t12\r\n4.2  做成了编译开关\t13\r\n4.3  两个不属于网络本身的坑\t13\r\n第 5 章  三个根因:PHY ID 读回 0x0,以及 ping 打崩堆\t13\r\n5.1  根因一:MDIO 时钟频率算错\t13\r\n5.2  根因二:PHY 参考时钟门控开晚了\t14\r\n5.3  根因三:ping 会打崩堆\t15\r\n第 6 章  结果,以及还没完全解决的问题\t16\r\n6.1  结果\t16\r\n6.2  加了一道安全网,但只挡得住一半的崩法\t17\r\n6.3  关于我\t17\r\n\r\n# 第 1 章  背景\r\nRK3576 上做 AMP(Linux 和 RT-Thread 同时跑),网上能找到的资料基本停在 GPIO \/ UART \/ rpmsg。我需要 RTOS 这一侧能独立收发网络包,找了一圈没看到有人做通过,只能自己啃。\r\n现在 ping 通了。记一下过程,主要是几个卡了很久的点 —— 包括一条走不通的路,那条也值得写,因为它能帮人省时间。\r\n\r\n## 1.1\t环境\r\n表 1-1  环境\r\n项\t内容\r\n板子\tNanoPi M5(RK3576)\r\nLinux 侧\tbuildroot 和 Ubuntu 24.04 都验过\r\nRTOS 侧\tRT-Thread\r\n起点\t同芯片已有的 AMP 参考实现 —— 到 GPIO \/ UART \/ rpmsg 为止,网络这一段没有\r\n\r\n## 1.2\t多核异构系统\r\n\r\nLinux测，少了一个cpu(核),lscpu,显示7个cpu（核）cpu3，配给rtos了。\r\n \r\n\r\n桌面：\r\n \r\nRtos，cpu3终端串口输出：\r\n \r\n配套硬件：Nanpi M5.\r\n \r\n## 1.3\t结论摘要\r\n表 1-2  结论摘要\r\n\t结论\r\n路线 A\t挂给 bus_mcu(Cortex-M0)—— 走不通,架构级隔离\r\n路线 B\t分一颗 A53(cpu3)给 RT-Thread —— 走通了\r\n成果\tGMAC1 完整驱动移植到 RTOS 侧,编译开关可切换网口归属,ping 通\r\n遗留\tcpu3 真死机(不报错、纯卡死)时没有看门狗兜底——已有的故障保护只挡得住「主动报错」这一种崩法,见第 6 章\r\n\r\n\r\n# 第 2 章  先说否定结论:Cortex-M0 那条路走不通\r\n \r\n图 2-1  两条路线\r\n2.1  最小验证:不写驱动,先看总线通不通\r\nRK3576 上有个 bus_mcu(Cortex-M0),最初想把 GMAC1 挂给它。写驱动之前先做了个最小验证:直接从 M0 侧读 GMAC1 的寄存器,看总线能不能摸到。\r\n结果是:总线正常应答,但读回来固定是 0 —— 没有 HardFault,没有卡死,是一次干净完成的读操作。\r\n2.2  为什么说这是访问控制,不是时钟或走线问题\r\n「正常应答 + 返回固定假数据」是访问控制类机制的典型特征\r\n如果总线根本不通,通常表现是访问直接异常。\r\n而这里是干净完成的读,只是内容是固定的 0。\r\n\r\n这正是总线防火墙 \/ 外设访问白名单的常见做法:不在白名单里的主设备读上去是干净的假数据,而不是总线错误。\r\n\r\n翻遍能拿到的 RK3576 寄存器头文件,没找到任何软件可见的白名单寄存器 ——就算真是这个原因,这套 SDK 也没给 bus_mcu 留解除限制的入口。\r\n\r\n两次上板读取结果一致,也排除了时序原因。\r\n2.3  两条外部信息的核实\r\n核实的事\t结果\r\n网上流传某商业 RTOS SDK「AMP 支持 GMAC」\t那条更新对应的是 RK3506,不是 RK3576。同一份更新日志里 RK3576 部分从没出现过 GMAC \/ AMP 网络相关条目\r\nRK3506 的成功案例能不能反推到 RK3576\t不能。RK3506 那边跑 RTOS 的是 Cortex-A7 应用核,不是 RK3576 这种打杂的 M0;而且它的 GMAC 时钟直接挂在普通低速外设总线下,跟 UART\/SPI 一个级别\r\n\r\n表 2-1  外部信息核实\r\n两颗芯片的总线和安全架构本来就不一样,不能互相反推。\r\n所以这一段的价值是:帮你省掉几周\r\n如果你也在考虑把网口挂给 RK3576 的 bus_mcu ——\r\n别走这条,现场已清理,转 A53。\r\n\r\n# 第 3 章  改走 AP+AP:从 Linux 手里剥一颗 A53\r\n \r\n图 3-1  最终的核与资源划分(实际还包含4*ARM Cortex-A72)\r\n## 3.1  好消息:不用打内核补丁\r\nRK3576 现有的内核驱动是支持这条路的 —— 把指定的 A53 从 Linux 的调度里剥出来交给 RTOS,内核侧不需要额外补丁,主要是设备树和配置的事。\r\n## 3.2  上板定位的几个根因\r\n根因\t现象\t代价\r\nmode 字段编码错误\t固件报「核已上电」,但代码从来没跑起来\t查了很久 —— 因为表象是启动成功\r\nGIC 中断范围过宽 + 自动触发不受控\t中断打到不该打的地方\t写坏了两次 Linux 根文件系统\r\nUART6 中断没预路由\t启动时空等约 10 秒\t启动慢,但不致命\r\n\r\n表 3-1  路线 B 上板定位的根因\r\n第二行是这一段最贵的教训\r\n中断要预路由,别指望默认值能自己避开。\r\n\r\nAMP 下两个核共用一套 GIC,范围没划清楚,RTOS 侧的中断会打到 Linux 的地盘上 —— 而这个后果可能是文件系统被写坏,不是简单的报错。\r\n\r\n后面移植 GMAC1 时提前把这条经验用上,给 GMAC1 中断做了预路由,绕开了同类问题。\r\n\r\nrpmsg 双向通信在更早的阶段已经打通,两侧可以互发消息 —— 这一段有现成的参考实现,主要是改板级配置。\r\n\r\n# 第 4 章  GMAC1 移植:这份 SDK 里 RK3576 的 GMAC HAL 是空壳\r\n \r\n图 4-1  要自己补的五项\r\n本来以为照搬 RK3568 就行。翻开一看,这份 SDK 里 RK3576 的 GMAC 支持完全是空的 —— 不是缺几个函数,是整块没有。\r\n\r\n## 4.1  要补的五项\r\n项\t难度\t说明\r\n寄存器结构体\t低\t照 RK3568 补\r\n基地址 \/ 中断号\t低\t查手册\r\n时钟 \/ 复位 ID\t高\t没有文档 —— 拿芯片上已有的同类 mux 当参照交叉验证推出来,不是凭空猜\r\n芯片专属回调\t高\tRK3576 的引脚延迟线机制跟 RK3568 不一样,直接抄会错\r\n板级接入\t中\teth_config 表、引脚复用、HwPreInit、中断表\r\n\r\n表 4-1  要自己补的五项\r\n## 4.2  做成了编译开关\r\n网口的归属做成了编译期开关 —— 同一套代码可以选这个网口给 Linux 还是给 RTOS,两种配置都能出镜像。实际项目里这个灵活性比想象中重要,因为调试阶段经常要切回去。\r\n\r\n## 4.3  两个不属于网络本身的坑\r\n坑\t现象\r\nlwIP 线程优先级\t断言崩溃:优先级超了 RT_THREAD_PRIORITY_MAX\r\n`RT_UNCACHE_HEAP_SIZE`\t链接脚本看不见 C 头文件里的宏 —— 这类「两套构建系统各看各的」问题,这次移植里反复出现\r\n\r\n表 4-2  两个构建系统层面的坑\r\n# 第 5 章  三个根因:PHY ID 读回 0x0,以及 ping 打崩堆\r\n驱动写完,PHY ID 读回来是干净的 0x0 —— 不是超时,是通信本身成功、但读到 0。\r\n加了 8 次、跨度约 4 秒的重试,全是 0;但链路协商线程 3 秒后的检查却能拿到真实值。这说明卡点不是时间,是顺序。\r\n## 5.1  根因一:MDIO 时钟频率算错\r\n \r\n图 5-1  一个没填的字段,一路错到底\r\n驱动靠 clk_get_rate(pclkID) 拿基准时钟,据此在几档里选 MDIO 的分频值。而我写的设备结构体里 —— 这个字段压根没填。\r\n对照 RK3568 的同名结构体会发现:人家是专门填了的。\r\n修复过程里的一个发现\r\n这份 SDK 的 RK3576 时钟名表里,根本没有对应的时钟节点 ——因为 GMAC 支持本来就是空的,配套的时钟表也没人补。\r\n\r\n但 CRU 的寄存器位域宏其实早就在头文件里,只是没人接上。\r\n\r\n照着芯片上已有的同类三选一 mux 的写法补齐即可。\r\n\r\n教训:SDK 里某个平台的支持是空的时候,缺的往往不止驱动,还有配套的时钟 \/ 复位表。\r\n\r\n## 5.2  根因二:PHY 参考时钟门控开晚了\r\n \r\n图 5-2  修复前后的顺序对比\r\n时钟频率修好后,MDIO 确实工作了(能协商出真实链路),但第一次读 PHY ID 依然是 0x0。\r\n顺着初始化函数的执行顺序查下去找到真根因:给 PHY 的 125M RGMII 参考时钟(这块板子 MAC 是这路时钟的主设备)的门控使能,压根不在复位 \/ 读 ID 这段代码里,而是在更晚的地方才执行。\r\n把门控使能挪到复位之前 —— 原调用点保留(clk_enable_by_id() 是幂等的置位操作,重复调用无害)。\r\n重刷验证\r\n```\r\nPHY found ID: 0x1cc916\r\n```\r\n\r\n第一次尝试就读对了,不用重试 —— 跟猜测完全吻合。\r\n\r\n至此 GMAC1 从 MAC 寄存器、时钟、复位、引脚、中断路由、PHY 探测到链路协商,全部走通。\r\n\r\n## 5.3  根因三:ping 会打崩堆\r\nPHY 通了、链路协商过了之后,ping 一跑就触发堆内存损坏。现象是堆的块头被写脏,而且不是每次都崩,看起来像是随机的内存踩踏。\r\n先说清楚是哪个方向:板子被外面 ping,一直没问题(第 6 章那张截图就是这个方向);出问题的是反过来 ——cpu3 自己主动发 ping 出去,跑不了几次堆就崩。\r\n最后定位到是内存管理层面一处隐藏的配置不一致,导致每次发送都会越界写几个字节,精确踩在下一个堆块的头部上。\r\n这类问题为什么难查:它不在越界的地方崩\r\n越界写不报错,数据写进了不该写的地方,但那块内存的真正主人大概率很快会自己申请或释放一次内存,顺手把被踩脏的地方覆盖回正常值——所以十次里有九次看起来什么事都没有。\r\n\r\n只有主人没来得及覆盖回去的那一次,才会真的炸。\r\n\r\n这就是它看起来「随机」的真正原因:不是竞态,是每次都会越界,只是大多数时候被别人无意中盖住了。\r\n\r\n查的时候得反过来查:不盯崩溃点,在每次分配 \/ 拷贝 \/ 发送前后打印相邻堆块头部的校验值,才第一次看到「原来每一次发送后都是坏的」。\r\n\r\n定位到之后,修复其实就是一处配置。但定位到该改哪里,是三个根因里最费时间的一个 —— 前两个是「查到就改」,这个是「先得把肇事现场锁定,才知道该往哪个方向查」。\r\n重刷验证\r\n连续 40 次主动 ping,堆检查点全程干净,零崩溃 ——从「偶尔崩」到「确认修好」,靠的就是上面那个「盯着相邻堆块」的笨办法。\r\n\r\n# 第 6 章  结果,以及还没完全解决的问题\r\n \r\n图 6-1  已跑通的,与还没完全解决的问题\r\n## 6.1  结果\r\nping 通了,数据面确实在收发:\r\n`60 bytes from 192.168.10.60 icmp_seq=3 ttl=128 time=4 ms`\r\n\r\n## 6.2  加了一道安全网,但只挡得住一半的崩法\r\ncpu3 这边一旦出问题,以前的经验是不管是不是在跑网络代码,Linux 都可能被拖到要物理断电才能恢复 —— 这是 AMP 的核间隔离问题,不是网络驱动本身的问题,但只要没解决,这套方案就不敢真的上产线。\r\n针对「堆崩溃这种情况」加了一道故障保护:触发断言的瞬间,自动停掉网络 DMA、屏蔽中断、把这颗核自己隔离下线 —— 不去修复现场,只求不连累对方。这套东西写好后一直没等到真的触发过,直到这次反向 ping 崩堆的过程中第一次被真实触发:\r\n第一次真实验证:触发之后,Linux 全程正常\r\n崩溃发生的瞬间保护逻辑生效,Linux 那边全程干净 ——没有卡死,没有需要断电,SSH 一直连着。\r\n\r\n这条防线从「写完但没验证过」变成「真出过事,扛住了」。\r\n\r\n但这只覆盖一种崩法:代码自己发现不对劲、主动触发断言的情况。真正的死机 \/ 卡死呢? —— 这颗核目前没有独立看门狗,如果它不是「主动报错」,而是纯粹卡死不动了,现在还是没人能把它拉回来。\r\n这条我还在弄。有做过 RK 平台 AMP 核间隔离 \/ 看门狗方案的,欢迎交流 —— 尤其是「卡死不报错」这种情况怎么兜底。\r\n## 6.3  关于我\r\n做嵌入式 Linux 和设备通信这块:Rockchip 板级移植、AMP、Modbus \/ CAN \/ 串口这些协议对接,上位机界面也自己做。\r\n完整的配置、补丁和踩坑细节比较长,这里放不下 —— 有需要的可以 [37152428@qq.com \/ 评论区留言我私信]。","updatetime":"2026-09-23 10:48:51","author":"austart"}}