cancel
Showing results for 
Search instead for 
Did you mean: 

Ethernet close connection and restart MCU

Pilous Droip
Senior

Hi,

I have STM32F767 - Nucleo kit. I implement FreeRTOS, lwIP. And I create this connect.

struct tcp_pcb *server_pcb;
 
struct server_struct
{
	uint8_t state; 			/* current connection state */
	uint8_t retries;
	struct tcp_pcb *pcb; 	/* pointer on the current tcp_pcb */
	struct pbuf *p; 		/* pointer on the received/to be transmitted pbuf */
};
 
struct server_struct *ss;
 
enum server_states
{
	ES_NONE = 0,
	ES_ACCEPTED,
	ES_RECEIVED,
	ES_CLOSING
};
 
static void tcp_server_send(struct tcp_pcb *tpcb, struct server_struct *es)
{
	struct pbuf *ptr;
	err_t wr_err = ERR_OK;
	while ((wr_err == ERR_OK) && (es->p != NULL) && (es->p->len <= tcp_sndbuf(tpcb)))
	{
		ptr = es->p;
		wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);
		if (wr_err == ERR_OK)
		{
			uint16_t plen;
			uint8_t freed;
			plen = ptr->len;
			es->p = ptr->next;
			if(es->p != NULL)
			{
				pbuf_ref(es->p);
			}
			do
			{
				freed = pbuf_free(ptr);
			}
			while(freed == 0);
			tcp_recved(tpcb, plen);
		}
		else if(wr_err == ERR_MEM)
		{
			es->p = ptr;
		}
		else
		{
			//other problem
		}
	}
}
 
static void tcp_server_connection_close(struct tcp_pcb *tpcb, struct server_struct *es)
{
	// remove all callbacks
	tcp_arg(tpcb, NULL);
	tcp_sent(tpcb, NULL);
	tcp_recv(tpcb, NULL);
	tcp_err(tpcb, NULL);
	tcp_poll(tpcb, NULL, 1);
	if (es != NULL)
	{
		mem_free(es);
	}
	tcp_close(tpcb);
}
 
static void tcp_server_error(void *arg, err_t err)
{
	//-TODO-
}
 
static err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb)
{
	err_t ret_err;
	struct server_struct *es;
	es = (struct server_struct *)arg;
 
	if (es != NULL)
	{
		if (es->p != NULL)
		{
		}
		else
		{
			if(es->state == ES_CLOSING)
		    {
				tcp_server_connection_close(tpcb, es);
		    }
		}
		ret_err = ERR_OK;
	}
	else
	{
		tcp_abort(tpcb);
		ret_err = ERR_ABRT;
	}
	return ret_err;
}
 
static err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len)
{
	struct server_struct *es;
	LWIP_UNUSED_ARG(len);
	es = (struct server_struct *)arg;
	es->retries = 0;
	if(es->p != NULL)
	{
		tcp_server_send(tpcb, es);
	}
	else
	{
		if(es->state == ES_CLOSING)
		{
			tcp_server_connection_close(tpcb, es);
		}
	}
	return ERR_OK;
}
 
static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
	char str1[100];
	struct server_struct *es;
	err_t ret_err;
	LWIP_ASSERT("arg != NULL",arg != NULL);
	es = (struct server_struct *)arg;
 
	if (p == NULL)
	{
		es->state = ES_CLOSING;
		if(es->p == NULL)
		{
			tcp_recved(tpcb, p->tot_len);
		}
		else
		{
			//acknowledge received packet
			tcp_sent(tpcb, tcp_server_sent);
			//send remaining data
			tcp_server_send(tpcb, es);
		}
		ret_err = ERR_OK;
	}
	else if(err != ERR_OK)
	{
		if (p != NULL)
		{
			es->p = NULL;
			pbuf_free(p);
		}
		ret_err = err;
	}
	else if(es->state == ES_ACCEPTED)
	{
		tcp_recved(tpcb, p->tot_len);
		strncpy(str1,p->payload,p->len);
		str1[p->len] = '\0';
/* -- Send data to Serial PORT */
		PrintSerial(str1);
		ret_err = ERR_OK;
	}
	else if (es->state == ES_RECEIVED)
	{
		if(es->p == NULL)
		{
			ret_err = ERR_OK;
		}
		else
		{
			struct pbuf *ptr;
			ptr = es->p;
			pbuf_chain(ptr,p);
		}
		ret_err = ERR_OK;
	}
	else if(es->state == ES_CLOSING)
	{
		tcp_recved(tpcb, p->tot_len);
		es->p = NULL;
		pbuf_free(p);
		ret_err = ERR_OK;
	}
	else
	{
		tcp_recved(tpcb, p->tot_len);
		es->p = NULL;
		pbuf_free(p);
		ret_err = ERR_OK;
	}
	return ret_err;
}
 
