主题中讨论的其他部件:HALCOGEN、 AM3505
您好!
我们发现 EMAC 模块中有新的(勘误表中没有记录)器件错误。
问题是 EMAC 控制器读取 NULL 指针终端数据包碎片链。 正确,此 NULL 是终结器。 但是、当我们需要在该链中添加新数据时、TRM 中存在官方建议、即不允许存在潜在的竞争条件。 下面是第32.2.6.2章发送和接收描述符队列的引用
在可能存在竞争条件的情况下,EMAC 可能会在中将描述符的“next”指针读取为 NULL
应用程序之前的瞬间通过修补指针将附加描述符附加到列表中。 这种情况
案例由软件应用程序处理、始终检查所有 EOP 的缓冲区描述符标志
数据包、查找称为队列结束(EOQ)的特殊标志。 EOQ 标志由上的 EMAC 设置
当描述符的“下一个”指针为 NULL 时,数据包的最后一个描述符。 这就是 EMAC 的方式
向软件应用程序表明它认为它已到达列表的末尾。 软件时
应用程序会看到 EOQ 标志集、此时应用程序可能会提交新列表或部分
通过将新列表指针写入启动的相同 HDP 而错过的附加列表
过程。
将数据包添加到发送列表、将空缓冲区添加到接收列表时、此过程适用。
这在 C 语言中是等效的
{ //将 BD 连接起来。 volatile struct emacTxBuffDesc *tail = txch->active_tail; tail->next =(volatile struct emacTxBuffDesc *) cppioOrder ((U32)(active_head)); if (((U32) 0!=(cppiOrder (tail->flags_pktlen)& EMAC_BUF_DESC_EOQ))) { /* *如果 DMA 引擎已经到达链的末尾、 *将设置 EOQ。 在这种情况下、应再次写入 HDP。 * /*写入标头描述符指针并启动 DMA */ EMACTxHdrDescPtrWrite (hdkif->EMAC_BASE、(unsigned int)(active_head)、0 /*通道*/); }
很明显。 此代码写入新指针以替换结束 在检查后、它不会太晚。 如果太晚、请重新启动传输器以进行新的 BEGIN。它完全符合 TRM 的建议。 但有时会失败。
看起来 EMAC 读取了终止 NULL、但没有同时设置 EOQ。 这种实施会有一些延迟。
来自 HalCoGen 的原始代码在源代码中具有此"权变措施":
while (EMAC_BUF_DESC_EOQ!=(EMACSwizleData (CURR_BD->FLAGS_pktlen)& EMAC_BUF_DESC_EOQ)) { } while ((((uint32) 0U!=*((uint32 *) 0xFCF78600U))) { }
此工作区有许多问题:
- 没有文档。 源代码中没有任何内容。 勘误表中无任何内容
- 此函数仅将一个数据包添加到 TX 链中、不允许将另一个数据包添加到队列中。 如果队列中只有一个数据包、则无法将其命名为队列。
- 此代码在前一个数据包未超时之前处于活动轮询状态。 这种乐观的情况是 MCU 处理器时间的延长。 在悲观的情况 下、前一个数据包可能会有延迟、以太网代码可能会在该轮询环路上等待很长的时间。
因此、我尝试设计另一种(更好)权变措施。 它有两个部分。 第一部分是将数据包添加到队列中:
if (txch->next _BD_TO_Process = NULL) { /*首次用填充的 BD 写入 HDP */ EMACTxHdrDescPtrWrite (hdkif->EMAC_BASE、(unsigned int)(active_head)、0); txch->next _BD_TO_Process = ACTIVE_HEAD; } 否则 { //将 BD 连接起来。 volatile struct emacTxBuffDesc *tail = txch->active_tail; tail->next =(volatile struct emacTxBuffDesc *) cppioOrder ((U32)(active_head)); }
第二部分在确认后调用以进行清理。
CURR_BD = txch->NEW_BD_TO_Process; IF (NULL!= CURR_BD) { /*检查数据包的正确起始位置*/ 而//内部中断 (cppioOrder (EMAC_BUF_DESC_SOP)=(CURR_BD->FLAGS_pktlen 和 cppioOrder (EMAC_BUF_DESC_ONER | EMAC_BUF_DESC_SOP))))) { hdkif->free_tail->next =(volatile struct emacTxBuffDesc *) cppioOrder ((U32) CURR_BD); /*到达数据包末尾之前的横移*/ while ((cppiOrder (CURR_BD->flags_pktlen)和 EMAC_BUF_DESC_EOP)!= EMAC_BUF_DESC_EOP) { CURR_BD->FLAGS_pktlen = 0; CURR_BD =(volatile struct emacTxBuffDesc *) cppiOrder ((U32)(CURR_BD->NEXT)); } /*确认 EMAC */ EMACTxCPWrite (hdkif->EMAC_BASE、0、(U32) CURR_BD); CURR_BD->FLAGS_pktlen &=~cppierder (EMAC_BUF_DESC_EOP | EMAC_BUF_DESC_SOP); /*释放相应的 pbuf */ pbuf_free ((struct pbuf *) CURR_BD->pbuf); volatile struct emacTxBuffDesc * next =(volatile struct emacTxBuffDesc*) cppioOrder ((U32)(CURR_BD->NEXT)); CURR_BD->NEXT =空; hdkif->free_tail = CURR_BD; CURR_BD =下一步; LINK_STATS_INC (link.xmit); 如果(NULL =CURR_BD) { txch->active_tail =空; break;// while loop } } if ((CURR_BD!=空) &&(0 =EMACTxHdrDescPtrRead (hdkif->EMAC_BASE、0 /*通道*/)) &&(0!=(CURR_BD->FLAGS_pktlen & cppierder (EMAC_BUF_DESC_ONER))) { /* *如果 DMA 引擎已经到达链的末尾。 *在这种情况下、应再次写入 HDP。 * *这是 EMAC 占用并使用 NULL 指针时的问题解决方法、 *但在返回0后读取 EOQ。 它看起来像是未经确认的器件问题 * EMACTxHdrDescPtrWrite (hdkif->EMAC_BASE、(unsigned int)(CURR_BD)、0 /*通道*/); } txch->next _BD_TO_Process = CURR_BD; }
这是完美的。 但我想确认这种变通办法是正确的。 主要条件:
if ((CURR_BD!=空) &&(0 =EMACTxHdrDescPtrRead (hdkif->EMAC_BASE、0 /*通道*/)) &&(0!=(CURR_BD->FLAGS_pktlen & cppierder (EMAC_BUF_DESC_ONER)))
是否可以确认它是正确的解决方法? 您能否发布此问题勘误表的更新(包括建议)?
问题是、许多 MCU 中包含的问题看起来像是问题。
我可以为 TMS570LC4357进行确认。 我在另一个硅上发现了同样的问题。 例如:
RM57请见 e2e.ti.com/.../526697
AM3505请参阅 https://e2e.ti.com/support/arm/sitara_arm/f/791/t/543686#pi316653
已用 MCU 为 TMS570LC4357BZWTQQ1修订版 B