Plus-TCP on SAMv71 hard fault debugging

jtbr wrote on Thursday, March 16, 2017:

Hello,

I’m trying to get FreeRTOS-Plus-TCP working on an Atmel SAM V71 (which has a cortex M7 and a KSZ8061RNBVA ethernet PHY). I started with a working FreeRTOS 8.2.3 using heap4, added Plus-TCP (r160919) using BufferAllocation2, and based my NetworkInterface.c upon the included Cortex M3/M4 example, while using the latest Atmel ASF drivers for the GMAC and ethernet_phy for my device.

This is producing a hard fault some time after the TCP tasks begin; it occurs whether or not there is a cable connected to the ethernet port. In either case, the vApplicationIPNetworkEventHook is correctly called with the eNetworkUp/Down event. And the GMAC_Handler interrupt handler is called at least once without apparent issues, when the cable is connected.

For debugging purposes, I have disabled all other tasks, and disabled the -Plus-Trace library and printfs via the serial UART. configASSERT, configCHECK_FOR_STACK_OVERFLOW (2), and configUSE_MALLOC_FAILED_HOOK are defined, but do not occur. configMAC_INTERRUPT_PRIORITY is configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY, which is 4. configLIBRARY_LOWEST_INTERRUPT_PRIORITY is 7 (configPRIO_BITS is 3). configUSE_PREEMPTION is 1, and configIDLE_SHOULD_YIELD is currently 0, as is configUSE_TICKLESS_IDLE So far as I know, the only interrupt registered is for the network interface (interrupt number 39 as defined in the GMAC driver).

I have seen the hard fault debugging advice here, but am having trouble making use of it. I have disabled ICache and DCache, TCM has never been enabled, and I set the DISDEFWBUF bit (SCnSCB->ACTLR |= 2;) to stop write buffering and prevent imprecise faults. I have confirmed that these are direct Hard faults, not MemManage, BusFault or UsageFaults, and are not imprecise (0xE000ED29 & 0x02 == 0).

However when I use the code on the linked page, I don’t seem to be getting useful information and am at a bit of a loss for how to debug this. When I first arrive in the HardFault_Handler, (0xe000ed04) & 0xFF , which should contain the interrupt number, is 3 (like I mentioned I thought there was only one interrupt, number 39). When I use the provided code to restore the registers, I get: r0=0, r1=0x20404e1c, r2=0x10000000, r3=0xe000ed04, r12=0x20404de4, lr=0x0040a41d, pc=0x20400004, psr=0x00000200. pc points to the DTCM section of memory rather than flash (which starts at 0x00400000), and is apparently not executable code; certainly I’m not able to put a breakpoint there. So I can’t determine the source of the hard faults.

I’m happy to share my current NetworkInterface.c code if it might help.

Many thanks for any help!

heinbali01 wrote on Thursday, March 16, 2017:

Hi Justin, one of the most probably causes of a hard-fault in a +TCP driver is writing outside malloc’d area. Or writing to space that has been free’d already. Therefor it is very important to check who is the owner of all network buffers at any moment in time.
configCHECK_FOR_STACK_OVERFLOW = 2 does give some protection, but while developing, just make sure that there is more than enough stack space.
If you want attach your NetworkInterface.c along with your FreeRTOSIPConfig.h, and I’ll have a look at them

jtbr wrote on Thursday, March 16, 2017:

Hein, I’m attaching NetworkInterface.c and including FreeRTOSIPConfig.h below. I tried increasing the configMINIMAL_STACK_SIZE from 130 to 1300, but I still see the same hard fault. I’ll look through the code for reads and writes going wrong.

Much appreciated.

#ifndef FREERTOS_IP_CONFIG_H
#define FREERTOS_IP_CONFIG_H

#define ipconfigHAS_DEBUG_PRINTF	0
#if( ipconfigHAS_DEBUG_PRINTF == 1 )
	#define FreeRTOS_debug_printf(X)	printf X
#endif

#define ipconfigHAS_PRINTF			0
#if( ipconfigHAS_PRINTF == 1 )
	#define FreeRTOS_printf(X)	extern SemaphoreHandle_t printMutex; xSemaphoreTake(printMutex, 0); portDISABLE_INTERRUPTS();  printf X ;  portENABLE_INTERRUPTS(); xSemaphoreGive(printMutex);