static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
	err_t ret_err;
	struct server_struct *es;
	LWIP_UNUSED_ARG(arg);
	LWIP_UNUSED_ARG(err);
	tcp_setprio(newpcb, TCP_PRIO_MIN);
	es = (struct server_struct *)mem_malloc(sizeof(struct server_struct));
	ss = (struct server_struct *)mem_malloc(sizeof(struct server_struct));
	if (es != NULL)
	{
		es->state = ES_ACCEPTED;
		es->pcb = newpcb;
	    ss->pcb = newpcb;
	    es->retries = 0;
	    es->p = NULL;
	    tcp_arg(newpcb, es);
	    tcp_recv(newpcb, tcp_server_recv);
	    tcp_err(newpcb, tcp_server_error);
	    tcp_sent(newpcb, tcp_server_sent);
	    tcp_poll(newpcb, tcp_server_poll, 1);
	    ret_err = ERR_OK;
	}
	else
	{
		tcp_server_connection_close(newpcb, es);
		ret_err = ERR_MEM;
	}
	return ret_err;
}
 
 
void Hello(void *pvParameters)
{
	ip_addr_t ipaddr;
	IP4_ADDR(&ipaddr,  192, 168,   11, 208);
 
	server_pcb = tcp_new();
	if (server_pcb != NULL)
	{
		err_t err;
		err = tcp_bind(server_pcb, IP_ADDR_ANY, 502);
		if (err == ERR_OK)
		{
			server_pcb = tcp_listen(server_pcb);
			tcp_accept(server_pcb, tcp_server_accept);
		}
		else
		{
			memp_free(MEMP_TCP_PCB, server_pcb);
		}
	}
 
 	while(1)
 	{
 		vTaskDelay(100);
 	}
}
 

In main function I create a task:

xTaskCreate( Hello, "Hello", 4096, ( void * ) NULL, 2, NULL );

And program works (when I send anything over TCP i see it in serial output). Problem is when I try disconnect. MCU is restart.

Any idea, what is wrong?

I always get this massage:

HardFault Exception 

Here is output:

  • >>>> HardFault Exception <<<<

  • Fault registers:
  • SCB->HFSR = 0x40000000
  •  DEBUGEVT = 0
  •  FORCED  = 1
  •  VECTTBL  = 0
  • SCB->CFSR = 0x00010000
  •  IACCVIOL = 0
  •  DACCVIOL = 0
  •  MUNSTKERR = 0
  •  MSTKERR  = 0
  •  MLSPERR  = 0
  •  MMARVALID = 0
  •  IBUSERR  = 0
  •  PRECISE E = 0
  •  IMPRECISE = 0
  •  UNSTKERR = 0
  •  STKERR  = 0
  •  LSPERR  = 0
  •  BFARVALID = 0
  •  UNDEFINSTR= 1
  •  INVSTATE = 0
  •  INVPC   = 0
  •  NOCP   = 0
  •  UNALIGNED = 0
  •  DIVBYZERO = 0
  • SCB->DFSR = 0x00000000
  • SCB->MMFAR = 0x00000000
  • SCB->BFAR = 0x00000000
  • SCB->AFSR = 0x00000000
  • SCB->ABFSR = 0x00000000

  • Exception stack frame:
  • R0  0x20045c98
  • R1  0x20068d8c
  • R2  0x00000003
  • R3  0x00009a90
  • R12  0x20068a54
  • LR  0x08007e87
  • PC  0x0800fd5c
  • xPSR 0x61000000

  •  >>> forcing SYSTEM RESET
