zynq解决使用LWIP时报错unable to alloc pbuf in recv_handler错误

zynq解决使用LWIP时报错unable to alloc pbuf in recv_handler错误

在使用LWIP时候zynq的SDK范例运行非常正常,但是在此基础上增加别的协议栈时容易发生unable to alloc pbuf in recv_handler错误。

错误原因1

1:这个错误在setup_rx_bds函数中发生,该函数功能是释放DMA的空闲bd块和传递以太网数据包。setup_rx_bds在emacps_recv_handler函数中调用。

void setup_rx_bds(xemacpsif_s *xemacpsif, XEmacPs_BdRing *rxring)
{
    XEmacPs_Bd *rxbd;
    XStatus status;
    struct pbuf *p;
    u32_t freebds;
    u32_t bdindex;
    u32 *temp;
    u32_t index;

    index = get_base_index_rxpbufsstorage (xemacpsif);

    freebds = XEmacPs_BdRingGetFreeCnt (rxring);
    while (freebds > 0) {
        freebds--;
#ifdef ZYNQMP_USE_JUMBO
        p = pbuf_alloc(PBUF_RAW, MAX_FRAME_SIZE_JUMBO, PBUF_POOL);
#else
        p = pbuf_alloc(PBUF_RAW, XEMACPS_MAX_FRAME_SIZE, PBUF_POOL);
#endif
        if (!p) {
#if LINK_STATS
            lwip_stats.link.memerr++;
            lwip_stats.link.drop++;
#endif
            printf("unable to alloc pbuf in recv_handler\r\n");  
            return;
        }
        status = XEmacPs_BdRingAlloc(rxring, 1, &rxbd);
        if (status != XST_SUCCESS) {
            LWIP_DEBUGF(NETIF_DEBUG, ("setup_rx_bds: Error allocating RxBD\r\n"));
            pbuf_free(p);
            return;
        }
        status = XEmacPs_BdRingToHw(rxring, 1, rxbd);
        if (status != XST_SUCCESS) {
            LWIP_DEBUGF(NETIF_DEBUG, ("Error committing RxBD to hardware: "));
            if (status == XST_DMA_SG_LIST_ERROR) {
                LWIP_DEBUGF(NETIF_DEBUG, ("XST_DMA_SG_LIST_ERROR: this function was called out of sequence with XEmacPs_BdRingAlloc()\r\n"));
            }
            else {
                LWIP_DEBUGF(NETIF_DEBUG, ("set of BDs was rejected because the first BD did not have its start-of-packet bit set, or the last BD did not have its end-of-packet bit set, or any one of the BD set has 0 as length value\r\n"));
            }

            pbuf_free(p);
            XEmacPs_BdRingUnAlloc(rxring, 1, rxbd);
            return;
        }
#ifdef ZYNQMP_USE_JUMBO
        if (xemacpsif->emacps.Config.IsCacheCoherent == 0) {
            Xil_DCacheInvalidateRange((UINTPTR)p->payload, (UINTPTR)MAX_FRAME_SIZE_JUMBO);
        }
#else
        if (xemacpsif->emacps.Config.IsCacheCoherent == 0) {
            Xil_DCacheInvalidateRange((UINTPTR)p->payload, (UINTPTR)XEMACPS_MAX_FRAME_SIZE);
        }
#endif
        bdindex = XEMACPS_BD_TO_INDEX(rxring, rxbd);
        temp = (u32 *)rxbd;
        if (bdindex == (XLWIP_CONFIG_N_RX_DESC - 1)) {
            *temp = 0x00000002;
        } else {
            *temp = 0;
        }
        temp++;
        *temp = 0;
        dsb();

        XEmacPs_BdSetAddressRx(rxbd, (UINTPTR)p->payload);
        rx_pbufs_storage[index + bdindex] = (UINTPTR)p;
    }
}

错误发生在 printf(“unable to alloc pbuf in recv_handler\r\n”); 这个地方,是申请pbuf失败。