#endif

#define ipconfigBYTE_ORDER pdFREERTOS_LITTLE_ENDIAN

#define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM   1

#define ipconfigSOCK_DEFAULT_RECEIVE_BLOCK_TIME	( 5000 )
#define	ipconfigSOCK_DEFAULT_SEND_BLOCK_TIME	( 5000 )

#define ipconfigIP_TASK_PRIORITY			( configMAX_PRIORITIES - 2 )

/* The size, in words (not bytes), of the stack allocated to the FreeRTOS+TCP task.*/
#define ipconfigIP_TASK_STACK_SIZE_WORDS	( configMINIMAL_STACK_SIZE * 5 )

/* ipconfigRAND32() is called by the IP stack to generate random numbers for
things such as a DHCP transaction number or initial sequence number.  */
/* JB: TODO - add true random number generator ASF module and implement this (if needed) */
/*extern UBaseType_t uxRand( void );
#define ipconfigRAND32()	uxRand()
*/

#define ipconfigUSE_NETWORK_EVENT_HOOK 1

#define ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS ( 5000 / portTICK_PERIOD_MS )

#define ipconfigARP_CACHE_ENTRIES		6

#define ipconfigMAX_ARP_RETRANSMISSIONS ( 5 )

#define ipconfigMAX_ARP_AGE			150

#define ipconfigINCLUDE_FULL_INET_ADDR	1

#define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS		60

/* A FreeRTOS queue is used to send events from application tasks to the IP stack.  ipconfigEVENT_QUEUE_LENGTH sets the maximum number of events that can be queued for processing at any one time.  The event queue must be a minimum of 5 greater than the total number of network buffers. */
#define ipconfigEVENT_QUEUE_LENGTH		( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS + 5 )

#define ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND 1

/* Defines the Time To Live (TTL) values used in outgoing UDP packets. */
#define ipconfigUDP_TIME_TO_LIVE		128
#define ipconfigTCP_TIME_TO_LIVE		128 /* also defined in FreeRTOSIPConfigDefaults.h */

/* USE_TCP: Use TCP and all its features */
#define ipconfigUSE_TCP				( 1 )

/* USE_WIN: Let TCP use windowing mechanism. */
#define ipconfigUSE_TCP_WIN			( 1 )

/* The MTU is the maximum number of bytes the payload of a network frame can contain.  For normal Ethernet V2 frames the maximum MTU is 1500.  Setting a lower value can save RAM, depending on the buffer management scheme used.  If ipconfigCAN_FRAGMENT_OUTGOING_PACKETS is 1 then (ipconfigNETWORK_MTU - 28) must be divisible by 8. */
#define ipconfigNETWORK_MTU		1200

/** DNS **/

/* Set ipconfigUSE_DNS to 1 to include a basic DNS client/resolver. */
#define ipconfigUSE_DNS			0

#if ipconfigUSE_DNS
	/*JB: if defined, apparently necessary to define xApplicationDNSQueryHook () */

	/* Include support for LLMNR: Link-local Multicast Name Resolution
	(non-Microsoft) */
	#define ipconfigUSE_LLMNR					( 1 )

	/* Include support for NBNS: NetBIOS Name Service (Microsoft) */
	#define ipconfigUSE_NBNS					( 1 )

	/* Include support for DNS caching. */
	#define ipconfigUSE_DNS_CACHE				( 1 )
	#define ipconfigDNS_CACHE_NAME_LENGTH		( 16 )
	#define ipconfigDNS_CACHE_ENTRIES			( 4 )
	#define ipconfigDNS_REQUEST_ATTEMPTS		( 2 )

#endif /* ipconfigUSE_DNS */

/** End DNS **/

/** DHCP **/

/* If ipconfigUSE_DHCP is 1 then FreeRTOS+TCP will attempt to retrieve an IP address, netmask, DNS server address and gateway address from a DHCP server.  If ipconfigUSE_DHCP is 0 then FreeRTOS+TCP will use a static IP address. */
#define ipconfigUSE_DHCP	0

#define ipconfigMAXIMUM_DISCOVER_TX_PERIOD		( 120000 / portTICK_PERIOD_MS )

/** End DHCP **/

