1. 项目概述与MII_RT模块的核心价值
在工业自动化、运动控制这些对时间要求严苛到微秒级的领域,传统的基于Linux或RTOS的处理器架构常常会面临一个根本性的挑战:操作系统调度、中断延迟以及内存访问的不确定性。当你需要确保一个以太网帧在精确的125微秒周期内被发送,或者一个编码器信号必须在几个纳秒内被响应时,通用处理器的“软实时”特性就显得力不从心了。这时,像TI Sitara系列处理器中的PRU-ICSS(可编程实时单元与工业通信子系统)这样的硬实时协处理器,就成了解决问题的关键。它不是跑操作系统的CPU,而是一个独立的、确定性的200MHz微控制器,能够直接操纵芯片的引脚和内部硬件模块,实现真正的“零延迟”响应。
今天我们要深入拆解的,就是PRU-ICSS中负责以太网物理层数据吞吐的MII_RT(MII实时接口)模块。你可以把它想象成PRU与外部PHY芯片之间的一座专用、高效、可编程的数据桥梁。它的核心价值在于,将原本需要CPU频繁中断处理的以太网数据包收发工作,卸载到PRU这个“硬件加速器”上,并通过一系列精巧的硬件队列(FIFO)和命令接口,让PRU能够以周期精确的方式控制每一个字节的流入和流出。这对于实现EtherCAT、PROFINET IRT、EtherNet/IP CIP Sync等工业以太网协议至关重要,因为这些协议不仅要求通信,更要求通信的时间确定性。
简单来说,如果你想让你的嵌入式设备成为一个高性能的工业以太网从站或主站,或者实现任何需要微秒级精度的网络数据交换,那么理解并驾驭MII_RT模块,就是你绕不开的必修课。它直接决定了你的数据通路是否高效、延迟是否可控、以及整个通信栈的实时性能上限。
2. MII_RT模块整体架构与数据流解析
要理解MII_RT,我们得先看看它在一个典型的PRU-ICSS应用中是处于什么位置。整个数据通路可以概括为:外部PHY芯片 <-> MII接口(RX_DV, RX_DATA, TX_EN, TX_DATA) <-> MII_RT模块 <-> PRU核心。MII_RT模块在其中扮演了交通枢纽和缓冲区的角色。
2.1 核心组件:三级FIFO缓冲体系
MII_RT模块的数据缓冲并非一个简单的队列,而是分为多级,每一级都有其特定的职责,共同确保了数据流的平滑和PRU处理的灵活性。根据技术手册,我们主要关注以下几个关键FIFO:
RX L1 FIFO (32字节):这是数据进入PRU-ICSS的第一站。它直接连接MII接收接口,用于缓冲从PHY芯片源源不断送来的以太网帧数据。其32字节的深度(通常可存储多个以太网字)是为了吸收MII接口(25MHz时钟,每个时钟传输4位,即半字节)与PRU处理速度之间的微小波动,防止数据溢出。当RX_DV信号有效时,数据就被推入此FIFO。
TX L1 FIFO (64字节):这是数据离开PRU-ICSS前的最后一个缓冲区,连接MII发送接口。它的深度(64字节)比RX L1更大,这背后有重要考量:发送侧需要组装完整的帧(包括前导码、帧起始定界符SFD、数据、帧校验序列FCS),并且可能涉及PRU的实时修改或生成,因此需要更大的缓冲空间来避免下溢(即MII接口需要发送数据时,缓冲区却空了)。PRU或自动转发逻辑将待发送的数据写入此FIFO,模块硬件会自动将其转换为MII信号流发送出去。
RX L2 Buffer:这是一个更接近PRU核心的缓冲区或“便签本”(Scratch Pad)。当使能时,它可以作为RX L1 FIFO和PRU寄存器之间的二级缓存。更重要的是,当不用于以太网数据接收时(通过
RXCFG0/1[RX_L2_ENABLE]配置),它可以被PRU当作一块通用的32字节x2 Bank的共享内存来使用,用于存储临时变量或中间计算结果,这为协议处理提供了额外的灵活存储空间。
2.2 核心接口:R30与R31寄存器
PRU核心与MII_RT模块的交互,几乎全部通过两个特殊的寄存器完成:R30和R31。这是PRU架构的精髓——内存映射的GPIO和事件接口。
- R31(命令与状态接口):这是一个多功能寄存器。在MII_RT的上下文中,向R31的高16位(bit 31:16)写入特定值,就是向硬件发送命令。例如,写入
TX_PUSH16命令,就是告诉MII_RT模块:“把我R30里的数据,按照当前配置推送到TX L1 FIFO去”。而从R31的低16位(bit 15:0)读取,则是获取来自RX L1 FIFO的数据。同时,R31的某些位也反映了FIFO的状态,如WORD_RDY(字就绪)标志。 - R30(数据输出与掩码接口):这个寄存器主要用于发送路径。PRU将待发送的数据写入R30的低16位。更强大的是,其高16位可以配置为发送掩码(TX Mask)。在TX Mask模式下,PRU可以实时地、按位选择是将自己生成的数据发送出去,还是将刚从接收路径得到的数据(经过可能的字节交换后)转发出去。这为实现数据透传、实时修改或协议转换提供了硬件级的原子操作能力。
2.3 核心数据通路模式
MII_RT模块的数据流不是固定的,可以通过配置寄存器(主要是TXCFG0/1和RXCFG0/1)来切换,以适应不同的应用场景:
- 寄存器模式(Register Mode):这是最灵活、最常用的模式。PRU通过主动读取R31来获取接收数据,通过向R30写入并发送R31命令来控制发送数据。PRU对每一个字节或字的处理都有完全的控制权,可以实现复杂的协议解析和封装。
- 自动转发模式(Auto-Forward Mode):当设置
TXCFG0/1[TX_AUTO_SEQUENCE]时,数据帧可以从RX L1 FIFO直接自动转发到TX L1 FIFO,完全无需PRU干预。这种模式延迟极低,适用于简单的网络交换或回环测试场景。但请注意,在此模式下,PRU无法对经过的数据进行任何处理。 - TX Mask模式:这是寄存器模式下的一个高级特性。它允许PRU在发送数据时,动态地混合来自接收路径的数据和PRU自身生成的数据。其操作由公式
TXDATA = (R30_data & MASK) | (RXDATA & ~MASK)定义。通过配置R30的高16位作为掩码,PRU可以逐位决定数据来源,实现诸如实时替换帧中特定字段(如目标地址、状态字)而无需先将整个帧读入PRU内存再写回,极大地提升了效率和实时性。
理解这三层FIFO、两个核心寄存器以及几种数据通路模式,就掌握了MII_RT模块的静态骨架。接下来,我们要看PRU如何通过“发号施令”来让这个骨架动起来。
3. PRU R31命令接口的深度剖析与实战编程
如果说MII_RT模块是硬件舞台,那么R31命令接口就是PRU核心指挥这个舞台的“遥控器”。向R31[31:16]的特定位写入‘1’,会产生一个单时钟脉冲的命令信号,直接控制MII_RT内部的状态机。这些命令是精细控制数据流的关键。
3.1 发送路径命令详解与配合时序
发送命令主要用于构建和发出一个以太网帧。一个典型的帧发送流程需要按顺序触发一系列命令。我们假设PRU已经将要发送的数据准备在了某个内存区域,并且已经配置好了TX Mask等参数。
帧开始与数据推送 (
TX_PUSH8/TX_PUSH16):- 作用:将R30[15:0]寄存器中的数据(可能经过Mask处理)推入TX L1 FIFO。
TX_PUSH8推送一个字节(使用低8位),TX_PUSH16推送两个字节。 - 实战注意:手册中特别强调,如果数据没有被完全掩码(即需要混合接收数据),
TX_PUSH命令必须先于对应的TX_POP命令发生。这是因为硬件需要先锁定发送数据的来源。在编��时,通常的序列是:设置R30数据和掩码 -> 执行TX_PUSH-> (如果需要接收数据)执行RX_POP。 - 代码示例(伪代码):
// 假设我们要发送两个字节 0xABCD,且不使用Mask(全部使用R30数据) R30 = 0xABCD; // 将数据写入R30低16位 R31 = (1 << 25); // 设置TX_PUSH16命令位(bit 25),其他位为0,并写入R31触发命令
- 作用:将R30[15:0]寄存器中的数据(可能经过Mask处理)推入TX L1 FIFO。
帧结束与CRC (
TX_EOF,TX_CRC_LOW,TX_CRC_HIGH):TX_EOF(bit 29):指示当前推入的数据是帧的最后一笔。发送此命令后,MII_RT模块会开始计算或追加帧校验序列(FCS),并完成帧的发送。TX_CRC_LOW(bit 26) &TX_CRC_HIGH(bit 27):当不使用硬件自动CRC,而由PRU软件计算CRC时,需要使用这两个命令将计算好的CRC值(32位)分高低16位推入FIFO。必须注意时序:TX_CRC_HIGH命令结束CRC计算并将高16位CRC值推入FIFO,之后需要等待至少6个时钟周期,TXCRC0/1寄存器中的值才会有效,然后才能发出TX_CRC_LOW命令推送低16位。- 关键配合:
TX_EOF命令可以与TX_CRC_ERR(bit 31)组合,在帧尾强制插入一个错误字节(0xA5),用于测试或特定协议需求。但前提是必须使能自动前导码转发(TX_AUTO_PREAMBLE)。
错误插入与FIFO复位 (
TX_ERROR_NIBBLE,TX_RESET):TX_ERROR_NIBBLE(bit 28):在帧中插入一个错误半字节(nibble),使帧无效。通常用于物理层容错测试。TX_RESET(bit 30):复位TX L1 FIFO,清空所有内容。这是从TX FIFO溢出错误中恢复的关键操作。一旦检测到发送FIFO满或错误,应首先执行此命令进行复位。
3.2 接收路径命令详解与状态管理
接收命令主要用于从RX FIFO中读取数据和管理接收状态。
数据弹出 (
RX_POP8/RX_POP16):- 作用:
RX_POP8使接收数据流前进一个字节,RX_POP16前进两个字节。只有在使用R31读取数据(即寄存器模式)时才需要此命令。执行RX_POP后,新的数据才会出现在R31[15:0]中供PRU读取。 - 至关重要的延迟:手册明确警告,
RX_POP命令发出后,需要等待2个时钟周期,WORD_RDY或BYTE_RDY状态位才会更新。PRU固件必须确保在发出RX_POP命令后,至少间隔2个周期再去检查R31的WORD_RDY位(bit 30?需查具体映射)或读取数据,否则会读到旧数据或状态。这是新手最容易忽略的硬件时序坑。 - 代码示例(伪代码):
// 等待接收数据就绪(假设WORD_RDY映射到R31的某个状态位) while (!(R31 & (1 << WORD_RDY_BIT))) { // 空循环或执行其他任务 } // 数据就绪,读取一个16位字 received_data = R31 & 0xFFFF; // 读取低16位数据 // 发出POP命令,让硬件准备下一个字 R31 = (1 << 17); // 设置RX_POP16命令位(bit 17) // !!! 重要:这里必须等待至少2个时钟周期,才能再次检查WORD_RDY !!! // 可以通过插入NOP指令或执行其他不依赖新数据的操作来实现延迟 __delay_cycles(2); // 伪代码,表示延迟2个周期
- 作用:
状态清除命令 (
RX_SOF_CLR,RX_SFD_CLR,RX_EOF_CLR,RX_ERROR_CLR):- 作用:这些命令(bit 20, 21, 22, 23)用于清除R31中对应的状态标志位。例如,当PRU检测到
RX_EOF(帧结束)标志后,在处理完该帧后,需要向RX_EOF_CLR位写‘1’来清除该标志,以便识别下一帧的开始。 - 实战技巧:通常在一个帧处理循环的开始或结束阶段,统一检查并清除这些状态位,是一个良好的编程习惯。
- 作用:这些命令(bit 20, 21, 22, 23)用于清除R31中对应的状态标志位。例如,当PRU检测到
接收FIFO复位 (
RX_RESET, bit 18):- 作用:复位RX L1 FIFO,清空所有内容。用于从接收FIFO溢出等错误中恢复。手册指出,如果在活动帧期间断言
RX_RESET,它会:1) 终止当前帧;2) 阻塞所有新数据;3) 清空FIFO;4) 使RX状态机回到空闲状态;5) 产生EOF事件;6) 如果帧未达到最小尺寸,则产生最小帧错误。 - 典型用例:在检测到
RX_EOF后,如果软件决定丢弃后续数据或进行错误恢复,可以立即发出RX_RESET。
- 作用:复位RX L1 FIFO,清空所有内容。用于从接收FIFO溢出等错误中恢复。手册指出,如果在活动帧期间断言
3.3 命令组合与原子操作
手册强调了一个关键点:当需要在同一时刻执行多个命令时,PRU固件必须在一条指令中设置所有这些命令位。这是因为对R31的写入操作是原子性的。例如,需要在帧尾同时标记EOF和插入CRC错误,那么就需要构造一个值,同时设置TX_EOF位和TX_CRC_ERR位,然后一次性写入R31。
// 原子操作:同时发送TX_EOF和TX_CRC_ERR命令 uint32_t command = 0; command |= (1 << 31); // TX_CRC_ERR command |= (1 << 29); // TX_EOF R31 = command; // 单条指令写入,两个命令同时生效理解并正确运用这些命令,是编写稳定、高效PRU以太网处理固件的基石。任何时序的疏忽都可能导致数据错位、丢失或硬件状态机挂起。
4. 数据通路配置实战:从寄存器到帧收发
掌握了命令,我们来看看如何通过配置寄存器,搭建起我们需要的数据通路。这就像在硬件上“布线”,决定了数据从哪里来,到哪里去,以及经过怎样的处理。
4.1 字节与半字节顺序交换
由于PRU核心是小端(Little-Endian)字节序,而网络数据通常是大端(Big-Endian),MII_RT模块提供了硬件级的字节/半字节交换功能,这能极大减轻PRU软件的处理负担。
接收侧交换 (
RXCFG0/1[RX_BYTE_SWAP]):- 默认(0):
R31[15:8]= 字节1 {半字节3, 半字节2};R31[7:0]= 字节0 {半字节1, 半字节0}。这符合MII接口先传高半字节的常规顺序。 - 使能(1):
R31[15:8]= 字节0 {半字节1, 半字节0};R31[7:0]= 字节1 {半字节3, 半字节2}。这相当于对16位字内的两个字节进行了交换。 - 何时使用:如果你的协议数据是网络字节序(大端),而PRU处理时需要小端序,可以设置此位,让硬件在数据存入R31前就完成交换,PRU读到的就是正确顺序的数据。
- 默认(0):
发送侧交换 (
TXCFG0/1[TX_BYTE_SWAP]):- 类似地,控制着从R30寄存器到TX FIFO时,数据字节和掩码字节的顺序。
- 配置一致性:务必确保发送和接收的交换配置与你的整个数据处理流程匹配。一个常见的策略是,在接收和发送都使能字节交换,这样PRU软件可以完全以本地小端序处理数据,硬件负责与网络大端序的转换。
4.2 前导码处理策略
以太网帧前导码(7字节0x55 + 1字节SFD 0xD5)通常由硬件处理。MII_RT提供了灵活的配置:
RX_CUT_PREAMBLE:决定是否将接收到的前导码传递给RX L1/L2 FIFO。通常应裁剪掉,因为协议处理一般不关心前导码。RX_AUTO_FWD_PRE:决定是否将接收到的前导码自动转发到TX L1 FIFO。在自动转发或某些桥接模式下可能有用。TX_AUTO_PREAMBLE:这是一个非常实用的功能。当使能时,TX接口逻辑会自动生成并附加前导码到TX数据流,在第一次向TX L1 FIFO推送数据时生效。这意味着PRU软件无需在内存中存储或发送前导码字节,简化了帧组装。但要注意:自动生成的前导码会占用TX FIFO的空间。软件必须考虑这一点,避免因FIFO空间计算错误导致溢出。
4.3 PRU与MII端口复用器配置
这是MII_RT模块一个强大的灵活性特性。默认映射是PRU0 -> TX1/RX0, PRU1 -> TX0/RX1。但你可以通过配置寄存器,改变PRU核心与物理MII端口的连接关系。
- 接收复用器:允许为每个PRU核心选择其接收数据来自哪个MII接口(RX_MII0 或 RX_MII1)。例如,你可以将两个MII端口的接收数据都送给PRU0处理,实现单核监控双网口。
- 发送复用器:允许为每个MII发送端口选择数据来源(来自PRU0、PRU1,甚至来自另一个MII接口的接收路径,实现硬件直通)。
- 配置时机警告:手册特别强调,复用器的选择线在一个帧的传输过程中不应改变,否则会导致数据交换错误。因此,配置应在通信初始化阶段完成,或在确保没有活跃帧时进行。
4.4 一个完整的寄存器模式数据收发流程示例
假设我们要配置PRU0,通过MII Port 0接收和发送数据,使用硬件自动前导码,并使能字节交换以简化软件处理。
初始化配置(通常在PRU初始化代码或主机配置中完成):
- 设置
RXCFG0[RX_BYTE_SWAP] = 1,使接收数据在存入R31时转换为小端序。 - 设置
TXCFG0[TX_BYTE_SWAP] = 1,使发送数据从R31取出时转换回网络序。 - 设置
TXCFG0[TX_AUTO_PREAMBLE] = 1,启用发送自动前导码。 - 设置
RX_CUT_PREAMBLE = 1,裁剪接收前导码。 - 配置复用器,将PRU0的RX连接到RX_MII0,TX连接到TX_MII0。
- 清除
TX_AUTO_SEQUENCE,确保使用寄存器模式。
- 设置
接收中断服务例程(或轮询循环)伪代码:
void pru_eth_rx_handler() { // 等待一个完整帧开始(SOF)或数据就绪(WORD_RDY) while (!(R31 & RX_WORD_RDY_MASK)) { // 可加入超时或休眠逻辑 } // 读取帧数据直到结束 do { uint16_t data_word = R31 & 0xFFFF; // 读取数据 // 处理data_word(例如存入缓冲区) process_rx_data(data_word); // 发出POP命令,获取下一个字 R31 = RX_POP16_CMD; // 等待2周期硬件延迟 __delay_cycles(2); // 检查是否帧结束(EOF) if (R31 & RX_EOF_MASK) { // 帧接收完成 finalize_frame_processing(); // 清除EOF状态位 R31 = RX_EOF_CLR_CMD; break; } } while (1); }发送一个数据帧的伪代码:
void pru_send_ethernet_frame(uint16_t *data_buffer, uint32_t data_len_words) { // 可选:复位TX FIFO,确保干净状态 R31 = TX_RESET_CMD; // 循环发送数据负载 for (int i = 0; i < data_len_words; i++) { R30 = data_buffer[i]; // 将数据写入R30 R31 = TX_PUSH16_CMD; // 推送数据到TX FIFO // 注意:这里通常不需要延迟,硬件会处理FIFO写入 } // 发送帧结束命令,硬件会自动添加CRC(如果使能) R31 = TX_EOF_CMD; // 等待发送完成(可通过查询状态或中断) // ... }
5. 高级功能与性能优化实战
在基础的数据通路搭建起来之后,我们需要关注如何让它跑得更稳、更快、更省资源。这部分内容往往在数据手册中一笔带过,但却是实战中的关键。
5.1 TX Mask模式的精妙应用
TX Mask模式是MII_RT的“杀手级”特性,它允许在数据流经硬件时进行实时修改。其核心公式TXDATA = (R30 & MASK) | (RXDATA & ~MASK)意味着:
- MASK位为1的对应位,发送数据来自R30(PRU提供)。
- MASK位为0的对应位,发送数据来自RXDATA(实时接收路径的数据)。
实战场景1:实时帧转发与地址替换假设你需要设计一个简单的2端口交换机,其中一个端口收到的帧,需要将其源MAC地址替换为设备自身的MAC地址后,从另一个端口转发出去。
- 无Mask模式:PRU需要将整个帧从RX FIFO读入内部内存,修改MAC地址字段,再将整个帧写入TX FIFO。这需要大量内存和CPU周期。
- 使用Mask模式:
- 配置为寄存器模式,并使能TX Mask。
- 在帧起始(目标MAC地址字段),将MASK全部设为0,直接转发接收到的目标MAC地址。
- 当到达源MAC地址字段时,PRU将自身的MAC地址写入R30低16位,并将对应位的MASK(R30高16位)设为全1。然后发出
TX_PUSH16命令。这样,硬件会自动用R30中的新地址覆盖掉流经的原始地址。 - 后续的数据字段,再将MASK设为0,恢复直接转发。 这样,PRU只在需要修改的6个字节(源MAC地址)处进行干预,其余字节全部由硬件自动转发,延迟极低,且PRU负载大幅减轻。
实战场景2:协议数据实时插入在工业协议中,常常需要在转发数据包时插入实时状态信息(如循环冗余校验值、设备状态字)。
- 可以预先将状态字准备好放在PRU内存中。
- 当数据流经过需要插入状态字的位置时,PRU将状态字写入R30,并设置对应MASK为全1,执行
TX_PUSH。 - 这种方法实现了“线速”修改,对帧转发延迟的影响最小。
5.2 FIFO深度管理与溢出预防
FIFO溢出是导致数据丢失的最常见原因。虽然MII_RT有硬件流控,但PRU软件必须积极管理。
- TX L1 FIFO (64字节):在使能
TX_AUTO_PREAMBLE后,前导码会占用FIFO空间。一个最大1518字节的以太网帧,需要约1526字节的FIFO空间(含前导码、SFD和FCS),远超64字节。因此,PRU绝不能一次性将整个大帧的数据全部推入FIFO。必须采用“流式”推送:推入一部分数据,等待FIFO有空间(通过查询状态或使用中断),再推入下一部分。或者,确保你的应用帧长较小,不会超过FIFO深度。 - RX L1 FIFO (32字节):如果PRU处理速度跟不上接收速度,RX FIFO会溢出。预防措施包括:
- 优化PRU代码:确保中断服务例程或轮询循环尽可能高效。
- 使用
RX_POP后的延迟:严格遵守2周期延迟规则,避免因过早查询状态导致的忙等待或错误弹出。 - 及时复位:一旦检测到溢出错误(可能通过状态位或帧校验错误发现),应立即使用
RX_RESET命令清空FIFO,恢复到一个已知状态,而不是尝试处理可能已损坏的数据。
5.3 利用RX L2作为高速共享内存
当RXCFG0/1[RX_L2_ENABLE]被禁用时,RX L2的32字节x2 Banks就变成了两块宝贵的共享内存。PRU可以通过XFR(快速寄存器)指令高速访问它们。
- 用途:存储协议解析的临时变量、统计计数器、时间戳、或作为一个小型的邮箱(mailbox)与另一个PRU核心或主机CPU进行低延迟通信。
- 性能优势:访问速度与访问寄存器文件相当,远快于访问外部DDR内存。对于需要频繁存取中间结果的实时算法非常有用。
- 注意:在Scratch Pad模式下,
RX_RESET命令对它无效。需要软件自行管理其内容的初始化和一致性。
5.4 调试与性能统计
PRU_ICSS_PRU_CTRL寄存器组提供了一些有用的调试信息:
- CYCLE寄存器:记录PRU使能后的运行周期数。可以用于粗略的性能分析和任务执行时间测量。
- STALL寄存器:记录PRU因等待指令获取而停滞的周期数。这个值异常高通常意味着指令存储器访问遇到了瓶颈,可能是代码位置不佳或内存冲突,需要优化。
- GPREG0-GPREG31:当PRU被禁用时,主机可以通过这些寄存器直接查看和修改PRU内部通用寄存器的值,是强大的调试工具。
6. 常见问题排查与避坑指南
在实际项目中使用MII_RT,你几乎一定会遇到下面这些问题。这里记录了我踩过的坑和总结的排查思路。
6.1 数据错位或乱码
- 症状:接收或发送的数据字节顺序不对,比如0x1234变成了0x3412。
- 排查:
- 首先检查
RX_BYTE_SWAP和TX_BYTE_SWAP配置。这是最常见的原因。确认你的软件对字节序的期望与硬件配置是否一致。 - 检查MII接口的连线。MII的
TXD[3:0]和RXD[3:0]每位应对应连接,确保没有错位。 - 在TX Mask模式下,检查MASK寄存器的设置是否正确。错误的MASK会导致数据来源混乱。
- 首先检查
6.2 发送帧不完整或CRC错误
- 症状:发送的帧被对端丢弃,Wireshark显示帧校验错误或帧过短。
- 排查:
- 检查
TX_EOF命令:是否在帧数据的最后正确发出了TX_EOF命令?没有它,硬件不知道帧结束,不会添加FCS。 - 检查
TX_AUTO_PREAMBLE:如果使能了自动前导码,PRU软件发送的数据不应包含前导码和SFD。如果包含了,会导致帧结构错误。 - 检查FIFO溢出:是否因为一次性写入数据太多导致TX FIFO溢出?溢出后数据会丢失。确保流式推送并监控FIFO状态(如果硬件提供状态位)。
- 手动CRC计算错误:如果使用PRU软件计算CRC,确保算法正确,并且严格按照
TX_CRC_HIGH-> 等待6周期 ->TX_CRC_LOW的顺序推送。一个常见的错误是等待周期不足。
- 检查
6.3 接收不到数据或数据丢失
- 症状:PRU始终读不到数据,或只能收到部分帧。
- 排查:
- 检查
RX_POP时序:这是头号嫌疑犯。你是否在发出RX_POP16/8命令后,等待了至少2个时钟周期才去读取R31或检查WORD_RDY?如果没有,你读到的就是旧数据或旧状态。在RX_POP后插入几条NOP指令是最简单的验证方法。 - 检查PHY和链路状态:确保物理链路已建立(link up),PHY配置正确。
- 检查复用器配置:PRU核心是否连接到了正确的MII RX端口?
RXCFG0/1中的复用选择寄存器是否正确? - 检查FIFO溢出:使用
RX_RESET复位FIFO,然后重新测试。如果复位后能收到一帧然后又收不到了,很可能是PRU处理太慢导致溢出。优化代码或考虑使用更大的缓冲区(如果支持)或更快的PRU时钟。 - 检查前导码裁剪:如果
RX_CUT_PREAMBLE设置错误,可能导致帧起始定位失败。
- 检查
6.4 自动转发模式不工作
- 症状:设置了
TX_AUTO_SEQUENCE,但数据没有从RX端口转发到TX端口。 - 排查:
- 确保发送和接收的MII端口通过复用器正确连接到了直通路径。自动转发通常需要配置TX复用器选择源为“另一个MII接口的RX”。
- 检查TX和RX的使能位是否都已正确设置。
- 确认在自动转发模式下,你没有同时让PRU去操作TX FIFO(如发送
TX_PUSH命令),这会造成冲突。
6.5 性能瓶颈分析
- 症状:系统无法达到预期的吞吐量或实时性。
- 排查:
- 查看STALL寄存器:如果停滞周期数占总周期数的比例很高,说明指令获取是瓶颈。尝试将最关键的、循环内的代码段移动到PRU的内部RAM(如果可用)或更靠近零等待周期的存储器中。
- 分析PRU汇编代码:使用
clpru编译器的优化选项(如-O2),并检查生成的汇编代码,消除不必要的内存访问和分支。 - 评估数据通路:是否使用了最高效的模式?对于简单的转发,自动转发模式延迟最低。对于需要修改的转发,TX Mask模式比完全软件处理更快。对于复杂协议解析,寄存器模式是唯一选择。
- 双PRU协作:对于高吞吐量应用,可以考虑让一个PRU核心专门处理接收(RX),另一个核心专门处理发送(TX),通过共享内存(如RX L2 Scratch Pad或外部存储器)传递数据,实现流水线处理。
驾驭PRU-ICSS的MII_RT模块,是一个从理解硬件逻辑到精细控制软件时序的完整过程。它要求开发者同时具备硬件思维和软件精度。开始时可能会被各种配置和时序问题困扰,但一旦打通,你将获得一个强大且确定性的实时网络处理引擎,足以应对最严苛的工业通信挑战。记住,多利用示波器或逻辑分析仪观察关键信号(如TX_EN,RXDV, PRU的GPIO调试引脚),结合寄存器的配置值,很多问题都会变得直观。