10 REPLIES 10
AvaTar
Lead

Tried to decode the SCB register contents ?

Here is a document for reference: www.keil.com/appnotes/files/apnt209.pdf

Pilous Droip
Senior

I think this is the answer to your question?

  •  UNDEFINSTR= 1
  •  FORCED  = 1

Or what specifically should I look for?

Pilous Droip
Senior

I looked into disassembly. But I don't see a problem.

xTaskCreate( BlinkLED, "BlinkLED", 4096, ( void * ) NULL, 2, NULL );
0800f8ce:   mov     r3, r4
0800f8d0:   mov.w   r2, #4096       ; 0x1000
0800f8d4:   ldr     r1, [pc, #20]   ; (0x800f8ec <NUCLEO_CB_SetupMyTasks+56>)
0800f8d6:   str     r4, [sp, #4]
0800f8d8:   str     r5, [sp, #0]
0800f8da:   ldr     r0, [pc, #20]   ; (0x800f8f0 <NUCLEO_CB_SetupMyTasks+60>)
0800f8dc:   bl      0x800236c <xTaskCreate>
146       }
0800f8e0:   add     sp, #12
0800f8e2:   pop     {r4, r5, pc}
0800f8e4:   strb    r4, [r5, r4]
0800f8e6:   lsrs    r1, r0, #32
0800f8e8:   ldrb.w  r0, [r5, <undefined>]
0800f8ec:   strb    r4, [r6, r4]
0800f8ee:   lsrs    r1, r0, #32
0800f8f0:           ; <UNDEFINED> instruction: 0xf7990800
          NMI_Handler:
0800f8f4:   bkpt    0x0000
0800f8f6:   b.n     0x800f8f6 <NMI_Handler+2>
 88       {
          HardFault_InfoDump:
0800f8f8:   stmdb   sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}
0800f8fc:   mov     r1, r0
102       	uint32_t hfsr = SCB->HFSR;
0800f8fe:   ldr     r5, [pc, #452]  ; (0x800fac4 <HardFault_InfoDump+460>)
0800f900:   sub     sp, #36 ; 0x24
110       	hfprintf("\n\n\n>>>> HardFault Exception <<<<\n");

AvaTar
Lead

Did you check the call stack in the toolchain's debugger ?

Or could you identify the C/Assembler intruction causing the fault ?

Reproduction should be easy if I understood correctly.

Perhaps an insufficient stack (task stack) size, or the mem_free() gets haywire ?

Piranha
Chief II

Maybe this is not the main problem, but You have made probably the most popular lwIP+RTOS related mistake in the world - "raw" APIs are not thread-safe!

https://www.nongnu.org/lwip/2_1_x/multithreading.html

http://lwip.100.n7.nabble.com/lwip-2-0-2-freertos-ARM7-td29599.html

From the second link: "If you use threads, you may want to use netconn or socket API if that's easier for you. You still can use the callback API, but you need to use tcpip_callback() or LOCK_TCPIP_CORE (if you configured lwIP to use core locking) to sync with lwIP thread."

This ir true also for netif_***(), dhcp_***() and other callback style APIs. The only exceptions are pbuf and memory alloc/free functions when SYS_LIGHTWEIGHT_PROT is enabled (enabled by default) - these are thread-safe. Also NETIF, DHCP and AUTOIP functions have thread-safe netifapi_***() alternatives.

Connection callbacks by default are called from lwIP core "tcpip_thread" and, if You enable LWIP_TCPIP_CORE_LOCKING_INPUT (disabled by default), then from the thread in which You call netif->input(). In both cases You must not use tcpip_callback() or LOCK_TCPIP_CORE() in callbacks.

If You don't do this right, then the firmware will always seem to work but fail occasionally. My suggested solution is to put LOCK_TCPIP_CORE() in line 234 and UNLOCK_TCPIP_CORE() in line 250. Test it and report if anything changes.

Ok, and what is the reported hard fault status related to this code?

Can't relate the prior reported PC to anything here.

Need to show the 5 instructions either side of the faulting instruction to get some minimal context.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

I enable core locking input in lwipopts.h

#define LWIP_TCPIP_CORE_LOCKING_INPUT (1)

And modificate my hello function:

void Hello(void *pvParameters)
{
	ip_addr_t ipaddr;
	IP4_ADDR(&ipaddr,  192, 168,   11, 208);
	LOCK_TCPIP_CORE();
	server_pcb = tcp_new();
	if (server_pcb != NULL)
	{
		err_t err;
		err = tcp_bind(server_pcb, IP_ADDR_ANY, 502);
		if (err == ERR_OK)
		{
			server_pcb = tcp_listen(server_pcb);
			tcp_accept(server_pcb, tcp_server_accept);
		}
		else
		{
			memp_free(MEMP_TCP_PCB, server_pcb);
		}
	}
	UNLOCK_TCPIP_CORE();
	while(1)
	{
  		vTaskDelay(100);
	}
}

And problem is the same.

>>>> HardFault Exception <<<<
 
Fault registers:
SCB->HFSR  = 0x40000000
 DEBUGEVT  = 0
 FORCED    = 1
 VECTTBL   = 0
SCB->CFSR  = 0x00010000
 IACCVIOL  = 0
 DACCVIOL  = 0
 MUNSTKERR = 0
 MSTKERR   = 0
 MLSPERR   = 0
 MMARVALID = 0
 IBUSERR   = 0
 PRECISE E = 0
 IMPRECISE = 0
 UNSTKERR  = 0
 STKERR    = 0
 LSPERR    = 0
 BFARVALID = 0
 UNDEFINSTR= 1
 INVSTATE  = 0
 INVPC     = 0
 NOCP      = 0
 UNALIGNED = 0
 DIVBYZERO = 0
SCB->DFSR  = 0x00000000
SCB->MMFAR = 0x00000000
SCB->BFAR  = 0x00000000
SCB->AFSR  = 0x00000000
SCB->ABFSR = 0x00000000
 
Exception stack frame:
R0    0x20045c7c
R1    0x2006896c
R2    0x00000003
R3    0x00009a90
R12   0x20068634
LR    0x08007e87
PC    0x0800fd48
xPSR  0x61000000
 
 
 >>> forcing SYSTEM RESET

I try debug.

When I send close connection, I go here:

if (p == NULL)
	{
		es->state = ES_CLOSING;
		if(es->p == NULL)
		{
			tcp_recved(tpcb, p->tot_len);
		}
		else
		{
			//acknowledge received packet
			tcp_sent(tpcb, tcp_server_sent);
			//send remaining data
			tcp_server_send(tpcb, es);
		}
		ret_err = ERR_OK;
	}

And when I get here (line 6):

tcp_recved(tpcb, p->tot_len);

Program crash

__attribute((naked))
void HardFault_Handler(void)
{
	/* Determine MSP/PSP usage and pass the value as first argument
	 * to get the stack frame dump */
 
	__asm volatile (
		"tst lr, #4 \n"
		"ite eq \n"
		"mrseq r0, msp \n"
		"mrsne r0, psp \n"
		"bl HardFault_InfoDump");
}

And program is restart.

0690X000006CsADQA0.pngSo I modificate my receive funciton, and now I close connection and program don't crash.

if (p == NULL)
	{
		LOCK_TCPIP_CORE();
		es->state = ES_CLOSING;
		if(es->p == NULL)
		{
			tcp_recved(tpcb, p->tot_len);
		}
		else
		{
			//acknowledge received packet
			tcp_sent(tpcb, tcp_server_sent);
			//send remaining data
			tcp_server_send(tpcb, es);
		}
		UNLOCK_TCPIP_CORE();
		ret_err = ERR_OK;
	}

But connection is bad closed.