/* If ipconfigREPLY_TO_INCOMING_PINGS is set to 1 then the IP stack will
generate replies to incoming ICMP echo (ping) requests. */
#define ipconfigREPLY_TO_INCOMING_PINGS				1

/* If ipconfigSUPPORT_OUTGOING_PINGS is set to 1 then the
FreeRTOS_SendPingRequest() API function is available. */
#define ipconfigSUPPORT_OUTGOING_PINGS				0

/* If ipconfigSUPPORT_SELECT_FUNCTION is set to 1 then the FreeRTOS_select()
(and associated) API function is available. */
#define ipconfigSUPPORT_SELECT_FUNCTION				1

/* If ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES is set to 1 then Ethernet frames that are not in Ethernet II format will be dropped.  This option is included for potential future IP stack developments. */
#define ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES  1

/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1 then it is the responsibility of the Ethernet interface to filter out packets that are of no interest.  If the Ethernet interface does not implement this functionality, then set ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES to 0 to have the IP stack perform the filtering instead (it is much less efficient for the stack to do it because the packet will already have been passed into the stack).  If the Ethernet driver does all the necessary filtering in hardware then software filtering can be removed by using a value other than 1 or 0. */
#define ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES	1

/* The windows simulator cannot really simulate MAC interrupts, and needs to block occasionally to allow other tasks to run. */
#define configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY ( 20 / portTICK_PERIOD_MS )

/* Advanced only: in order to access 32-bit fields in the IP packets with 32-bit memory instructions, all packets will be stored 32-bit-aligned, plus 16-bits. This has to do with the contents of the IP-packets: all 32-bit fields are 32-bit-aligned, plus 16-bit(!) */
#define ipconfigPACKET_FILLER_SIZE 2

/* Define the size of the pool of TCP window descriptors.  On the average, each TCP socket will use up to 2 x 6 descriptors, meaning that it can have 2 x 6 outstanding packets (for Rx and Tx).  When using up to 10 TP sockets simultaneously, one could define TCP_WIN_SEG_COUNT as 120. */
#define ipconfigTCP_WIN_SEG_COUNT		240

/* Each TCP socket has a circular buffers for Rx and Tx, which have a fixed maximum size.  Define the size of Rx buffer for TCP sockets. */
#define ipconfigTCP_RX_BUFFER_LENGTH			( 1000 )

/* Define the size of Tx buffer for TCP sockets. */
#define ipconfigTCP_TX_BUFFER_LENGTH			( 1000 )

/* When using call-back handlers, the driver may check if the handler points to real program memory (RAM or flash) or just has a random non-zero value. */
#define ipconfigIS_VALID_PROG_ADDRESS(x) ( (x) != NULL )

/* Include support for TCP hang protection.  All sockets in a connecting or disconnecting stage will timeout after a period of non-activity. */
#define ipconfigTCP_HANG_PROTECTION			( 1 )
#define ipconfigTCP_HANG_PROTECTION_TIME	( 30 )

/* Include support for TCP keep-alive messages. */
#define ipconfigTCP_KEEP_ALIVE				( 1 )
#define ipconfigTCP_KEEP_ALIVE_INTERVAL		( 20 ) /* in seconds */

/*JB: from website (undefined in defaults)
When ipconfigUSE_LINKED_RX_MESSAGES is set to 1 it is possible to reduce CPU  load during periods of heavy network traffic by linking multiple received packets together, then passing all the linked packets to the IP RTOS task in one go */
#define ipconfigUSE_LINKED_RX_MESSAGES      0

/* JB: from website (undefined in defaults)
TCP time stamp functionality is available, but its usage is quite limited. Time-stamps can only be used if the initial SYN packet contains the time-stamp option. In most cases, incoming connection won't have the time-stamp option set. */
#define ipconfigUSE_TCP_TIMESTAMPS          0

/* JB: extra debugging 
A FreeRTOS queue is used to send events from application tasks to the IP stack. ipconfigEVENT_QUEUE_LENGTH sets the maximum number of events that can be queued for processing at any one time. If ipconfigCHECK_IP_QUEUE_SPACE is set to 1 then the uxGetMinimumIPQueueSpace() function can be used to query the minimum amount of free space that has existed in the queue since the system booted. */
#define ipconfigCHECK_IP_QUEUE_SPACE		1

#define portINLINE __inline