网上最多查到的办法是增大pbuf部分的内存,例如 MEM_SIZE, MEMP_NUM_PBUF, PBUF_POOL_SIZE.这些参数。但是使用LWIP范例时候同样的用法为什么不会报错呢?我们再分析一下进入emacps_recv_handler函数分析一下。

错误原因2

2:emacps_recv_handler函数是DMA函数的回调函数,以太网DMA收到数据包时候将会调用该函数。

void emacps_recv_handler(void *arg)
{
    struct pbuf *p;
    XEmacPs_Bd *rxbdset, *curbdptr;
    struct xemac_s *xemac;
    xemacpsif_s *xemacpsif;
    XEmacPs_BdRing *rxring;
    volatile s32_t bd_processed;
    s32_t rx_bytes, k;
    u32_t bdindex;
    u32_t regval;
    u32_t index;
    u32_t gigeversion;

    xemac = (struct xemac_s *)(arg);
    xemacpsif = (xemacpsif_s *)(xemac->state);
    rxring = &XEmacPs_GetRxRing(&xemacpsif->emacps);

#ifdef OS_IS_FREERTOS
    xInsideISR++;
#endif

    gigeversion = ((Xil_In32(xemacpsif->emacps.Config.BaseAddress + 0xFC)) >> 16) & 0xFFF;
    index = get_base_index_rxpbufsstorage (xemacpsif);
    /*
     * If Reception done interrupt is asserted, call RX call back function
     * to handle the processed BDs and then raise the according flag.
     */
    regval = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET);
    XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET, regval);
    if (gigeversion <= 2) {
            resetrx_on_no_rxdata(xemacpsif);
    }
//
    while(1) {

        bd_processed = XEmacPs_BdRingFromHwRx(rxring, XLWIP_CONFIG_N_RX_DESC, &rxbdset);
        if (bd_processed <= 0) {
            break;
        }

        for (k = 0, curbdptr=rxbdset; k < bd_processed; k++) {

            bdindex = XEMACPS_BD_TO_INDEX(rxring, curbdptr);
            p = (struct pbuf *)rx_pbufs_storage[index + bdindex];

            /*
             * Adjust the buffer size to the actual number of bytes received.
             */
#ifdef ZYNQMP_USE_JUMBO
            rx_bytes = XEmacPs_GetRxFrameSize(&xemacpsif->emacps, curbdptr);
#else
            rx_bytes = XEmacPs_BdGetLength(curbdptr);
#endif
            pbuf_realloc(p, rx_bytes);

            /* store it in the receive queue,
             * where it'll be processed by a different handler
             */
            if (pq_enqueue(xemacpsif->recv_q, (void*)p) < 0) {
#if LINK_STATS
                lwip_stats.link.memerr++;
                lwip_stats.link.drop++;
#endif
                pbuf_free(p);
            }
            curbdptr = XEmacPs_BdRingNext( rxring, curbdptr);
        }
        /* free up the BD's */
        XEmacPs_BdRingFree(rxring, bd_processed, rxbdset);
        setup_rx_bds(xemacpsif, rxring);  //函数在这里调用
#if !NO_SYS
        sys_sem_signal(&xemac->sem_rx_data_available);
#endif
    }

#ifdef OS_IS_FREERTOS
    xInsideISR--;
#endif
    return;
}

我们看到调用setup_rx_bds(xemacpsif, rxring); 的时候是在循环中使用。这样会多次调用他导致重复的释放db块,然后引起FreeCnt计数错误,发生错误后每次都增加FreeCnt的计数器。

解决办法

