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:这个错误在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: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的方法

以下是基于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地址和端口号需要在客户端代码中正确设置。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值