#endif /* FREERTOS_IP_CONFIG_H */

heinbali01 wrote on Thursday, March 16, 2017:

Before I look further in your networkInterface, can you check if the code gets into FreeRTOS_ARP.c.

One user, Stefan Battmer, recently reported about an unaligned access in FreeRTOS_ARP.c.

Maybe you can set a breakpoint both in ulSenderProtocolAddress() and in eARPProcessPacket().
it is the field ulSenderProtocolAddress that is 2-byte aligned and that might cause a crash / hardfault.

If that is the cause, we’ll make an update available. Thanks, Hein

jtbr wrote on Thursday, March 16, 2017:

I put a breakpoint at the top of eARPProcessPacket() and vARPGenerateRequestPacket(), which appear to be the only functions to use ulSenderProtocolAddress (I couldn’t find a function with the same name). I can confirm that I got a hard fault without either of these triggering.

jtbr wrote on Monday, March 27, 2017:

@Hein Just wondering if you’ve had a chance to look at the code. So far I’m still unsure what is causing the hard faults. Really appreciate it.

-Justin

heinbali01 wrote on Tuesday, March 28, 2017:

Hi Justin, I’m sorry, I had forgotten about your post. It is no problem to ring the bell earlier next time.

The function gmac_dev_write() ends as follows:

uint32_t gmac_dev_write(gmac_device_t* p_gmac_dev, void *p_buffer,
		uint32_t ul_size, gmac_dev_tx_cb_t func_tx_cb)
{
	...
	/* Now start to transmit if it is still not done */
	gmac_start_transmission(p_hw);

	return GMAC_OK;
}

Your driver is sending out a packet, and immediately after that, it releases the network Buffer:

BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend )
{
	if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
	{
		/* Not interested in a call-back after TX. */
		gmac_dev_write( &gs_gmac_dev, QUEUE_INDEX, (void *)pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, NULL );
		iptraceNETWORK_INTERFACE_TRANSMIT();
	}

	if( bReleaseAfterSend != pdFALSE )
	{
		vReleaseNetworkBufferAndDescriptor( pxDescriptor );
	}
	return pdTRUE;
}
/*-----------------------------------------------------------*/

When vReleaseNetworkBufferAndDescriptor() is called, the pucEthernetBuffer might still be used in a DMA buffer.

It is not correct but it won’t lead to hard fault.

Your port has a function gmac_dev_read().

In case there are less than 2 Network buffers available, the function will be called with a NULL pointer. We want to flush the DMA buffer without using its contents.
Did you adapt that function already to test for NULL?

In the port for SAM4E, I made quite a few changes to gmac_dev_read() :

uint32_t gmac_dev_read(gmac_device_t* p_gmac_dev, uint8_t* p_frame,
		uint32_t ul_frame_size, uint32_t* p_rcv_size)
{
	int32_t nextIdx;	/* A copy of the Rx-index 'ul_rx_idx' */
	int32_t bytesLeft = gmac_dev_poll (p_gmac_dev);
	gmac_rx_descriptor_t *pxHead;

	if (bytesLeft == 0 )
	{
		return GMAC_RX_NULL;
	}

	/* gmac_dev_poll has confirmed that there is a complete frame at
	 * the current position 'ul_rx_idx'
	 */
	nextIdx = p_gmac_dev->ul_rx_idx;

	/* Read +2 bytes because buffers are aligned at -2 bytes */
	bytesLeft = min( bytesLeft + 2, ( int32_t )ul_frame_size );

	/* The frame will be copied in 1 or 2 memcpy's */
	if( ( p_frame != NULL ) && ( bytesLeft != 0 ) )
	{
	const uint8_t *source;
	int32_t left;
	int32_t toCopy;

		source = p_gmac_dev->p_rx_buffer + nextIdx * GMAC_RX_UNITSIZE;
		left = bytesLeft;
		toCopy = ( p_gmac_dev->ul_rx_list_size - nextIdx ) * GMAC_RX_UNITSIZE;
		if(toCopy > left )
		{
			toCopy = left;
		}
		memcpy (p_frame, source, toCopy);
		left -= toCopy;

		if( left != 0ul )
		{
			memcpy (p_frame + toCopy, (void*)p_gmac_dev->p_rx_buffer, left);
		}
	}

	do
	{
		pxHead = &p_gmac_dev->p_rx_dscr[nextIdx];
		pxHead->addr.val &= ~(GMAC_RXD_OWNERSHIP);
		circ_inc32 (&nextIdx, p_gmac_dev->ul_rx_list_size);
	} while ((pxHead->status.val & GMAC_RXD_EOF) == 0);

	p_gmac_dev->ul_rx_idx = nextIdx;

	*p_rcv_size = bytesLeft;

	return GMAC_OK;
}