所以我们把setup_rx_bds(xemacpsif, rxring); 放到while循环外,避免多次调用导致可能的错误计数发生。

    while(1) {

        bd_processed = XEmacPs_BdRingFromHwRx(rxring, XLWIP_CONFIG_N_RX_DESC, &rxbdset);
        if (bd_processed <= 0) {
            break;
        }

        for (k = 0, curbdptr=rxbdset; k < bd_processed; k++) {

            bdindex = XEMACPS_BD_TO_INDEX(rxring, curbdptr);
            p = (struct pbuf *)rx_pbufs_storage[index + bdindex];

            /*
             * Adjust the buffer size to the actual number of bytes received.
             */
#ifdef ZYNQMP_USE_JUMBO
            rx_bytes = XEmacPs_GetRxFrameSize(&xemacpsif->emacps, curbdptr);
#else
            rx_bytes = XEmacPs_BdGetLength(curbdptr);
#endif
            pbuf_realloc(p, rx_bytes);

            /* store it in the receive queue,
             * where it'll be processed by a different handler
             */
            if (pq_enqueue(xemacpsif->recv_q, (void*)p) < 0) {
#if LINK_STATS
                lwip_stats.link.memerr++;
                lwip_stats.link.drop++;
#endif
                pbuf_free(p);
            }
            curbdptr = XEmacPs_BdRingNext( rxring, curbdptr);
        }
        /* free up the BD's */
        XEmacPs_BdRingFree(rxring, bd_processed, rxbdset);
//        setup_rx_bds(xemacpsif, rxring);  //函数在这里调用
#if !NO_SYS
        sys_sem_signal(&xemac->sem_rx_data_available);
#endif
    }
        setup_rx_bds(xemacpsif, rxring);  //放到这里
#ifdef OS_IS_FREERTOS
    xInsideISR--;
#endif
    return;
}

总结

所以解决他的两种方法:

1:参考Xlinx社区方法增大内存但是我试了试好像不管用 lwIP netif: ‘unable to alloc pbuf in recv_handler’ message

2:就是我们在循环外调用setup_rx_bds的方法

————————————————

版权声明:本文为CSDN博主「qq_21748131」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_21748131/article/details/123038734

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于Xilinx Zynq SoC的LWIP UDP通信配置步骤: 1. 创建一个新的Zynq SoC设计并添加lwip库。 2. 在lwipopts.h文件中,将以下选项设置为1来启用UDP协议栈: #define LWIP_UDP 1 3. 在lwip下的apps文件夹中,创建一个新的文件夹udp_echo,并在其中添加udp_echo.c和udp_echo.h文件。 4. 在udp_echo.c文件中,添加以下代码来创建UDP服务器: #include "lwip/udp.h" #define UDP_SERVER_PORT 7 static struct udp_pcb *udp_server = NULL; void udp_server_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { // TODO: process received data // send response udp_sendto(pcb, p, addr, port); // free the pbuf pbuf_free(p); } void udp_server_init(void) { // create a new UDP PCB structure udp_server = udp_new(); // bind the UDP PCB to the specified port number udp_bind(udp_server, IP_ADDR_ANY, UDP_SERVER_PORT); // set the callback function for UDP server udp_recv(udp_server, udp_server_callback, NULL); } 5. 在main函数中,调用udp_server_init()函数来启动UDP服务器: int main() { // initialize the UDP server udp_server_init(); // TODO: other initialization while(1) { // TODO: main loop } return 0; } 6. 在另一个设备上,使用UDP客户端向Zynq SoC发送数据。以下是示例代码: #include "lwip/udp.h" #define UDP_SERVER_IP "192.168.1.10" #define UDP_SERVER_PORT 7 void udp_client_send(char *data, int len) { struct udp_pcb *pcb; ip_addr_t dest_ip; // convert server IP address from string to binary format ipaddr_aton(UDP_SERVER_IP, &dest_ip); // create a new UDP PCB structure pcb = udp_new(); // send the data to the server udp_sendto(pcb, pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM), &dest_ip, UDP_SERVER_PORT); // free the UDP PCB and pbuf udp_remove(pcb); } 7. 在UDP服务器的回调函数中,可以使用p->payload指针来访问接收到的数据。以下是示例代码: void udp_server_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { // print received data xil_printf("Received data: %s\r\n", (char*)p->payload); // send response udp_sendto(pcb, p, addr, port); // free the pbuf pbuf_free(p); } 注意:在使用UDP通信,需要确保两个设备在同一网络中,并且服务器的IP地址和端口号需要在客户端代码中正确设置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值