The code will test if p_frame equals NULL.

I hope that it helps, please report back. Regards, Hein

jtbr wrote on Thursday, March 30, 2017:

Thanks for that. I hadn’t noticed the functional changes that were made to the GMAC driver in the example. I’ll work to integrate them and let you know how it goes.

jtbr wrote on Friday, March 31, 2017:

I think I’ve addressed the issues you raised, but I’m still facing at least two types of Hard fault, both due to the program counter pointing to invalid instructions/non-code. I haven’t truly got to test these changes yet, though.

I’ve been able to confirm the source of one of the hard faults, however, which remains despite the changes. The use case is simple. I don’t have a cable connected to the ethernet port. I attempt to auto-negotiate/initialize and fail. xNetworkInterfaceInitialise() returns pdFAIL. At FreeRTOS_IP.c: 1339, vTaskDelay() is called. (It would occur in another place if vTaskDelay were called prior to this point, as I tried to do while waiting for the PHY to be ready).

By the time vTaskDelay arrives at the end of the function, the value it pops to the program counter is invalid (on the heap). After the pop, the program counter (always) it points to 0x20400044, which is in SRAM. Hence it always causes a hard fault after executing the last line below (the code shows disassembly of the end of vTaskDelay).

	if( xAlreadyYielded == pdFALSE )

0040C8A2 ldr r3, [r7, #20]
0040C8A4 cmp r3, #0
0040C8A6 bne #14
portYIELD_WITHIN_API();
0040C8A8 ldr r3, [pc, #52]
0040C8AA mov.w r2, #268435456
0040C8AE str r2, [r3]
0040C8B0 dsb sy
0040C8B4 isb sy
}
0040C8B8 nop
0040C8BA adds r7, #24
0040C8BC mov sp, r7
0040C8BE pop {r7, pc}

During the course of vTaskDelay, I noticed that xPortPendSVHandler executes twice, and it’s just after finishing the second time that the end of vTaskDelay (above) executes, leading to the error. I guess this is the context switching that is occurring, but it seems something about the saving and restoring of the state is failing.

Any idea what might be going wrong here? Note I am using FreeRTOS 8.2.3. I have also tried tripling my stack size to 0xF000 (60kb), which had no effect.

heinbali01 wrote on Saturday, April 01, 2017:

Justin,

You’re getting closer …

By the time vTaskDelay arrives at the end of the function, the value
it pops to the program counter is invalid (on the heap).

Not ‘heap’ but ‘stack’, I assume :slight_smile:

After the pop, the program counter (always) it points to 0x20400044,
which is in SRAM. Hence it always causes a hard fault after executing
the last line below (the code shows disassembly of the end of vTaskDelay).

When vTaskDelay crashes at the end, it looks to me that the task’s stack has been corrupted / overwritten in a meanwhile.

Any idea what might be going wrong here? Note I am using FreeRTOS 8.2.3.

There are no known issues of this type with 8.2.3. Also +TCP was initially developed and tested with that kernel release.

I have also tried tripling my stack size to 0xF000 (60kb), which had no effect.

The size of what stack?
There is a general program stack, that is used at boot time until the scheduler is started.

But when the IP-task runs, it uses a stack that is determined as:

	#define configMINIMAL_STACK_SIZE           ( 130 )
    #define ipconfigIP_TASK_STACK_SIZE_WORDS   ( configMINIMAL_STACK_SIZE * 5 )

That makes 5 x 130 = 650 words ( 2600 bytes ). Should be more than enough.

Unless you’re using stack-expensive routines for logging?

Isn’t there some pointer that is not properly initialised and writing in the wrong space?

What about the other tasks you are running? Do they have enough stack? Can you log or trace what happens during the call to vTaskDelay() ? That is maybe the first time that some other task becomes active?

jtbr wrote on Monday, April 03, 2017:

Hein,

Thanks again for your insight.

When vTaskDelay crashes at the end, it looks to me that the task’s stack has been corrupted / overwritten in a meanwhile.

I think that’s correct.

I have also tried tripling my stack size to 0xF000 (60kb), which had no effect.

The size of what stack?
There is a general program stack, that is used at boot time until the scheduler is started.

I momentarily forgot about the task stacks and was talking about the program stack.

But when the IP-task runs, it uses a stack that is determined as: …
That makes 5 x 130 = 650 words ( 2600 bytes ). Should be more than enough.

Unless you’re using stack-expensive routines for logging?

I am using printf for logging. But I had set configMINIMAL_STACK_SIZE to 1300 for testing, which should be more than adequate so I doubt it’s that (I’ve now reset it to a more reasonable 450; no difference).

Isn’t there some pointer that is not properly initialised and writing in the wrong space?

This is possible. But so far I haven’t been able to find anything. And there’s not actually a lot of my code that’s running before this happens. I’ll keep digging into this though.

What about the other tasks you are running? Do they have enough stack? Can you log or trace what happens during the call to vTaskDelay() ? That is maybe the first time that some other task becomes active?

I have removed all user tasks from this test program so the only tasks running are those used by FreeRTOS and +TCP. I’ve tried debugging through vTaskDelay() but not found anything to really be happening. After vTaskDelay invokes PendSV_Handler, it drops into the loop in portTASK_FUNCTION(). It repeatedly tests if there are any tasks to delete (which there never are) and then since I have configUSE_PREEMPTION == 1 and configIDLE_SHOULD_YIELD == 1, it checks to see if it needs to yield. I set a breakpoint on taskYIELD, but strangely this never gets hit, so I’m not sure how control leaves this function. I can only assume it itself is getting preempted somehow. This loop executes hundreds of times for even a 1ms delay (I know because I stepped through it hundreds of times) without anything else apparently happening. Eventually PendSVHandler is called again and at this point the program pointer popped from the stack is corrupted (always in the same way), causing the Usage Fault.

Do you perhaps you have some ideas for other places I could put breakpoints to see if anything else is occurring during vTaskDelay without my knowledge?

jtbr wrote on Monday, April 03, 2017:

I should note that I have the following in FreeRTOSConfig.h:

/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS standard names. */
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define xPortSysTickHandler SysTick_Handler

The PendSV_Handler that gets called is xPortPendSVHandler() in freertos-8.2.3/Source/portable/GCC/ARM_CM7/r0p1/port.c

rtel wrote on Monday, April 03, 2017:

Indirect replies only -

Make sure you have configCHECK_FOR_STACK_OVERFLOW set to 2, and the
overflow hook function set to something that halts the program. That
will take out a lot of guess work.

configMINIMAL_STACK_SIZE only effects the size of the idle task stack,
unless you are using it yourself to dimension the size of your
application tasks.

Have you tried taking out all the calls to printf()?

Do you have compiler optimisation on? If so, that may explain why your
break point is not being hit.

jtbr wrote on Tuesday, April 04, 2017:

Thank you. I did indeed have a -Og snuck in there for the C++ compiler (optimization was disabled for the C compiler though, so it shouldn’t have affected this code). I still do not get the breakpoint on taskYIELD() to hit however.

It appears that all tasks (again, the only tasks I have are created by FreeRTOS or +TCP) use stack size as a multiple of configMINIMAL_STACK_SIZE:

#define configTIMER_TASK_STACK_DEPTH	( configMINIMAL_STACK_SIZE * 2 )
#define tskIDLE_STACK_SIZE	configMINIMAL_STACK_SIZE
#define ipconfigIP_TASK_STACK_SIZE_WORDS	( configMINIMAL_STACK_SIZE * 5 )
// Used within the NetworkInterface:
#define configEMAC_TASK_STACK_SIZE ( 4 * configMINIMAL_STACK_SIZE )

NOTE: the EMAC TASK is not yet launched when this Usage Fault occurs.

configCHECK_FOR_STACK_OVERFLOW has been set to 2 but vApplicationStackOverflowHook is seemingly never called. Previously it didn’t disable interrupts from within that function though, so I just did that to be sure. Still never see it getting called.

jtbr wrote on Thursday, April 06, 2017:

Still stumped by this one. It’s difficult to debug because I don’t know when/what is causing the portTASK_FUNCTION to return to the original vTaskDelayFunction(). I expect it’s just the timeout expiring and causing a context switch back. But then how/why would the stack be corrupted? This is just core RTOS code. I don’t know the mechanics of the delay expiring, and as I noted, taskYIELD() is seemingly never called from portTASK_FUNCTION.

I have now tried the example demo for the SAM4E on SAM4E hardware and and found that it does indeed work. In that demo, it uses heap_5. Is there any reason that I should use that over heap_4, which is what I’m currently using? But generally I’m not seeing any clear reason why it should work while the SAMV71 adaptation doesn’t.

Could this potentially have to do with the lower number of available user interrupt priorities on the Cortex M7 vs M4E (7 vs 15)? Or perhaps the fact that there are floating point registers to save for the M7 that might not have been needed for the M4E?

Many thanks.

rtel wrote on Friday, April 07, 2017:

I have now tried the example demo for the SAM4E on SAM4E hardware and
and found that it does indeed work. In that demo, it uses heap_5. Is
there any reason that I should use that over heap_4, which is what I’m
currently using? But generally I’m not seeing any clear reason why it
should work while the SAMV71 adaptation doesn’t.

Heap_4 and heap_5 use the same algorithm. Heap_5 just has the added
ability to be split across multiple memory blocks.

Could this potentially have to do with the lower number of available
user interrupt priorities on the Cortex M7 vs M4E (7 vs 15)?

Unlikely.

Or perhaps
the fact that there are floating point registers to save for the M7 that
might not have been needed for the M4E?

Unlikely.

I’ve not read back through this thread, so maybe you have said already,
but which version of FreeRTOS are you using? Unless you are using
FreeRTOS V9 then the Cortex-M4F code is not completely compatible with
the Cortex-M7. More importantly, what is the revision of the Cortex-M7
core you are using? If it is R0P1 then there are silicon errata that
need to be worked around, and you will need to use the FreeRTOS port
that is specific to that revision of the core (which is in the
FreeRTOS/Source/Portable/GCC/ARM_CM7/r0p1 directory.

heinbali01 wrote on Friday, April 07, 2017:

Thanks Richard, for those comments.

the fact that there are floating point registers to save for the M7

Richard, can there be a problem with memcpy() due to unsaved floating point registers like for the Cortex-A9?

The SAM4E project is indeed using heap_5 because it has 3 different blocks of RAM memory.

When there is a single heap block, heap_5 can still be usefull because it allows to use linker symbols that determine the location and size of the available RAM. When using heap_4, you need to set configTOTAL_HEAP_SIZE at compile time, which is often not known exactly before you have linked the project.

rtel wrote on Friday, April 07, 2017:

Richard, can there be a problem with |memcpy()| due to unsaved floating
point registers like for the Cortex-A9
http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html#floating-point?

No, the way floating point works on the M class devices is different to
the A class in-so-much-as the hardware detects if the FPU has been used,
whereas on the A the user has to say. The context switch on the M
devices looks for a bit being set in hardware, and if its set, handles
the FPU context switch. In other words, if a library accessed an FPU
register (as was happening on the A devices) the context switch code
would notice, and the FPU registers would from then on form part of the
task’s context automatically.

jtbr wrote on Friday, April 07, 2017:

I am using FreeRTOS 8.2.3. It sounds like you would recommend upgrading to 9.0? (I only need to run on SAMV71Q21, not the SAM4E)

I did not find any documentation of which cortex revision this board uses, but when I check the CPUID register, it indicates it is revision r0p1. I am already using the FreeRTOS port code for that revision. (Prior to integrating +TCP, my basic tests showed FreeRTOS to be working in the project).

rtel wrote on Friday, April 07, 2017:

I did not find any documentation of which cortex revision this board
uses, but when I check the CPUID register, it indicates it is revision
r0p1.

Ok - so the Cortex-M4F code is not going to work reliably on that part.
It is only that core revision that has the problem, any other revision
and using the Cortex-M4F code from FreeRTOS V9 onwards will be fine.

I am already using the FreeRTOS port code for that revision.

Do you mean you have switched to the port layer found in the V9 code?
Previously you said you were using V8.x code, an the code with the
silicon bug fix is only available in V9.