From 01d17dd5f9f9c87f0a90aea328ccff74e319999a Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 22 Sep 2016 12:20:56 +0800 Subject: [PATCH 001/110] Backport the static allocation feature from FreeRTOS V9.0.0 This feature allows to use static buffers (or from a pool of memory which is not controlled by FreeRTOS). In order to reduce the impact of the changes, the static feature has only been added to the queus (and in consequence to the semaphores and the mutexes) and the tasks. The Timer task is always dynamically allocated and also the idle task(s), which in the case of the ESP-IDF is ok, since we always need to have dynamic allocation enabled. --- .../freertos/include/freertos/FreeRTOS.h | 162 ++++ .../include/freertos/FreeRTOSConfig.h | 2 + components/freertos/include/freertos/queue.h | 110 ++- components/freertos/include/freertos/semphr.h | 382 +++++++- components/freertos/include/freertos/task.h | 161 +++- components/freertos/queue.c | 351 +++++--- components/freertos/tasks.c | 839 +++++++++++------- 7 files changed, 1523 insertions(+), 484 deletions(-) diff --git a/components/freertos/include/freertos/FreeRTOS.h b/components/freertos/include/freertos/FreeRTOS.h index 04b39b65e..f6c9aa497 100644 --- a/components/freertos/include/freertos/FreeRTOS.h +++ b/components/freertos/include/freertos/FreeRTOS.h @@ -74,6 +74,7 @@ * Include the generic headers required for the FreeRTOS port being used. */ #include +#include "sys/reent.h" /* * If stdint.h cannot be located then: @@ -739,6 +740,20 @@ extern "C" { #define portTICK_TYPE_IS_ATOMIC 0 #endif +#ifndef configSUPPORT_STATIC_ALLOCATION + /* Defaults to 0 for backward compatibility. */ + #define configSUPPORT_STATIC_ALLOCATION 0 +#endif + +#ifndef configSUPPORT_DYNAMIC_ALLOCATION + /* Defaults to 1 for backward compatibility. */ + #define configSUPPORT_DYNAMIC_ALLOCATION 1 +#endif + +#if( ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) ) + #error configSUPPORT_STATIC_ALLOCATION and configSUPPORT_DYNAMIC_ALLOCATION cannot both be 0, but can both be 1. +#endif + #if( portTICK_TYPE_IS_ATOMIC == 0 ) /* Either variables of tick type cannot be read atomically, or portTICK_TYPE_IS_ATOMIC was not set - map the critical sections used when @@ -791,6 +806,153 @@ V8 if desired. */ #define configESP32_PER_TASK_DATA 1 #endif +/* + * In line with software engineering best practice, FreeRTOS implements a strict + * data hiding policy, so the real structures used by FreeRTOS to maintain the + * state of tasks, queues, semaphores, etc. are not accessible to the application + * code. However, if the application writer wants to statically allocate such + * an object then the size of the object needs to be know. Dummy structures + * that are guaranteed to have the same size and alignment requirements of the + * real objects are used for this purpose. The dummy list and list item + * structures below are used for inclusion in such a dummy structure. + */ +struct xSTATIC_LIST_ITEM +{ + TickType_t xDummy1; + void *pvDummy2[ 4 ]; +}; +typedef struct xSTATIC_LIST_ITEM StaticListItem_t; + +/* See the comments above the struct xSTATIC_LIST_ITEM definition. */ +struct xSTATIC_MINI_LIST_ITEM +{ + TickType_t xDummy1; + void *pvDummy2[ 2 ]; +}; +typedef struct xSTATIC_MINI_LIST_ITEM StaticMiniListItem_t; + +/* See the comments above the struct xSTATIC_LIST_ITEM definition. */ +typedef struct xSTATIC_LIST +{ + UBaseType_t uxDummy1; + void *pvDummy2; + StaticMiniListItem_t xDummy3; +} StaticList_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the Task structure used internally by + * FreeRTOS is not accessible to application code. However, if the application + * writer wants to statically allocate the memory required to create a task then + * the size of the task object needs to be know. The StaticTask_t structure + * below is provided for this purpose. Its sizes and alignment requirements are + * guaranteed to match those of the genuine structure, no matter which + * architecture is being used, and no matter how the values in FreeRTOSConfig.h + * are set. Its contents are somewhat obfuscated in the hope users will + * recognise that it would be unwise to make direct use of the structure members. + */ +typedef struct xSTATIC_TCB +{ + void *pxDummy1; + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xDummy2; + #endif + StaticListItem_t xDummy3[ 2 ]; + UBaseType_t uxDummy5; + void *pxDummy6; + uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; + UBaseType_t uxDummyCoreId; + #if ( portSTACK_GROWTH > 0 ) + void *pxDummy8; + #endif + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxDummy9; + uint32_t OldInterruptState; + #endif + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy10[ 2 ]; + #endif + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxDummy12[ 2 ]; + #endif + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + void *pxDummy14; + #endif + #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + void *pvDummy15[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) + void *pvDummyLocalStorageCallBack[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #endif + #endif + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + uint32_t ulDummy16; + #endif + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + struct _reent xDummy17; + #endif + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + uint32_t ulDummy18; + uint32_t ucDummy19; + #endif + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t uxDummy20; + #endif + +} StaticTask_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the Queue structure used internally by + * FreeRTOS is not accessible to application code. However, if the application + * writer wants to statically allocate the memory required to create a queue + * then the size of the queue object needs to be know. The StaticQueue_t + * structure below is provided for this purpose. Its sizes and alignment + * requirements are guaranteed to match those of the genuine structure, no + * matter which architecture is being used, and no matter how the values in + * FreeRTOSConfig.h are set. Its contents are somewhat obfuscated in the hope + * users will recognise that it would be unwise to make direct use of the + * structure members. + */ +typedef struct xSTATIC_QUEUE +{ + void *pvDummy1[ 3 ]; + + union + { + void *pvDummy2; + UBaseType_t uxDummy2; + } u; + + StaticList_t xDummy3[ 2 ]; + UBaseType_t uxDummy4[ 3 ]; + BaseType_t ucDummy5[ 2 ]; + + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucDummy6; + #endif + + #if ( configUSE_QUEUE_SETS == 1 ) + void *pvDummy7; + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy8; + uint8_t ucDummy9; + #endif + + struct { + volatile uint32_t mux; + #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG + const char *lastLockedFn; + int lastLockedLine; + #endif + } mux; + +} StaticQueue_t; +typedef StaticQueue_t StaticSemaphore_t; + #ifdef __cplusplus } #endif diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 089d799dd..f3f2df73c 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -241,6 +241,8 @@ #define configUSE_NEWLIB_REENTRANT 1 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 + /* Test FreeRTOS timers (with timer task) and more. */ /* Some files don't compile if this flag is disabled */ #define configUSE_TIMERS 1 diff --git a/components/freertos/include/freertos/queue.h b/components/freertos/include/freertos/queue.h index 2095c59b0..876f1a1b3 100644 --- a/components/freertos/include/freertos/queue.h +++ b/components/freertos/include/freertos/queue.h @@ -170,7 +170,95 @@ typedef void * QueueSetMemberHandle_t; * \defgroup xQueueCreate xQueueCreate * \ingroup QueueManagement */ -#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( uxQueueLength, uxItemSize, queueQUEUE_TYPE_BASE ) +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) +#endif + +/** + * queue. h + *
+ QueueHandle_t xQueueCreateStatic(
+							  UBaseType_t uxQueueLength,
+							  UBaseType_t uxItemSize,
+							  uint8_t *pucQueueStorageBuffer,
+							  StaticQueue_t *pxQueueBuffer
+						  );
+ * 
+ * + * Creates a new queue instance, and returns a handle by which the new queue + * can be referenced. + * + * Internally, within the FreeRTOS implementation, queues use two blocks of + * memory. The first block is used to hold the queue's data structures. The + * second block is used to hold items placed into the queue. If a queue is + * created using xQueueCreate() then both blocks of memory are automatically + * dynamically allocated inside the xQueueCreate() function. (see + * http://www.freertos.org/a00111.html). If a queue is created using + * xQueueCreateStatic() then the application writer must provide the memory that + * will get used by the queue. xQueueCreateStatic() therefore allows a queue to + * be created without using any dynamic memory allocation. + * + * http://www.FreeRTOS.org/Embedded-RTOS-Queues.html + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @param pucQueueStorageBuffer If uxItemSize is not zero then + * pucQueueStorageBuffer must point to a uint8_t array that is at least large + * enough to hold the maximum number of items that can be in the queue at any + * one time - which is ( uxQueueLength * uxItemsSize ) bytes. If uxItemSize is + * zero then pucQueueStorageBuffer can be NULL. + * + * @param pxQueueBuffer Must point to a variable of type StaticQueue_t, which + * will be used to hold the queue's data structure. + * + * @return If the queue is created then a handle to the created queue is + * returned. If pxQueueBuffer is NULL then NULL is returned. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ };
+
+ #define QUEUE_LENGTH 10
+ #define ITEM_SIZE sizeof( uint32_t )
+
+ // xQueueBuffer will hold the queue structure.
+ StaticQueue_t xQueueBuffer;
+
+ // ucQueueStorage will hold the items posted to the queue.  Must be at least
+ // [(queue length) * ( queue item size)] bytes long.
+ uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold.
+							ITEM_SIZE	  // The size of each item in the queue
+							&( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue.
+							&xQueueBuffer ); // The buffer that will hold the queue structure.
+
+	// The queue is guaranteed to be created successfully as no dynamic memory
+	// allocation is used.  Therefore xQueue1 is now a handle to a valid queue.
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueCreateStatic xQueueCreateStatic + * \ingroup QueueManagement + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer ) xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * queue. h @@ -1479,7 +1567,9 @@ BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTi * these functions directly. */ QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; void* xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; /* @@ -1538,10 +1628,22 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION #endif /* - * Generic version of the queue creation function, which is in turn called by - * any queue, semaphore or mutex creation function or macro. + * Generic version of the function used to creaet a queue using dynamic memory + * allocation. This is called by other functions and macros that create other + * RTOS objects that use the queue structure as their base. */ -QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#endif + +/* + * Generic version of the function used to creaet a queue using dynamic memory + * allocation. This is called by other functions and macros that create other + * RTOS objects that use the queue structure as their base. + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#endif /* * Queue sets provide a mechanism to allow a task to block (pend) on a read diff --git a/components/freertos/include/freertos/semphr.h b/components/freertos/include/freertos/semphr.h index 5866ab1ec..6343d0190 100644 --- a/components/freertos/include/freertos/semphr.h +++ b/components/freertos/include/freertos/semphr.h @@ -128,19 +128,37 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary * \ingroup Semaphores */ -#define vSemaphoreCreateBinary( xSemaphore ) \ - { \ - ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \ - if( ( xSemaphore ) != NULL ) \ - { \ - ( void ) xSemaphoreGive( ( xSemaphore ) ); \ - } \ - } +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define vSemaphoreCreateBinary( xSemaphore ) \ + { \ + ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \ + if( ( xSemaphore ) != NULL ) \ + { \ + ( void ) xSemaphoreGive( ( xSemaphore ) ); \ + } \ + } +#endif /** * semphr. h *
SemaphoreHandle_t xSemaphoreCreateBinary( void )
* + * Creates a new binary semaphore instance, and returns a handle by which the + * new semaphore can be referenced. + * + * In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a binary semaphore! + * http://www.freertos.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, binary semaphores use a block + * of memory, in which the semaphore structure is stored. If a binary semaphore + * is created using xSemaphoreCreateBinary() then the required memory is + * automatically dynamically allocated inside the xSemaphoreCreateBinary() + * function. (see http://www.freertos.org/a00111.html). If a binary semaphore + * is created using xSemaphoreCreateBinaryStatic() then the application writer + * must provide the memory. xSemaphoreCreateBinaryStatic() therefore allows a + * binary semaphore to be created without using any dynamic memory allocation. + * * The old vSemaphoreCreateBinary() macro is now deprecated in favour of this * xSemaphoreCreateBinary() function. Note that binary semaphores created using * the vSemaphoreCreateBinary() macro are created in a state such that the @@ -182,7 +200,68 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary * \ingroup Semaphores */ -#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ) +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ) +#endif + +/** + * semphr. h + *
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
+ * + * Creates a new binary semaphore instance, and returns a handle by which the + * new semaphore can be referenced. + * + * NOTE: In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a binary semaphore! + * http://www.freertos.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, binary semaphores use a block + * of memory, in which the semaphore structure is stored. If a binary semaphore + * is created using xSemaphoreCreateBinary() then the required memory is + * automatically dynamically allocated inside the xSemaphoreCreateBinary() + * function. (see http://www.freertos.org/a00111.html). If a binary semaphore + * is created using xSemaphoreCreateBinaryStatic() then the application writer + * must provide the memory. xSemaphoreCreateBinaryStatic() therefore allows a + * binary semaphore to be created without using any dynamic memory allocation. + * + * This type of semaphore can be used for pure synchronisation between tasks or + * between an interrupt and a task. The semaphore need not be given back once + * obtained, so one task/interrupt can continuously 'give' the semaphore while + * another continuously 'takes' the semaphore. For this reason this type of + * semaphore does not use a priority inheritance mechanism. For an alternative + * that does use priority inheritance see xSemaphoreCreateMutex(). + * + * @param pxSemaphoreBuffer Must point to a variable of type StaticSemaphore_t, + * which will then be used to hold the semaphore's data structure, removing the + * need for the memory to be allocated dynamically. + * + * @return If the semaphore is created then a handle to the created semaphore is + * returned. If pxSemaphoreBuffer is NULL then NULL is returned. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore = NULL;
+ StaticSemaphore_t xSemaphoreBuffer;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to xSemaphoreCreateBinary().
+    // The semaphore's data structures will be placed in the xSemaphoreBuffer
+    // variable, the address of which is passed into the function.  The
+    // function's parameter is not NULL, so the function will not attempt any
+    // dynamic memory allocation, and therefore the function will not return
+    // return NULL.
+    xSemaphore = xSemaphoreCreateBinary( &xSemaphoreBuffer );
+
+    // Rest of task code goes here.
+ }
+ 
+ * \defgroup xSemaphoreCreateBinaryStatic xSemaphoreCreateBinaryStatic + * \ingroup Semaphores + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateBinaryStatic( pxStaticSemaphore ) xQueueGenericCreateStatic( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticSemaphore, queueQUEUE_TYPE_BINARY_SEMAPHORE ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h @@ -652,9 +731,18 @@ typedef QueueHandle_t SemaphoreHandle_t; * Macro that implements a mutex semaphore by using the existing queue * mechanism. * - * Mutexes created using this macro can be accessed using the xSemaphoreTake() + * Internally, within the FreeRTOS implementation, mutex semaphores use a block + * of memory, in which the mutex structure is stored. If a mutex is created + * using xSemaphoreCreateMutex() then the required memory is automatically + * dynamically allocated inside the xSemaphoreCreateMutex() function. (see + * http://www.freertos.org/a00111.html). If a mutex is created using + * xSemaphoreCreateMutexStatic() then the application writer must provided the + * memory. xSemaphoreCreateMutexStatic() therefore allows a mutex to be created + * without using any dynamic memory allocation. + * + * Mutexes created using this function can be accessed using the xSemaphoreTake() * and xSemaphoreGive() macros. The xSemaphoreTakeRecursive() and - * xSemaphoreGiveRecursive() macros should not be used. + * xSemaphoreGiveRecursive() macros must not be used. * * This type of semaphore uses a priority inheritance mechanism so a task * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the @@ -667,8 +755,9 @@ typedef QueueHandle_t SemaphoreHandle_t; * semaphore and another always 'takes' the semaphore) and from within interrupt * service routines. * - * @return xSemaphore Handle to the created mutex semaphore. Should be of type - * SemaphoreHandle_t. + * @return If the mutex was successfully created then a handle to the created + * semaphore is returned. If there was not enough heap to allocate the mutex + * data structures then NULL is returned. * * Example usage:
@@ -690,19 +779,93 @@ typedef QueueHandle_t SemaphoreHandle_t;
  * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex
  * \ingroup Semaphores
  */
-#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
+#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
+	#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
+#endif
+
+/**
+ * semphr. h
+ * 
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )
+ * + * Creates a new mutex type semaphore instance, and returns a handle by which + * the new mutex can be referenced. + * + * Internally, within the FreeRTOS implementation, mutex semaphores use a block + * of memory, in which the mutex structure is stored. If a mutex is created + * using xSemaphoreCreateMutex() then the required memory is automatically + * dynamically allocated inside the xSemaphoreCreateMutex() function. (see + * http://www.freertos.org/a00111.html). If a mutex is created using + * xSemaphoreCreateMutexStatic() then the application writer must provided the + * memory. xSemaphoreCreateMutexStatic() therefore allows a mutex to be created + * without using any dynamic memory allocation. + * + * Mutexes created using this function can be accessed using the xSemaphoreTake() + * and xSemaphoreGive() macros. The xSemaphoreTakeRecursive() and + * xSemaphoreGiveRecursive() macros must not be used. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See xSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @param pxMutexBuffer Must point to a variable of type StaticSemaphore_t, + * which will be used to hold the mutex's data structure, removing the need for + * the memory to be allocated dynamically. + * + * @return If the mutex was successfully created then a handle to the created + * mutex is returned. If pxMutexBuffer was NULL then NULL is returned. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore;
+ StaticSemaphore_t xMutexBuffer;
+
+ void vATask( void * pvParameters )
+ {
+    // A mutex cannot be used before it has been created.  xMutexBuffer is
+    // into xSemaphoreCreateMutexStatic() so no dynamic memory allocation is
+    // attempted.
+    xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer );
+
+    // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
+    // so there is no need to check it.
+ }
+ 
+ * \defgroup xSemaphoreCreateMutexStatic xSemaphoreCreateMutexStatic + * \ingroup Semaphores + */ + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateMutexStatic( pxMutexBuffer ) xQueueCreateMutexStatic( queueQUEUE_TYPE_MUTEX, ( pxMutexBuffer ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
* - * Macro that implements a recursive mutex by using the existing queue - * mechanism. + * Creates a new recursive mutex type semaphore instance, and returns a handle + * by which the new recursive mutex can be referenced. + * + * Internally, within the FreeRTOS implementation, recursive mutexs use a block + * of memory, in which the mutex structure is stored. If a recursive mutex is + * created using xSemaphoreCreateRecursiveMutex() then the required memory is + * automatically dynamically allocated inside the + * xSemaphoreCreateRecursiveMutex() function. (see + * http://www.freertos.org/a00111.html). If a recursive mutex is created using + * xSemaphoreCreateRecursiveMutexStatic() then the application writer must + * provide the memory that will get used by the mutex. + * xSemaphoreCreateRecursiveMutexStatic() therefore allows a recursive mutex to + * be created without using any dynamic memory allocation. * * Mutexes created using this macro can be accessed using the * xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() macros. The - * xSemaphoreTake() and xSemaphoreGive() macros should not be used. + * xSemaphoreTake() and xSemaphoreGive() macros must not be used. * * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex * doesn't become available again until the owner has called @@ -745,14 +908,104 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex * \ingroup Semaphores */ -#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX ) +#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) + #define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX ) +#endif + +/** + * semphr. h + *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )
+ * + * Creates a new recursive mutex type semaphore instance, and returns a handle + * by which the new recursive mutex can be referenced. + * + * Internally, within the FreeRTOS implementation, recursive mutexs use a block + * of memory, in which the mutex structure is stored. If a recursive mutex is + * created using xSemaphoreCreateRecursiveMutex() then the required memory is + * automatically dynamically allocated inside the + * xSemaphoreCreateRecursiveMutex() function. (see + * http://www.freertos.org/a00111.html). If a recursive mutex is created using + * xSemaphoreCreateRecursiveMutexStatic() then the application writer must + * provide the memory that will get used by the mutex. + * xSemaphoreCreateRecursiveMutexStatic() therefore allows a recursive mutex to + * be created without using any dynamic memory allocation. + * + * Mutexes created using this macro can be accessed using the + * xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() macros. The + * xSemaphoreTake() and xSemaphoreGive() macros must not be used. + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See xSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @param pxMutexBuffer Must point to a variable of type StaticSemaphore_t, + * which will then be used to hold the recursive mutex's data structure, + * removing the need for the memory to be allocated dynamically. + * + * @return If the recursive mutex was successfully created then a handle to the + * created recursive mutex is returned. If pxMutexBuffer was NULL then NULL is + * returned. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore;
+ StaticSemaphore_t xMutexBuffer;
+
+ void vATask( void * pvParameters )
+ {
+    // A recursive semaphore cannot be used before it is created.  Here a
+    // recursive mutex is created using xSemaphoreCreateRecursiveMutexStatic().
+    // The address of xMutexBuffer is passed into the function, and will hold
+    // the mutexes data structures - so no dynamic memory allocation will be
+    // attempted.
+    xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer );
+
+    // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
+    // so there is no need to check it.
+ }
+ 
+ * \defgroup xSemaphoreCreateRecursiveMutexStatic xSemaphoreCreateRecursiveMutexStatic + * \ingroup Semaphores + */ +#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) + #define xSemaphoreCreateRecursiveMutexStatic( pxStaticSemaphore ) xQueueCreateMutexStatic( queueQUEUE_TYPE_RECURSIVE_MUTEX, pxStaticSemaphore ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h *
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
* - * Macro that creates a counting semaphore by using the existing - * queue mechanism. + * Creates a new counting semaphore instance, and returns a handle by which the + * new counting semaphore can be referenced. + * + * In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a counting semaphore! + * http://www.freertos.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, counting semaphores use a + * block of memory, in which the counting semaphore structure is stored. If a + * counting semaphore is created using xSemaphoreCreateCounting() then the + * required memory is automatically dynamically allocated inside the + * xSemaphoreCreateCounting() function. (see + * http://www.freertos.org/a00111.html). If a counting semaphore is created + * using xSemaphoreCreateCountingStatic() then the application writer can + * instead optionally provide the memory that will get used by the counting + * semaphore. xSemaphoreCreateCountingStatic() therefore allows a counting + * semaphore to be created without using any dynamic memory allocation. * * Counting semaphores are typically used for two things: * @@ -808,7 +1061,94 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup xSemaphoreCreateCounting xSemaphoreCreateCounting * \ingroup Semaphores */ -#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) ) +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) ) +#endif + +/** + * semphr. h + *
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer )
+ * + * Creates a new counting semaphore instance, and returns a handle by which the + * new counting semaphore can be referenced. + * + * In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a counting semaphore! + * http://www.freertos.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, counting semaphores use a + * block of memory, in which the counting semaphore structure is stored. If a + * counting semaphore is created using xSemaphoreCreateCounting() then the + * required memory is automatically dynamically allocated inside the + * xSemaphoreCreateCounting() function. (see + * http://www.freertos.org/a00111.html). If a counting semaphore is created + * using xSemaphoreCreateCountingStatic() then the application writer must + * provide the memory. xSemaphoreCreateCountingStatic() therefore allows a + * counting semaphore to be created without using any dynamic memory allocation. + * + * Counting semaphores are typically used for two things: + * + * 1) Counting events. + * + * In this usage scenario an event handler will 'give' a semaphore each time + * an event occurs (incrementing the semaphore count value), and a handler + * task will 'take' a semaphore each time it processes an event + * (decrementing the semaphore count value). The count value is therefore + * the difference between the number of events that have occurred and the + * number that have been processed. In this case it is desirable for the + * initial count value to be zero. + * + * 2) Resource management. + * + * In this usage scenario the count value indicates the number of resources + * available. To obtain control of a resource a task must first obtain a + * semaphore - decrementing the semaphore count value. When the count value + * reaches zero there are no free resources. When a task finishes with the + * resource it 'gives' the semaphore back - incrementing the semaphore count + * value. In this case it is desirable for the initial count value to be + * equal to the maximum count value, indicating that all resources are free. + * + * @param uxMaxCount The maximum count value that can be reached. When the + * semaphore reaches this value it can no longer be 'given'. + * + * @param uxInitialCount The count value assigned to the semaphore when it is + * created. + * + * @param pxSemaphoreBuffer Must point to a variable of type StaticSemaphore_t, + * which will then be used to hold the semaphore's data structure, removing the + * need for the memory to be allocated dynamically. + * + * @return If the counting semaphore was successfully created then a handle to + * the created counting semaphore is returned. If pxSemaphoreBuffer was NULL + * then NULL is returned. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore;
+ StaticSemaphore_t xSemaphoreBuffer;
+
+ void vATask( void * pvParameters )
+ {
+ SemaphoreHandle_t xSemaphore = NULL;
+
+    // Counting semaphore cannot be used before they have been created.  Create
+    // a counting semaphore using xSemaphoreCreateCountingStatic().  The max
+    // value to which the semaphore can count is 10, and the initial value
+    // assigned to the count will be 0.  The address of xSemaphoreBuffer is
+    // passed in and will be used to hold the semaphore structure, so no dynamic
+    // memory allocation will be used.
+    xSemaphore = xSemaphoreCreateCounting( 10, 0, &xSemaphoreBuffer );
+
+    // No memory allocation was attempted so xSemaphore cannot be NULL, so there
+    // is no need to check its value.
+ }
+ 
+ * \defgroup xSemaphoreCreateCountingStatic xSemaphoreCreateCountingStatic + * \ingroup Semaphores + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, pxSemaphoreBuffer ) xQueueCreateCountingSemaphoreStatic( ( uxMaxCount ), ( uxInitialCount ), ( pxSemaphoreBuffer ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h diff --git a/components/freertos/include/freertos/task.h b/components/freertos/include/freertos/task.h index ddf7a7589..6a781dc8c 100644 --- a/components/freertos/include/freertos/task.h +++ b/components/freertos/include/freertos/task.h @@ -177,6 +177,7 @@ typedef struct xTASK_STATUS UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */ UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ + StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */ uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ } TaskStatus_t; @@ -281,8 +282,19 @@ is used in assert() statements. */ );
* * Create a new task and add it to the list of tasks that are ready to run. - * On multicore environments, this will give no specific affinity to the task. - * Use xTaskCreatePinnedToCore to give affinity. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * http://www.freertos.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * See xTaskCreateStatic() for a version that does not use any dynamic memory + * allocation. * * xTaskCreate() can only be used to create a task that has unrestricted * access to the entire microcontroller memory map. Systems that include MPU @@ -350,8 +362,139 @@ is used in assert() statements. */ * \defgroup xTaskCreate xTaskCreate * \ingroup Tasks */ -#define xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskGenericCreate( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), ( NULL ), ( NULL ), tskNO_AFFINITY ) -#define xTaskCreatePinnedToCore( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID ) xTaskGenericCreate( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), ( NULL ), ( NULL ), xCoreID ) +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint16_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + const BaseType_t xCoreID); + +#define xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskCreatePinnedToCore( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), tskNO_AFFINITY ) +#endif + +/** + * task. h + *
+ TaskHandle_t xTaskCreateStatic( TaskFunction_t pvTaskCode,
+								 const char * const pcName,
+								 uint32_t ulStackDepth,
+								 void *pvParameters,
+								 UBaseType_t uxPriority,
+								 StackType_t *pxStackBuffer,
+								 StaticTask_t *pxTaskBuffer,
+                                 const BaseType_t xCoreID );
+ + * + * Create a new task and add it to the list of tasks that are ready to run. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * http://www.freertos.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. The maximum length of the string is defined by + * configMAX_TASK_NAME_LEN in FreeRTOSConfig.h. + * + * @param ulStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 32-bits wide and ulStackDepth is defined as 100 then 400 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task will run. + * + * @param pxStackBuffer Must point to a StackType_t array that has at least + * ulStackDepth indexes - the array will then be used as the task's stack, + * removing the need for the stack to be allocated dynamically. + * + * @param pxTaskBuffer Must point to a variable of type StaticTask_t, which will + * then be used to hold the task's data structures, removing the need for the + * memory to be allocated dynamically. + * + * @return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will + * be created and pdPASS is returned. If either pxStackBuffer or pxTaskBuffer + * are NULL then the task will not be created and + * errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned. + * + * Example usage: +
+
+    // Dimensions the buffer that the task being created will use as its stack.
+    // NOTE:  This is the number of words the stack will hold, not the number of
+    // bytes.  For example, if each stack item is 32-bits, and this is set to 100,
+    // then 400 bytes (100 * 32-bits) will be allocated.
+    #define STACK_SIZE 200
+
+    // Structure that will hold the TCB of the task being created.
+    StaticTask_t xTaskBuffer;
+
+    // Buffer that the task being created will use as its stack.  Note this is
+    // an array of StackType_t variables.  The size of StackType_t is dependent on
+    // the RTOS port.
+    StackType_t xStack[ STACK_SIZE ];
+
+    // Function that implements the task being created.
+    void vTaskCode( void * pvParameters )
+    {
+        // The parameter value is expected to be 1 as 1 is passed in the
+        // pvParameters value in the call to xTaskCreateStatic().
+        configASSERT( ( uint32_t ) pvParameters == 1UL );
+
+        for( ;; )
+        {
+            // Task code goes here.
+        }
+    }
+
+    // Function that creates a task.
+    void vOtherFunction( void )
+    {
+        TaskHandle_t xHandle = NULL;
+
+        // Create the task without using any dynamic memory allocation.
+        xHandle = xTaskCreateStatic(
+                      vTaskCode,       // Function that implements the task.
+                      "NAME",          // Text name for the task.
+                      STACK_SIZE,      // Stack size in words, not bytes.
+                      ( void * ) 1,    // Parameter passed into the task.
+                      tskIDLE_PRIORITY,// Priority at which the task is created.
+                      xStack,          // Array to use as the task's stack.
+                      &xTaskBuffer );  // Variable to hold the task's data structure.
+
+        // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
+        // been created, and xHandle will be the task's handle.  Use the handle
+        // to suspend the task.
+        vTaskSuspend( xHandle );
+    }
+   
+ * \defgroup xTaskCreateStatic xTaskCreateStatic + * \ingroup Tasks + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + TaskHandle_t xTaskCreateStaticPinnedToCore( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer, + const BaseType_t xCoreID ); + +#define xTaskCreateStatic( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxStackBuffer, pxTaskBuffer ) xTaskCreateStaticPinnedToCore( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxStackBuffer ), ( pxTaskBuffer ), tskNO_AFFINITY ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * task. h @@ -420,7 +563,9 @@ TaskHandle_t xHandle; * \defgroup xTaskCreateRestricted xTaskCreateRestricted * \ingroup Tasks */ -#define xTaskCreateRestricted( x, pxCreatedTask ) xTaskGenericCreate( ((x)->pvTaskCode), ((x)->pcName), ((x)->usStackDepth), ((x)->pvParameters), ((x)->uxPriority), (pxCreatedTask), ((x)->puxStackBuffer), ((x)->xRegions) ) +#if( portUSING_MPU_WRAPPERS == 1 ) + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif /** * task. h @@ -1968,12 +2113,6 @@ void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTIO */ BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; -/* - * Generic version of the task creation function which is in turn called by the - * xTaskCreate() and xTaskCreateRestricted() macros. - */ -BaseType_t xTaskGenericCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, StackType_t * const puxStackBuffer, const MemoryRegion_t * const xRegions, const BaseType_t xCoreID) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - /* * Get the uxTCBNumber assigned to the task referenced by the xTask parameter. */ diff --git a/components/freertos/queue.c b/components/freertos/queue.c index 248ae7c00..445a9e2e9 100644 --- a/components/freertos/queue.c +++ b/components/freertos/queue.c @@ -166,15 +166,19 @@ typedef struct QueueDefinition volatile BaseType_t xRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ volatile BaseType_t xTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ - #if ( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxQueueNumber; - uint8_t ucQueueType; + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */ #endif #if ( configUSE_QUEUE_SETS == 1 ) struct QueueDefinition *pxQueueSetContainer; #endif + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxQueueNumber; + uint8_t ucQueueType; + #endif + portMUX_TYPE mux; } xQUEUE; @@ -255,6 +259,21 @@ static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; #endif +/* + * Called after a Queue_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION; + +/* + * Mutexes are a special type of queue. When a mutex is created, first the + * queue is created, then prvInitialiseMutex() is called to configure the queue + * as a mutex. + */ +#if( configUSE_MUTEXES == 1 ) + static void prvInitialiseMutex( Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION; +#endif + /*-----------------------------------------------------------*/ /* @@ -333,134 +352,165 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } /*-----------------------------------------------------------*/ -QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) -{ -Queue_t *pxNewQueue; -size_t xQueueSizeInBytes; -QueueHandle_t xReturn = NULL; -int8_t *pcAllocatedBuffer; +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) + { + Queue_t *pxNewQueue; + + configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); + + /* The StaticQueue_t structure and the queue storage area must be + supplied. */ + configASSERT( pxStaticQueue != NULL ); + + /* A queue storage area should be provided if the item size is not 0, and + should not be provided if the item size is 0. */ + configASSERT( !( ( pucQueueStorage != NULL ) && ( uxItemSize == 0 ) ) ); + configASSERT( !( ( pucQueueStorage == NULL ) && ( uxItemSize != 0 ) ) ); + + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticQueue_t or StaticSemaphore_t equals the size of + the real queue and semaphore structures. */ + volatile size_t xSize = sizeof( StaticQueue_t ); + configASSERT( xSize == sizeof( Queue_t ) ); + } + #endif /* configASSERT_DEFINED */ + + /* The address of a statically allocated queue was passed in, use it. + The address of a statically allocated storage area was also passed in + but is already set. */ + pxNewQueue = ( Queue_t * ) pxStaticQueue; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + + if( pxNewQueue != NULL ) + { + #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Queues can be allocated wither statically or dynamically, so + note this queue was allocated statically in case the queue is + later deleted. */ + pxNewQueue->ucStaticallyAllocated = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); + } + + return pxNewQueue; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) + { + Queue_t *pxNewQueue; + size_t xQueueSizeInBytes; + uint8_t *pucQueueStorage; + + configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); + + if( uxItemSize == ( UBaseType_t ) 0 ) + { + /* There is not going to be a queue storage area. */ + xQueueSizeInBytes = ( size_t ) 0; + } + else + { + /* Allocate enough space to hold the maximum number of items that + can be in the queue at any time. */ + xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + + pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); + + if( pxNewQueue != NULL ) + { + /* Jump past the queue structure to find the location of the queue + storage area. */ + pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t ); + + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* Queues can be created either statically or dynamically, so + note this task was created dynamically in case it is later + deleted. */ + pxNewQueue->ucStaticallyAllocated = pdFALSE; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); + } + + return pxNewQueue; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ) +{ /* Remove compiler warnings about unused parameters should configUSE_TRACE_FACILITY not be set to 1. */ ( void ) ucQueueType; - configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); - if( uxItemSize == ( UBaseType_t ) 0 ) { - /* There is not going to be a queue storage area. */ - xQueueSizeInBytes = ( size_t ) 0; + /* No RAM was allocated for the queue storage area, but PC head cannot + be set to NULL because NULL is used as a key to say the queue is used as + a mutex. Therefore just set pcHead to point to the queue as a benign + value that is known to be within the memory map. */ + pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; } else { - /* The queue is one byte longer than asked for to make wrap checking - easier/faster. */ - xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + /* Set the head to the start of the queue storage area. */ + pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; } - /* Allocate the new queue structure and storage area. */ - pcAllocatedBuffer = ( int8_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); + /* Initialise the queue members as described where the queue type is + defined. */ + pxNewQueue->uxLength = uxQueueLength; + pxNewQueue->uxItemSize = uxItemSize; + ( void ) xQueueGenericReset( pxNewQueue, pdTRUE ); - if( pcAllocatedBuffer != NULL ) + #if ( configUSE_TRACE_FACILITY == 1 ) { - pxNewQueue = ( Queue_t * ) pcAllocatedBuffer; /*lint !e826 MISRA The buffer cannot be to small because it was dimensioned by sizeof( Queue_t ) + xQueueSizeInBytes. */ - - if( uxItemSize == ( UBaseType_t ) 0 ) - { - /* No RAM was allocated for the queue storage area, but PC head - cannot be set to NULL because NULL is used as a key to say the queue - is used as a mutex. Therefore just set pcHead to point to the queue - as a benign value that is known to be within the memory map. */ - pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; - } - else - { - /* Jump past the queue structure to find the location of the queue - storage area - adding the padding bytes to get a better alignment. */ - pxNewQueue->pcHead = pcAllocatedBuffer + sizeof( Queue_t ); - } - - /* Initialise the queue members as described above where the queue type - is defined. */ - pxNewQueue->uxLength = uxQueueLength; - pxNewQueue->uxItemSize = uxItemSize; - ( void ) xQueueGenericReset( pxNewQueue, pdTRUE ); - - #if ( configUSE_TRACE_FACILITY == 1 ) - { - pxNewQueue->ucQueueType = ucQueueType; - } - #endif /* configUSE_TRACE_FACILITY */ - - #if( configUSE_QUEUE_SETS == 1 ) - { - pxNewQueue->pxQueueSetContainer = NULL; - } - #endif /* configUSE_QUEUE_SETS */ - - traceQUEUE_CREATE( pxNewQueue ); - xReturn = pxNewQueue; + pxNewQueue->ucQueueType = ucQueueType; } - else + #endif /* configUSE_TRACE_FACILITY */ + + #if( configUSE_QUEUE_SETS == 1 ) { - mtCOVERAGE_TEST_MARKER(); + pxNewQueue->pxQueueSetContainer = NULL; } + #endif /* configUSE_QUEUE_SETS */ - configASSERT( xReturn ); - - return xReturn; + traceQUEUE_CREATE( pxNewQueue ); } /*-----------------------------------------------------------*/ -#if ( configUSE_MUTEXES == 1 ) +#if( configUSE_MUTEXES == 1 ) - QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) + static void prvInitialiseMutex( Queue_t *pxNewQueue ) { - Queue_t *pxNewQueue; - - /* Prevent compiler warnings about unused parameters if - configUSE_TRACE_FACILITY does not equal 1. */ - ( void ) ucQueueType; - - /* Allocate the new queue structure. */ - pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) ); if( pxNewQueue != NULL ) { - /* Information required for priority inheritance. */ + /* The queue create function will set all the queue structure members + correctly for a generic queue, but this function is creating a + mutex. Overwrite those members that need to be set differently - + in particular the information required for priority inheritance. */ pxNewQueue->pxMutexHolder = NULL; pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; - /* Queues used as a mutex no data is actually copied into or out - of the queue. */ - pxNewQueue->pcWriteTo = NULL; - pxNewQueue->u.pcReadFrom = NULL; + /* In case this is a recursive mutex. */ + pxNewQueue->u.uxRecursiveCallCount = 0; - /* Each mutex has a length of 1 (like a binary semaphore) and - an item size of 0 as nothing is actually copied into or out - of the mutex. */ - pxNewQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; - pxNewQueue->uxLength = ( UBaseType_t ) 1U; - pxNewQueue->uxItemSize = ( UBaseType_t ) 0U; - pxNewQueue->xRxLock = queueUNLOCKED; - pxNewQueue->xTxLock = queueUNLOCKED; - - #if ( configUSE_TRACE_FACILITY == 1 ) - { - pxNewQueue->ucQueueType = ucQueueType; - } - #endif - - #if ( configUSE_QUEUE_SETS == 1 ) - { - pxNewQueue->pxQueueSetContainer = NULL; - } - #endif - - /* Ensure the event queues start with the correct state. */ - vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) ); - vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) ); - - vPortCPUInitializeMutex(&pxNewQueue->mux); + vPortCPUInitializeMutex(&pxNewQueue->mux); traceCREATE_MUTEX( pxNewQueue ); @@ -471,8 +521,41 @@ int8_t *pcAllocatedBuffer; { traceCREATE_MUTEX_FAILED(); } + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) + { + Queue_t *pxNewQueue; + const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; + + pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); + prvInitialiseMutex( pxNewQueue ); + + return pxNewQueue; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) + { + Queue_t *pxNewQueue; + const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; + + /* Prevent compiler warnings about unused parameters if + configUSE_TRACE_FACILITY does not equal 1. */ + ( void ) ucQueueType; + + pxNewQueue = ( Queue_t * ) xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType ); + prvInitialiseMutex( pxNewQueue ); - configASSERT( pxNewQueue ); return pxNewQueue; } @@ -607,7 +690,35 @@ int8_t *pcAllocatedBuffer; #endif /* configUSE_RECURSIVE_MUTEXES */ /*-----------------------------------------------------------*/ -#if ( configUSE_COUNTING_SEMAPHORES == 1 ) +#if( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) + { + QueueHandle_t xHandle; + + configASSERT( uxMaxCount != 0 ); + configASSERT( uxInitialCount <= uxMaxCount ); + + xHandle = xQueueGenericCreateStatic( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticQueue, queueQUEUE_TYPE_COUNTING_SEMAPHORE ); + + if( xHandle != NULL ) + { + ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; + + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } + + return xHandle; + } + +#endif /* ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) { @@ -633,7 +744,7 @@ int8_t *pcAllocatedBuffer; return xHandle; } -#endif /* configUSE_COUNTING_SEMAPHORES */ +#endif /* ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ /*-----------------------------------------------------------*/ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) @@ -1777,7 +1888,33 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; vQueueUnregisterQueue( pxQueue ); } #endif - vPortFree( pxQueue ); + + #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + { + /* The queue can only have been allocated dynamically - free it + again. */ + vPortFree( pxQueue ); + } + #elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + { + /* The queue could have been allocated statically or dynamically, so + check before attempting to free the memory. */ + if( pxQueue->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + { + vPortFree( pxQueue ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else + { + /* The queue must have been statically allocated, so is not going to be + deleted. Avoid compiler warnings about the unused parameter. */ + ( void ) pxQueue; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ } /*-----------------------------------------------------------*/ @@ -2477,7 +2614,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; #endif /* configUSE_TIMERS */ /*-----------------------------------------------------------*/ -#if ( configUSE_QUEUE_SETS == 1 ) +#if( ( configUSE_QUEUE_SETS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) { diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index ff3a0530d..034ffc08e 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -85,7 +85,6 @@ task.h is included from an application file. */ #include "StackMacros.h" #include "portmacro.h" #include "semphr.h" -#include "sys/reent.h" /* Lint e961 and e750 are suppressed as a MISRA exception justified because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the @@ -129,6 +128,8 @@ functions but without including stdio.h here. */ } while(0) #endif + + /* Value that can be assigned to the eNotifyState member of the TCB. */ typedef enum { @@ -137,6 +138,26 @@ typedef enum eNotified } eNotifyValue; +/* Sometimes the FreeRTOSConfig.h settings only allow a task to be created using +dynamically allocated RAM, in which case when any task is deleted it is known +that both the task's stack and TCB need to be freed. Sometimes the +FreeRTOSConfig.h settings only allow a task to be created using statically +allocated RAM, in which case when any task is deleted it is known that neither +the task's stack or TCB should be freed. Sometimes the FreeRTOSConfig.h +settings allow a task to be created using either statically or dynamically +allocated RAM, in which case a member of the TCB is used to record whether the +stack and/or TCB were allocated statically or dynamically, so when a task is +deleted the RAM that was allocated dynamically is freed again and no attempt is +made to free the RAM that was allocated statically. +tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE is only true if it is possible for a +task to be created using either statically or dynamically allocated RAM. Note +that if portUSING_MPU_WRAPPERS is 1 then a protected task can be created with +a statically allocated stack and a dynamically allocated TCB. */ +#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE ( ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) || ( portUSING_MPU_WRAPPERS == 1 ) ) +#define tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 0 ) +#define tskSTATICALLY_ALLOCATED_STACK_ONLY ( ( uint8_t ) 1 ) +#define tskSTATICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 2 ) + /* * Task control block. A task control block (TCB) is allocated for each task, * and stores task state information, including a pointer to the task's context @@ -148,7 +169,6 @@ typedef struct tskTaskControlBlock #if ( portUSING_MPU_WRAPPERS == 1 ) xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */ - BaseType_t xUsingStaticallyAllocatedStack; /* Set to pdTRUE if the stack is a statically allocated array, and pdFALSE if the stack is dynamically allocated. */ #endif ListItem_t xGenericListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ @@ -208,6 +228,12 @@ typedef struct tskTaskControlBlock volatile eNotifyValue eNotifyState; #endif + /* See the comments above the definition of + tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */ + #endif + } tskTCB; /* The old tskTCB name is maintained above then typedefed to the new TCB_t name @@ -455,12 +481,6 @@ to its original value when it is released. */ /* File private functions. --------------------------------*/ -/* - * Utility to ready a TCB for a given task. Mainly just copies the parameters - * into the TCB structure. - */ -static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority, const MemoryRegion_t * const xRegions, const uint16_t usStackDepth, const BaseType_t xCoreID ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - /** * Utility task that simply returns pdTRUE if the task referenced by xTask is * currently in the Suspended state, or pdFALSE if the task referenced by xTask @@ -515,12 +535,6 @@ static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; */ static void prvAddCurrentTaskToDelayedList( const portBASE_TYPE xCoreID, const TickType_t xTimeToWake ) PRIVILEGED_FUNCTION; -/* - * Allocates memory from the heap for a TCB and associated stack. Checks the - * allocation was successful. - */ -static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer ) PRIVILEGED_FUNCTION; - /* * Fills an TaskStatus_t structure with information on each task that is * referenced from the pxList list (which may be a ready list, a delayed list, @@ -577,6 +591,26 @@ static void prvResetNextTaskUnblockTime( void ); #endif +/* + * Called after a Task_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t *pxNewTCB, + const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/* + * Called after a new task has been created and initialised to place the task + * under the control of the scheduler. + */ +static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID ) PRIVILEGED_FUNCTION; + + /*-----------------------------------------------------------*/ @@ -589,114 +623,403 @@ static void vTaskInitializeLocalMuxes( void ) /*-----------------------------------------------------------*/ -BaseType_t xTaskGenericCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, StackType_t * const puxStackBuffer, const MemoryRegion_t * const xRegions, const BaseType_t xCoreID) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ -{ -BaseType_t xReturn; -TCB_t * pxNewTCB; -StackType_t *pxTopOfStack; -BaseType_t i; - - /* Initialize mutexes, if they're not already initialized. */ - if (xMutexesInitialised == pdFALSE) vTaskInitializeLocalMuxes(); - - configASSERT( pxTaskCode ); - configASSERT( ( ( uxPriority & ( ~portPRIVILEGE_BIT ) ) < configMAX_PRIORITIES ) ); - configASSERT( (xCoreID>=0 && xCoreIDxUsingStaticallyAllocatedStack = pdTRUE; - } - else - { - /* The stack was allocated dynamically. Note this so it can be - deleted again if the task is deleted. */ - pxNewTCB->xUsingStaticallyAllocatedStack = pdFALSE; - } - #endif /* portUSING_MPU_WRAPPERS == 1 */ + configASSERT( puxStackBuffer != NULL ); + configASSERT( pxTaskBuffer != NULL ); + configASSERT( (xCoreID>=0 && xCoreIDpxStack + ( usStackDepth - ( uint16_t ) 1 ); - pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) ); /*lint !e923 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. */ + /* The memory used for the task's TCB and stack are passed into this + function - use them. */ + pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; - /* Check the alignment of the calculated top of stack is correct. */ - configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL ); + prvAddNewTaskToReadyList( pxNewTCB, pxTaskCode, xCoreID ); + } + else + { + xReturn = NULL; + } + + return xReturn; + } + +#endif /* SUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if( portUSING_MPU_WRAPPERS == 1 ) + + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) + { + TCB_t *pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer ); + + if( pxTaskDefinition->puxStackBuffer != NULL ) + { + /* Allocate space for the TCB. Where the memory comes from depends + on the implementation of the port malloc function and whether or + not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + /* Tasks can be created statically or dynamically, so note + this task had a statically allocated stack in case it is + later deleted. The TCB was allocated dynamically. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + ( uint32_t ) pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + + prvAddNewTaskToReadyList( pxNewTCB, pxTaskDefinition->pvTaskCode, tskNO_AFFINITY ); + xReturn = pdPASS; + } + } + + return xReturn; + } + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint16_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + const BaseType_t xCoreID ) + { + TCB_t *pxNewTCB; + BaseType_t xReturn; + + /* If the stack grows down then allocate the stack then the TCB so the stack + does not grow into the TCB. Likewise if the stack grows up then allocate + the TCB then the stack. */ + #if( portSTACK_GROWTH > 0 ) + { + /* Allocate space for the TCB. Where the memory comes from depends on + the implementation of the port malloc function and whether or not static + allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + /* Allocate space for the stack used by the task being created. + The base of the stack memory stored in the TCB so the task can + be deleted later if required. */ + pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + if( pxNewTCB->pxStack == NULL ) + { + /* Could not allocate the stack. Delete the allocated TCB. */ + vPortFree( pxNewTCB ); + pxNewTCB = NULL; + } + } } #else /* portSTACK_GROWTH */ { - pxTopOfStack = pxNewTCB->pxStack; + StackType_t *pxStack; - /* Check the alignment of the stack buffer is correct. */ - configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + /* Allocate space for the stack used by the task being created. */ + pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - /* If we want to use stack checking on architectures that use - a positive stack growth direction then we also need to store the - other extreme of the stack space. */ - pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( usStackDepth - 1 ); + if( pxStack != NULL ) + { + /* Allocate space for the TCB. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */ + + if( pxNewTCB != NULL ) + { + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxStack; + } + else + { + /* The stack cannot be used as the TCB was not created. Free + it again. */ + vPortFree( pxStack ); + } + } + else + { + pxNewTCB = NULL; + } } #endif /* portSTACK_GROWTH */ - /* Setup the newly allocated TCB with the initial state of the task. */ - prvInitialiseTCBVariables( pxNewTCB, pcName, uxPriority, xRegions, usStackDepth, xCoreID ); + if( pxNewTCB != NULL ) + { + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + task was created dynamically in case it is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ - /* Initialize the TCB stack to look as if the task was already running, - but had been interrupted by the scheduler. The return address is set - to the start of the task function. Once the stack has been initialised - the top of stack variable is updated. */ - #if( portUSING_MPU_WRAPPERS == 1 ) - { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); + prvAddNewTaskToReadyList( pxNewTCB, pxTaskCode, xCoreID ); + xReturn = pdPASS; } - #else /* portUSING_MPU_WRAPPERS */ + else { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; } - #endif /* portUSING_MPU_WRAPPERS */ - if( ( void * ) pxCreatedTask != NULL ) + return xReturn; + } + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t *pxNewTCB, + const MemoryRegion_t * const xRegions ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +{ +StackType_t *pxTopOfStack; +UBaseType_t x; + + #if( portUSING_MPU_WRAPPERS == 1 ) + /* Should the task be created in privileged mode? */ + BaseType_t xRunPrivileged; + if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) { - /* Pass the TCB out - in an anonymous way. The calling function/ - task can use this as a handle to delete the task later if - required.*/ - *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; + xRunPrivileged = pdTRUE; + } + else + { + xRunPrivileged = pdFALSE; + } + uxPriority &= ~portPRIVILEGE_BIT; + #endif /* portUSING_MPU_WRAPPERS == 1 */ + + /* Avoid dependency on memset() if it is not required. */ + #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + { + /* Fill the stack with a known value to assist debugging. */ + ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); + } + #endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */ + + /* Calculate the top of stack address. This depends on whether the stack + grows from high memory to low (as per the 80x86) or vice versa. + portSTACK_GROWTH is used to make the result positive or negative as required + by the port. */ + #if( portSTACK_GROWTH < 0 ) + { + pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); + pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. */ + + /* Check the alignment of the calculated top of stack is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + } + #else /* portSTACK_GROWTH */ + { + pxTopOfStack = pxNewTCB->pxStack; + + /* Check the alignment of the stack buffer is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + /* The other extreme of the stack space is required if stack checking is + performed. */ + pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); + } + #endif /* portSTACK_GROWTH */ + + /* Store the task name in the TCB. */ + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + pxNewTCB->pcTaskName[ x ] = pcName[ x ]; + + /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than + configMAX_TASK_NAME_LEN characters just in case the memory after the + string is not accessible (extremely unlikely). */ + if( pcName[ x ] == 0x00 ) + { + break; } else { mtCOVERAGE_TEST_MARKER(); } + } - /* Ensure interrupts don't access the task lists while they are being - updated. */ - taskENTER_CRITICAL(&xTaskQueueMutex); + /* Ensure the name string is terminated in the case that the string length + was greater or equal to configMAX_TASK_NAME_LEN. */ + pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; + + /* This is used as an array index so must ensure it's not too large. First + remove the privilege bit if one is present. */ + if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxNewTCB->uxPriority = uxPriority; + #if ( configUSE_MUTEXES == 1 ) + { + pxNewTCB->uxBasePriority = uxPriority; + pxNewTCB->uxMutexesHeld = 0; + } + #endif /* configUSE_MUTEXES */ + + vListInitialiseItem( &( pxNewTCB->xGenericListItem ) ); + vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); + + /* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get + back to the containing TCB from a generic item in a list. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xGenericListItem ), pxNewTCB ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + { + pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U; + } + #endif /* portCRITICAL_NESTING_IN_TCB */ + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + { + pxNewTCB->pxTaskTag = NULL; + } + #endif /* configUSE_APPLICATION_TASK_TAG */ + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxNewTCB->ulRunTimeCounter = 0UL; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth ); + } + #else + { + /* Avoid compiler warning about unreferenced parameter. */ + ( void ) xRegions; + } + #endif + + #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) + { + for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) { - uxCurrentNumberOfTasks++; + pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL; + } + } + #endif + + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + pxNewTCB->ulNotifiedValue = 0; + pxNewTCB->eNotifyState = eNotWaitingNotification; + } + #endif + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Initialise this task's Newlib reent structure. */ + _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); + } + #endif + + #if( INCLUDE_xTaskAbortDelay == 1 ) + { + pxNewTCB->ucDelayAborted = pdFALSE; + } + #endif + + /* Initialize the TCB stack to look as if the task was already running, + but had been interrupted by the scheduler. The return address is set + to the start of the task function. Once the stack has been initialised + the top of stack variable is updated. */ + #if( portUSING_MPU_WRAPPERS == 1 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #else /* portUSING_MPU_WRAPPERS */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif /* portUSING_MPU_WRAPPERS */ + + if( ( void * ) pxCreatedTask != NULL ) + { + /* Pass the handle out in an anonymous way. The handle can be used to + change the created task's priority, delete the created task, etc.*/ + *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } +} +/*-----------------------------------------------------------*/ + +static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID ) +{ + BaseType_t i; + + /* Ensure interrupts don't access the task lists while the lists are being + updated. */ + taskENTER_CRITICAL(&xTaskQueueMutex); + { + uxCurrentNumberOfTasks++; + if( pxCurrentTCB[ xPortGetCoreID() ] == NULL ) + { + /* There are no other tasks, or all the other tasks are in + the suspended state - make this the current task. */ + pxCurrentTCB[ xPortGetCoreID() ] = pxNewTCB; + if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) { /* This is the first task to be created so do the preliminary @@ -704,6 +1027,16 @@ BaseType_t i; fails, but we will report the failure. */ prvInitialiseTaskLists(); } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If the scheduler is not already running, make this task the + current task if it is the highest priority task to be created + so far. */ if( xSchedulerRunning == pdFALSE ) { /* Scheduler isn't running yet. We need to determine on which CPU to run this task. */ @@ -713,7 +1046,7 @@ BaseType_t i; if (xCoreID == tskNO_AFFINITY || xCoreID == i) { /* Schedule if nothing is scheduled yet, or overwrite a task of lower prio. */ - if ( pxCurrentTCB[i] == NULL || pxCurrentTCB[i]->uxPriority <= uxPriority ) + if ( pxCurrentTCB[i] == NULL || pxCurrentTCB[i]->uxPriority <= pxNewTCB->uxPriority ) { #if portFIRST_TASK_HOOK if ( i == 0) { @@ -731,56 +1064,45 @@ BaseType_t i; { mtCOVERAGE_TEST_MARKER(); } - - uxTaskNumber++; - - #if ( configUSE_TRACE_FACILITY == 1 ) - { - /* Add a counter into the TCB for tracing only. */ - pxNewTCB->uxTCBNumber = uxTaskNumber; - } - #endif /* configUSE_TRACE_FACILITY */ - traceTASK_CREATE( pxNewTCB ); - - prvAddTaskToReadyList( pxNewTCB ); - - xReturn = pdPASS; - portSETUP_TCB( pxNewTCB ); } - taskEXIT_CRITICAL(&xTaskQueueMutex); - } - else - { - xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; - traceTASK_CREATE_FAILED(); - } - if( xReturn == pdPASS ) - { - if( xSchedulerRunning != pdFALSE ) + uxTaskNumber++; + + #if ( configUSE_TRACE_FACILITY == 1 ) { - /* Scheduler is running. If the created task is of a higher priority than an executing task - then it should run now. - ToDo: This only works for the current core. If a task is scheduled on an other processor, - the other processor will keep running the task it's working on, and only switch to the newer - task on a timer interrupt. */ - //No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. - if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < uxPriority ) - { - taskYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + } + #endif /* configUSE_TRACE_FACILITY */ + traceTASK_CREATE( pxNewTCB ); + + prvAddTaskToReadyList( pxNewTCB ); + + portSETUP_TCB( pxNewTCB ); + } + taskEXIT_CRITICAL(&xTaskQueueMutex); + + if( xSchedulerRunning != pdFALSE ) + { + /* Scheduler is running. If the created task is of a higher priority than an executing task + then it should run now. + ToDo: This only works for the current core. If a task is scheduled on an other processor, + the other processor will keep running the task it's working on, and only switch to the newer + task on a timer interrupt. */ + //No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. + if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < pxNewTCB->uxPriority ) + { + taskYIELD_IF_USING_PREEMPTION(); } else { mtCOVERAGE_TEST_MARKER(); } } - - return xReturn; + else + { + mtCOVERAGE_TEST_MARKER(); + } } /*-----------------------------------------------------------*/ @@ -2971,120 +3293,6 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) #endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ -static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority, const MemoryRegion_t * const xRegions, const uint16_t usStackDepth, const BaseType_t xCoreID ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ -{ -UBaseType_t x; - - /* Store the task name in the TCB. */ - for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) - { - pxTCB->pcTaskName[ x ] = pcName[ x ]; - - /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than - configMAX_TASK_NAME_LEN characters just in case the memory after the - string is not accessible (extremely unlikely). */ - if( pcName[ x ] == 0x00 ) - { - break; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - /* Ensure the name string is terminated in the case that the string length - was greater or equal to configMAX_TASK_NAME_LEN. */ - pxTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; - - /* This is used as an array index so must ensure it's not too large. First - remove the privilege bit if one is present. */ - if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) - { - uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - pxTCB->uxPriority = uxPriority; - pxTCB->xCoreID = xCoreID; - #if ( configUSE_MUTEXES == 1 ) - { - pxTCB->uxBasePriority = uxPriority; - pxTCB->uxMutexesHeld = 0; - } - #endif /* configUSE_MUTEXES */ - - vListInitialiseItem( &( pxTCB->xGenericListItem ) ); - vListInitialiseItem( &( pxTCB->xEventListItem ) ); - - /* Set the pxTCB as a link back from the ListItem_t. This is so we can get - back to the containing TCB from a generic item in a list. */ - listSET_LIST_ITEM_OWNER( &( pxTCB->xGenericListItem ), pxTCB ); - - /* Event lists are always in priority order. */ - listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB ); - - #if ( portCRITICAL_NESTING_IN_TCB == 1 ) - { - pxTCB->uxCriticalNesting = ( UBaseType_t ) 0U; - } - #endif /* portCRITICAL_NESTING_IN_TCB */ - - #if ( configUSE_APPLICATION_TASK_TAG == 1 ) - { - pxTCB->pxTaskTag = NULL; - } - #endif /* configUSE_APPLICATION_TASK_TAG */ - - #if ( configGENERATE_RUN_TIME_STATS == 1 ) - { - pxTCB->ulRunTimeCounter = 0UL; - } - #endif /* configGENERATE_RUN_TIME_STATS */ - - #if ( portUSING_MPU_WRAPPERS == 1 ) - { - vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, pxTCB->pxStack, usStackDepth ); - } - #else /* portUSING_MPU_WRAPPERS */ - { - ( void ) xRegions; - ( void ) usStackDepth; - } - #endif /* portUSING_MPU_WRAPPERS */ - - #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) - { - for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) - { - pxTCB->pvThreadLocalStoragePointers[ x ] = NULL; - #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) - pxTCB->pvThreadLocalStoragePointersDelCallback[ x ] = (TlsDeleteCallbackFunction_t)NULL; - #endif - } - } - #endif - - - #if ( configUSE_TASK_NOTIFICATIONS == 1 ) - { - pxTCB->ulNotifiedValue = 0; - pxTCB->eNotifyState = eNotWaitingNotification; - } - #endif - - #if ( configUSE_NEWLIB_REENTRANT == 1 ) - { - /* Initialise this task's Newlib reent structure. */ - _REENT_INIT_PTR( ( &( pxTCB->xNewLib_reent ) ) ); - } - #endif /* configUSE_NEWLIB_REENTRANT */ -} -/*-----------------------------------------------------------*/ #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) @@ -3280,81 +3488,6 @@ static void prvAddCurrentTaskToDelayedList( const BaseType_t xCoreID, const Tick } /*-----------------------------------------------------------*/ -static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer ) -{ -TCB_t *pxNewTCB; - - /* If the stack grows down then allocate the stack then the TCB so the stack - does not grow into the TCB. Likewise if the stack grows up then allocate - the TCB then the stack. */ - #if( portSTACK_GROWTH > 0 ) - { - /* Allocate space for the TCB. Where the memory comes from depends on - the implementation of the port malloc function. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); - - if( pxNewTCB != NULL ) - { - /* Allocate space for the stack used by the task being created. - The base of the stack memory stored in the TCB so the task can - be deleted later if required. */ - pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - - if( pxNewTCB->pxStack == NULL ) - { - /* Could not allocate the stack. Delete the allocated TCB. */ - vPortFree( pxNewTCB ); - pxNewTCB = NULL; - } - } - } - #else /* portSTACK_GROWTH */ - { - StackType_t *pxStack; - - /* Allocate space for the stack used by the task being created. */ - pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - - if( pxStack != NULL ) - { - /* Allocate space for the TCB. Where the memory comes from depends - on the implementation of the port malloc function. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); - - if( pxNewTCB != NULL ) - { - /* Store the stack location in the TCB. */ - pxNewTCB->pxStack = pxStack; - } - else - { - /* The stack cannot be used as the TCB was not created. Free it - again. */ - vPortFree( pxStack ); - } - } - else - { - pxNewTCB = NULL; - } - } - #endif /* portSTACK_GROWTH */ - - if( pxNewTCB != NULL ) - { - /* Avoid dependency on memset() if it is not required. */ - #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) - { - /* Just to help debugging. */ - ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) usStackDepth * sizeof( StackType_t ) ); - } - #endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */ - } - - return pxNewTCB; -} -/*-----------------------------------------------------------*/ - #if ( configUSE_TRACE_FACILITY == 1 ) static UBaseType_t prvListTaskWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) @@ -3509,22 +3642,40 @@ TCB_t *pxNewTCB; } #endif /* configUSE_NEWLIB_REENTRANT */ - #if( portUSING_MPU_WRAPPERS == 1 ) + #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) { - /* Only free the stack if it was allocated dynamically in the first - place. */ - if( pxTCB->xUsingStaticallyAllocatedStack == pdFALSE ) + /* The task can only have been allocated dynamically - free both + the stack and TCB. */ + vPortFreeAligned( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + #elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 ) + { + /* The task could have been allocated statically or dynamically, so + check what was statically allocated before trying to free the + memory. */ + if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ) { + /* Both the stack and TCB were allocated dynamically, so both + must be freed. */ vPortFreeAligned( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) + { + /* Only the stack was statically allocated, so the TCB is the + only memory that must be freed. */ + vPortFree( pxTCB ); + } + else + { + /* Neither the stack nor the TCB were allocated dynamically, so + nothing needs to be freed. */ + configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ) + mtCOVERAGE_TEST_MARKER(); } } - #else - { - vPortFreeAligned( pxTCB->pxStack ); - } - #endif - - vPortFree( pxTCB ); + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ } #endif /* INCLUDE_vTaskDelete */ @@ -3932,7 +4083,9 @@ scheduler will re-enable the interrupts instead. */ function is executing. */ uxArraySize = uxCurrentNumberOfTasks; - /* Allocate an array index for each task. */ + /* Allocate an array index for each task. NOTE! if + configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + equate to NULL. */ pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); if( pxTaskStatusArray != NULL ) @@ -3972,7 +4125,8 @@ scheduler will re-enable the interrupts instead. */ pcWriteBuffer += strlen( pcWriteBuffer ); } - /* Free the array again. */ + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + is 0 then vPortFree() will be #defined to nothing. */ vPortFree( pxTaskStatusArray ); } else @@ -4030,7 +4184,9 @@ scheduler will re-enable the interrupts instead. */ function is executing. */ uxArraySize = uxCurrentNumberOfTasks; - /* Allocate an array index for each task. */ + /* Allocate an array index for each task. NOTE! If + configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + equate to NULL. */ pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); if( pxTaskStatusArray != NULL ) @@ -4096,7 +4252,8 @@ scheduler will re-enable the interrupts instead. */ mtCOVERAGE_TEST_MARKER(); } - /* Free the array again. */ + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + is 0 then vPortFree() will be #defined to nothing. */ vPortFree( pxTaskStatusArray ); } else From 9c0cd10d4885a03d4cb97a323c88ca7dd96555eb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 4 Oct 2016 12:37:10 +1100 Subject: [PATCH 002/110] Build system tests: Add test cases for out-of-tree builds (currently failing) See github #38 --- make/test_build_system.sh | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/make/test_build_system.sh b/make/test_build_system.sh index 5be1504e3..b6aaa9e58 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -62,7 +62,7 @@ function run_tests() print_status "Partition CSV file rebuilds partitions" take_build_snapshot touch ${IDF_PATH}/components/partition_table/partitions_singleapp.csv - make partition_table + make partition_table || failure "Failed to build partition table" assert_rebuilt partitions_singleapp.bin assert_not_rebuilt app-template.bin app-template.elf ${BOOTLOADER_BINS} @@ -70,18 +70,31 @@ function run_tests() take_build_snapshot # verify no build files are refreshed by a partial make ALL_BUILD_FILES=$(find ${BUILD} -type f | sed "s@${BUILD}/@@") - make + make || failure "Partial build failed" assert_not_rebuilt ${ALL_BUILD_FILES} print_status "Cleaning should remove all files from build" - make clean + make clean || failure "Failed to make clean" ALL_BUILD_FILES=$(find ${BUILD} -type f) if [ -n "${ALL_BUILD_FILES}" ]; then failure "Files weren't cleaned: ${ALL_BUILD_FILES}" fi + print_status "Moving BUILD_DIR_BASE out of tree should still build OK" + rm -rf --preserve-root ${BUILD}/* + OUTOFTREE_BUILD=${TESTDIR}/alt_build + make BUILD_DIR_BASE=${OUTOFTREE_BUILD} || failure "Failed to build with BUILD_DIR_BASE overriden" + NEW_BUILD_FILES=$(find ${OUTOFREE_BUILD} -type f) + if [ -z "${NEW_BUILD_FILES}" ]; then + failure "No files found in new build directory!" + fi + DEFAULT_BUILD_FILES=$(find ${BUILD} -mindepth 1) + if [ -n "${DEFAULT_BUILD_FILES}" ]; then + failure "Some files were incorrectly put into the default build directory: ${DEFAULT_BUILD_FILES}" + fi + print_status "Can still clean build if all text files are CRLFs" - make clean + make clean || failure "Unexpected failure to make clean" find . -exec unix2dos {} \; # CRLFify template dir # make a copy of esp-idf and CRLFify it CRLF_ESPIDF=${TESTDIR}/esp-idf-crlf @@ -89,12 +102,11 @@ function run_tests() cp -rv ${IDF_PATH}/* ${CRLF_ESPIDF} # don't CRLFify executable files, as Linux will fail to execute them find ${CRLF_ESPIDF} -type f ! -perm 755 -exec unix2dos {} \; - make IDF_PATH=${CRLF_ESPIDF} + make IDF_PATH=${CRLF_ESPIDF} || failure "Failed to build with CRLFs in source" # do the same checks we do for the clean build assert_built ${APP_BINS} ${BOOTLOADER_BINS} partitions_singleapp.bin [ -f ${BUILD}/partition*.bin ] || failure "A partition table should have been built in CRLF mode" - # NOTE: If adding new tests, add them above this CRLF test... print_status "All tests completed" if [ -n "${FAILURES}" ]; then From 66882347e86e142ba0c72c8c3e6fe1a946a25a78 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 4 Oct 2016 15:03:48 +1100 Subject: [PATCH 003/110] build system: Fix out-of-tree building via BUILD_DIR_BASE Closes #38 --- components/bootloader/Makefile.projbuild | 16 +++++----- components/esp32/component.mk | 4 ++- make/common.mk | 2 +- make/project.mk | 3 +- make/project_config.mk | 38 ++++++++++-------------- 5 files changed, 31 insertions(+), 32 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index d45cf144e..7c5cde3b8 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -14,17 +14,18 @@ BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH) BOOTLOADER_BUILD_DIR=$(BUILD_DIR_BASE)/bootloader BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin +# Custom recursive make for bootloader sub-project +BOOTLOADER_MAKE=$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ + MAKEFLAGS= V=$(V) \ + BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ + .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) $(BOOTLOADER_BIN): $(COMPONENT_PATH)/src/sdkconfig - $(Q) PROJECT_PATH= \ - BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ - $(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src MAKEFLAGS= V=$(V) TARGET_BIN_LAYOUT="$(BOOTLOADER_TARGET_BIN_LAYOUT)" $(BOOTLOADER_BIN) + $(Q) $(BOOTLOADER_MAKE) $@ bootloader-clean: - $(Q) PROJECT_PATH= \ - BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ - $(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src app-clean MAKEFLAGS= V=$(V) + $(Q) $(BOOTLOADER_MAKE) app-clean clean: bootloader-clean @@ -43,7 +44,8 @@ $(COMPONENT_PATH)/src/sdkconfig: $(PROJECT_PATH)/sdkconfig # bootloader-flash calls flash in the bootloader dummy project bootloader-flash: $(BOOTLOADER_BIN) - $(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src flash MAKEFLAGS= V=$(V) + $(BOOTLOADER_MAKE) flash + else CFLAGS += -D BOOTLOADER_BUILD=1 -I $(IDF_PATH)/components/esp32/include diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 6eac77afd..85040d77a 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -6,7 +6,7 @@ # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, # please read the esp-idf build system document if you need to do this. # --include $(PROJECT_PATH)/build/include/config/auto.conf +-include $(BUILD_DIR_BASE)/include/config/auto.conf COMPONENT_SRCDIRS := . hwcrypto @@ -44,6 +44,8 @@ $(COMPONENT_LIBRARY): $(ALL_LIB_FILES) # saves us from having to add the target to a Makefile.projbuild $(COMPONENT_LIBRARY): esp32_out.ld +# .. is BUILD_DIR_BASE here, as component makefiles +# are evaluated with CWD=component build dir esp32_out.ld: $(COMPONENT_PATH)/ld/esp32.ld ../include/sdkconfig.h $(CC) -I ../include -C -P -x c -E $< -o $@ diff --git a/make/common.mk b/make/common.mk index a515584a9..b2917d5c9 100644 --- a/make/common.mk +++ b/make/common.mk @@ -6,7 +6,7 @@ # # (Note that we only rebuild auto.conf automatically for some targets, # see project_config.mk for details.) --include $(PROJECT_PATH)/build/include/config/auto.conf +-include $(BUILD_DIR_BASE)/include/config/auto.conf #Handling of V=1/VERBOSE=1 flag # diff --git a/make/project.mk b/make/project.mk index 35dccaf24..74943334a 100644 --- a/make/project.mk +++ b/make/project.mk @@ -50,6 +50,7 @@ endif #The directory where we put all objects/libraries/binaries. The project Makefile can #configure this if needed. BUILD_DIR_BASE ?= $(PROJECT_PATH)/build +export BUILD_DIR_BASE #Component directories. These directories are searched for components. #The project Makefile can override these component dirs, or define extra component directories. @@ -105,7 +106,7 @@ COMPONENT_INCLUDES := $(abspath $(foreach comp,$(COMPONENT_PATHS_BUILDABLE),$(ad $(call GetVariable,$(comp),COMPONENT_ADD_INCLUDEDIRS)))) #Also add project include path, for sdk includes -COMPONENT_INCLUDES += $(PROJECT_PATH)/build/include/ +COMPONENT_INCLUDES += $(BUILD_DIR_BASE)/include/ export COMPONENT_INCLUDES #COMPONENT_LDFLAGS has a list of all flags that are needed to link the components together. It's collected diff --git a/make/project_config.mk b/make/project_config.mk index d2909bb30..92937e8bf 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -13,13 +13,15 @@ $(KCONFIG_TOOL_DIR)/mconf $(KCONFIG_TOOL_DIR)/conf: CC=$(HOSTCC) LD=$(HOSTLD) \ $(MAKE) -C $(KCONFIG_TOOL_DIR) -menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) - $(summary) MENUCONFIG - $(Q) KCONFIG_AUTOHEADER=$(PROJECT_PATH)/build/include/sdkconfig.h \ +# use a wrapper environment for where we run Kconfig tools +KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(BUILD_DIR_BASE)/include/sdkconfig.h \ KCONFIG_CONFIG=$(PROJECT_PATH)/sdkconfig \ COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" \ - COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" \ - $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig + COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" + +menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) + $(summary) MENUCONFIG + $(Q) $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig ifeq ("$(wildcard $(PROJECT_PATH)/sdkconfig)","") #No sdkconfig found. Need to run menuconfig to make this if we need it. @@ -28,17 +30,13 @@ endif defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) $(summary) DEFCONFIG - $(Q) mkdir -p $(PROJECT_PATH)/build/include/config - $(Q) KCONFIG_AUTOHEADER=$(PROJECT_PATH)/build/include/sdkconfig.h \ - KCONFIG_CONFIG=$(PROJECT_PATH)/sdkconfig \ - COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" \ - COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" \ - $(KCONFIG_TOOL_DIR)/conf --olddefconfig $(IDF_PATH)/Kconfig + $(Q) mkdir -p $(BUILD_DIR_BASE)/include/config + $(Q) $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --olddefconfig $(IDF_PATH)/Kconfig # Work out of whether we have to build the Kconfig makefile # (auto.conf), or if we're in a situation where we don't need it NON_CONFIG_TARGETS := clean %-clean get_variable help menuconfig defconfig -AUTO_CONF_REGEN_TARGET := $(PROJECT_PATH)/build/include/config/auto.conf +AUTO_CONF_REGEN_TARGET := $(BUILD_DIR_BASE)/include/config/auto.conf # disable AUTO_CONF_REGEN_TARGET if all targets are non-config targets # (and not building default target) @@ -46,19 +44,15 @@ ifneq ("$(MAKECMDGOALS)","") ifeq ($(filter $(NON_CONFIG_TARGETS), $(MAKECMDGOALS)),$(MAKECMDGOALS)) AUTO_CONF_REGEN_TARGET := # dummy target -$(PROJECT_PATH)/build/include/config/auto.conf: +$(BUILD_DIR_BASE)/include/config/auto.conf: endif endif -$(AUTO_CONF_REGEN_TARGET) $(PROJECT_PATH)/build/include/sdkconfig.h: $(PROJECT_PATH)/sdkconfig $(KCONFIG_TOOL_DIR)/conf $(COMPONENT_KCONFIGS) $(COMPONENT_KCONFIGS_PROJBUILD) +$(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h: $(PROJECT_PATH)/sdkconfig $(KCONFIG_TOOL_DIR)/conf $(COMPONENT_KCONFIGS) $(COMPONENT_KCONFIGS_PROJBUILD) $(summary) GENCONFIG - $(Q) mkdir -p $(PROJECT_PATH)/build/include/config - $(Q) cd build; KCONFIG_AUTOHEADER="$(PROJECT_PATH)/build/include/sdkconfig.h" \ - KCONFIG_CONFIG=$(PROJECT_PATH)/sdkconfig \ - COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" \ - COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" \ - $(KCONFIG_TOOL_DIR)/conf --silentoldconfig $(IDF_PATH)/Kconfig - $(Q) touch $(AUTO_CONF_REGEN_TARGET) $(PROJECT_PATH)/build/include/sdkconfig.h + $(Q) mkdir -p $(BUILD_DIR_BASE)/include/config + $(Q) cd $(BUILD_DIR_BASE); $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --silentoldconfig $(IDF_PATH)/Kconfig + $(Q) touch $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h # touch to ensure both output files are newer - as 'conf' can also update sdkconfig (a dependency). Without this, # sometimes you can get an infinite make loop on Windows where sdkconfig always gets regenerated newer # than the target(!) @@ -68,4 +62,4 @@ clean: config-clean config-clean: $(summary RM CONFIG) $(MAKE) -C $(KCONFIG_TOOL_DIR) clean - $(Q) rm -rf $(PROJECT_PATH)/build/include/config $(PROJECT_PATH)/build/include/sdkconfig.h + $(Q) rm -rf $(BUILD_DIR_BASE)/include/config $(BUILD_DIR_BASE)/include/sdkconfig.h From 477d71e589a18af96472199caf918097c22ed0bc Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 4 Oct 2016 15:38:20 +1100 Subject: [PATCH 004/110] config system: Fix configuration when BUILD_DIR_BASE out-of-tree Ref #38. Also no longer generates bootloader sdkconfig in source tree. --- .gitignore | 3 --- components/bootloader/Makefile.projbuild | 20 ++++++++++++-------- make/project_config.mk | 13 ++++++++----- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 85027773b..5ec57a167 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,3 @@ GPATH examples/*/sdkconfig examples/*/sdkconfig.old examples/*/build - -# Bootloader files -components/bootloader/src/sdkconfig.old \ No newline at end of file diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 7c5cde3b8..559769957 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -13,19 +13,21 @@ ifeq ("$(IS_BOOTLOADER_BUILD)","") BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH) BOOTLOADER_BUILD_DIR=$(BUILD_DIR_BASE)/bootloader BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin +BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ - MAKEFLAGS= V=$(V) \ + MAKEFLAGS= V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) -$(BOOTLOADER_BIN): $(COMPONENT_PATH)/src/sdkconfig +$(BOOTLOADER_BIN): | $(BOOTLOADER_BUILD_DIR)/sdkconfig $(Q) $(BOOTLOADER_MAKE) $@ bootloader-clean: - $(Q) $(BOOTLOADER_MAKE) app-clean + $(Q) $(BOOTLOADER_MAKE) app-clean config-clean + $(Q) rm -f $(BOOTLOADER_SDKCONFIG) $(BOOTLOADER_SDKCONFIG).old clean: bootloader-clean @@ -37,15 +39,17 @@ all_binaries: $(BOOTLOADER_BIN) ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN) -# synchronise the project level config to the component's -# config -$(COMPONENT_PATH)/src/sdkconfig: $(PROJECT_PATH)/sdkconfig - $(Q) cp $< $@ - # bootloader-flash calls flash in the bootloader dummy project bootloader-flash: $(BOOTLOADER_BIN) $(BOOTLOADER_MAKE) flash +# synchronise the project level config to the bootloader's +# config +$(BOOTLOADER_SDKCONFIG): $(PROJECT_PATH)/sdkconfig | $(BOOTLOADER_BUILD_DIR) + $(Q) cp $< $@ + +$(BOOTLOADER_BUILD_DIR): + $(Q) mkdir -p $@ else CFLAGS += -D BOOTLOADER_BUILD=1 -I $(IDF_PATH)/components/esp32/include diff --git a/make/project_config.mk b/make/project_config.mk index 92937e8bf..40f51ff8f 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -7,6 +7,10 @@ COMPONENT_KCONFIGS_PROJBUILD := $(foreach component,$(COMPONENT_PATHS),$(wildcar #For doing make menuconfig etc KCONFIG_TOOL_DIR=$(IDF_PATH)/tools/kconfig +# set SDKCONFIG to the project's sdkconfig, +# unless it's overriden (happens for bootloader) +SDKCONFIG ?= $(PROJECT_PATH)/sdkconfig + # clear MAKEFLAGS as the menuconfig makefile uses implicit compile rules $(KCONFIG_TOOL_DIR)/mconf $(KCONFIG_TOOL_DIR)/conf: MAKEFLAGS="" \ @@ -15,17 +19,16 @@ $(KCONFIG_TOOL_DIR)/mconf $(KCONFIG_TOOL_DIR)/conf: # use a wrapper environment for where we run Kconfig tools KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(BUILD_DIR_BASE)/include/sdkconfig.h \ - KCONFIG_CONFIG=$(PROJECT_PATH)/sdkconfig \ - COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" \ + COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" KCONFIG_CONFIG=$(SDKCONFIG) \ COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) $(summary) MENUCONFIG $(Q) $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig -ifeq ("$(wildcard $(PROJECT_PATH)/sdkconfig)","") +ifeq ("$(wildcard $(SDKCONFIG))","") #No sdkconfig found. Need to run menuconfig to make this if we need it. -$(PROJECT_PATH)/sdkconfig: menuconfig +$(SDKCONFIG): menuconfig endif defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) @@ -48,7 +51,7 @@ $(BUILD_DIR_BASE)/include/config/auto.conf: endif endif -$(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h: $(PROJECT_PATH)/sdkconfig $(KCONFIG_TOOL_DIR)/conf $(COMPONENT_KCONFIGS) $(COMPONENT_KCONFIGS_PROJBUILD) +$(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h: $(SDKCONFIG) $(KCONFIG_TOOL_DIR)/conf $(COMPONENT_KCONFIGS) $(COMPONENT_KCONFIGS_PROJBUILD) $(summary) GENCONFIG $(Q) mkdir -p $(BUILD_DIR_BASE)/include/config $(Q) cd $(BUILD_DIR_BASE); $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --silentoldconfig $(IDF_PATH)/Kconfig From eccf54b93969396d991012e453fa187cb8684c15 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 4 Oct 2016 15:54:27 +1100 Subject: [PATCH 005/110] build system tests: Add some more notes about test internals --- make/test_build_system.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/make/test_build_system.sh b/make/test_build_system.sh index b6aaa9e58..401c8376c 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -2,7 +2,7 @@ # # Test the build system for basic consistency # -# Just a bash script that tests some likely make failure scenarios in a row +# A bash script that tests some likely make failure scenarios in a row # Creates its own test build directory under TMP and cleans it up when done. # # Environment variables: @@ -11,6 +11,17 @@ # ESP_IDF_TEMPLATE_GIT - Can override git clone source for template app. Otherwise github. # NOCLEANUP - Set to '1' if you want the script to leave its temporary directory when done, for post-mortem. # +# +# Internals: +# * The tests run in sequence & the system keeps track of all failures to print at the end. +# * BUILD directory is set to default BUILD_DIR_BASE +# * The "print_status" function both prints a status line to the log and keeps track of which test is running. +# * Calling the "failure" function prints a failure message to the log and also adds to the list of failures to print at the end. +# * The function "assert_built" tests for a file relative to the BUILD directory. +# * The function "take_build_snapshot" can be paired with the functions "assert_rebuilt" and "assert_not_rebuilt" to compare file timestamps and verify if they were rebuilt or not since the snapshot was taken. +# +# To add a new test case, add it to the end of the run_tests function. Note that not all test cases do comprehensive cleanup +# (although very invasive ones like appending CRLFs to all files take a copy of the esp-idf tree.) # Set up some variables # From f720e82d403a8a09cf94a176067aea71263c307f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 6 Oct 2016 18:05:51 +1100 Subject: [PATCH 006/110] build system: Allow BUILD_DIR_BASE to be a relative directory (see github #38) --- components/bootloader/Makefile.projbuild | 2 +- components/esp32/component.mk | 2 +- make/project.mk | 2 +- make/project_config.mk | 2 +- make/test_build_system.sh | 10 +++++++++- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 559769957..02135e1c6 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -11,7 +11,7 @@ ifeq ("$(IS_BOOTLOADER_BUILD)","") BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH) -BOOTLOADER_BUILD_DIR=$(BUILD_DIR_BASE)/bootloader +BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 85040d77a..b3d95f1b1 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -6,7 +6,7 @@ # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, # please read the esp-idf build system document if you need to do this. # --include $(BUILD_DIR_BASE)/include/config/auto.conf +-include include/config/auto.conf COMPONENT_SRCDIRS := . hwcrypto diff --git a/make/project.mk b/make/project.mk index 74943334a..c4bf68db6 100644 --- a/make/project.mk +++ b/make/project.mk @@ -106,7 +106,7 @@ COMPONENT_INCLUDES := $(abspath $(foreach comp,$(COMPONENT_PATHS_BUILDABLE),$(ad $(call GetVariable,$(comp),COMPONENT_ADD_INCLUDEDIRS)))) #Also add project include path, for sdk includes -COMPONENT_INCLUDES += $(BUILD_DIR_BASE)/include/ +COMPONENT_INCLUDES += $(abspath $(BUILD_DIR_BASE)/include/) export COMPONENT_INCLUDES #COMPONENT_LDFLAGS has a list of all flags that are needed to link the components together. It's collected diff --git a/make/project_config.mk b/make/project_config.mk index 40f51ff8f..00452dda4 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -18,7 +18,7 @@ $(KCONFIG_TOOL_DIR)/mconf $(KCONFIG_TOOL_DIR)/conf: $(MAKE) -C $(KCONFIG_TOOL_DIR) # use a wrapper environment for where we run Kconfig tools -KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(BUILD_DIR_BASE)/include/sdkconfig.h \ +KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(abspath $(BUILD_DIR_BASE)/include/sdkconfig.h) \ COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" KCONFIG_CONFIG=$(SDKCONFIG) \ COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" diff --git a/make/test_build_system.sh b/make/test_build_system.sh index 401c8376c..b7004eece 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -91,7 +91,7 @@ function run_tests() failure "Files weren't cleaned: ${ALL_BUILD_FILES}" fi - print_status "Moving BUILD_DIR_BASE out of tree should still build OK" + print_status "Moving BUILD_DIR_BASE out of tree" rm -rf --preserve-root ${BUILD}/* OUTOFTREE_BUILD=${TESTDIR}/alt_build make BUILD_DIR_BASE=${OUTOFTREE_BUILD} || failure "Failed to build with BUILD_DIR_BASE overriden" @@ -104,6 +104,14 @@ function run_tests() failure "Some files were incorrectly put into the default build directory: ${DEFAULT_BUILD_FILES}" fi + print_status "BUILD_DIR_BASE inside default build directory" + rm -rf --preserve-root ${BUILD}/* + make BUILD_DIR_BASE=build/subdirectory || failure "Failed to build with BUILD_DIR_BASE as subdir" + NEW_BUILD_FILES=$(find ${BUILD}/subdirectory -type f) + if [ -z "${NEW_BUILD_FILES}" ]; then + failure "No files found in new build directory!" + fi + print_status "Can still clean build if all text files are CRLFs" make clean || failure "Unexpected failure to make clean" find . -exec unix2dos {} \; # CRLFify template dir From 6a890e6c49d1b4b00080fe9544f730b4b517331e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 6 Oct 2016 18:06:52 +1100 Subject: [PATCH 007/110] build system tests: Untabify shell script --- make/test_build_system.sh | 260 +++++++++++++++++++------------------- 1 file changed, 130 insertions(+), 130 deletions(-) diff --git a/make/test_build_system.sh b/make/test_build_system.sh index b7004eece..f72158838 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -32,123 +32,123 @@ export V=1 function run_tests() { - FAILURES= - STATUS="Starting" - print_status "Checking prerequisites" - [ -z ${IDF_PATH} ] && echo "IDF_PATH is not set. Need path to esp-idf installation." && exit 2 + FAILURES= + STATUS="Starting" + print_status "Checking prerequisites" + [ -z ${IDF_PATH} ] && echo "IDF_PATH is not set. Need path to esp-idf installation." && exit 2 - print_status "Cloning template from ${ESP_IDF_TEMPLATE_GIT}..." - git clone ${ESP_IDF_TEMPLATE_GIT} template - cd template - git checkout ${CI_BUILD_REF_NAME} || echo "Using esp-idf-template default branch..." + print_status "Cloning template from ${ESP_IDF_TEMPLATE_GIT}..." + git clone ${ESP_IDF_TEMPLATE_GIT} template + cd template + git checkout ${CI_BUILD_REF_NAME} || echo "Using esp-idf-template default branch..." - print_status "Updating template config..." - make defconfig || exit $? + print_status "Updating template config..." + make defconfig || exit $? - BOOTLOADER_BINS="bootloader/bootloader.elf bootloader/bootloader.bin" - APP_BINS="app-template.elf app-template.bin" + BOOTLOADER_BINS="bootloader/bootloader.elf bootloader/bootloader.bin" + APP_BINS="app-template.elf app-template.bin" - print_status "Initial clean build" - # if make fails here, everything fails - make || exit $? - # check all the expected build artifacts from the clean build - assert_built ${APP_BINS} ${BOOTLOADER_BINS} partitions_singleapp.bin - [ -f ${BUILD}/partition*.bin ] || failure "A partition table should have been built" + print_status "Initial clean build" + # if make fails here, everything fails + make || exit $? + # check all the expected build artifacts from the clean build + assert_built ${APP_BINS} ${BOOTLOADER_BINS} partitions_singleapp.bin + [ -f ${BUILD}/partition*.bin ] || failure "A partition table should have been built" - print_status "Updating component source file rebuilds component" - # touch a file & do a build - take_build_snapshot - touch ${IDF_PATH}/components/esp32/syscalls.c - make || failure "Failed to partial build" - assert_rebuilt ${APP_BINS} esp32/libesp32.a esp32/syscalls.o - assert_not_rebuilt lwip/liblwip.a freertos/libfreertos.a ${BOOTLOADER_BINS} partitions_singleapp.bin + print_status "Updating component source file rebuilds component" + # touch a file & do a build + take_build_snapshot + touch ${IDF_PATH}/components/esp32/syscalls.c + make || failure "Failed to partial build" + assert_rebuilt ${APP_BINS} esp32/libesp32.a esp32/syscalls.o + assert_not_rebuilt lwip/liblwip.a freertos/libfreertos.a ${BOOTLOADER_BINS} partitions_singleapp.bin - print_status "Bootloader source file rebuilds bootloader" - take_build_snapshot - touch ${IDF_PATH}/components/bootloader/src/main/bootloader_start.c - make bootloader || failure "Failed to partial build bootloader" - assert_rebuilt ${BOOTLOADER_BINS} bootloader/main/bootloader_start.o - assert_not_rebuilt ${APP_BINS} partitions_singleapp.bin + print_status "Bootloader source file rebuilds bootloader" + take_build_snapshot + touch ${IDF_PATH}/components/bootloader/src/main/bootloader_start.c + make bootloader || failure "Failed to partial build bootloader" + assert_rebuilt ${BOOTLOADER_BINS} bootloader/main/bootloader_start.o + assert_not_rebuilt ${APP_BINS} partitions_singleapp.bin - print_status "Partition CSV file rebuilds partitions" - take_build_snapshot - touch ${IDF_PATH}/components/partition_table/partitions_singleapp.csv - make partition_table || failure "Failed to build partition table" - assert_rebuilt partitions_singleapp.bin - assert_not_rebuilt app-template.bin app-template.elf ${BOOTLOADER_BINS} + print_status "Partition CSV file rebuilds partitions" + take_build_snapshot + touch ${IDF_PATH}/components/partition_table/partitions_singleapp.csv + make partition_table || failure "Failed to build partition table" + assert_rebuilt partitions_singleapp.bin + assert_not_rebuilt app-template.bin app-template.elf ${BOOTLOADER_BINS} - print_status "Partial build doesn't compile anything by default" - take_build_snapshot - # verify no build files are refreshed by a partial make - ALL_BUILD_FILES=$(find ${BUILD} -type f | sed "s@${BUILD}/@@") - make || failure "Partial build failed" - assert_not_rebuilt ${ALL_BUILD_FILES} + print_status "Partial build doesn't compile anything by default" + take_build_snapshot + # verify no build files are refreshed by a partial make + ALL_BUILD_FILES=$(find ${BUILD} -type f | sed "s@${BUILD}/@@") + make || failure "Partial build failed" + assert_not_rebuilt ${ALL_BUILD_FILES} - print_status "Cleaning should remove all files from build" - make clean || failure "Failed to make clean" - ALL_BUILD_FILES=$(find ${BUILD} -type f) - if [ -n "${ALL_BUILD_FILES}" ]; then - failure "Files weren't cleaned: ${ALL_BUILD_FILES}" - fi + print_status "Cleaning should remove all files from build" + make clean || failure "Failed to make clean" + ALL_BUILD_FILES=$(find ${BUILD} -type f) + if [ -n "${ALL_BUILD_FILES}" ]; then + failure "Files weren't cleaned: ${ALL_BUILD_FILES}" + fi - print_status "Moving BUILD_DIR_BASE out of tree" - rm -rf --preserve-root ${BUILD}/* - OUTOFTREE_BUILD=${TESTDIR}/alt_build - make BUILD_DIR_BASE=${OUTOFTREE_BUILD} || failure "Failed to build with BUILD_DIR_BASE overriden" - NEW_BUILD_FILES=$(find ${OUTOFREE_BUILD} -type f) - if [ -z "${NEW_BUILD_FILES}" ]; then - failure "No files found in new build directory!" - fi - DEFAULT_BUILD_FILES=$(find ${BUILD} -mindepth 1) - if [ -n "${DEFAULT_BUILD_FILES}" ]; then - failure "Some files were incorrectly put into the default build directory: ${DEFAULT_BUILD_FILES}" - fi + print_status "Moving BUILD_DIR_BASE out of tree" + rm -rf --preserve-root ${BUILD}/* + OUTOFTREE_BUILD=${TESTDIR}/alt_build + make BUILD_DIR_BASE=${OUTOFTREE_BUILD} || failure "Failed to build with BUILD_DIR_BASE overriden" + NEW_BUILD_FILES=$(find ${OUTOFREE_BUILD} -type f) + if [ -z "${NEW_BUILD_FILES}" ]; then + failure "No files found in new build directory!" + fi + DEFAULT_BUILD_FILES=$(find ${BUILD} -mindepth 1) + if [ -n "${DEFAULT_BUILD_FILES}" ]; then + failure "Some files were incorrectly put into the default build directory: ${DEFAULT_BUILD_FILES}" + fi - print_status "BUILD_DIR_BASE inside default build directory" - rm -rf --preserve-root ${BUILD}/* - make BUILD_DIR_BASE=build/subdirectory || failure "Failed to build with BUILD_DIR_BASE as subdir" - NEW_BUILD_FILES=$(find ${BUILD}/subdirectory -type f) - if [ -z "${NEW_BUILD_FILES}" ]; then - failure "No files found in new build directory!" - fi + print_status "BUILD_DIR_BASE inside default build directory" + rm -rf --preserve-root ${BUILD}/* + make BUILD_DIR_BASE=build/subdirectory || failure "Failed to build with BUILD_DIR_BASE as subdir" + NEW_BUILD_FILES=$(find ${BUILD}/subdirectory -type f) + if [ -z "${NEW_BUILD_FILES}" ]; then + failure "No files found in new build directory!" + fi - print_status "Can still clean build if all text files are CRLFs" - make clean || failure "Unexpected failure to make clean" - find . -exec unix2dos {} \; # CRLFify template dir - # make a copy of esp-idf and CRLFify it - CRLF_ESPIDF=${TESTDIR}/esp-idf-crlf - mkdir -p ${CRLF_ESPIDF} - cp -rv ${IDF_PATH}/* ${CRLF_ESPIDF} - # don't CRLFify executable files, as Linux will fail to execute them - find ${CRLF_ESPIDF} -type f ! -perm 755 -exec unix2dos {} \; - make IDF_PATH=${CRLF_ESPIDF} || failure "Failed to build with CRLFs in source" - # do the same checks we do for the clean build - assert_built ${APP_BINS} ${BOOTLOADER_BINS} partitions_singleapp.bin - [ -f ${BUILD}/partition*.bin ] || failure "A partition table should have been built in CRLF mode" + print_status "Can still clean build if all text files are CRLFs" + make clean || failure "Unexpected failure to make clean" + find . -exec unix2dos {} \; # CRLFify template dir + # make a copy of esp-idf and CRLFify it + CRLF_ESPIDF=${TESTDIR}/esp-idf-crlf + mkdir -p ${CRLF_ESPIDF} + cp -rv ${IDF_PATH}/* ${CRLF_ESPIDF} + # don't CRLFify executable files, as Linux will fail to execute them + find ${CRLF_ESPIDF} -type f ! -perm 755 -exec unix2dos {} \; + make IDF_PATH=${CRLF_ESPIDF} || failure "Failed to build with CRLFs in source" + # do the same checks we do for the clean build + assert_built ${APP_BINS} ${BOOTLOADER_BINS} partitions_singleapp.bin + [ -f ${BUILD}/partition*.bin ] || failure "A partition table should have been built in CRLF mode" - print_status "All tests completed" - if [ -n "${FAILURES}" ]; then - echo "Some failures were detected:" - echo -e "${FAILURES}" - exit 1 - else - echo "Build tests passed." - fi + print_status "All tests completed" + if [ -n "${FAILURES}" ]; then + echo "Some failures were detected:" + echo -e "${FAILURES}" + exit 1 + else + echo "Build tests passed." + fi } function print_status() { - echo "******** $1" - STATUS="$1" + echo "******** $1" + STATUS="$1" } function failure() { - echo "!!!!!!!!!!!!!!!!!!!" - echo "FAILURE: $1" - echo "!!!!!!!!!!!!!!!!!!!" - FAILURES="${FAILURES}${STATUS} :: $1\n" + echo "!!!!!!!!!!!!!!!!!!!" + echo "FAILURE: $1" + echo "!!!!!!!!!!!!!!!!!!!" + FAILURES="${FAILURES}${STATUS} :: $1\n" } TESTDIR=${TMP}/build_system_tests_$$ @@ -164,62 +164,62 @@ BUILD=${TESTDIR}/template/build # copy all the build output to a snapshot directory function take_build_snapshot() { - rm -rf ${SNAPSHOT} - cp -ap ${TESTDIR}/template/build ${SNAPSHOT} + rm -rf ${SNAPSHOT} + cp -ap ${TESTDIR}/template/build ${SNAPSHOT} } # verify that all the arguments are present in the build output directory function assert_built() { - until [ -z "$1" ]; do - if [ ! -f "${BUILD}/$1" ]; then - failure "File $1 should be in the build output directory" - fi - shift - done + until [ -z "$1" ]; do + if [ ! -f "${BUILD}/$1" ]; then + failure "File $1 should be in the build output directory" + fi + shift + done } # Test if a file has been rebuilt. function file_was_rebuilt() { - # can't use [ a -ot b ] here as -ot only gives second resolution - # but stat -c %y seems to be microsecond at least for tmpfs, ext4.. - if [ "$(stat -c %y ${SNAPSHOT}/$1)" != "$(stat -c %y ${BUILD}/$1)" ]; then - return 0 - else - return 1 - fi + # can't use [ a -ot b ] here as -ot only gives second resolution + # but stat -c %y seems to be microsecond at least for tmpfs, ext4.. + if [ "$(stat -c %y ${SNAPSHOT}/$1)" != "$(stat -c %y ${BUILD}/$1)" ]; then + return 0 + else + return 1 + fi } # verify all the arguments passed in were rebuilt relative to the snapshot function assert_rebuilt() { - until [ -z "$1" ]; do - assert_built "$1" - if [ ! -f "${SNAPSHOT}/$1" ]; then - failure "File $1 should have been original build snapshot" - fi - if ! file_was_rebuilt "$1"; then - failure "File $1 should have been rebuilt" - fi - shift - done + until [ -z "$1" ]; do + assert_built "$1" + if [ ! -f "${SNAPSHOT}/$1" ]; then + failure "File $1 should have been original build snapshot" + fi + if ! file_was_rebuilt "$1"; then + failure "File $1 should have been rebuilt" + fi + shift + done } # verify all the arguments are in the build directory & snapshot, # but were not rebuilt function assert_not_rebuilt() { - until [ -z "$1" ]; do - assert_built "$1" - if [ ! -f "${SNAPSHOT}/$1" ]; then - failure "File $1 should be in snapshot build directory" - fi - if file_was_rebuilt "$1"; then - failure "File $1 should not have been rebuilt" - fi - shift - done + until [ -z "$1" ]; do + assert_built "$1" + if [ ! -f "${SNAPSHOT}/$1" ]; then + failure "File $1 should be in snapshot build directory" + fi + if file_was_rebuilt "$1"; then + failure "File $1 should not have been rebuilt" + fi + shift + done } cd ${TESTDIR} From 305bc9fd9c532286a23a281231c23e4673566a23 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 6 Oct 2016 18:29:34 +1100 Subject: [PATCH 008/110] build system: Run parallel builds without warnings Ref github #38 --- components/bootloader/Makefile.projbuild | 4 ++-- make/project.mk | 3 ++- make/project_config.mk | 5 ++--- make/test_build_system.sh | 17 +++++++++++++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 02135e1c6..91be3a6d6 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -16,8 +16,8 @@ BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig # Custom recursive make for bootloader sub-project -BOOTLOADER_MAKE=$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ - MAKEFLAGS= V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ +BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ + V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) diff --git a/make/project.mk b/make/project.mk index c4bf68db6..579788986 100644 --- a/make/project.mk +++ b/make/project.mk @@ -37,6 +37,7 @@ help: @echo "'make partition_table', etc, etc." # disable built-in make rules, makes debugging saner +MAKEFLAGS_OLD := $(MAKEFLAGS) MAKEFLAGS +=-rR # Figure out PROJECT_PATH if not set @@ -231,7 +232,7 @@ define GenerateComponentPhonyTarget # $(2) - target to generate (build, clean) .PHONY: $(notdir $(1))-$(2) $(notdir $(1))-$(2): | $(BUILD_DIR_BASE)/$(notdir $(1)) - @+$(MAKE) -C $(BUILD_DIR_BASE)/$(notdir $(1)) -f $(1)/component.mk COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(notdir $(1)) $(2) + $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(notdir $(1)) -f $(1)/component.mk COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(notdir $(1)) $(2) endef define GenerateComponentTargets diff --git a/make/project_config.mk b/make/project_config.mk index 00452dda4..7ca83ce5a 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -11,10 +11,9 @@ KCONFIG_TOOL_DIR=$(IDF_PATH)/tools/kconfig # unless it's overriden (happens for bootloader) SDKCONFIG ?= $(PROJECT_PATH)/sdkconfig -# clear MAKEFLAGS as the menuconfig makefile uses implicit compile rules +# reset MAKEFLAGS as the menuconfig makefile uses implicit compile rules $(KCONFIG_TOOL_DIR)/mconf $(KCONFIG_TOOL_DIR)/conf: - MAKEFLAGS="" \ - CC=$(HOSTCC) LD=$(HOSTLD) \ + MAKEFLAGS=$(ORIGINAL_MAKEFLAGS) CC=$(HOSTCC) LD=$(HOSTLD) \ $(MAKE) -C $(KCONFIG_TOOL_DIR) # use a wrapper environment for where we run Kconfig tools diff --git a/make/test_build_system.sh b/make/test_build_system.sh index f72158838..c6e54d922 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -92,7 +92,7 @@ function run_tests() fi print_status "Moving BUILD_DIR_BASE out of tree" - rm -rf --preserve-root ${BUILD}/* + clean_build_dir OUTOFTREE_BUILD=${TESTDIR}/alt_build make BUILD_DIR_BASE=${OUTOFTREE_BUILD} || failure "Failed to build with BUILD_DIR_BASE overriden" NEW_BUILD_FILES=$(find ${OUTOFREE_BUILD} -type f) @@ -105,13 +105,20 @@ function run_tests() fi print_status "BUILD_DIR_BASE inside default build directory" - rm -rf --preserve-root ${BUILD}/* + clean_build_dir make BUILD_DIR_BASE=build/subdirectory || failure "Failed to build with BUILD_DIR_BASE as subdir" NEW_BUILD_FILES=$(find ${BUILD}/subdirectory -type f) if [ -z "${NEW_BUILD_FILES}" ]; then failure "No files found in new build directory!" fi + print_status "Parallel builds should work OK" + clean_build_dir + (make -j5 2>&1 | tee ${TESTDIR}/parallel_build.log) || failure "Failed to build in parallel" + if grep -q "warning: jobserver unavailable" ${TESTDIR}/parallel_build.log; then + failure "Parallel build prints 'warning: jobserver unavailable' errors" + fi + print_status "Can still clean build if all text files are CRLFs" make clean || failure "Unexpected failure to make clean" find . -exec unix2dos {} \; # CRLFify template dir @@ -222,5 +229,11 @@ function assert_not_rebuilt() done } +# do a "clean" that doesn't depend on 'make clean' +function clean_build_dir() +{ + rm -rf --preserve-root ${BUILD}/* +} + cd ${TESTDIR} run_tests From 924fea7cc0f78832bfdd711d285c8d756912e336 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 11 Oct 2016 00:05:15 -0600 Subject: [PATCH 009/110] freertos: fix setting xCoreID for new task --- components/freertos/tasks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 63a659b5d..7fb40e771 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -629,7 +629,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode configASSERT( puxStackBuffer != NULL ); configASSERT( pxTaskBuffer != NULL ); - configASSERT( (xCoreID>=0 && xCoreID=0 && xCoreIDpxStack = ( StackType_t * ) puxStackBuffer; + pxNewTCB->xCoreID = xCoreID; #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) { From 89097d5f113bc670de96380bb686fe709c8e068d Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 12 Oct 2016 17:46:15 +0800 Subject: [PATCH 010/110] Add xCoreID arg to prvInitialiseNewTask code; initialize pvThreadLocalStoragePointersDelCallback array to NULL --- components/freertos/tasks.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 7fb40e771..b93841842 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -601,7 +601,7 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, TCB_t *pxNewTCB, - const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const MemoryRegion_t * const xRegions, const BaseType_t xCoreID) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /* * Called after a new task has been created and initialised to place the task @@ -637,7 +637,6 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode function - use them. */ pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; - pxNewTCB->xCoreID = xCoreID; #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) { @@ -647,7 +646,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode } #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ - prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL ); + prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL, xCoreID ); prvAddNewTaskToReadyList( pxNewTCB, pxTaskCode, xCoreID ); } else @@ -693,7 +692,8 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode pxTaskDefinition->pvParameters, pxTaskDefinition->uxPriority, pxCreatedTask, pxNewTCB, - pxTaskDefinition->xRegions ); + pxTaskDefinition->xRegions, + tskNO_AFFINITY ); prvAddNewTaskToReadyList( pxNewTCB, pxTaskDefinition->pvTaskCode, tskNO_AFFINITY ); xReturn = pdPASS; @@ -785,7 +785,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode } #endif /* configSUPPORT_STATIC_ALLOCATION */ - prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); + prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL, tskNO_AFFINITY ); prvAddNewTaskToReadyList( pxNewTCB, pxTaskCode, xCoreID ); xReturn = pdPASS; } @@ -807,7 +807,7 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, TCB_t *pxNewTCB, - const MemoryRegion_t * const xRegions ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const MemoryRegion_t * const xRegions, const BaseType_t xCoreID ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { StackType_t *pxTopOfStack; UBaseType_t x; @@ -893,6 +893,7 @@ UBaseType_t x; } pxNewTCB->uxPriority = uxPriority; + pxNewTCB->xCoreID = xCoreID; #if ( configUSE_MUTEXES == 1 ) { pxNewTCB->uxBasePriority = uxPriority; @@ -945,6 +946,9 @@ UBaseType_t x; for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) { pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL; + #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS == 1) + pxNewTCB->pvThreadLocalStoragePointersDelCallback[ x ] = NULL; + #endif } } #endif From 03bd5b6d220c86da983736dbffac4d52e66ed502 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 12 Oct 2016 18:17:58 +0800 Subject: [PATCH 011/110] Fix offset of coreid in tasktcb --- components/freertos/xtensa_vectors.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index f0d874a59..0e83cb085 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -96,7 +96,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Define for workaround: pin no-cpu-affinity tasks to a cpu when fpu is used. Please change this when the tcb structure is changed */ -#define TASKTCB_XCOREID_OFFSET (0x3C+configMAX_TASK_NAME_LEN+3)&~3 +#define TASKTCB_XCOREID_OFFSET (0x38+configMAX_TASK_NAME_LEN+3)&~3 .extern pxCurrentTCB /* Enable stack backtrace across exception/interrupt - see below */ From 20b508e62ed1606c5e515b30902bc3ea106074bd Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 13 Oct 2016 11:01:30 +1100 Subject: [PATCH 012/110] build system tests: Verify bootloader doesn't build any files outside build/bootloader & config See TW7505. Looks like bug was fixed via prior refactors, but adding the test ensures it will stay fixed. --- make/test_build_system.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/make/test_build_system.sh b/make/test_build_system.sh index 5be1504e3..86be1f82e 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -80,6 +80,13 @@ function run_tests() failure "Files weren't cleaned: ${ALL_BUILD_FILES}" fi + print_status "Bootloader build shouldn't leave build output anywhere else" + rm -rf --preserve-root ${BUILD} + make bootloader + # find wizardry: find any file not named sdkconfig.h that + # isn't in the "bootloader" or "config" directories + find ${BUILD} -type d \( -name bootloader -o -name config \) -prune , -type f ! -name sdkconfig.h || failure "Bootloader built files outside the bootloader or config directories" + print_status "Can still clean build if all text files are CRLFs" make clean find . -exec unix2dos {} \; # CRLFify template dir From 72712c00a760bd43620d3619c4f19157f33e2783 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 14 Oct 2016 21:24:58 +0800 Subject: [PATCH 013/110] freertos: forward task affinity argument to prvInitializeNewTask --- components/freertos/tasks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index b93841842..0e2bf6e8e 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -785,7 +785,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode } #endif /* configSUPPORT_STATIC_ALLOCATION */ - prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL, tskNO_AFFINITY ); + prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL, xCoreID ); prvAddNewTaskToReadyList( pxNewTCB, pxTaskCode, xCoreID ); xReturn = pdPASS; } From 4370794d07847c3253fe305c0cd4a30882b9c777 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 14 Oct 2016 21:52:43 +0800 Subject: [PATCH 014/110] wifi libraries: update to match new FreeRTOS header files --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index a1e5f8b95..9c71f406d 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit a1e5f8b953c7934677ba7a6ed0a6dd2da0e6bd0f +Subproject commit 9c71f406de7be4774e1049b247c11a1983e6dfaa From 0aab006bb799242bad310cd9745678fd099577ff Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 17 Oct 2016 12:18:17 +0800 Subject: [PATCH 015/110] Add Trax-support to esp-idf --- components/esp32/Kconfig | 14 +++- components/esp32/cpu_start.c | 14 ++++ components/esp32/heap_alloc_caps.c | 4 + components/freertos/tasks.c | 5 +- components/xtensa-debug-module/component.mk | 5 ++ components/xtensa-debug-module/eri.c | 19 +++++ components/xtensa-debug-module/include/eri.h | 31 ++++++++ components/xtensa-debug-module/include/trax.h | 62 +++++++++++++++ .../include/xtensa-debug-module.h | 75 +++++++++++++++++++ components/xtensa-debug-module/trax.c | 64 ++++++++++++++++ 10 files changed, 289 insertions(+), 4 deletions(-) create mode 100755 components/xtensa-debug-module/component.mk create mode 100644 components/xtensa-debug-module/eri.c create mode 100644 components/xtensa-debug-module/include/eri.h create mode 100644 components/xtensa-debug-module/include/trax.h create mode 100644 components/xtensa-debug-module/include/xtensa-debug-module.h create mode 100644 components/xtensa-debug-module/trax.c diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 535df23eb..4aacd937a 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -63,10 +63,22 @@ config MEMMAP_TRACEMEM of memory that can't be used for general purposes anymore. Disable this if you do not know what this is. +config MEMMAP_TRACEMEM_TWOBANKS + bool "Reserve memory for tracing both pro as well as app cpu execution" + default "n" + depends on MEMMAP_TRACEMEM && MEMMAP_SMP + help + The ESP32 contains a feature which allows you to trace the execution path the processor + has taken through the program. This is stored in a chunk of 32K (16K for single-processor) + of memory that can't be used for general purposes anymore. Disable this if you do not know + what this is. + + # Memory to reverse for trace, used in linker script config TRACEMEM_RESERVE_DRAM hex - default 0x8000 if MEMMAP_TRACEMEM + default 0x8000 if MEMMAP_TRACEMEM && MEMMAP_TRACEMEM_TWOBANKS + default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS default 0x0 config MEMMAP_SPISRAM diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 7b2ccdc60..5c7a411c8 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -43,6 +43,8 @@ #include "esp_ipc.h" #include "esp_log.h" +#include "trax.h" + void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))); void start_cpu0_default(void) IRAM_ATTR; #if !CONFIG_FREERTOS_UNICORE @@ -131,6 +133,15 @@ void IRAM_ATTR call_start_cpu1() void start_cpu0_default(void) { +//Enable trace memory and immediately start trace. +#if CONFIG_MEMMAP_TRACEMEM +#if CONFIG_MEMMAP_TRACEMEM_TWOBANKS + trax_enable(TRAX_ENA_PRO_APP); +#else + trax_enable(TRAX_ENA_PRO); +#endif + trax_start_trace(TRAX_DOWNCOUNT_WORDS); +#endif esp_set_cpu_freq(); // set CPU frequency configured in menuconfig uart_div_modify(0, (APB_CLK_FREQ << 4) / 115200); ets_setup_syscalls(); @@ -147,6 +158,9 @@ void start_cpu0_default(void) #if !CONFIG_FREERTOS_UNICORE void start_cpu1_default(void) { +#if CONFIG_MEMMAP_TRACEMEM_TWOBANKS + trax_start_trace(TRAX_DOWNCOUNT_WORDS); +#endif // Wait for FreeRTOS initialization to finish on PRO CPU while (port_xSchedulerRunning[0] == 0) { ; diff --git a/components/esp32/heap_alloc_caps.c b/components/esp32/heap_alloc_caps.c index 46b1125cc..04e2dc8c8 100644 --- a/components/esp32/heap_alloc_caps.c +++ b/components/esp32/heap_alloc_caps.c @@ -186,7 +186,11 @@ void heap_alloc_caps_init() { #endif #if CONFIG_MEMMAP_TRACEMEM +#if CONFIG_MEMMAP_TRACEMEM_TWOBANKS disable_mem_region((void*)0x3fff8000, (void*)0x40000000); //knock out trace mem region +#else + disable_mem_region((void*)0x3fff8000, (void*)0x3fffc000); //knock out trace mem region +#endif #endif #if 0 diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 3cde3bf13..95e7811dd 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -3755,9 +3755,8 @@ In fact, nothing below this line has/is. /* Gotcha (which seems to be deliberate in FreeRTOS, according to http://www.freertos.org/FreeRTOS_Support_Forum_Archive/December_2012/freertos_PIC32_Bug_-_vTaskEnterCritical_6400806.html -) is that calling vTaskEnterCritical followed by vTaskExitCritical will leave the interrupts DISABLED! Re-enabling the -scheduler will re-enable the interrupts instead. */ - +) is that calling vTaskEnterCritical followed by vTaskExitCritical will leave the interrupts DISABLED when the scheduler +is not running. Re-enabling the scheduler will re-enable the interrupts instead. */ #if ( portCRITICAL_NESTING_IN_TCB == 1 ) diff --git a/components/xtensa-debug-module/component.mk b/components/xtensa-debug-module/component.mk new file mode 100755 index 000000000..a57ae0b12 --- /dev/null +++ b/components/xtensa-debug-module/component.mk @@ -0,0 +1,5 @@ +# +# Component Makefile +# + +include $(IDF_PATH)/make/component_common.mk diff --git a/components/xtensa-debug-module/eri.c b/components/xtensa-debug-module/eri.c new file mode 100644 index 000000000..e2c7e41eb --- /dev/null +++ b/components/xtensa-debug-module/eri.c @@ -0,0 +1,19 @@ +#include +#include "eri.h" + +uint32_t eri_read(int addr) { + uint32_t ret; + asm( + "RER %0,%1" + :"=r"(ret):"r"(addr) + ); + return ret; +} + +void eri_write(int addr, uint32_t data) { + asm volatile ( + "WER %0,%1" + ::"r"(data),"r"(addr) + ); +} + diff --git a/components/xtensa-debug-module/include/eri.h b/components/xtensa-debug-module/include/eri.h new file mode 100644 index 000000000..33e4dd091 --- /dev/null +++ b/components/xtensa-debug-module/include/eri.h @@ -0,0 +1,31 @@ +#ifndef ERI_H +#define ERI_H + +#include + +/* + The ERI is a bus internal to each Xtensa core. It connects, amongst others, to the debug interface, where it + allows reading/writing the same registers as available over JTAG. +*/ + + +/** + * @brief Perform an ERI read + * @param addr : ERI register to read from + * + * @return Value read + */ +uint32_t eri_read(int addr); + + +/** + * @brief Perform an ERI write + * @param addr : ERI register to write to + * @param data : Value to write + * + * @return Value read + */ +void eri_write(int addr, uint32_t data); + + +#endif \ No newline at end of file diff --git a/components/xtensa-debug-module/include/trax.h b/components/xtensa-debug-module/include/trax.h new file mode 100644 index 000000000..c1b3608eb --- /dev/null +++ b/components/xtensa-debug-module/include/trax.h @@ -0,0 +1,62 @@ +#include "soc/dport_reg.h" +#include "sdkconfig.h" +#include "esp_err.h" +#include "eri.h" +#include "xtensa-debug-module.h" + + +typedef enum { + TRAX_DOWNCOUNT_WORDS, + TRAX_DOWNCOUNT_INSTRUCTIONS +} trax_downcount_unit_t; + +typedef enum { + TRAX_ENA_NONE = 0, + TRAX_ENA_PRO, + TRAX_ENA_APP, + TRAX_ENA_PRO_APP, + TRAX_ENA_PRO_APP_SWAP +} trax_ena_select_t; + + +/** + * @brief Enable the trax memory blocks to be used as Trax memory. + * + * @param pro_cpu_enable : true if Trax needs to be enabled for the pro CPU + * @param app_cpu_enable : true if Trax needs to be enabled for the pro CPU + * @param swap_regions : Normally, the pro CPU writes to Trax mem block 0 while + * the app cpu writes to block 1. Setting this to true + * inverts this. + * + * @return esp_err_t. Fails with ESP_ERR_NO_MEM if Trax enable is requested for 2 CPUs + * but memmap only has room for 1, or if Trax memmap is disabled + * entirely. + */ +int trax_enable(trax_ena_select_t ena); + +/** + * @brief Start a Trax trace on the current CPU + * + * @param units_until_stop : Set the units of the delay that gets passed to + * trax_trigger_traceend_after_delay. One of TRAX_DOWNCOUNT_WORDS + * or TRAX_DOWNCOUNT_INSTRUCTIONS. + * + * @return esp_err_t. Fails with ESP_ERR_NO_MEM if Trax is disabled. + */ +int trax_start_trace(trax_downcount_unit_t units_until_stop); + + +/** + * @brief Trigger a Trax trace stop after the indicated delay. If this is called + * before and the previous delay hasn't ended yet, this will overwrite + * that delay with the new value. The delay will always start at the time + * the function is called. + * + * @param delay : The delay to stop the trace in, in the unit indicated to + * trax_start_trace. Note: the trace memory has 4K words available. + * + * @return esp_err_t + */ +int trax_trigger_traceend_after_delay(int delay); + + diff --git a/components/xtensa-debug-module/include/xtensa-debug-module.h b/components/xtensa-debug-module/include/xtensa-debug-module.h new file mode 100644 index 000000000..61b218253 --- /dev/null +++ b/components/xtensa-debug-module/include/xtensa-debug-module.h @@ -0,0 +1,75 @@ +#ifndef XTENSA_DEBUG_MODULE_H +#define XTENSA_DEBUG_MODULE_H + +/* +ERI registers / OCD offsets and field definitions +*/ + +#define ERI_DEBUG_OFFSET 0x100000 + +#define ERI_TRAX_OFFSET (ERI_DEBUG_OFFSET+0) +#define ERI_PERFMON_OFFSET (ERI_DEBUG_OFFSET+0x1000) +#define ERI_OCDREG_OFFSET (ERI_DEBUG_OFFSET+0x2000) +#define ERI_MISCDBG_OFFSET (ERI_DEBUG_OFFSET+0x3000) +#define ERI_CORESIGHT_OFFSET (ERI_DEBUG_OFFSET+0x3F00) + +#define ERI_TRAX_TRAXID (ERI_TRAX_OFFSET+0x00) +#define ERI_TRAX_TRAXCTRL (ERI_TRAX_OFFSET+0x04) +#define ERI_TRAX_TRAXSTAT (ERI_TRAX_OFFSET+0x08) +#define ERI_TRAX_TRAXDATA (ERI_TRAX_OFFSET+0x0C) +#define ERI_TRAX_TRAXADDR (ERI_TRAX_OFFSET+0x10) +#define ERI_TRAX_TRIGGERPC (ERI_TRAX_OFFSET+0x14) +#define ERI_TRAX_PCMATCHCTRL (ERI_TRAX_OFFSET+0x18) +#define ERI_TRAX_DELAYCNT (ERI_TRAX_OFFSET+0x1C) +#define ERI_TRAX_MEMADDRSTART (ERI_TRAX_OFFSET+0x20) +#define ERI_TRAX_MEMADDREND (ERI_TRAX_OFFSET+0x24) + +#define TRAXCTRL_TREN (1<<0) //Trace enable. Tracing starts on 0->1 +#define TRAXCTRL_TRSTP (1<<1) //Trace Stop. Make 1 to stop trace. +#define TRAXCTRL_PCMEN (1<<2) //PC match enable +#define TRAXCTRL_PTIEN (1<<4) //Processor-trigger enable +#define TRAXCTRL_CTIEN (1<<5) //Cross-trigger enable +#define TRAXCTRL_TMEN (1<<7) //Tracemem Enable. Always set. +#define TRAXCTRL_CNTU (1<<9) //Post-stop-trigger countdown units; selects when DelayCount-- happens. + //0 - every 32-bit word written to tracemem, 1 - every cpu instruction +#define TRAXCTRL_TSEN (1<<11) //Undocumented/deprecated? +#define TRAXCTRL_SMPER_SHIFT 12 //Send sync every 2^(9-smper) messages. 7=reserved, 0=no sync msg +#define TRAXCTRL_SMPER_MASK 0x7 //Synchronization message period +#define TRAXCTRL_PTOWT (1<<16) //Processor Trigger Out (OCD halt) enabled when stop triggered +#define TRAXCTRL_PTOWS (1<<17) //Processor Trigger Out (OCD halt) enabled when trace stop completes +#define TRAXCTRL_CTOWT (1<<20) //Cross-trigger Out enabled when stop triggered +#define TRAXCTRL_CTOWS (1<<21) //Cross-trigger Out enabled when trace stop completes +#define TRAXCTRL_ITCTO (1<<22) //Integration mode: cross-trigger output +#define TRAXCTRL_ITCTIA (1<<23) //Integration mode: cross-trigger ack +#define TRAXCTRL_ITATV (1<<24) //replaces ATID when in integration mode: ATVALID output +#define TRAXCTRL_ATID_MASK 0x7F //ARB source ID +#define TRAXCTRL_ATID_SHIFT 24 +#define TRAXCTRL_ATEN (1<<31) //ATB interface enable + +#define TRAXSTAT_TRACT (1<<0) //Trace active flag. +#define TRAXSTAT_TRIG (1<<1) //Trace stop trigger. Clears on TREN 1->0 +#define TRAXSTAT_PCMTG (1<<2) //Stop trigger caused by PC match. Clears on TREN 1->0 +#define TRAXSTAT_PJTR (1<<3) //JTAG transaction result. 1=err in preceding jtag transaction. +#define TRAXSTAT_PTITG (1<<4) //Stop trigger caused by Processor Trigger Input. Clears on TREN 1->0 +#define TRAXSTAT_CTITG (1<<5) //Stop trigger caused by Cross-Trigger Input. Clears on TREN 1->0 +#define TRAXSTAT_MEMSZ_SHIFT 8 //Traceram size inducator. Usable trace ram is 2^MEMSZ bytes. +#define TRAXSTAT_MEMSZ_MASK 0x1F +#define TRAXSTAT_PTO (1<<16) //Processor Trigger Output: current value +#define TRAXSTAT_CTO (1<<17) //Cross-Trigger Output: current value +#define TRAXSTAT_ITCTOA (1<<22) //Cross-Trigger Out Ack: current value +#define TRAXSTAT_ITCTI (1<<23) //Cross-Trigger Input: current value +#define TRAXSTAT_ITATR (1<<24) //ATREADY Input: current value + +#define TRAXADDR_TADDR_SHIFT 0 //Trax memory address, in 32-bit words. +#define TRAXADDR_TADDR_MASK 0x1FFFFF //Actually is only as big as the trace buffer size max addr. +#define TRAXADDR_TWRAP_SHIFT 21 //Amount of times TADDR has overflown +#define TRAXADDR_TWRAP_MASK 0x3FF +#define TRAXADDR_TWSAT (1<<31) //1 if TWRAP has overflown, clear by disabling tren. + +#define PCMATCHCTRL_PCML_SHIFT 0 //Amount of lower bits to ignore in pc trigger register +#define PCMATCHCTRL_PCML_MASK 0x1F +#define PCMATCHCTRL_PCMS (1<<31) //PC Match Sense, 0 - match when procs PC is in-range, 1 - match when + //out-of-range + + +#endif \ No newline at end of file diff --git a/components/xtensa-debug-module/trax.c b/components/xtensa-debug-module/trax.c new file mode 100644 index 000000000..18c260a97 --- /dev/null +++ b/components/xtensa-debug-module/trax.c @@ -0,0 +1,64 @@ +#include +#include "soc/dport_reg.h" +#include "sdkconfig.h" +#include "esp_err.h" +#include "eri.h" +#include "xtensa-debug-module.h" +#include "trax.h" +#include "esp_log.h" + +#define TRACEMEM_MUX_PROBLK0_APPBLK1 0 +#define TRACEMEM_MUX_BLK0_ONLY 1 +#define TRACEMEM_MUX_BLK1_ONLY 2 +#define TRACEMEM_MUX_PROBLK1_APPBLK0 3 + +static const char* TAG = "log"; + +int trax_enable(trax_ena_select_t which) { +#if !CONFIG_MEMMAP_TRACEMEM + return ESP_ERR_NO_MEM; +#endif +#if !CONFIG_MEMMAP_TRACEMEM_TWOBANKS + if (which == TRAX_ENA_PRO_APP || which == TRAX_ENA_PRO_APP_SWAP) return ESP_ERR_NO_MEM; +#endif + if (which == TRAX_ENA_PRO_APP || which == TRAX_ENA_PRO_APP_SWAP) { + WRITE_PERI_REG(DPORT_TRACEMEM_MUX_MODE_REG, (which == TRAX_ENA_PRO_APP_SWAP)?TRACEMEM_MUX_PROBLK1_APPBLK0:TRACEMEM_MUX_PROBLK0_APPBLK1); + } else { + WRITE_PERI_REG(DPORT_TRACEMEM_MUX_MODE_REG, TRACEMEM_MUX_BLK0_ONLY); + } + WRITE_PERI_REG(DPORT_PRO_TRACEMEM_ENA_REG, (which == TRAX_ENA_PRO_APP || which == TRAX_ENA_PRO_APP_SWAP || which == TRAX_ENA_PRO)); + WRITE_PERI_REG(DPORT_APP_TRACEMEM_ENA_REG, (which == TRAX_ENA_PRO_APP || which == TRAX_ENA_PRO_APP_SWAP || which == TRAX_ENA_APP)); + return ESP_OK; +} + + +int trax_start_trace(trax_downcount_unit_t units_until_stop) { +#if !CONFIG_MEMMAP_TRACEMEM + return ESP_ERR_NO_MEM; +#endif + uint32_t v; + if (eri_read(ERI_TRAX_TRAXSTAT)&TRAXSTAT_TRACT) { + ESP_LOGI(TAG, "Stopping active trace first."); + //Trace is active. Stop trace. + eri_write(ERI_TRAX_DELAYCNT, 0); + eri_write(ERI_TRAX_TRAXCTRL, eri_read(ERI_TRAX_TRAXCTRL)|TRAXCTRL_TRSTP); + //ToDo: This will probably trigger a trace done interrupt. ToDo: Fix, but how? -JD + eri_write(ERI_TRAX_TRAXCTRL, 0); + } + eri_write(ERI_TRAX_PCMATCHCTRL, 31); //do not stop at any pc match + v=TRAXCTRL_TREN | TRAXCTRL_TMEN | TRAXCTRL_PTOWS | (1< Date: Mon, 17 Oct 2016 12:38:17 +0800 Subject: [PATCH 016/110] build system: add menuconfig choice for optimization level, reorganize C*FLAGS This change adds two options (Debug/Release) for optimization level. Debug enables -O0, release enables -Os and adds -DNDEBUG (which removes all assert() statements). Debugging symbols are kept in both cases, although we may add an option to strip output file if necessary. Also we used to define all common compiler flags in CPPFLAGS, and then appended them to CFLAGS/CXXFLAGS. It makes it impossible to add preprocessor macros to CPPFLAGS at component level (one has to use CFLAGS/CXXFLAGS instead). Some third party libraries are not compatible with this approach. Changed to the more common way of using these variables. --- Kconfig | 11 +++++++++++ components/log/log.c | 2 +- make/component_common.mk | 10 +++++----- make/project.mk | 35 ++++++++++++++++++++++++++++------- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/Kconfig b/Kconfig index 11ea099de..936181a9c 100644 --- a/Kconfig +++ b/Kconfig @@ -23,6 +23,17 @@ endmenu source "$COMPONENT_KCONFIGS_PROJBUILD" +choice OPTIMIZATION_LEVEL + prompt "Optimization level" + default OPTIMIZATION_LEVEL_DEBUG + help + This option sets compiler optimization level. +config OPTIMIZATION_LEVEL_DEBUG + bool "Debug" +config OPTIMIZATION_LEVEL_RELEASE + bool "Release" +endchoice + menu "Component config" source "$COMPONENT_KCONFIGS" endmenu diff --git a/components/log/log.c b/components/log/log.c index aae12a773..a2b41d7e6 100644 --- a/components/log/log.c +++ b/components/log/log.c @@ -284,7 +284,7 @@ static inline void heap_swap(int i, int j) } #endif //BOOTLOADER_BUILD -inline IRAM_ATTR uint32_t esp_log_early_timestamp() +IRAM_ATTR uint32_t esp_log_early_timestamp() { return xthal_get_ccount() / (CPU_CLK_FREQ_ROM / 1000); } diff --git a/make/component_common.mk b/make/component_common.mk index ebad525a7..bf2eace01 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -91,15 +91,15 @@ define GenerateCompileTargets # $(1) - directory containing source files, relative to $(COMPONENT_PATH) $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.c | $(1) $$(summary) CC $$@ - $$(Q) $$(CC) $$(CFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + $$(Q) $$(CC) $$(CFLAGS) $(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp | $(1) - $$(summary) CC $$@ - $$(Q) $$(CXX) $$(CXXFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + $$(summary) CXX $$@ + $$(Q) $$(CXX) $$(CXXFLAGS) $(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S | $(1) - $$(summary) CC $$@ - $$(Q) $$(CC) $$(CFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + $$(summary) AS $$@ + $$(Q) $$(CC) $$(CFLAGS) $(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ # CWD is build dir, create the build subdirectory if it doesn't exist $(1): diff --git a/make/project.mk b/make/project.mk index d91165cfd..49c349946 100644 --- a/make/project.mk +++ b/make/project.mk @@ -157,15 +157,36 @@ LDFLAGS ?= -nostdlib \ # If you need your component to add CFLAGS/etc globally for all source # files, set CFLAGS += in your component's Makefile.projbuild -# CPPFLAGS used by an compile pass that uses the C preprocessor -CPPFLAGS = -DESP_PLATFORM -Og -g3 -Wpointer-arith -Werror -Wno-error=unused-function -Wno-error=unused-but-set-variable \ - -Wno-error=unused-variable -Wall -ffunction-sections -fdata-sections -mlongcalls -nostdlib -MMD -MP +# CPPFLAGS used by C preprocessor +CPPFLAGS = -DESP_PLATFORM -# C flags use by C only -CFLAGS = $(CPPFLAGS) -std=gnu99 -g3 -fstrict-volatile-bitfields +# Warnings-related flags relevant both for C and C++ +COMMON_WARNING_FLAGS = -Wall -Werror \ + -Wno-error=unused-function \ + -Wno-error=unused-but-set-variable \ + -Wno-error=unused-variable -# CXXFLAGS uses by C++ only -CXXFLAGS = $(CPPFLAGS) -Og -std=gnu++11 -g3 -fno-exceptions -fstrict-volatile-bitfields -fno-rtti +# Flags which control code generation and dependency generation, both for C and C++ +COMMON_FLAGS = \ + -ffunction-sections -fdata-sections \ + -fstrict-volatile-bitfields \ + -mlongcalls \ + -nostdlib \ + -MMD -MP + +# Optimization flags are set based on menuconfig choice +ifneq ("$(CONFIG_OPTIMIZATION_LEVEL_RELEASE)","") +OPTMIZATION_FLAGS = -Os +CPPFLAGS += -DNDEBUG +else +OPTMIZATION_FLAGS = -O0 +endif + +# List of flags to pass to C compiler +CFLAGS = -ggdb -std=gnu99 $(strip $(OPTMIZATION_FLAGS) $(COMMON_FLAGS) $(COMMON_WARNING_FLAGS)) + +# List of flags to pass to C++ compiler +CXXFLAGS = -ggdb -std=gnu++11 -fno-exceptions -fno-rtti $(strip $(OPTMIZATION_FLAGS) $(COMMON_FLAGS) $(COMMON_WARNING_FLAGS)) export CFLAGS CPPFLAGS CXXFLAGS From 1cd572c7b982cd876d2f16958c1b2d75cf5f0ea5 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 17 Oct 2016 12:45:42 +0800 Subject: [PATCH 017/110] Add test for compiling in release mode, fix warnings and errors which appeared --- .gitlab-ci.yml | 6 ++++++ components/nghttp/library/nghttp2_session.c | 2 +- components/nvs_flash/src/nvs_page.cpp | 2 +- components/nvs_flash/src/nvs_pagemanager.cpp | 4 +++- components/nvs_flash/src/nvs_storage.cpp | 3 +-- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dd4049358..aff9bea8d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,6 +37,12 @@ build_template_app: # branch - git checkout ${CI_BUILD_REF_NAME} || echo "Using esp-idf-template default branch..." - make defconfig + # Test debug build (default) + - make all V=1 + # Now test release build + - make clean + - sed -i.bak -e's/CONFIG_OPTIMIZATION_LEVEL_DEBUG\=y/CONFIG_OPTIMIZATION_LEVEL_RELEASE=y/' sdkconfig + - make defconfig - make all V=1 diff --git a/components/nghttp/library/nghttp2_session.c b/components/nghttp/library/nghttp2_session.c index f93cd1729..0100dd32c 100644 --- a/components/nghttp/library/nghttp2_session.c +++ b/components/nghttp/library/nghttp2_session.c @@ -7177,7 +7177,7 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, return session->remote_settings.max_header_list_size; } - assert(0); + abort(); } static int nghttp2_session_upgrade_internal(nghttp2_session *session, diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index 4f6a198c6..f4fc5430c 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -769,7 +769,7 @@ void Page::invalidateCache() void Page::debugDump() const { - printf("state=%x addr=%x seq=%d\nfirstUsed=%d nextFree=%d used=%d erased=%d\n", mState, mBaseAddress, mSeqNumber, static_cast(mFirstUsedEntry), static_cast(mNextFreeEntry), mUsedEntryCount, mErasedEntryCount); + printf("state=%x addr=%x seq=%d\nfirstUsed=%d nextFree=%d used=%d erased=%d\n", (int) mState, mBaseAddress, mSeqNumber, static_cast(mFirstUsedEntry), static_cast(mNextFreeEntry), mUsedEntryCount, mErasedEntryCount); size_t skip = 0; for (size_t i = 0; i < ENTRY_COUNT; ++i) { printf("%3d: ", static_cast(i)); diff --git a/components/nvs_flash/src/nvs_pagemanager.cpp b/components/nvs_flash/src/nvs_pagemanager.cpp index 790ab7e19..f4d02a7d4 100644 --- a/components/nvs_flash/src/nvs_pagemanager.cpp +++ b/components/nvs_flash/src/nvs_pagemanager.cpp @@ -49,7 +49,7 @@ esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount) return activatePage(); } else { uint32_t lastSeqNo; - assert(mPageList.back().getSeqNumber(lastSeqNo) == ESP_OK); + ESP_ERROR_CHECK( mPageList.back().getSeqNumber(lastSeqNo) ); mSeqNumber = lastSeqNo + 1; } @@ -142,7 +142,9 @@ esp_err_t PageManager::requestNewPage() Page* newPage = &mPageList.back(); Page* erasedPage = maxErasedItemsPageIt; +#ifndef NDEBUG size_t usedEntries = erasedPage->getUsedEntryCount(); +#endif err = erasedPage->markFreeing(); if (err != ESP_OK) { return err; diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index 4a217ebe1..eb90cac5b 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -123,8 +123,7 @@ esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key if (findPage) { if (findPage->state() == Page::PageState::UNINITIALIZED || findPage->state() == Page::PageState::INVALID) { - auto err = findItem(nsIndex, datatype, key, findPage, item); - assert(err == ESP_OK); + ESP_ERROR_CHECK( findItem(nsIndex, datatype, key, findPage, item) ); } err = findPage->eraseItem(nsIndex, datatype, key); if (err == ESP_ERR_FLASH_OP_FAIL) { From 34fa6a60a9812b4a91ac16535ad5ee86d5755a22 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 17 Oct 2016 13:47:13 +0800 Subject: [PATCH 018/110] build system: fix typo, move -ggdb to OPTIMIZATION_FLAGS --- make/project.mk | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/make/project.mk b/make/project.mk index 49c349946..9088eda85 100644 --- a/make/project.mk +++ b/make/project.mk @@ -176,17 +176,20 @@ COMMON_FLAGS = \ # Optimization flags are set based on menuconfig choice ifneq ("$(CONFIG_OPTIMIZATION_LEVEL_RELEASE)","") -OPTMIZATION_FLAGS = -Os +OPTIMIZATION_FLAGS = -Os CPPFLAGS += -DNDEBUG else -OPTMIZATION_FLAGS = -O0 +OPTIMIZATION_FLAGS = -O0 endif +# Enable generation of debugging symbols +OPTIMIZATION_FLAGS += -ggdb + # List of flags to pass to C compiler -CFLAGS = -ggdb -std=gnu99 $(strip $(OPTMIZATION_FLAGS) $(COMMON_FLAGS) $(COMMON_WARNING_FLAGS)) +CFLAGS = -std=gnu99 $(strip $(OPTIMIZATION_FLAGS) $(COMMON_FLAGS) $(COMMON_WARNING_FLAGS)) # List of flags to pass to C++ compiler -CXXFLAGS = -ggdb -std=gnu++11 -fno-exceptions -fno-rtti $(strip $(OPTMIZATION_FLAGS) $(COMMON_FLAGS) $(COMMON_WARNING_FLAGS)) +CXXFLAGS = -std=gnu++11 -fno-exceptions -fno-rtti $(strip $(OPTIMIZATION_FLAGS) $(COMMON_FLAGS) $(COMMON_WARNING_FLAGS)) export CFLAGS CPPFLAGS CXXFLAGS From eaace9846af25f95dbfe06fff7c309748a3b651b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 17 Oct 2016 14:12:16 +0800 Subject: [PATCH 019/110] smartconfig: update to match new FreeRTOS header files --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 9c71f406d..aac7d416a 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 9c71f406de7be4774e1049b247c11a1983e6dfaa +Subproject commit aac7d416a702215f2501a6237caf034ec7e8e80a From 0403d43b194f5c7c77dd246aece7d936d6985fce Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 17 Oct 2016 18:09:15 +0800 Subject: [PATCH 020/110] Optimize xPortGetCoreID to 2-instruction inline assembly. --- components/freertos/include/freertos/portable.h | 3 +-- .../freertos/include/freertos/xtensa_context.h | 7 +------ components/freertos/panic.c | 1 - components/freertos/portasm.S | 12 ------------ 4 files changed, 2 insertions(+), 21 deletions(-) diff --git a/components/freertos/include/freertos/portable.h b/components/freertos/include/freertos/portable.h index 4030ca0c0..58e690366 100644 --- a/components/freertos/include/freertos/portable.h +++ b/components/freertos/include/freertos/portable.h @@ -192,8 +192,7 @@ void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; #endif /* Multi-core: get current core ID */ -int xPortGetCoreID( void ); - +#define xPortGetCoreID() __extension__({int id; asm volatile("rsr.prid %0; extui %0,%0,13,1":"=r"(id)); id;}) #ifdef __cplusplus } diff --git a/components/freertos/include/freertos/xtensa_context.h b/components/freertos/include/freertos/xtensa_context.h index 3167c8472..b748a0d1a 100644 --- a/components/freertos/include/freertos/xtensa_context.h +++ b/components/freertos/include/freertos/xtensa_context.h @@ -322,12 +322,7 @@ STRUCT_END(XtSolFrame) #ifdef __ASSEMBLER__ .macro getcoreid reg rsr.prid \reg - bbci \reg,1,1f - movi \reg,1 - j 2f -1: - movi \reg,0 -2: + extui \reg,\reg,13,1 .endm #endif diff --git a/components/freertos/panic.c b/components/freertos/panic.c index 940086735..6a87679d1 100644 --- a/components/freertos/panic.c +++ b/components/freertos/panic.c @@ -76,7 +76,6 @@ inline static void panicPutHex(int a) { } inline static void panicPutDec(int a) { } #endif -int xPortGetCoreID(); void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) { panicPutStr("***ERROR*** A stack overflow in task "); diff --git a/components/freertos/portasm.S b/components/freertos/portasm.S index 65406b0b0..3a45b19a3 100644 --- a/components/freertos/portasm.S +++ b/components/freertos/portasm.S @@ -51,18 +51,6 @@ port_switch_flag: .text - - -/* C function to get proc ID.*/ - .global xPortGetCoreID - .type xPortGetCoreID,@function - .align 4 -xPortGetCoreID: - ENTRY(16) - getcoreid a2 - RET(16) - - /* ******************************************************************************* * _frxt_setup_switch From c03549e1170f76871a5f66cbc709371e38dbf9d2 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 17 Oct 2016 18:30:13 +0800 Subject: [PATCH 021/110] Make uxPortCompareSet into a macro. 25uS -> 24uS --- components/freertos/port.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/components/freertos/port.c b/components/freertos/port.c index 6117a2804..f42cba936 100644 --- a/components/freertos/port.c +++ b/components/freertos/port.c @@ -263,17 +263,14 @@ void vPortAssertIfInISR() * *bitwise inverse* of the old mem if the mem wasn't written. This doesn't seem to happen on the * ESP32, though. (Would show up directly if it did because the magic wouldn't match.) */ -uint32_t uxPortCompareSet(volatile uint32_t *mux, uint32_t compare, uint32_t set) -{ - __asm__ __volatile__ ( - "WSR %2,SCOMPARE1 \n" //initialize SCOMPARE1 - "ISYNC \n" //wait sync - "S32C1I %0, %1, 0 \n" //store id into the lock, if the lock is the same as comparel. Otherwise, no write-access - :"=r"(set) \ - :"r"(mux), "r"(compare), "0"(set) \ - ); - return set; -} +#define uxPortCompareSet(mux, compare, set) \ + __asm__ __volatile__( \ + "WSR %2,SCOMPARE1 \n" \ + "ISYNC \n" \ + "S32C1I %0, %1, 0 \n" \ + :"=r"(*set) \ + :"r"(mux), "r"(compare), "0"(*set) \ + ); \ /* * For kernel use: Initialize a per-CPU mux. Mux will be initialized unlocked. @@ -310,7 +307,8 @@ void vPortCPUAcquireMutex(portMUX_TYPE *mux) { irqStatus=portENTER_CRITICAL_NESTED(); do { //Lock mux if it's currently unlocked - res=uxPortCompareSet(&mux->mux, portMUX_FREE_VAL, (xPortGetCoreID()<mux, portMUX_FREE_VAL, &res); //If it wasn't free and we're the owner of the lock, we are locking recursively. if ( (res != portMUX_FREE_VAL) && (((res&portMUX_VAL_MASK)>>portMUX_VAL_SHIFT) == xPortGetCoreID()) ) { //Mux was already locked by us. Just bump the recurse count by one. @@ -362,7 +360,8 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) { if ( (mux->mux & portMUX_MAGIC_MASK) != portMUX_MAGIC_VAL ) ets_printf("ERROR: vPortCPUReleaseMutex: mux %p is uninitialized (0x%X)!\n", mux, mux->mux); #endif //Unlock mux if it's currently locked with a recurse count of 0 - res=uxPortCompareSet(&mux->mux, (xPortGetCoreID()<mux, (xPortGetCoreID()< Date: Mon, 17 Oct 2016 18:49:19 +0800 Subject: [PATCH 022/110] Detect success before errors in vPortCPUReleaseMutex. Shaves off another half uS. --- components/freertos/port.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/components/freertos/port.c b/components/freertos/port.c index f42cba936..ef72b1cb5 100644 --- a/components/freertos/port.c +++ b/components/freertos/port.c @@ -363,27 +363,30 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) { res=portMUX_FREE_VAL; uxPortCompareSet(&mux->mux, (xPortGetCoreID()<>portMUX_VAL_SHIFT) == xPortGetCoreID() ) { + //Lock is valid, we can return safely. Just need to check if it's a recursive lock; if so we need to decrease the refcount. + if ( ((res&portMUX_CNT_MASK)>>portMUX_CNT_SHIFT)!=0) { + //We locked this, but the reccount isn't zero. Decrease refcount and continue. + recCnt=(res&portMUX_CNT_MASK)>>portMUX_CNT_SHIFT; + recCnt--; +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG_RECURSIVE + ets_printf("Recursive unlock: recCnt=%d last locked %s line %d, curr %s line %d\n", recCnt, lastLockedFn, lastLockedLine, fnName, line); +#endif + mux->mux=portMUX_MAGIC_VAL|(recCnt<>portMUX_VAL_SHIFT) != xPortGetCoreID() ) { + } else { #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG ets_printf("ERROR: vPortCPUReleaseMutex: mux %p wasn't locked by this core (%d) but by core %d (ret=%x, mux=%x).\n", mux, xPortGetCoreID(), ((res&portMUX_VAL_MASK)>>portMUX_VAL_SHIFT), res, mux->mux); ets_printf("Last non-recursive lock %s line %d\n", lastLockedFn, lastLockedLine); ets_printf("Called by %s line %d\n", fnName, line); #endif ret=pdFALSE; - } else if ( ((res&portMUX_CNT_MASK)>>portMUX_CNT_SHIFT)!=0) { - //We locked this, but the reccount isn't zero. Decrease refcount and continue. - recCnt=(res&portMUX_CNT_MASK)>>portMUX_CNT_SHIFT; - recCnt--; -#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG_RECURSIVE - ets_printf("Recursive unlock: recCnt=%d last locked %s line %d, curr %s line %d\n", recCnt, lastLockedFn, lastLockedLine, fnName, line); -#endif - mux->mux=portMUX_MAGIC_VAL|(recCnt< Date: Tue, 18 Oct 2016 10:51:08 +0800 Subject: [PATCH 023/110] Some more optimizations, mostly in involuntary task switches. Doesn not really help here, but might in other cases. --- components/freertos/portasm.S | 48 +++++++++++----------------- components/freertos/xtensa_vectors.S | 7 ++-- 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/components/freertos/portasm.S b/components/freertos/portasm.S index 3a45b19a3..ad65a103e 100644 --- a/components/freertos/portasm.S +++ b/components/freertos/portasm.S @@ -69,9 +69,8 @@ _frxt_setup_switch: ENTRY(16) getcoreid a3 - slli a3, a3, 2 movi a2, port_switch_flag - add a2, a2, a3 + addx4 a2, a3, a2 movi a3, 1 s32i a3, a2, 0 @@ -116,12 +115,11 @@ _frxt_int_enter: Manage nesting directly rather than call the generic IntEnter() (in windowed ABI we can't call a C function here anyway because PS.EXCM is still set). */ - getcoreid a3 - slli a4, a3, 2 /* a4 = cpuid * 4 */ + getcoreid a4 movi a2, port_xSchedulerRunning - add a2, a2, a4 + addx4 a2, a4, a2 movi a3, port_interruptNesting - add a3, a3, a4 + addx4 a3, a4, a3 l32i a2, a2, 0 /* a2 = port_xSchedulerRunning */ beqz a2, 1f /* scheduler not running, no tasks */ l32i a2, a3, 0 /* a2 = port_interruptNesting */ @@ -130,14 +128,13 @@ _frxt_int_enter: bnei a2, 1, .Lnested /* !=0 before incr, so nested */ movi a2, pxCurrentTCB - add a2, a2, a4 + addx4 a2, a4, a2 l32i a2, a2, 0 /* a2 = current TCB */ beqz a2, 1f s32i a1, a2, TOPOFSTACK_OFFS /* pxCurrentTCB->pxTopOfStack = SP */ movi a1, port_IntStackTop /* a1 = top of intr stack */ movi a2, configISR_STACK_SIZE - getcoreid a3 - mull a2, a3, a2 + mull a2, a4, a2 add a1, a1, a2 /* for current proc */ .Lnested: @@ -165,12 +162,11 @@ _frxt_int_enter: .align 4 _frxt_int_exit: - getcoreid a3 - slli a4, a3, 2 /* a4 is core * 4 */ + getcoreid a4 movi a2, port_xSchedulerRunning - add a2, a2, a4 + addx4 a2, a4, a2 movi a3, port_interruptNesting - add a3, a3, a4 + addx4 a3, a4, a3 rsil a0, XCHAL_EXCM_LEVEL /* lock out interrupts */ l32i a2, a2, 0 /* a2 = port_xSchedulerRunning */ beqz a2, .Lnoswitch /* scheduler not running, no tasks */ @@ -180,13 +176,13 @@ _frxt_int_exit: bnez a2, .Lnesting /* !=0 after decr so still nested */ movi a2, pxCurrentTCB - add a2, a2, a4 + addx4 a2, a4, a2 l32i a2, a2, 0 /* a2 = current TCB */ beqz a2, 1f /* no task ? go to dispatcher */ l32i a1, a2, TOPOFSTACK_OFFS /* SP = pxCurrentTCB->pxTopOfStack */ movi a2, port_switch_flag /* address of switch flag */ - add a2, a2, a4 /* point to flag for this cpu */ + addx4 a2, a4, a2 /* point to flag for this cpu */ l32i a3, a2, 0 /* a3 = port_switch_flag */ beqz a3, .Lnoswitch /* flag = 0 means no switch reqd */ movi a3, 0 @@ -392,14 +388,12 @@ _frxt_dispatch: call0 vTaskSwitchContext // Get next TCB to resume movi a2, pxCurrentTCB getcoreid a3 - slli a3, a3, 2 - add a2, a2, a3 + addx4 a2, a3, a2 #else call4 vTaskSwitchContext // Get next TCB to resume movi a2, pxCurrentTCB getcoreid a3 - slli a3, a3, 2 - add a2, a2, a3 + addx4 a2, a3, a2 #endif l32i a3, a2, 0 l32i sp, a3, TOPOFSTACK_OFFS /* SP = next_TCB->pxTopOfStack; */ @@ -439,8 +433,7 @@ _frxt_dispatch: /* Restore CPENABLE from task's co-processor save area. */ movi a3, pxCurrentTCB /* cp_state = */ getcoreid a2 - slli a2, a2, 2 - add a3, a2, a3 + addx4 a3, a2, a3 l32i a3, a3, 0 l32i a2, a3, CP_TOPOFSTACK_OFFS /* StackType_t *pxStack; */ l16ui a3, a2, XT_CPENABLE /* CPENABLE = cp_state->cpenable; */ @@ -529,8 +522,7 @@ vPortYield: movi a2, pxCurrentTCB getcoreid a3 - slli a3, a3, 2 - add a2, a2, a3 + addx4 a2, a3, a2 l32i a2, a2, 0 /* a2 = pxCurrentTCB */ movi a3, 0 s32i a3, sp, XT_SOL_EXIT /* 0 to flag as solicited frame */ @@ -581,8 +573,7 @@ vPortYieldFromInt: /* Save CPENABLE in task's co-processor save area, and clear CPENABLE. */ movi a3, pxCurrentTCB /* cp_state = */ getcoreid a2 - slli a2, a2, 2 - add a3, a2, a3 + addx4 a3, a2, a3 l32i a3, a3, 0 l32i a2, a3, CP_TOPOFSTACK_OFFS @@ -625,18 +616,17 @@ _frxt_task_coproc_state: /* We can use a3 as a scratchpad, the instances of code calling XT_RTOS_CP_STATE don't seem to need it saved. */ getcoreid a3 - slli a3, a3, 2 /* a3=coreid*4 */ movi a15, port_xSchedulerRunning /* if (port_xSchedulerRunning */ - add a15, a15, a3 + addx4 a15, a3,a15 l32i a15, a15, 0 beqz a15, 1f movi a15, port_interruptNesting /* && port_interruptNesting == 0 */ - add a15, a15, a3 + addx4 a15, a3, a15 l32i a15, a15, 0 bnez a15, 1f movi a15, pxCurrentTCB - add a15, a3, a15 + addx4 a15, a3, a15 l32i a15, a15, 0 /* && pxCurrentTCB != 0) { */ diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index f0d874a59..7c2fc2960 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -904,16 +904,13 @@ _xt_coproc_exc: core we're running on now. */ movi a2, pxCurrentTCB getcoreid a3 - slli a3, a3, 2 - add a2, a2, a3 + addx4 a2, a3, a2 l32i a2, a2, 0 /* a2 = start of pxCurrentTCB[cpuid] */ addi a2, a2, TASKTCB_XCOREID_OFFSET /* offset to xCoreID in tcb struct */ - getcoreid a3 s32i a3, a2, 0 /* store current cpuid */ /* Grab correct xt_coproc_owner_sa for this core */ - getcoreid a2 - movi a3, XCHAL_CP_MAX << 2 + movi a2, XCHAL_CP_MAX << 2 mull a2, a2, a3 movi a3, _xt_coproc_owner_sa /* a3 = base of owner array */ add a3, a3, a2 From 340a722715c0710ee1058db88cf5c359f15b88c3 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 18 Oct 2016 11:50:19 +0800 Subject: [PATCH 024/110] Warn user if trax is disabled in menuconfig but functions are called anyway. --- components/xtensa-debug-module/trax.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/components/xtensa-debug-module/trax.c b/components/xtensa-debug-module/trax.c index 18c260a97..d216a3df9 100644 --- a/components/xtensa-debug-module/trax.c +++ b/components/xtensa-debug-module/trax.c @@ -12,10 +12,12 @@ #define TRACEMEM_MUX_BLK1_ONLY 2 #define TRACEMEM_MUX_PROBLK1_APPBLK0 3 -static const char* TAG = "log"; +static const char* TAG = "trax"; -int trax_enable(trax_ena_select_t which) { +int trax_enable(trax_ena_select_t which) +{ #if !CONFIG_MEMMAP_TRACEMEM + ESP_LOGE(TAG, "Trax_enable called, but trax is disabled in menuconfig!"); return ESP_ERR_NO_MEM; #endif #if !CONFIG_MEMMAP_TRACEMEM_TWOBANKS @@ -32,8 +34,10 @@ int trax_enable(trax_ena_select_t which) { } -int trax_start_trace(trax_downcount_unit_t units_until_stop) { +int trax_start_trace(trax_downcount_unit_t units_until_stop) +{ #if !CONFIG_MEMMAP_TRACEMEM + ESP_LOGE(TAG, "Trax_start_trace called, but trax is disabled in menuconfig!"); return ESP_ERR_NO_MEM; #endif uint32_t v; @@ -54,8 +58,10 @@ int trax_start_trace(trax_downcount_unit_t units_until_stop) { } -int trax_trigger_traceend_after_delay(int delay) { +int trax_trigger_traceend_after_delay(int delay) +{ #if !CONFIG_MEMMAP_TRACEMEM + ESP_LOGE(TAG, "Trax_trigger_traceend_after_delay called, but trax is disabled in menuconfig!"); return ESP_ERR_NO_MEM; #endif eri_write(ERI_TRAX_DELAYCNT, delay); From 746ad41d890f8e2e60b5ace63a7910b7c60b89e6 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 18 Oct 2016 15:35:17 +1100 Subject: [PATCH 025/110] Build tests: Use & document clean_build_dir --- make/test_build_system.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/make/test_build_system.sh b/make/test_build_system.sh index a6de14987..7c8cbc1f1 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -21,7 +21,8 @@ # * The function "take_build_snapshot" can be paired with the functions "assert_rebuilt" and "assert_not_rebuilt" to compare file timestamps and verify if they were rebuilt or not since the snapshot was taken. # # To add a new test case, add it to the end of the run_tests function. Note that not all test cases do comprehensive cleanup -# (although very invasive ones like appending CRLFs to all files take a copy of the esp-idf tree.) +# (although very invasive ones like appending CRLFs to all files take a copy of the esp-idf tree), however the clean_build_dir +# function can be used to force-delete all files from the build output directory. # Set up some variables # @@ -92,7 +93,7 @@ function run_tests() fi print_status "Bootloader build shouldn't leave build output anywhere else" - rm -rf --preserve-root ${BUILD} + clean_build_dir make bootloader # find wizardry: find any file not named sdkconfig.h that # isn't in the "bootloader" or "config" directories From 3f521c4afae7ac1e1145ce6b7fa5f15211bb6ec7 Mon Sep 17 00:00:00 2001 From: Yinling Date: Wed, 19 Oct 2016 15:11:38 +0800 Subject: [PATCH 026/110] update KnowIssues: 1. unsupported cases by "phy" command 2. DHCP server behavior different from ESP8266 3. TCP failed cases causes by unstable TCP behavior 4. UDP failed cases by poor UDP performance --- .../idf_test/integration_test/KnownIssues | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/components/idf_test/integration_test/KnownIssues b/components/idf_test/integration_test/KnownIssues index 08bc48f18..0c48a811e 100644 --- a/components/idf_test/integration_test/KnownIssues +++ b/components/idf_test/integration_test/KnownIssues @@ -23,6 +23,24 @@ TCPIP_IGMP_0204 ^TCPIP_IGMP_0203 ^TCPIP_IGMP_0204 +# don't support PHY mode command +WIFI_SCAN_0201 +WIFI_SCAN_0302 +WIFI_PHY_0401 +WIFI_PHY_0402 +WIFI_PHY_0403 +WIFI_PHY_0404 +WIFI_PHY_0405 +WIFI_PHY_0407 +WIFI_PHY_0406 +WIFI_PHY_0408 +WIFI_PHY_0501 +WIFI_PHY_0502 +WIFI_PHY_0503 +WIFI_PHY_0504 +WIFI_PHY_0505 +WIFI_PHY_0506 + # BUG # auth change event @@ -46,6 +64,10 @@ TCPIP_DHCP_0207 ^TCPIP_DHCP_0207 TCPIP_DHCP_0208 ^TCPIP_DHCP_0208 +TCPIP_DHCP_0205 +^TCPIP_DHCP_0205 +TCPIP_DHCP_0209 +^TCPIP_DHCP_0209 # TCP issue TCPIP_TCP_0402 @@ -54,6 +76,9 @@ TCPIP_TCP_0402 TCPIP_TCP_0210 ^TCPIP_TCP_0210 TCPIP_TCP_0103 +^TCPIP_TCP_0103 +TCPIP_TCP_0112 +^TCPIP_TCP_0112 # UDP issue @@ -61,6 +86,8 @@ TCPIP_UDP_0103 ^TCPIP_UDP_0103 TCPIP_UDP_0110 ^TCPIP_UDP_0110 +TCPIP_UDP_0305 +^TCPIP_UDP_0305 From de7b2f5a09b3852cc92465af24f4f243bab9f5ee Mon Sep 17 00:00:00 2001 From: Yinling Date: Wed, 19 Oct 2016 15:16:47 +0800 Subject: [PATCH 027/110] fix following test case bugs: 1. (^)TCPIP_DHCP_0101, (^)TCPIP_DHCP_0301: IDF set IP 0.0.0.0 may return error, don't check the result of setting IP to 0.0.0.0; 2. rename (^)TCPIP_DHCP_0102 to (^)TCPIP_DHCP_0212 as it's a DHCP server test case; 3. (^)TCPIP_TCP_0204,(^)TCPIP_TCP_0210,(^)TCPIP_UDP_0201,(^)TCPIP_UDP_0202: recv thread can't be deleted, change case to not create recv thread when create socket ; 4. (^)TCPIP_TCP_0206,(^)TCPIP_TCP_0212: query TCP server status format changed. 5. WIFI_SCAN_0301: check command error and test environment not correct 6. WIFI_SCAN_0302, WIFI_SCAN_0303, WIFI_SCAN_0304: test environment not correct --- .../idf_test/integration_test/TestCaseAll.yml | 369 +++++++----------- 1 file changed, 143 insertions(+), 226 deletions(-) diff --git a/components/idf_test/integration_test/TestCaseAll.yml b/components/idf_test/integration_test/TestCaseAll.yml index 0835c146b..a7f63a185 100644 --- a/components/idf_test/integration_test/TestCaseAll.yml +++ b/components/idf_test/integration_test/TestCaseAll.yml @@ -87,7 +87,7 @@ test cases: - - SSC SSC1 dhcp -E -o 1 - ['R SSC1 C +DHCP:STA,OK'] - - SSC SSC1 ip -S -i 0.0.0.0 - - ['R SSC1 C +IP:OK'] + - [R SSC1 C +IP] - - SSC SSC1 sta -C -s -p - [''] - - DELAY 20 @@ -121,54 +121,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DHCP client function test - version: v1 (2016-8-15) -- CI ready: 'Yes' - ID: TCPIP_DHCP_0102 - SDK: '8266_NonOS - - 8266_RTOS - - ESP32_IDF' - Test App: SSC - allow fail: '' - auto test: 'Yes' - category: Function - cmd set: - - '' - - - SSC SSC1 ap -S -s -p -t - - ['R SSC1 C +SAP:OK'] - - - SSC SSC1 dhcp -E -o 2 - - ['R SSC1 C +DHCP:AP,OK'] - - - SSC SSC2 sta -C -s -p - - [''] - - - DELAY 20 - - [P PC_COM C +DELAYDONE, 'P SSC2 NC +JAP:CONNECTED'] - - - SSC SSC1 dhcp -S -o 2 - - ['R SSC1 C +DHCP:AP,OK'] - - - SSC SSC2 sta -C -s -p - - ['R SSC2 C +JAP:CONNECTED'] - comment: '' - execution time: 0.0 - expected result: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target\ - \ 1,FAIL \n4.target1 打开DHCP OK\n5.target2 jap target 1,ok" - initial condition: T2_1 - initial condition description (auto): target 1 as SoftAP, target 2 as STA, will - autogen a TC with initial condition T2_2 - level: Integration - module: TCPIP - steps: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target 1,FAIL \n\ - 4.target1 打开DHCP OK\n5.target2 jap target 1,ok" - sub module: DHCP - summary: dhcp server function test - test environment: SSC_T2_1 - test environment description (auto): 'PC has 1 wired NIC connected to AP. - - PC has 1 WiFi NIC. - - 2 SSC target connect with PC by UART.' - test point 1: basic function - test point 2: DHCP client function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_DHCP_0103 SDK: '8266_NonOS @@ -967,6 +920,53 @@ test cases: test point 1: basic function test point 2: DHCP server function test version: v1 (2016-8-15) +- CI ready: 'Yes' + ID: TCPIP_DHCP_0212 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + allow fail: '' + auto test: 'Yes' + category: Function + cmd set: + - '' + - - SSC SSC1 ap -S -s -p -t + - ['R SSC1 C +SAP:OK'] + - - SSC SSC1 dhcp -E -o 2 + - ['R SSC1 C +DHCP:AP,OK'] + - - SSC SSC2 sta -C -s -p + - [''] + - - DELAY 20 + - [P PC_COM C +DELAYDONE, 'P SSC2 NC +JAP:CONNECTED'] + - - SSC SSC1 dhcp -S -o 2 + - ['R SSC1 C +DHCP:AP,OK'] + - - SSC SSC2 sta -C -s -p + - ['R SSC2 C +JAP:CONNECTED'] + comment: '' + execution time: 0.0 + expected result: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target\ + \ 1,FAIL \n4.target1 打开DHCP OK\n5.target2 jap target 1,ok" + initial condition: T2_1 + initial condition description (auto): target 1 as SoftAP, target 2 as STA, will + autogen a TC with initial condition T2_2 + level: Integration + module: TCPIP + steps: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target 1,FAIL \n\ + 4.target1 打开DHCP OK\n5.target2 jap target 1,ok" + sub module: DHCP + summary: dhcp server function test + test environment: SSC_T2_1 + test environment description (auto): 'PC has 1 wired NIC connected to AP. + + PC has 1 WiFi NIC. + + 2 SSC target connect with PC by UART.' + test point 1: basic function + test point 2: DHCP server function test + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_DHCP_0301 SDK: '8266_NonOS @@ -987,7 +987,7 @@ test cases: - - SSC SSC1 sta -C -s -p - ['R SSC1 C +JAP:CONNECTED'] - - SSC SSC1 ip -S -i 0.0.0.0 -o 1 - - ['R SSC1 C +IP:OK'] + - [R SSC1 C +IP] - - SSC SSC1 sta -C -s -p - [''] - - DELAY 10 @@ -1027,7 +1027,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: interaction test point 2: static IP and DHCP interaction test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_DHCP_0302 SDK: '8266_NonOS @@ -3189,14 +3189,12 @@ test cases: - '' - - SOC SOC1 LISTEN - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t TCP + - - SSC SSC1 soc -B -t TCP -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - SSC SSC1 soc -C -s -i -p - ['R SSC1 RE CONNECT:\d+,OK'] - - SOC SOC1 ACCEPT SOC2 - [R SOC_COM L OK] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC2 SEND 146000 - [P SOC_COM R *] - - SSC SSC1 soc -W -s -o 1 @@ -3246,7 +3244,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: use TCP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_TCP_0206 SDK: '8266_NonOS @@ -3280,7 +3278,7 @@ test cases: - ['R SSC1 A :ACCEPT:(\d+),\d+,.+,\d+'] - - SSC SSC1 soc -I - ['P SSC1 RE "SOCINFO:%%s,2,%%s,\d+,%%s,%%d"%%(,,,)', - 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d,.+,\d+"%%(,)', + 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d"%%(,)', 'P SSC1 RE "SOCINFO:%%s,2,%%s,%%d,%%s,\d+"%%(,,,)'] comment: '' execution time: 0.0 @@ -3531,14 +3529,12 @@ test cases: - '' - - SOC SOC1 LISTEN - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t TCP + - - SSC SSC1 soc -B -t TCP -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - SSC SSC1 soc -C -s -i -p - ['R SSC1 RE CONNECT:\d+,OK'] - - SOC SOC1 ACCEPT SOC2 - [R SOC_COM L OK] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC2 SEND 146000 - [P SOC_COM R *] - - SSC SSC1 soc -W -s -o 1 @@ -3586,7 +3582,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: use TCP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_TCP_0212 SDK: '8266_NonOS @@ -3620,7 +3616,7 @@ test cases: - ['R SSC1 A :ACCEPT:(\d+),\d+,.+,\d+'] - - SSC SSC1 soc -I - ['P SSC1 RE "SOCINFO:%%s,2,%%s,\d+,%%s,%%d"%%(,,,)', - 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d,.+,\d+"%%(,)', + 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d"%%(,)', 'P SSC1 RE "SOCINFO:%%s,2,%%s,%%d,%%s,\d+"%%(,,,)'] comment: '' execution time: 0.0 @@ -5119,10 +5115,8 @@ test cases: - '' - - SOC SOC1 BIND - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t UDP -p + - - SSC SSC1 soc -B -t UDP -p -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC1 SENDTO 1472 - [''] - - SOC SOC1 SENDTO 1472 @@ -5180,7 +5174,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: abnormal/special use test point 2: use UDP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_UDP_0202 SDK: '8266_NonOS @@ -5198,8 +5192,8 @@ test cases: - [R SOC_COM L OK] - - SSC SSC1 soc -B -t UDP -p - ['R SSC1 A :BIND:(\d+),OK'] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] + - - SOC SOC1 SENDTO 1472 + - [''] - - SOC SOC1 SENDTO 1472 - [''] - - SOC SOC1 SENDTO 1472 @@ -5257,7 +5251,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: abnormal/special use test point 2: use UDP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_UDP_0301 SDK: '8266_NonOS @@ -7915,7 +7909,7 @@ test cases: - - SSC SSC1 sta -S - [''] - - SSC SSC1 sta -S - - [P SSC1 C +SCANFAIL, 'P SSC1 P +SCAN:', R SSC1 C +SCANDONE] + - [P SSC1 C +SCANFAIL, 'P SSC1 C +SCAN:', R SSC1 C +SCANDONE] comment: '' execution time: 0.0 expected result: '1. second scan failed @@ -7931,14 +7925,12 @@ test cases: 2. do scan before scan finished' sub module: WIFI Scan summary: reject scan request before scan finished - test environment: SSC_T2_PhyMode - test environment description (auto): '2 SSC target connect with PC by UART. + test environment: SSC_T1_1 + test environment description (auto): 'PC has 2 wired NIC connected to AP. - PC has one WiFi NIC support capture wlan packet using libpcap. + PC has 1 WiFi NIC. - Set 4 AP with phy mode 11b, 11g, 11n HT20, 11n HT40. - - Put 4 APs near SSC targets.' + 1 SSC target connect with PC by UART.' test point 1: interaction test point 2: Scan interact with other WiFi operation version: v1 (2015-8-15) @@ -7987,14 +7979,12 @@ test cases: 3. target 2 scan in AP channel in 11b.g,n,ht40 mode' sub module: WIFI Scan summary: scan in congest channel - test environment: SSC_T2_PhyMode - test environment description (auto): '2 SSC target connect with PC by UART. + test environment: SSC_T2_1 + test environment description (auto): 'PC has 1 wired NIC connected to AP. - PC has one WiFi NIC support capture wlan packet using libpcap. + PC has 1 WiFi NIC. - Set 4 AP with phy mode 11b, 11g, 11n HT20, 11n HT40. - - Put 4 APs near SSC targets.' + 2 SSC target connect with PC by UART.' test point 1: interaction test point 2: Scan interact with other WiFi operation version: v1 (2015-8-15) @@ -8022,8 +8012,9 @@ test cases: expected result: '2. scan succeed, JAP succeed 5. JAP succeed, scan succeed' - initial condition: T2_2 - initial condition description (auto): target 1 as AP+STA, target 2 as AP+STA (autogen) + initial condition: STAM1 + initial condition description (auto): sta mode, quit AP, DHCP on, will autogen a + TC with initial condition STAAP1 level: Integration module: WIFI MAC steps: '1. target 1 STA join AP @@ -8037,14 +8028,12 @@ test cases: 5. target 1 JAP before scan succeed' sub module: WIFI Scan summary: scan during JAP - test environment: SSC_T2_PhyMode - test environment description (auto): '2 SSC target connect with PC by UART. + test environment: SSC_T1_1 + test environment description (auto): 'PC has 2 wired NIC connected to AP. - PC has one WiFi NIC support capture wlan packet using libpcap. + PC has 1 WiFi NIC. - Set 4 AP with phy mode 11b, 11g, 11n HT20, 11n HT40. - - Put 4 APs near SSC targets.' + 1 SSC target connect with PC by UART.' test point 1: interaction test point 2: Scan interact with other WiFi operation version: v1 (2015-8-15) @@ -8087,14 +8076,12 @@ test cases: 5. target 2 STA JAP before target 1 STA scan succeed' sub module: WIFI Scan summary: scan during ext STA join SoftAP - test environment: SSC_T2_PhyMode - test environment description (auto): '2 SSC target connect with PC by UART. + test environment: SSC_T2_1 + test environment description (auto): 'PC has 1 wired NIC connected to AP. - PC has one WiFi NIC support capture wlan packet using libpcap. + PC has 1 WiFi NIC. - Set 4 AP with phy mode 11b, 11g, 11n HT20, 11n HT40. - - Put 4 APs near SSC targets.' + 2 SSC target connect with PC by UART.' test point 1: interaction test point 2: Scan interact with other WiFi operation version: v1 (2015-8-15) @@ -8114,7 +8101,7 @@ test cases: - - SSC SSC1 dhcp -E -o 1 - ['R SSC1 C +DHCP:STA,OK'] - - SSC SSC1 ip -S -i 0.0.0.0 - - ['R SSC1 C +IP:OK'] + - [R SSC1 C +IP] - - SSC SSC1 sta -C -s -p - [''] - - DELAY 20 @@ -8148,53 +8135,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DHCP client function test - version: v1 (2016-8-15) -- CI ready: 'Yes' - ID: ^TCPIP_DHCP_0102 - SDK: '8266_NonOS - - 8266_RTOS - - ESP32_IDF' - Test App: SSC - allow fail: '' - auto test: 'Yes' - category: Function - cmd set: - - '' - - - SSC SSC1 ap -S -s -p -t - - ['R SSC1 C +SAP:OK'] - - - SSC SSC1 dhcp -E -o 2 - - ['R SSC1 C +DHCP:AP,OK'] - - - SSC SSC2 sta -C -s -p - - [''] - - - DELAY 20 - - [P PC_COM C +DELAYDONE, 'P SSC2 NC +JAP:CONNECTED'] - - - SSC SSC1 dhcp -S -o 2 - - ['R SSC1 C +DHCP:AP,OK'] - - - SSC SSC2 sta -C -s -p - - ['R SSC2 C +JAP:CONNECTED'] - comment: '' - execution time: 0.0 - expected result: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target\ - \ 1,FAIL \n4.target1 打开DHCP OK\n5.target2 jap target 1,ok" - initial condition: T2_2 - initial condition description (auto): target 1 as AP+STA, target 2 as AP+STA (autogen) - level: Integration - module: TCPIP - steps: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target 1,FAIL \n\ - 4.target1 打开DHCP OK\n5.target2 jap target 1,ok" - sub module: DHCP - summary: dhcp server function test - test environment: SSC_T2_1 - test environment description (auto): 'PC has 1 wired NIC connected to AP. - - PC has 1 WiFi NIC. - - 2 SSC target connect with PC by UART.' - test point 1: basic function - test point 2: DHCP client function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_DHCP_0103 SDK: '8266_NonOS @@ -8982,6 +8923,52 @@ test cases: test point 1: basic function test point 2: DHCP server function test version: v1 (2016-8-15) +- CI ready: 'Yes' + ID: ^TCPIP_DHCP_0212 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + allow fail: '' + auto test: 'Yes' + category: Function + cmd set: + - '' + - - SSC SSC1 ap -S -s -p -t + - ['R SSC1 C +SAP:OK'] + - - SSC SSC1 dhcp -E -o 2 + - ['R SSC1 C +DHCP:AP,OK'] + - - SSC SSC2 sta -C -s -p + - [''] + - - DELAY 20 + - [P PC_COM C +DELAYDONE, 'P SSC2 NC +JAP:CONNECTED'] + - - SSC SSC1 dhcp -S -o 2 + - ['R SSC1 C +DHCP:AP,OK'] + - - SSC SSC2 sta -C -s -p + - ['R SSC2 C +JAP:CONNECTED'] + comment: '' + execution time: 0.0 + expected result: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target\ + \ 1,FAIL \n4.target1 打开DHCP OK\n5.target2 jap target 1,ok" + initial condition: T2_2 + initial condition description (auto): target 1 as AP+STA, target 2 as AP+STA (autogen) + level: Integration + module: TCPIP + steps: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target 1,FAIL \n\ + 4.target1 打开DHCP OK\n5.target2 jap target 1,ok" + sub module: DHCP + summary: dhcp server function test + test environment: SSC_T2_1 + test environment description (auto): 'PC has 1 wired NIC connected to AP. + + PC has 1 WiFi NIC. + + 2 SSC target connect with PC by UART.' + test point 1: basic function + test point 2: DHCP server function test + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_DHCP_0301 SDK: '8266_NonOS @@ -9002,7 +8989,7 @@ test cases: - - SSC SSC1 sta -C -s -p - ['R SSC1 C +JAP:CONNECTED'] - - SSC SSC1 ip -S -i 0.0.0.0 -o 1 - - ['R SSC1 C +IP:OK'] + - [R SSC1 C +IP] - - SSC SSC1 sta -C -s -p - [''] - - DELAY 10 @@ -9042,7 +9029,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: interaction test point 2: static IP and DHCP interaction test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_DHCP_0302 SDK: '8266_NonOS @@ -11199,14 +11186,12 @@ test cases: - '' - - SOC SOC1 LISTEN - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t TCP + - - SSC SSC1 soc -B -t TCP -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - SSC SSC1 soc -C -s -i -p - ['R SSC1 RE CONNECT:\d+,OK'] - - SOC SOC1 ACCEPT SOC2 - [R SOC_COM L OK] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC2 SEND 146000 - [P SOC_COM R *] - - SSC SSC1 soc -W -s -o 1 @@ -11254,7 +11239,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: use TCP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_TCP_0206 SDK: '8266_NonOS @@ -11288,7 +11273,7 @@ test cases: - ['R SSC1 A :ACCEPT:(\d+),\d+,.+,\d+'] - - SSC SSC1 soc -I - ['P SSC1 RE "SOCINFO:%%s,2,%%s,\d+,%%s,%%d"%%(,,,)', - 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d,.+,\d+"%%(,)', + 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d"%%(,)', 'P SSC1 RE "SOCINFO:%%s,2,%%s,%%d,%%s,\d+"%%(,,,)'] comment: '' execution time: 0.0 @@ -11539,14 +11524,12 @@ test cases: - '' - - SOC SOC1 LISTEN - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t TCP + - - SSC SSC1 soc -B -t TCP -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - SSC SSC1 soc -C -s -i -p - ['R SSC1 RE CONNECT:\d+,OK'] - - SOC SOC1 ACCEPT SOC2 - [R SOC_COM L OK] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC2 SEND 146000 - [P SOC_COM R *] - - SSC SSC1 soc -W -s -o 1 @@ -11592,7 +11575,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: use TCP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_TCP_0212 SDK: '8266_NonOS @@ -11626,7 +11609,7 @@ test cases: - ['R SSC1 A :ACCEPT:(\d+),\d+,.+,\d+'] - - SSC SSC1 soc -I - ['P SSC1 RE "SOCINFO:%%s,2,%%s,\d+,%%s,%%d"%%(,,,)', - 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d,.+,\d+"%%(,)', + 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d"%%(,)', 'P SSC1 RE "SOCINFO:%%s,2,%%s,%%d,%%s,\d+"%%(,,,)'] comment: '' execution time: 0.0 @@ -12981,10 +12964,8 @@ test cases: - '' - - SOC SOC1 BIND - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t UDP -p + - - SSC SSC1 soc -B -t UDP -p -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC1 SENDTO 1472 - [''] - - SOC SOC1 SENDTO 1472 @@ -13042,7 +13023,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: abnormal/special use test point 2: use UDP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_UDP_0202 SDK: '8266_NonOS @@ -13060,8 +13041,8 @@ test cases: - [R SOC_COM L OK] - - SSC SSC1 soc -B -t UDP -p - ['R SSC1 A :BIND:(\d+),OK'] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] + - - SOC SOC1 SENDTO 1472 + - [''] - - SOC SOC1 SENDTO 1472 - [''] - - SOC SOC1 SENDTO 1472 @@ -13119,7 +13100,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: abnormal/special use test point 2: use UDP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_UDP_0301 SDK: '8266_NonOS @@ -14652,67 +14633,3 @@ test cases: test point 1: basic function test point 2: scan with different config version: v1 (2016-8-15) -- CI ready: 'Yes' - ID: ^WIFI_SCAN_0105 - SDK: '8266_NonOS - - 8266_RTOS - - ESP32_IDF' - Test App: SSC - allow fail: '' - auto test: 'Yes' - category: Function - cmd set: - - '' - - - SSC SSC1 sta -D - - ['R SSC1 C +QAP:'] - - - SSC SSC1 ap -S -s -p 123456789 -t 3 -h 0 -n 11 - - ['R SSC1 C +SAP:OK'] - - - SSC SSC2 sta -S -s -b -n 11 - - [R SSC2 P C +SCANDONE] - - - SSC SSC2 sta -S -s -b -n 11 - - [R SSC2 NP C +SCANDONE] - - - SSC SSC2 sta -S -s -b ff:ff:ff:ff:ff:11 -n 11 - - [R SSC2 P , R SSC2 NP C +SCANDONE] - - - SSC SSC2 sta -S -s -b -n 10 - - [R SSC2 P , R SSC2 NP C +SCANDONE] - comment: '' - execution time: 0.0 - expected result: '1.target1 QAP - - 2. target1 set AP,set ssid broad cast,set channel 11 - - 3.target2 上查询到 - - 4.target2 上查询不到 - - 5.target2 上查询不到 - - 6.target2 上查询不到' - initial condition: T2_2 - initial condition description (auto): target 1 as AP+STA, target 2 as AP+STA (autogen) - level: Integration - module: WIFI MAC - steps: '1.target1 QAP - - 2. target1 set AP,set ssid broad cast,set channel 11 - - 3.target2 上查询到 - - 4.target2 上查询不到 - - 5.target2 上查询不到 - - 6.target2 上查询不到' - sub module: WIFI Scan - summary: scan with several configs - test environment: SSC_T2_1 - test environment description (auto): 'PC has 1 wired NIC connected to AP. - - PC has 1 WiFi NIC. - - 2 SSC target connect with PC by UART.' - test point 1: basic function - test point 2: scan with different config - version: v1 (2016-8-15) From c2b63a614ee110759ffc211f2fd571fa4c50fdf0 Mon Sep 17 00:00:00 2001 From: Yinling Date: Wed, 19 Oct 2016 15:22:13 +0800 Subject: [PATCH 028/110] sync test env from auto_test_script (add SSC_T5_IOT1) --- components/idf_test/integration_test/TestEnvAll.yml | 4 ++++ components/idf_test/uint_test/TestEnvAll.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/components/idf_test/integration_test/TestEnvAll.yml b/components/idf_test/integration_test/TestEnvAll.yml index afa6cb812..2e59961d9 100644 --- a/components/idf_test/integration_test/TestEnvAll.yml +++ b/components/idf_test/integration_test/TestEnvAll.yml @@ -229,6 +229,10 @@ test environment: Put 4 APs near SSC targets.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 5.0, script path: EnvBase.py, tag: SSC_T5_1, test environment detail: 5 SSC target connect with PC by UART., test script: EnvBase} +- {PC OS: '', Special: Y, Target Count: 5.0, script path: EnvBase.py, tag: SSC_T5_IOT1, + test environment detail: '5 SSC targets connect with PC by UART. + + some Android smart phone are placed near SSC targets.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 1.0, script path: EnvBase.py, tag: SSC_T6_1, test environment detail: 'PC has 1 wired NIC connected to AP. diff --git a/components/idf_test/uint_test/TestEnvAll.yml b/components/idf_test/uint_test/TestEnvAll.yml index afa6cb812..2e59961d9 100644 --- a/components/idf_test/uint_test/TestEnvAll.yml +++ b/components/idf_test/uint_test/TestEnvAll.yml @@ -229,6 +229,10 @@ test environment: Put 4 APs near SSC targets.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 5.0, script path: EnvBase.py, tag: SSC_T5_1, test environment detail: 5 SSC target connect with PC by UART., test script: EnvBase} +- {PC OS: '', Special: Y, Target Count: 5.0, script path: EnvBase.py, tag: SSC_T5_IOT1, + test environment detail: '5 SSC targets connect with PC by UART. + + some Android smart phone are placed near SSC targets.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 1.0, script path: EnvBase.py, tag: SSC_T6_1, test environment detail: 'PC has 1 wired NIC connected to AP. From 0324373cd1c89570dc3b454410d124504df8e10f Mon Sep 17 00:00:00 2001 From: Yinling Date: Wed, 19 Oct 2016 15:23:07 +0800 Subject: [PATCH 029/110] update auto generated CI job config --- .../integration_test/CIConfigs/IT_Function_TCPIP_02.yml | 8 ++++---- .../integration_test/CIConfigs/IT_Function_TCPIP_03.yml | 2 +- .../integration_test/CIConfigs/IT_Function_TCPIP_04.yml | 2 +- .../integration_test/CIConfigs/IT_Function_WIFI_01.yml | 8 ++++---- .../integration_test/CIConfigs/IT_Function_WIFI_02.yml | 6 +++--- .../integration_test/CIConfigs/IT_Function_WIFI_06.yml | 5 ++--- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml index 61adea982..73618e0d7 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml @@ -4,7 +4,7 @@ Filter: - Add: ID: [TCPIP_IGMP_0203, ^TCPIP_TCP_0403, ^TCPIP_TCP_0408, TCPIP_UDP_0201, TCPIP_UDP_0202, ^TCPIP_DHCP_0301, ^TCPIP_TCP_0101, ^TCPIP_TCP_0103, ^TCPIP_TCP_0105, ^TCPIP_TCP_0104, - ^TCPIP_TCP_0107, ^TCPIP_TCP_0106, ^TCPIP_DHCP_0210, ^TCPIP_DHCP_0211, ^TCPIP_TCP_0404, - TCPIP_TCP_0212, TCPIP_TCP_0210, ^TCPIP_TCP_0406, ^TCPIP_TCP_0407, ^TCPIP_TCP_0401, - ^TCPIP_TCP_0210, ^TCPIP_TCP_0212, TCPIP_DHCP_0211, TCPIP_DHCP_0210, TCPIP_DHCP_0101, - TCPIP_DHCP_0103, TCPIP_DHCP_0102, TCPIP_DHCP_0206, TCPIP_DHCP_0207, ^TCPIP_IP_0102] + ^TCPIP_TCP_0107, ^TCPIP_TCP_0106, ^TCPIP_DHCP_0210, ^TCPIP_DHCP_0211, ^TCPIP_DHCP_0212, + ^TCPIP_TCP_0404, TCPIP_TCP_0212, TCPIP_TCP_0210, ^TCPIP_TCP_0406, ^TCPIP_TCP_0407, + ^TCPIP_TCP_0401, ^TCPIP_TCP_0210, ^TCPIP_TCP_0212, TCPIP_DHCP_0211, TCPIP_DHCP_0210, + TCPIP_DHCP_0212, TCPIP_DHCP_0101, TCPIP_DHCP_0103, TCPIP_DHCP_0206, TCPIP_DHCP_0207] diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml index 9d64630e9..b326ed721 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml @@ -2,7 +2,7 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [^TCPIP_UDP_0105, ^TCPIP_UDP_0107, ^TCPIP_UDP_0106, ^TCPIP_UDP_0101, TCPIP_TCP_0203, + ID: [^TCPIP_IP_0102, ^TCPIP_UDP_0105, ^TCPIP_UDP_0107, ^TCPIP_UDP_0106, ^TCPIP_UDP_0101, TCPIP_TCP_0202, ^TCPIP_UDP_0108, ^TCPIP_IGMP_0201, ^TCPIP_IGMP_0203, ^TCPIP_IGMP_0202, ^TCPIP_IGMP_0103, TCPIP_UDP_0114, TCPIP_UDP_0113, TCPIP_UDP_0112, TCPIP_DHCP_0205, TCPIP_DHCP_0202, TCPIP_DHCP_0203, ^TCPIP_TCP_0102, TCPIP_TCP_0106, TCPIP_TCP_0107, diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml index 6be01f698..314de4a7e 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml @@ -5,6 +5,6 @@ Filter: ID: [^TCPIP_TCP_0110, ^TCPIP_TCP_0111, TCPIP_DHCP_0209, ^TCPIP_DHCP_0209, ^TCPIP_DHCP_0207, ^TCPIP_DHCP_0206, ^TCPIP_DHCP_0205, ^TCPIP_DHCP_0204, ^TCPIP_DHCP_0203, ^TCPIP_DHCP_0202, ^TCPIP_DHCP_0201, TCPIP_TCP_0204, TCPIP_TCP_0207, TCPIP_TCP_0206, TCPIP_TCP_0201, - ^TCPIP_DHCP_0101, ^TCPIP_DHCP_0102, ^TCPIP_DHCP_0103, ^TCPIP_DHCP_0208, TCPIP_TCP_0208, + ^TCPIP_DHCP_0101, TCPIP_TCP_0203, ^TCPIP_DHCP_0103, ^TCPIP_DHCP_0208, TCPIP_TCP_0208, ^TCPIP_TCP_0202, ^TCPIP_TCP_0203, TCPIP_DHCP_0204, ^TCPIP_TCP_0201, ^TCPIP_TCP_0206, ^TCPIP_TCP_0207, ^TCPIP_TCP_0204, TCPIP_DHCP_0201, ^TCPIP_TCP_0208, TCPIP_DHCP_0208] diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_01.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_01.yml index c22bc59bd..9f8424f6d 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_01.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_01.yml @@ -4,7 +4,7 @@ Filter: - Add: ID: [^WIFI_CONN_0601, ^WIFI_ADDR_0101, WIFI_SCAN_0103, WIFI_SCAN_0102, WIFI_SCAN_0101, WIFI_SCAN_0105, WIFI_SCAN_0104, ^WIFI_CONN_0103, WIFI_CONN_0201, WIFI_CONN_0904, - ^WIFI_SCAN_0102, ^WIFI_SCAN_0103, ^WIFI_SCAN_0104, ^WIFI_SCAN_0105, WIFI_CONN_0401, - WIFI_ADDR_0101, WIFI_ADDR_0102, WIFI_CONN_0301, ^WIFI_CONN_0801, ^WIFI_CONN_0301, - WIFI_CONN_0501, WIFI_CONN_0502, ^WIFI_CONN_0401, WIFI_MODE_0101, WIFI_MODE_0103, - WIFI_MODE_0102, ^WIFI_CONN_0904, ^WIFI_CONN_0901, WIFI_CONN_0601, ^WIFI_CONN_0201] + ^WIFI_SCAN_0102, ^WIFI_SCAN_0103, ^WIFI_SCAN_0104, WIFI_CONN_0401, WIFI_ADDR_0101, + WIFI_ADDR_0102, WIFI_CONN_0301, WIFI_SCAN_0301, WIFI_SCAN_0303, ^WIFI_CONN_0801, + WIFI_SCAN_0304, ^WIFI_CONN_0301, WIFI_CONN_0501, WIFI_CONN_0502, ^WIFI_CONN_0401, + WIFI_MODE_0101, WIFI_MODE_0103, WIFI_MODE_0102, ^WIFI_CONN_0904, ^WIFI_CONN_0901] diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_02.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_02.yml index 049054dba..74e6ca612 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_02.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_02.yml @@ -2,6 +2,6 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [^WIFI_ADDR_0102, WIFI_CONN_0901, WIFI_CONN_0801, ^WIFI_CONN_0104, WIFI_CONN_0104, - WIFI_CONN_0101, WIFI_CONN_0102, WIFI_CONN_0103, ^WIFI_SCAN_0101, ^WIFI_CONN_0101, - ^WIFI_CONN_0502, ^WIFI_CONN_0501] + ID: [WIFI_SCAN_0302, WIFI_CONN_0601, ^WIFI_CONN_0201, ^WIFI_ADDR_0102, WIFI_CONN_0901, + WIFI_CONN_0801, ^WIFI_CONN_0104, WIFI_CONN_0104, WIFI_CONN_0101, WIFI_CONN_0102, + WIFI_CONN_0103, ^WIFI_SCAN_0101, ^WIFI_CONN_0101, ^WIFI_CONN_0502, ^WIFI_CONN_0501] diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_06.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_06.yml index 9d639f912..dd42815a9 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_06.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_06.yml @@ -2,6 +2,5 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [WIFI_SCAN_0301, WIFI_SCAN_0303, WIFI_SCAN_0304, WIFI_SCAN_0302, WIFI_SCAN_0201, - WIFI_PHY_0403, WIFI_PHY_0402, WIFI_PHY_0401, WIFI_PHY_0407, WIFI_PHY_0406, WIFI_PHY_0405, - WIFI_PHY_0404, WIFI_PHY_0408] + ID: [WIFI_SCAN_0201, WIFI_PHY_0403, WIFI_PHY_0402, WIFI_PHY_0401, WIFI_PHY_0407, + WIFI_PHY_0406, WIFI_PHY_0405, WIFI_PHY_0404, WIFI_PHY_0408] From 6eaf595a0e7e66ecebd64ab153e3651e657d5c5b Mon Sep 17 00:00:00 2001 From: Yinling Date: Wed, 19 Oct 2016 18:00:27 +0800 Subject: [PATCH 030/110] change test WAN server URL: form iot.espressif.cn to factory.espressif.cn --- .../idf_test/integration_test/TestCaseAll.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/components/idf_test/integration_test/TestCaseAll.yml b/components/idf_test/integration_test/TestCaseAll.yml index a7f63a185..315682c3f 100644 --- a/components/idf_test/integration_test/TestCaseAll.yml +++ b/components/idf_test/integration_test/TestCaseAll.yml @@ -1143,7 +1143,7 @@ test cases: category: Function cmd set: - '' - - - SSC SSC1 soc -H -d iot.espressif.cn + - - SSC SSC1 soc -H -d factory.espressif.cn - ['R SSC1 A :\+HOSTIP:OK,(.+)\r\n'] - - SSC SSC1 soc -B -t TCP - ['R SSC1 A :\+BIND:(\d+),OK'] @@ -1178,7 +1178,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DNS function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_DNS_0103 SDK: '8266_NonOS @@ -1192,7 +1192,7 @@ test cases: category: Function cmd set: - '' - - - SSC SSC1 soc -H -d iot.espressif.cn + - - SSC SSC1 soc -H -d factory.espressif.cn - ['R SSC1 A :\+HOSTIP:OK,(.+)\r\n'] - - SSC SSC1 soc -B -t UDP - ['R SSC1 A :\+BIND:(\d+),OK'] @@ -1223,7 +1223,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DNS function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_ICMP_0101 SDK: '8266_NonOS @@ -9144,7 +9144,7 @@ test cases: category: Function cmd set: - '' - - - SSC SSC1 soc -H -d iot.espressif.cn + - - SSC SSC1 soc -H -d factory.espressif.cn - ['R SSC1 A :\+HOSTIP:OK,(.+)\r\n'] - - SSC SSC1 soc -B -t TCP - ['R SSC1 A :\+BIND:(\d+),OK'] @@ -9179,7 +9179,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DNS function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_DNS_0103 SDK: '8266_NonOS @@ -9193,7 +9193,7 @@ test cases: category: Function cmd set: - '' - - - SSC SSC1 soc -H -d iot.espressif.cn + - - SSC SSC1 soc -H -d factory.espressif.cn - ['R SSC1 A :\+HOSTIP:OK,(.+)\r\n'] - - SSC SSC1 soc -B -t UDP - ['R SSC1 A :\+BIND:(\d+),OK'] @@ -9224,7 +9224,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DNS function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_ICMP_0101 SDK: '8266_NonOS From 18ebc6411e811393edcfbbb24e1d9ae9c936f5f0 Mon Sep 17 00:00:00 2001 From: Yinling Date: Wed, 19 Oct 2016 18:03:43 +0800 Subject: [PATCH 031/110] fix bug for WIFI_SCAN_0304: need to set AP config first --- components/idf_test/integration_test/TestCaseAll.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/idf_test/integration_test/TestCaseAll.yml b/components/idf_test/integration_test/TestCaseAll.yml index 315682c3f..a98b89b40 100644 --- a/components/idf_test/integration_test/TestCaseAll.yml +++ b/components/idf_test/integration_test/TestCaseAll.yml @@ -8046,6 +8046,8 @@ test cases: category: Function cmd set: - '' + - - SSC SSC1 ap -S -s -p -t + - ['R SSC1 C +SAP:OK'] - - SSC SSC2 sta -C -s -p - ['R SSC2 C +JAP:OK'] - - SSC SSC1 sta -S From c350972eacc85ae1ead7d71ffe8dc26b057b5b15 Mon Sep 17 00:00:00 2001 From: Yinling Date: Wed, 19 Oct 2016 20:22:43 +0800 Subject: [PATCH 032/110] update following know issues: 1. ^WIFI_CONN_0801: auth change event not correct 2. WIFI_SCAN_0303: scan interact with JAP not correct 3. ^WIFI_SCAN_0103,^WIFI_SCAN_0105: no scan done event 4. ^TCPIP_UDP_0304,TCPIP_UDP_0104: UDP poor performance --- components/idf_test/integration_test/KnownIssues | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/idf_test/integration_test/KnownIssues b/components/idf_test/integration_test/KnownIssues index 0c48a811e..5e8143040 100644 --- a/components/idf_test/integration_test/KnownIssues +++ b/components/idf_test/integration_test/KnownIssues @@ -45,6 +45,7 @@ WIFI_PHY_0506 # auth change event WIFI_CONN_0801 +^WIFI_CONN_0801 # disconnect reason WIFI_CONN_0904 @@ -56,6 +57,11 @@ WIFI_CONN_0901 WIFI_CONN_0104 ^WIFI_CONN_0104 +# Wifi scan issue +WIFI_SCAN_0303 +^WIFI_SCAN_0103 +^WIFI_SCAN_0105 + # DHCP issues ^TCPIP_DHCP_0301 TCPIP_DHCP_0301 @@ -88,6 +94,8 @@ TCPIP_UDP_0110 ^TCPIP_UDP_0110 TCPIP_UDP_0305 ^TCPIP_UDP_0305 +^TCPIP_UDP_0304 +TCPIP_UDP_0104 From 2d393f05300b6050788561ba1dada185c27917f8 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 20 Oct 2016 11:23:59 +0800 Subject: [PATCH 033/110] Change inline assembly bits from macros to inline functions --- .../freertos/include/freertos/portable.h | 9 ++++++++- .../freertos/include/freertos/portmacro.h | 20 +++++++++++++++++++ components/freertos/port.c | 19 ------------------ 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/components/freertos/include/freertos/portable.h b/components/freertos/include/freertos/portable.h index 58e690366..a3d39bd5a 100644 --- a/components/freertos/include/freertos/portable.h +++ b/components/freertos/include/freertos/portable.h @@ -192,7 +192,14 @@ void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; #endif /* Multi-core: get current core ID */ -#define xPortGetCoreID() __extension__({int id; asm volatile("rsr.prid %0; extui %0,%0,13,1":"=r"(id)); id;}) +inline uint32_t xPortGetCoreID() { + int id; + asm volatile( + "rsr.prid %0\n" + " extui %0,%0,13,1" + :"=r"(id)); + return id; +} #ifdef __cplusplus } diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index ab83b0e05..5e2386d72 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -225,6 +225,26 @@ static inline unsigned portENTER_CRITICAL_NESTED() { unsigned state = XTOS_SET_I #define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state) +/* + * Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare + * *mux to compare, and if it's the same, will set *mux to set. It will return the old value + * of *addr in *set. + * + * Warning: From the ISA docs: in some (unspecified) cases, the s32c1i instruction may return the + * *bitwise inverse* of the old mem if the mem wasn't written. This doesn't seem to happen on the + * ESP32, though. (Would show up directly if it did because the magic wouldn't match.) + */ +inline void uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) { + __asm__ __volatile__( + "WSR %2,SCOMPARE1 \n" + "ISYNC \n" + "S32C1I %0, %1, 0 \n" + :"=r"(*set) + :"r"(addr), "r"(compare), "0"(*set) + ); +} + + /*-----------------------------------------------------------*/ /* Architecture specifics. */ diff --git a/components/freertos/port.c b/components/freertos/port.c index ef72b1cb5..a982db7d4 100644 --- a/components/freertos/port.c +++ b/components/freertos/port.c @@ -253,25 +253,6 @@ void vPortAssertIfInISR() configASSERT(port_interruptNesting[xPortGetCoreID()]==0) } - -/* - * Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare - * *mux to compare, and if it's the same, will set *mux to set. It will return the old value - * of *addr. - * - * Warning: From the ISA docs: in some (unspecified) cases, the s32c1i instruction may return the - * *bitwise inverse* of the old mem if the mem wasn't written. This doesn't seem to happen on the - * ESP32, though. (Would show up directly if it did because the magic wouldn't match.) - */ -#define uxPortCompareSet(mux, compare, set) \ - __asm__ __volatile__( \ - "WSR %2,SCOMPARE1 \n" \ - "ISYNC \n" \ - "S32C1I %0, %1, 0 \n" \ - :"=r"(*set) \ - :"r"(mux), "r"(compare), "0"(*set) \ - ); \ - /* * For kernel use: Initialize a per-CPU mux. Mux will be initialized unlocked. */ From 5e8849d03206b83f19532d38c6eabffeacd9b573 Mon Sep 17 00:00:00 2001 From: Yinling Date: Thu, 20 Oct 2016 11:28:06 +0800 Subject: [PATCH 034/110] modify TCPIP_DHCP_0211: delay 30s to make sure STA got enough time to disconnect and reconnect. --- components/idf_test/integration_test/TestCaseAll.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/idf_test/integration_test/TestCaseAll.yml b/components/idf_test/integration_test/TestCaseAll.yml index a98b89b40..3b2242787 100644 --- a/components/idf_test/integration_test/TestCaseAll.yml +++ b/components/idf_test/integration_test/TestCaseAll.yml @@ -880,7 +880,7 @@ test cases: - ['R SSC1 C +DHCP:AP,OK'] - - SSC SSC2 sta -C -s -p - ['R SSC2 C +JAP:CONNECTED'] - - - DELAY 10 + - - DELAY 30 - [''] - - SSC SSC1 ap -L - [R SSC1 C 192.168.4.2 C 192.168.4.3 P P ] @@ -8886,7 +8886,7 @@ test cases: - ['R SSC1 C +DHCP:AP,OK'] - - SSC SSC2 sta -C -s -p - ['R SSC2 C +JAP:CONNECTED'] - - - DELAY 10 + - - DELAY 30 - [''] - - SSC SSC1 ap -L - [R SSC1 C 192.168.4.2 C 192.168.4.3 P P ] From 39a06319e245b7b4bd9ec5f8dbfe12d00a1c7940 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 20 Oct 2016 16:10:51 +0800 Subject: [PATCH 035/110] build system: use -Og instead of -O0 for debug builds, expand help text in menuconfig --- Kconfig | 5 ++++- make/project.mk | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Kconfig b/Kconfig index 936181a9c..97da1f01f 100644 --- a/Kconfig +++ b/Kconfig @@ -27,7 +27,10 @@ choice OPTIMIZATION_LEVEL prompt "Optimization level" default OPTIMIZATION_LEVEL_DEBUG help - This option sets compiler optimization level. + This option sets optimization level. + For "Release" setting, -Os flag is added to CFLAGS, + and -DNDEBUG flag is added to CPPFLAGS. + For "Debug" setting, -Og flag is added to CFLAGS. config OPTIMIZATION_LEVEL_DEBUG bool "Debug" config OPTIMIZATION_LEVEL_RELEASE diff --git a/make/project.mk b/make/project.mk index 9088eda85..b646dfc41 100644 --- a/make/project.mk +++ b/make/project.mk @@ -179,7 +179,7 @@ ifneq ("$(CONFIG_OPTIMIZATION_LEVEL_RELEASE)","") OPTIMIZATION_FLAGS = -Os CPPFLAGS += -DNDEBUG else -OPTIMIZATION_FLAGS = -O0 +OPTIMIZATION_FLAGS = -Og endif # Enable generation of debugging symbols From d3d8c0453516e9a2c6b8a340086eef63e7ee0cbe Mon Sep 17 00:00:00 2001 From: Yinling Date: Thu, 20 Oct 2016 16:43:04 +0800 Subject: [PATCH 036/110] add known issue exception when setting mac address: WIFI_ADDR_0101,^WIFI_ADDR_0101 --- components/idf_test/integration_test/KnownIssues | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/idf_test/integration_test/KnownIssues b/components/idf_test/integration_test/KnownIssues index 5e8143040..85b5b2f21 100644 --- a/components/idf_test/integration_test/KnownIssues +++ b/components/idf_test/integration_test/KnownIssues @@ -62,6 +62,10 @@ WIFI_SCAN_0303 ^WIFI_SCAN_0103 ^WIFI_SCAN_0105 +# set mac address may lead to exception +WIFI_ADDR_0101 +^WIFI_ADDR_0101 + # DHCP issues ^TCPIP_DHCP_0301 TCPIP_DHCP_0301 From dfe0dcaed477023443bda6fa2319132b1f5bf20f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 20 Oct 2016 17:17:54 +0800 Subject: [PATCH 037/110] build system: fix setting C**FLAGS from project makefile --- Kconfig | 10 ++++++++-- make/project.mk | 28 +++++++++++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Kconfig b/Kconfig index 97da1f01f..deb0cea83 100644 --- a/Kconfig +++ b/Kconfig @@ -28,9 +28,15 @@ choice OPTIMIZATION_LEVEL default OPTIMIZATION_LEVEL_DEBUG help This option sets optimization level. - For "Release" setting, -Os flag is added to CFLAGS, + + - for "Release" setting, -Os flag is added to CFLAGS, and -DNDEBUG flag is added to CPPFLAGS. - For "Debug" setting, -Og flag is added to CFLAGS. + + - for "Debug" setting, -Og flag is added to CFLAGS. + + To override any of these settings, set CFLAGS and/or CPPFLAGS + in project makefile, before including $(IDF_PATH)/make/project.mk. + config OPTIMIZATION_LEVEL_DEBUG bool "Debug" config OPTIMIZATION_LEVEL_RELEASE diff --git a/make/project.mk b/make/project.mk index b646dfc41..887086fd4 100644 --- a/make/project.mk +++ b/make/project.mk @@ -149,16 +149,16 @@ LDFLAGS ?= -nostdlib \ -Wl,-EL # Set default CPPFLAGS, CFLAGS, CXXFLAGS -# # These are exported so that components can use them when compiling. -# # If you need your component to add CFLAGS/etc for it's own source compilation only, set CFLAGS += in your component's Makefile. -# # If you need your component to add CFLAGS/etc globally for all source -# files, set CFLAGS += in your component's Makefile.projbuild +# files, set CFLAGS += in your component's Makefile.projbuild +# If you need to set CFLAGS/CPPFLAGS/CXXFLAGS at project level, set them in application Makefile +# before including project.mk. Default flags will be added before the ones provided in application Makefile. # CPPFLAGS used by C preprocessor -CPPFLAGS = -DESP_PLATFORM +# If any flags are defined in application Makefile, add them at the end. +CPPFLAGS := -DESP_PLATFORM $(CPPFLAGS) # Warnings-related flags relevant both for C and C++ COMMON_WARNING_FLAGS = -Wall -Werror \ @@ -186,10 +186,24 @@ endif OPTIMIZATION_FLAGS += -ggdb # List of flags to pass to C compiler -CFLAGS = -std=gnu99 $(strip $(OPTIMIZATION_FLAGS) $(COMMON_FLAGS) $(COMMON_WARNING_FLAGS)) +# If any flags are defined in application Makefile, add them at the end. +CFLAGS := $(strip \ + -std=gnu99 \ + $(OPTIMIZATION_FLAGS) \ + $(COMMON_FLAGS) \ + $(COMMON_WARNING_FLAGS) \ + $(CFLAGS)) # List of flags to pass to C++ compiler -CXXFLAGS = -std=gnu++11 -fno-exceptions -fno-rtti $(strip $(OPTIMIZATION_FLAGS) $(COMMON_FLAGS) $(COMMON_WARNING_FLAGS)) +# If any flags are defined in application Makefile, add them at the end. +CXXFLAGS := $(strip \ + -std=gnu++11 \ + -fno-exceptions \ + -fno-rtti \ + $(OPTIMIZATION_FLAGS) \ + $(COMMON_FLAGS) \ + $(COMMON_WARNING_FLAGS) \ + $(CXXFLAGS)) export CFLAGS CPPFLAGS CXXFLAGS From 6b8504005920535764a51d8ec4e9a392e3fcb178 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 20 Oct 2016 20:11:13 +0800 Subject: [PATCH 038/110] Also export HOSTCC etc for components --- make/project.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/make/project.mk b/make/project.mk index 2b2eff3f5..d822d6a21 100644 --- a/make/project.mk +++ b/make/project.mk @@ -176,6 +176,7 @@ HOSTCC := $(CC) HOSTLD := $(LD) HOSTAR := $(AR) HOSTOBJCOPY := $(OBJCOPY) +export HOSTCC HOSTLD HOSTAR HOSTOBJCOPY #Set target compiler. Defaults to whatever the user has #configured as prefix + yer olde gcc commands From feca308f1f13f5191a510064fc1a6c00a6235da2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 21 Oct 2016 10:44:05 +1100 Subject: [PATCH 039/110] rom/spi_flash.h: Remove first parameter of SPI_read_status_high() Corrects the prototype to match the one compiled into ROM. --- components/esp32/include/rom/spi_flash.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/esp32/include/rom/spi_flash.h b/components/esp32/include/rom/spi_flash.h index ba8eebc2c..d182b51ca 100644 --- a/components/esp32/include/rom/spi_flash.h +++ b/components/esp32/include/rom/spi_flash.h @@ -218,7 +218,7 @@ void SelectSpiFunction(uint32_t ishspi); void spi_flash_attach(uint32_t ishspi, bool legacy); /** - * @brief SPI Read Flash status register. We use CMD 0x05. + * @brief SPI Read Flash status register. We use CMD 0x05 (RDSR). * Please do not call this function in SDK. * * @param SpiFlashChip *spi : The information for Flash, which is exported from ld file. @@ -232,7 +232,7 @@ void spi_flash_attach(uint32_t ishspi, bool legacy); SpiFlashOpResult SPI_read_status(SpiFlashChip *spi, uint32_t *status); /** - * @brief SPI Read Flash status register high 16 bit. We use CMD 0x35. + * @brief SPI Read Flash status register bits 8-15. We use CMD 0x35 (RDSR2). * Please do not call this function in SDK. * * @param SpiFlashChip *spi : The information for Flash, which is exported from ld file. @@ -243,7 +243,7 @@ SpiFlashOpResult SPI_read_status(SpiFlashChip *spi, uint32_t *status); * SPI_FLASH_RESULT_ERR : read error. * SPI_FLASH_RESULT_TIMEOUT : read timeout. */ -SpiFlashOpResult SPI_read_status_high(SpiFlashChip *spi, uint32_t *status); +SpiFlashOpResult SPI_read_status_high(uint32_t *status); /** * @brief Write status to Falsh status register. From 7104284e31aff45949bd087a7a278ca58751eef6 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 21 Oct 2016 15:26:11 +1100 Subject: [PATCH 040/110] Bump esptool version Incorporates fix for locked flash #50 --- components/esptool_py/esptool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 197ba605f..5c6962e89 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 197ba605fe0c05e16bf4c5ec07b726adc8d86abc +Subproject commit 5c6962e894e0a118c9a4b5760876433493449260 From 42827ff8693e229bd541c295651f3ef96aa6012d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 18 Oct 2016 19:03:59 +0800 Subject: [PATCH 041/110] bootloader, menuconfig: add flash size setting support --- .../bootloader/src/main/bootloader_config.h | 2 +- .../bootloader/src/main/bootloader_start.c | 37 ++++++++++++++++++- components/esp32/include/rom/spi_flash.h | 6 +++ components/esp32/ld/esp32.rom.ld | 1 + components/esptool_py/Kconfig.projbuild | 27 ++++++++++++++ components/esptool_py/Makefile.projbuild | 7 +++- 6 files changed, 75 insertions(+), 5 deletions(-) diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 709ff41b1..f99a1c94e 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -51,7 +51,7 @@ enum { SPI_SPEED_20M, SPI_SPEED_80M = 0xF }; -/*suppport flash size in esp32 */ +/*supported flash sizes*/ enum { SPI_SIZE_1MB = 0, SPI_SIZE_2MB, diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index a61ea77d5..e87f579f4 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -58,6 +58,7 @@ void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, uint32_t irom_load_addr, uint32_t irom_size, uint32_t entry_addr); +static void update_flash_config(struct flash_hdr* pfhdr); void IRAM_ATTR call_start_cpu0() @@ -258,7 +259,7 @@ void bootloader_main() memset(&bs, 0, sizeof(bs)); ESP_LOGI(TAG, "compile time " __TIME__ ); - /* close watch dog here */ + /* disable watch dog here */ REG_CLR_BIT( RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN ); REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN ); SPIUnlock(); @@ -269,6 +270,8 @@ void bootloader_main() print_flash_info(&fhdr); + update_flash_config(&fhdr); + if (!load_partition_table(&bs, PARTITION_ADD)) { ESP_LOGE(TAG, "load partition table error!"); return; @@ -364,7 +367,7 @@ void unpack_load_app(const partition_pos_t* partition) uint32_t irom_size = 0; /* Reload the RTC memory sections whenever a non-deepsleep reset - is occuring */ + is occurring */ bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET; ESP_LOGD(TAG, "bin_header: %u %u %u %u %08x", image_header.magic, @@ -482,6 +485,36 @@ void IRAM_ATTR set_cache_and_start_app( (*entry)(); } +static void update_flash_config(struct flash_hdr* pfhdr) +{ + uint32_t size; + switch(pfhdr->spi_size) { + case SPI_SIZE_1MB: + size = 1; + break; + case SPI_SIZE_2MB: + size = 2; + break; + case SPI_SIZE_4MB: + size = 4; + break; + case SPI_SIZE_8MB: + size = 8; + break; + case SPI_SIZE_16MB: + size = 16; + break; + default: + size = 2; + } + Cache_Read_Disable( 0 ); + // Set flash chip size + SPIParamCfg(g_rom_flashchip.deviceId, size * 0x100000, 0x10000, 0x1000, 0x100, 0xffff); + // TODO: set mode + // TODO: set frequency + Cache_Flush(0); + Cache_Read_Enable( 0 ); +} void print_flash_info(struct flash_hdr* pfhdr) { diff --git a/components/esp32/include/rom/spi_flash.h b/components/esp32/include/rom/spi_flash.h index d182b51ca..1f14c6617 100644 --- a/components/esp32/include/rom/spi_flash.h +++ b/components/esp32/include/rom/spi_flash.h @@ -503,6 +503,12 @@ void SPI_Write_Encrypt_Disable(void); */ SpiFlashOpResult SPI_Encrypt_Write(uint32_t flash_addr, uint32_t *data, uint32_t len); + +/** @brief Global SpiFlashChip structure used by ROM functions + * + */ +extern SpiFlashChip g_rom_flashchip; + /** * @} */ diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index cc14d3258..0fa28397e 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -286,6 +286,7 @@ PROVIDE ( _global_impure_ptr = 0x3ffae0b0 ); PROVIDE ( gmtime = 0x40059848 ); PROVIDE ( gmtime_r = 0x40059868 ); PROVIDE ( g_phyFuns_instance = 0x3ffae0c4 ); +PROVIDE ( g_rom_flashchip = 0x3ffae270 ); PROVIDE ( gpio_init = 0x40009c20 ); PROVIDE ( gpio_input_get = 0x40009b88 ); PROVIDE ( gpio_input_get_high = 0x40009b9c ); diff --git a/components/esptool_py/Kconfig.projbuild b/components/esptool_py/Kconfig.projbuild index 3da802296..8bab51225 100644 --- a/components/esptool_py/Kconfig.projbuild +++ b/components/esptool_py/Kconfig.projbuild @@ -94,4 +94,31 @@ config ESPTOOLPY_FLASHFREQ default "26m" if ESPTOOLPY_FLASHFREQ_26M default "20m" if ESPTOOLPY_FLASHFREQ_20M + +choice ESPTOOLPY_FLASHSIZE + prompt "Flash size" + default ESPTOOLPY_FLASHSIZE_2MB + help + SPI flash size, in megabytes + +config ESPTOOLPY_FLASHSIZE_1MB + bool "1 MB" +config ESPTOOLPY_FLASHSIZE_2MB + bool "2 MB" +config ESPTOOLPY_FLASHSIZE_4MB + bool "4 MB" +config ESPTOOLPY_FLASHSIZE_8MB + bool "8 MB" +config ESPTOOLPY_FLASHSIZE_16MB + bool "16 MB" +endchoice + +config ESPTOOLPY_FLASHSIZE + string + default "1MB" if ESPTOOLPY_FLASHSIZE_1MB + default "2MB" if ESPTOOLPY_FLASHSIZE_2MB + default "4MB" if ESPTOOLPY_FLASHSIZE_4MB + default "8MB" if ESPTOOLPY_FLASHSIZE_8MB + default "16MB" if ESPTOOLPY_FLASHSIZE_16MB + endmenu diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 4d0dd1b3e..69c01e1e7 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -4,6 +4,7 @@ ESPPORT ?= $(CONFIG_ESPTOOLPY_PORT) ESPBAUD ?= $(CONFIG_ESPTOOLPY_BAUD) ESPFLASHMODE ?= $(CONFIG_ESPTOOLPY_FLASHMODE) ESPFLASHFREQ ?= $(CONFIG_ESPTOOLPY_FLASHFREQ) +ESPFLASHSIZE ?= $(CONFIG_ESPTOOLPY_FLASHSIZE) PYTHON ?= $(call dequote,$(CONFIG_PYTHON)) @@ -15,13 +16,15 @@ ESPTOOLPY_SRC := $(COMPONENT_PATH)/esptool/esptool.py ESPTOOLPY := $(PYTHON) $(ESPTOOLPY_SRC) --chip esp32 ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) +ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) + # the no-stub argument is temporary until esptool.py fully supports compressed uploads -ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) +ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS) ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) $(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) - $(Q) $(ESPTOOLPY) elf2image --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) -o $@ $< + $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) -o $@ $< flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." From 8e8caca2e2dc0bdad5966e59026e2f58975c8621 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 21 Oct 2016 16:02:06 +1100 Subject: [PATCH 042/110] Replace ROM SPIUnlock function with a version that can't lock flash Avoid bug where a bad status read is copied back to flash and can set lock bits. --- components/esp32/ld/esp32.rom.ld | 2 +- components/spi_flash/spi_flash_rom_patch.c | 72 ++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 components/spi_flash/spi_flash_rom_patch.c diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index 0fa28397e..4b0c1c17e 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -1598,7 +1598,7 @@ PROVIDE ( SPI_read_status = 0x4006226c ); PROVIDE ( SPI_read_status_high = 0x40062448 ); PROVIDE ( SPIUnlock = 0x400628b0 ); PROVIDE ( SPI_user_command_read = 0x400621b0 ); -PROVIDE ( spi_w25q16 = 0x3ffae270 ); +PROVIDE ( SPI_flashchip_data = 0x3ffae270 ); PROVIDE ( SPIWrite = 0x40062d50 ); /* This is static function, but can be used, not generated by script*/ PROVIDE ( SPI_write_enable = 0x40062320 ); diff --git a/components/spi_flash/spi_flash_rom_patch.c b/components/spi_flash/spi_flash_rom_patch.c new file mode 100644 index 000000000..7e23beaea --- /dev/null +++ b/components/spi_flash/spi_flash_rom_patch.c @@ -0,0 +1,72 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "rom/spi_flash.h" +#include "soc/spi_reg.h" + +static const uint32_t STATUS_QIE_BIT = (1 << 9); /* Quad Enable */ + +#define SPI_IDX 1 +#define OTH_IDX 0 + +extern SpiFlashChip SPI_flashchip_data; + +static void IRAM_ATTR Wait_SPI_Idle(void) +{ + /* Wait for SPI state machine to be idle */ + while((REG_READ(SPI_EXT2_REG(SPI_IDX)) & SPI_ST)) { + } + while(REG_READ(SPI_EXT2_REG(OTH_IDX)) & SPI_ST) { + } +} + +/* Modified version of SPIUnlock() that replaces version in ROM. + + This works around a bug where SPIUnlock sometimes reads the wrong + high status byte (RDSR2 result) and then copies it back to the + flash status, which can cause the CMP bit or Status Register + Protect bit to become set. + + Like other ROM SPI functions, this function is not designed to be + called directly from an RTOS environment without taking precautions + about interrupts, CPU coordination, flash mapping. However some of + the functions in esp_spi_flash.c call it. + */ +SpiFlashOpResult IRAM_ATTR SPIUnlock(void) +{ + uint32_t status; + + Wait_SPI_Idle(); + + if (SPI_read_status_high(&status) != SPI_FLASH_RESULT_OK) { + return SPI_FLASH_RESULT_ERR; + } + + /* Clear all bits except QIE, if it is set. + (This is different from ROM SPIUnlock, which keeps all bits as-is.) + */ + status &= STATUS_QIE_BIT; + + Wait_SPI_Idle(); + REG_WRITE(SPI_CMD_REG(SPI_IDX), SPI_FLASH_WREN); + while(REG_READ(SPI_CMD_REG(SPI_IDX)) != 0) { + } + Wait_SPI_Idle(); + + SET_PERI_REG_MASK(SPI_CTRL_REG(SPI_IDX), SPI_WRSR_2B); + if (SPI_write_status(&SPI_flashchip_data, status) != SPI_FLASH_RESULT_OK) { + return SPI_FLASH_RESULT_ERR; + } + + return SPI_FLASH_RESULT_OK; +} From f37e70ebd60a3b807a09299006ba94c3f32f65dc Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 21 Oct 2016 17:44:34 +1100 Subject: [PATCH 043/110] Bootloader: Export IS_BOOTLOADER_BUILD during make process --- components/bootloader/Makefile.projbuild | 2 +- components/bootloader/src/Makefile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 91be3a6d6..50c95f9fe 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -8,7 +8,7 @@ # basically runs Make in the src/ directory but it needs to zero some variables # the ESP-IDF project.mk makefile exports first, to not let them interfere. # -ifeq ("$(IS_BOOTLOADER_BUILD)","") +ifndef IS_BOOTLOADER_BUILD BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH) BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index f30e314a5..ddf664d44 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -10,6 +10,7 @@ COMPONENTS := esptool_py bootloader log # # IS_BOOTLOADER_BUILD tells the component Makefile.projbuild to be a no-op IS_BOOTLOADER_BUILD := 1 +export IS_BOOTLOADER_BUILD #We cannot include the esp32 component directly but we need its includes. #This is fixed by adding CFLAGS from Makefile.projbuild From 1413ec3ff0ba9409bbb9f839be644be25543f20f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 21 Oct 2016 17:08:05 +1100 Subject: [PATCH 044/110] Remove SPIUnlock from linker script symbols Add a comment about why it was removed and where it went. --- components/bootloader/src/Makefile | 2 +- components/esp32/ld/esp32.rom.ld | 3 ++- components/spi_flash/component.mk | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index ddf664d44..add9c15d6 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -4,7 +4,7 @@ # PROJECT_NAME := bootloader -COMPONENTS := esptool_py bootloader log +COMPONENTS := esptool_py bootloader log spi_flash # The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included. # diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index 4b0c1c17e..5266a679b 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -1585,6 +1585,8 @@ PROVIDE ( SPIEraseBlock = 0x40062c4c ); PROVIDE ( SPIEraseChip = 0x40062c14 ); PROVIDE ( SPIEraseSector = 0x40062ccc ); PROVIDE ( spi_flash_attach = 0x40062a6c ); +/* NB: SPIUnlock @ 0x400628b0 has been replaced with an updated + version in the "spi_flash" component */ PROVIDE ( SPILock = 0x400628f0 ); PROVIDE ( SPIMasterReadModeCnfig = 0x40062b64 ); PROVIDE ( spi_modes = 0x3ff99270 ); @@ -1596,7 +1598,6 @@ PROVIDE ( SPIReadModeCnfig = 0x40062944 ); PROVIDE ( SPI_read_status = 0x4006226c ); /* This is static function, but can be used, not generated by script*/ PROVIDE ( SPI_read_status_high = 0x40062448 ); -PROVIDE ( SPIUnlock = 0x400628b0 ); PROVIDE ( SPI_user_command_read = 0x400621b0 ); PROVIDE ( SPI_flashchip_data = 0x3ffae270 ); PROVIDE ( SPIWrite = 0x40062d50 ); diff --git a/components/spi_flash/component.mk b/components/spi_flash/component.mk index ef497a7ec..459da0641 100755 --- a/components/spi_flash/component.mk +++ b/components/spi_flash/component.mk @@ -1,3 +1,8 @@ COMPONENT_ADD_INCLUDEDIRS := include +ifdef IS_BOOTLOADER_BUILD +# Bootloader needs updated SPIUnlock from this file +COMPONENT_OBJS := spi_flash_rom_patch.o +endif + include $(IDF_PATH)/make/component_common.mk From 53146799a003439e3594c3c1957306889af76aad Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 21 Oct 2016 17:59:57 +0800 Subject: [PATCH 045/110] Initial addition of wdt and brownout code --- components/esp32/Kconfig | 113 +++++++++++++ components/esp32/brownout.c | 37 ++++ components/esp32/cpu_start.c | 12 ++ components/esp32/include/esp_brownout.h | 6 + components/esp32/include/esp_int_wdt.h | 6 + components/esp32/include/esp_task_wdt.h | 8 + components/esp32/int_wdt.c | 82 +++++++++ components/esp32/task_wdt.c | 158 ++++++++++++++++++ .../include/freertos/FreeRTOSConfig.h | 4 +- 9 files changed, 424 insertions(+), 2 deletions(-) create mode 100644 components/esp32/brownout.c create mode 100644 components/esp32/include/esp_brownout.h create mode 100644 components/esp32/include/esp_int_wdt.h create mode 100644 components/esp32/include/esp_task_wdt.h create mode 100644 components/esp32/int_wdt.c create mode 100644 components/esp32/task_wdt.c diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 535df23eb..c40d7930d 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -140,4 +140,117 @@ config ULP_COPROC_RESERVE_MEM default 0 depends on !ULP_COPROC_ENABLED +menu "Watchdogs & brown-out detection" + +config INT_WDT + bool "Interrupt watchdog" + default y + help + This watchdog timer can detect if the FreeRTOS tick interrupt has not been called for a certain time, + either because a task turned off interrupts and did not turn them on for a long time, or because an + interrupt handler did not return. It will try to invoke the panic handler first and failing that + reset the SoC. + +config INT_WDT_TIMEOUT_MS_MIN + default (2000/CONFIG_FREERTOS_HZ) + +config INT_WDT_TIMEOUT_MS + int "Interrupt watchdog timeout (ms)" + depends on INT_WDT + default 100 + range INT_WDT_TIMEOUT_MS_MIN 10000 + help + The timeout of the watchdog, in miliseconds. + +config TASK_WDT + bool "Task watchdog" + default y + help + This watchdog timer can be used to make sure individual tasks are still running. + +config TASK_WDT_PANIC + bool "Invoke panic handler when Task Watchdog is triggered" + depends on TASK_WDT + default n + help + Normally, the Task Watchdog will only print out a warning if it detects it has not + been fed. If this is enabled, it will invoke the panic handler instead, which + can then halt or reboot the chip. + +config TASK_WDT_TIMEOUT_S + int "Task watchdog timeout (seconds)" + depends on TASK_WDT + range 1 60 + default 5 + help + Timeout for the task WDT, in seconds. + +config TASK_WDT_CHECK_IDLE_TASK + bool "Task watchdog watches idle tasks" + depends on TASK_WDT + default y + help + With this turned on, the task WDT can detect if the idle task is not called within the task + watchdog timeout period. The idle task not being called usually is a symptom of another + task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the + idle task getting some runtime every now and then. + +config BROWNOUT_DET + bool "Hardware brownout detect & reset" + default y + help + The ESP32 has a built-in brownout detector which can detect if the voltage is lower than + a specific value. If this happens, it will reset the chip in order to prevent unintended + behaviour. + +choice BROWNOUT_DET_LVL_SEL + prompt "Brownout voltage level" + depends on BROWNOUT_DET + default BROWNOUT_DET_LVL_SEL_25 + help + The brownout detector will reset the chip when the supply voltage is below this level. + +config BROWNOUT_DET_LVL_SEL_0 + bool "2.1V" +config BROWNOUT_DET_LVL_SEL_1 + bool "2.2V" +config BROWNOUT_DET_LVL_SEL_2 + bool "2.3V" +config BROWNOUT_DET_LVL_SEL_3 + bool "2.4V" +config BROWNOUT_DET_LVL_SEL_4 + bool "2.5V" +config BROWNOUT_DET_LVL_SEL_5 + bool "2.6V" +config BROWNOUT_DET_LVL_SEL_6 + bool "2.7V" +config BROWNOUT_DET_LVL_SEL_7 + bool "2.8V" +endchoice + +config BROWNOUT_DET_LVL + int + default 0 if BROWNOUT_DET_LVL_SEL_0 + default 1 if BROWNOUT_DET_LVL_SEL_1 + default 2 if BROWNOUT_DET_LVL_SEL_2 + default 3 if BROWNOUT_DET_LVL_SEL_3 + default 4 if BROWNOUT_DET_LVL_SEL_4 + default 5 if BROWNOUT_DET_LVL_SEL_5 + default 6 if BROWNOUT_DET_LVL_SEL_6 + default 7 if BROWNOUT_DET_LVL_SEL_7 + + +config BROWNOUT_DET_RESETDELAY + int "Brownout reset delay (in uS)" + depends on BROWNOUT_DET + range 0 6820 + default 1000 + help + The brownout detector can reset the chip after a certain delay, in order to make sure e.g. a voltage dip has entirely passed + before trying to restart the chip. You can set the delay here. + +endmenu + + + endmenu diff --git a/components/esp32/brownout.c b/components/esp32/brownout.c new file mode 100644 index 000000000..173d63a0c --- /dev/null +++ b/components/esp32/brownout.c @@ -0,0 +1,37 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#include +#include +#include +#include +#include "sdkconfig.h" +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" + + +void esp_brownout_init() { +// WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, +// RTC_CNTL_BROWN_OUT_ENA | (CONFIG_BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S) | +// RTC_CNTL_BROWN_OUT_RST_ENA | (((CONFIG_BROWNOUT_DET_RESETDELAY*150)/1000) << RTC_CNTL_BROWN_OUT_RST_WAIT_S) | +// RTC_CNTL_BROWN_OUT_PD_RF_ENA|RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA); + + + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, + RTC_CNTL_BROWN_OUT_ENA | (4 << RTC_CNTL_DBROWN_OUT_THRES_S) | + RTC_CNTL_BROWN_OUT_RST_ENA | (0x3FF << RTC_CNTL_BROWN_OUT_RST_WAIT_S) | + RTC_CNTL_BROWN_OUT_PD_RF_ENA | RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA); + +} \ No newline at end of file diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 7b2ccdc60..6a482c539 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -43,6 +43,8 @@ #include "esp_ipc.h" #include "esp_log.h" +#include "esp_brownout.h" + void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))); void start_cpu0_default(void) IRAM_ATTR; #if !CONFIG_FREERTOS_UNICORE @@ -137,6 +139,16 @@ void start_cpu0_default(void) do_global_ctors(); esp_ipc_init(); spi_flash_init(); +#if CONFIG_BROWNOUT_DET + esp_brownout_init(); +#endif +#if CONFIG_INT_WDT + int_wdt_init() +#endif +#if CONFIG_TASK_WDT + task_wdt_init() +#endif + xTaskCreatePinnedToCore(&main_task, "main", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL, 0); diff --git a/components/esp32/include/esp_brownout.h b/components/esp32/include/esp_brownout.h new file mode 100644 index 000000000..acce05e0d --- /dev/null +++ b/components/esp32/include/esp_brownout.h @@ -0,0 +1,6 @@ +#ifndef BROWNOUT_H +#define BROWNOUT_H + +void esp_brownout_init(); + +#endif \ No newline at end of file diff --git a/components/esp32/include/esp_int_wdt.h b/components/esp32/include/esp_int_wdt.h new file mode 100644 index 000000000..81404e305 --- /dev/null +++ b/components/esp32/include/esp_int_wdt.h @@ -0,0 +1,6 @@ +#ifndef INT_WDT_H +#define INT_WDT_H + +void int_wdt_init(); + +#endif \ No newline at end of file diff --git a/components/esp32/include/esp_task_wdt.h b/components/esp32/include/esp_task_wdt.h new file mode 100644 index 000000000..9163e6907 --- /dev/null +++ b/components/esp32/include/esp_task_wdt.h @@ -0,0 +1,8 @@ +#ifndef TASK_WDT_H +#define TASK_WDT_H + +void task_wdt_feed(); +void task_wdt_delete(); +void task_wdt_init(); + +#endif \ No newline at end of file diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c new file mode 100644 index 000000000..392ed6b6a --- /dev/null +++ b/components/esp32/int_wdt.c @@ -0,0 +1,82 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/* +This routine enables a watchdog to catch instances of processes disabling +interrupts for too long, or code within interrupt handlers taking too long. +It does this by setting up a watchdog which gets fed from the FreeRTOS +task switch interrupt. When this watchdog times out, initially it will call +a high-level interrupt routine that will panic FreeRTOS in order to allow +for forensic examination of the state of the CPU. When this interrupt +handler is not called and the watchdog times out a second time, it will +reset the SoC. + +This uses the TIMERG1 WDT. +*/ + +#include "sdkconfig.h" +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_err.h" +#include "esp_intr.h" +#include "soc/timer_group_struct.h" + +#include "esp_int_wdt.h" + +#if CONFIG_INT_WDT + + +#define WDT_INT_NUM 24 + + +#define WDT_WRITE_KEY 0x50D83AA1 + +static void esp_int_wdt_isr(void *arg) { + abort(); +} + + +void int_wdt_init() { + TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG1.wdt_config0.level_int_en=1; + TIMERG1.wdt_config0.stg0=1; //1st stage timeout: interrupt + TIMERG1.wdt_config0.stg1=3; //2nd stage timeout: reset system + TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_config0.en=1; + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + ESP_INTR_DISABLE(WDT_INT_NUM); + intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); + xt_set_interrupt_handler(WDT_INT_NUM, int_wdt_isr, NULL); + ESP_INTR_ENABLE(WDT_INT_NUM); +} + + + +void vApplicationTickHook(void) { + TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; +} + +#endif \ No newline at end of file diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c new file mode 100644 index 000000000..7ae3dfb2e --- /dev/null +++ b/components/esp32/task_wdt.c @@ -0,0 +1,158 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/* +This routine enables a more general-purpose task watchdog: tasks can individually +feed the watchdog and the watchdog will bark if one or more tasks haven't fed the +watchdog within the specified time. Optionally, the idle tasks can also configured +to feed the watchdog in a similar fashion, to detect CPU starvation. + +This uses the TIMERG0 WDT. +*/ + +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_err.h" +#include "esp_intr.h" +#include "soc/timer_group_struct.h" +#include "esp_log.h" + +#include "esp_task_wdt.h" + +#if CONFIG_TASK_WDT + +static const char* TAG = "task_wdt"; + + +typedef struct wdt_task_t wdt_task_t; +struct wdt_task_t { + TaskHandle_t task_handle; + bool fed_watchdog; + wdt_task_t *next; +}; + + +static wdt_task_t *wdt_task_list=NULL; + +#define WDT_INT_NUM 24 + + +#define WDT_WRITE_KEY 0x50D83AA1 + +static void task_wdt_isr(void *arg) { + abort(); +} + + +void task_wdt_feed() { + wdt_task_t *wdttask=wdt_task_list; + bool found_task=false, do_feed_wdt=true; + TaskHandle_t handle=xTaskGetCurrentTaskHandle(); + //Walk the linked list of wdt tasks to find this one, as well as see if we need to feed + //the real watchdog timer. + while (wdttask!=NULL) { + //See if we are at the current task. + if (wdttask->task_handle == handle) { + wdttask->fed_watchdog=true; + found_task=true; + } + //If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog. + if (!wdttask->fed_watchdog) do_feed_wdt=false; + //Next entry. + wdttask=wdttask->next; + } + + if (!found_task) { + //This is the first time the task calls the task_wdt_feed function. Create a new entry for it in + //the linked list. + wdt_task_t *newtask=malloc(sizeof(wdt_task_t)); + memset(newtask, 0, sizeof(wdt_task_t)); + newtask->task_handle=handle; + newtask->fed_watchdog=true; + if (wdt_task_list == NULL) { + wdt_task_list=newtask; + } else { + wdttask=wdt_task_list; + while (!(wdttask->next == NULL)) wdttask=wdttask->next; + wdttask->next=wdttask; + } + } + if (do_feed_wdt) { + //All tasks have checked in; time to feed the hw watchdog. + TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; + } +} + +void task_wdt_delete() { + TaskHandle_t handle=xTaskGetCurrentTaskHandle(); + wdt_task_t *wdttask=wdt_task_list; + //Wdt task list can't be empty + if (!wdt_task_list) { + ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?"); + return; + } + if (handle==wdt_task_list) { + //Current task is first on list. + wdt_task_list=wdt_task_list->next; + free(wdttask); + } else { + //Find current task in list + while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next; + if (!wdttask->next) { + ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!"); + return; + } + wdt_task_t *freeme=wdttask->next; + wdttask->next=wdttask->next->next; + free(freeme); + } +} + +void task_wdt_init() { + TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG0.wdt_config0.level_int_en=1; + TIMERG0.wdt_config0.stg0=1; //1st stage timeout: interrupt + TIMERG0.wdt_config0.stg1=3; //2nd stage timeout: reset system + TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000; //Set timeout before interrupt + TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000; //Set timeout before reset + TIMERG0.wdt_config0.en=1; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; + ESP_INTR_DISABLE(ETS_T0_WDT_INUM); + intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); + xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL); + ESP_INTR_ENABLE(ETS_T0_WDT_INUM); +} + + +#if CONFIG_TASK_WDT_CHECK_IDLE_TASK +void vApplicationIdleHook(void) { + task_wdt_feed(); +} +#endif + +#endif \ No newline at end of file diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index d1958e770..a5483d6bb 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -152,9 +152,9 @@ *----------------------------------------------------------*/ #define configUSE_PREEMPTION 1 -#define configUSE_IDLE_HOOK 0 +#define configUSE_IDLE_HOOK ( CONFIG_TASK_WDT_CHECK_IDLE_TASK ) -#define configUSE_TICK_HOOK 0 +#define configUSE_TICK_HOOK ( CONFIG_INT_WDT ) #define configTICK_RATE_HZ ( CONFIG_FREERTOS_HZ ) From 2b8a49365954b1ecd74ccaef4523b0543ee73ff6 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 21 Oct 2016 18:01:08 +0800 Subject: [PATCH 046/110] Add licenses to Trax files --- components/xtensa-debug-module/eri.c | 13 +++++++++++++ components/xtensa-debug-module/trax.c | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/components/xtensa-debug-module/eri.c b/components/xtensa-debug-module/eri.c index e2c7e41eb..fc96b531f 100644 --- a/components/xtensa-debug-module/eri.c +++ b/components/xtensa-debug-module/eri.c @@ -1,3 +1,16 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include #include "eri.h" diff --git a/components/xtensa-debug-module/trax.c b/components/xtensa-debug-module/trax.c index d216a3df9..5174e4477 100644 --- a/components/xtensa-debug-module/trax.c +++ b/components/xtensa-debug-module/trax.c @@ -1,3 +1,17 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include #include "soc/dport_reg.h" #include "sdkconfig.h" From ae5c5630803c67964540d3943b661ea1a2c822cd Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 21 Oct 2016 19:30:29 +0800 Subject: [PATCH 047/110] Brownout works (in as far brownout can work...), int wdt works. --- components/esp32/Kconfig | 9 +++------ components/esp32/brownout.c | 12 +++--------- components/esp32/cpu_start.c | 6 ++++-- components/esp32/int_wdt.c | 9 +++------ components/freertos/xtensa_vectors.S | 13 ++++++++++--- 5 files changed, 23 insertions(+), 26 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index c40d7930d..a2faa926a 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -151,16 +151,13 @@ config INT_WDT interrupt handler did not return. It will try to invoke the panic handler first and failing that reset the SoC. -config INT_WDT_TIMEOUT_MS_MIN - default (2000/CONFIG_FREERTOS_HZ) - config INT_WDT_TIMEOUT_MS int "Interrupt watchdog timeout (ms)" depends on INT_WDT - default 100 - range INT_WDT_TIMEOUT_MS_MIN 10000 + default 10 + range INT_WDT_TIMEOUT_MIN 10000 help - The timeout of the watchdog, in miliseconds. + The timeout of the watchdog, in miliseconds. Make this higher than the FreeRTOS tick rate. config TASK_WDT bool "Task watchdog" diff --git a/components/esp32/brownout.c b/components/esp32/brownout.c index 173d63a0c..8e8805b8e 100644 --- a/components/esp32/brownout.c +++ b/components/esp32/brownout.c @@ -23,15 +23,9 @@ void esp_brownout_init() { -// WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, -// RTC_CNTL_BROWN_OUT_ENA | (CONFIG_BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S) | -// RTC_CNTL_BROWN_OUT_RST_ENA | (((CONFIG_BROWNOUT_DET_RESETDELAY*150)/1000) << RTC_CNTL_BROWN_OUT_RST_WAIT_S) | -// RTC_CNTL_BROWN_OUT_PD_RF_ENA|RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA); - - WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, - RTC_CNTL_BROWN_OUT_ENA | (4 << RTC_CNTL_DBROWN_OUT_THRES_S) | - RTC_CNTL_BROWN_OUT_RST_ENA | (0x3FF << RTC_CNTL_BROWN_OUT_RST_WAIT_S) | - RTC_CNTL_BROWN_OUT_PD_RF_ENA | RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA); + RTC_CNTL_BROWN_OUT_ENA | (CONFIG_BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S) | + RTC_CNTL_BROWN_OUT_RST_ENA | (((CONFIG_BROWNOUT_DET_RESETDELAY*150)/1000) << RTC_CNTL_BROWN_OUT_RST_WAIT_S) | + RTC_CNTL_BROWN_OUT_PD_RF_ENA|RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA); } \ No newline at end of file diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 6a482c539..51f6cebb2 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -44,6 +44,8 @@ #include "esp_log.h" #include "esp_brownout.h" +#include "esp_int_wdt.h" +#include "esp_task_wdt.h" void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))); void start_cpu0_default(void) IRAM_ATTR; @@ -143,10 +145,10 @@ void start_cpu0_default(void) esp_brownout_init(); #endif #if CONFIG_INT_WDT - int_wdt_init() + int_wdt_init(); #endif #if CONFIG_TASK_WDT - task_wdt_init() + task_wdt_init(); #endif xTaskCreatePinnedToCore(&main_task, "main", diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c index 392ed6b6a..2b4d9e084 100644 --- a/components/esp32/int_wdt.c +++ b/components/esp32/int_wdt.c @@ -47,11 +47,6 @@ This uses the TIMERG1 WDT. #define WDT_WRITE_KEY 0x50D83AA1 -static void esp_int_wdt_isr(void *arg) { - abort(); -} - - void int_wdt_init() { TIMERG1.wdt_wprotect=WDT_WRITE_KEY; TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS @@ -67,7 +62,9 @@ void int_wdt_init() { TIMERG1.wdt_wprotect=0; ESP_INTR_DISABLE(WDT_INT_NUM); intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); - xt_set_interrupt_handler(WDT_INT_NUM, int_wdt_isr, NULL); + //We do not register a handler for the interrupt because it is interrupt level 4 which + //is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for + //this interrupt. ESP_INTR_ENABLE(WDT_INT_NUM); } diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index 7c2fc2960..89a47f741 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -339,12 +339,12 @@ _xt_panic: rsr a0, EXCSAVE_1 /* save interruptee's a0 */ s32i a0, sp, XT_STK_A0 - /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */ - movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE + /* Set up PS for C, disable all interrupts, and clear EXCM. */ + movi a0, PS_INTLEVEL(7) | PS_UM | PS_WOE wsr a0, PS //Call panic handler - mov a2,sp + mov a6,sp call4 panicHandler 1: j 1b /* loop infinitely */ @@ -1607,6 +1607,13 @@ _xt_highint4: ADD HIGH PRIORITY LEVEL 4 INTERRUPT HANDLER CODE HERE. */ + /* On the ESP32, this level is used for the INT_WDT handler. If that triggers, the program is stuck with interrupts + off and the CPU should panic. */ + rsr a0, EXCSAVE_4 + wsr a0, EXCSAVE_1 /* panic handler reads this register */ + call0 _xt_panic + + .align 4 .L_xt_highint4_exit: rsr a0, EXCSAVE_4 /* restore a0 */ From 60fb9a8c813778ab2b3b149d84eb888d046992e2 Mon Sep 17 00:00:00 2001 From: liuzhifu Date: Sun, 23 Oct 2016 00:49:41 +0800 Subject: [PATCH 048/110] components/lwip - add per socket tcp window Add code to support per socket tcp window and tcp send buffer size configuration. --- components/lwip/api/api_msg.c | 12 +++++----- components/lwip/api/lwip_debug.c | 3 +++ components/lwip/api/sockets.c | 20 ++++++++++++++++ components/lwip/core/init.c | 16 ++++++------- components/lwip/core/tcp.c | 23 +++++++++++-------- components/lwip/core/tcp_in.c | 6 ++--- components/lwip/core/tcp_out.c | 21 ++++++++++++----- components/lwip/include/lwip/lwip/opt.h | 12 +++++----- .../lwip/include/lwip/lwip/priv/tcp_priv.h | 2 +- components/lwip/include/lwip/lwip/sockets.h | 6 ++++- components/lwip/include/lwip/lwip/tcp.h | 13 +++++++++-- components/lwip/include/lwip/port/lwipopts.h | 21 +++++++++-------- 12 files changed, 103 insertions(+), 52 deletions(-) diff --git a/components/lwip/api/api_msg.c b/components/lwip/api/api_msg.c index e8e967ef4..9cac2e0e9 100755 --- a/components/lwip/api/api_msg.c +++ b/components/lwip/api/api_msg.c @@ -314,8 +314,8 @@ poll_tcp(void *arg, struct tcp_pcb *pcb) if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) { /* If the queued byte- or pbuf-count drops below the configured low-water limit, let select mark this pcb as writable again. */ - if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && - (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT(conn->pcb.tcp)) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT(conn->pcb.tcp))) { conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); } @@ -348,8 +348,8 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) /* If the queued byte- or pbuf-count drops below the configured low-water limit, let select mark this pcb as writable again. */ - if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && - (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT(conn->pcb.tcp) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT(conn->pcb.tcp)))) { conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); } @@ -1540,8 +1540,8 @@ err_mem: and let poll_tcp check writable space to mark the pcb writable again */ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; - } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || - (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) { + } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT(conn->pcb.tcp)) || + (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT(conn->pcb.tcp))) { /* The queued byte- or pbuf-count exceeds the configured low-water limit, let select mark this pcb as non-writable. */ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); diff --git a/components/lwip/api/lwip_debug.c b/components/lwip/api/lwip_debug.c index 1e5fed40d..d73a23e1a 100644 --- a/components/lwip/api/lwip_debug.c +++ b/components/lwip/api/lwip_debug.c @@ -48,6 +48,9 @@ static void dbg_lwip_tcp_pcb_one_show(struct tcp_pcb* pcb) printf("rttest=%d rtseq=%d sa=%d sv=%d\n", pcb->rttest, pcb->rtseq, pcb->sa, pcb->sv); printf("rto=%d nrtx=%d\n", pcb->rto, pcb->nrtx); printf("dupacks=%d lastack=%d\n", pcb->dupacks, pcb->lastack); +#if ESP_PER_SOC_TCP_WND + printf("per_soc_window=%d per_soc_snd_buf=%d\n", pcb->per_soc_tcp_wnd, pcb->per_soc_tcp_snd_buf); +#endif printf("cwnd=%d ssthreash=%d\n", pcb->cwnd, pcb->ssthresh); printf("snd_next=%d snd_wl1=%d snd_wl2=%d\n", pcb->snd_nxt, pcb->snd_wl1, pcb->snd_wl2); printf("snd_lbb=%d snd_wnd=%d snd_wnd_max=%d\n", pcb->snd_lbb, pcb->snd_wnd, pcb->snd_wnd_max); diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index 350847b57..15226be20 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -2395,6 +2395,16 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt s, *(int *)optval)); break; #endif /* LWIP_TCP_KEEPALIVE */ + +#if ESP_PER_SOC_TCP_WND + case TCP_WINDOW: + *(int*)optval = (int)sock->conn->pcb.tcp->per_soc_tcp_wnd; + break; + case TCP_SNDBUF: + *(int*)optval = (int)sock->conn->pcb.tcp->per_soc_tcp_snd_buf; + break; +#endif + default: LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", s, optname)); @@ -2792,6 +2802,16 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ s, sock->conn->pcb.tcp->keep_cnt)); break; #endif /* LWIP_TCP_KEEPALIVE */ + +#if ESP_PER_SOC_TCP_WND + case TCP_WINDOW: + sock->conn->pcb.tcp->per_soc_tcp_wnd = ((u32_t)(*(const int*)optval)) * TCP_MSS; + break; + case TCP_SNDBUF: + sock->conn->pcb.tcp->per_soc_tcp_snd_buf = ((u32_t)(*(const int*)optval)) * TCP_MSS; + break; +#endif + default: LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", s, optname)); diff --git a/components/lwip/core/init.c b/components/lwip/core/init.c index 2a410d0e4..f974aedaf 100755 --- a/components/lwip/core/init.c +++ b/components/lwip/core/init.c @@ -135,13 +135,15 @@ //#endif #else /* LWIP_WND_SCALE */ -#ifndef LWIP_ESP8266 +#if (ESP_PER_SOC_TCP_WND == 0) #if (LWIP_TCP && (TCP_WND > 0xffff)) #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h (or enable window scaling)" #endif #endif #endif /* LWIP_WND_SCALE */ + +#if (ESP_PER_SOC_TCP_WND == 0) #if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" #endif @@ -149,7 +151,6 @@ #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" #endif -#ifndef LWIP_ESP8266 #if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" #endif @@ -289,6 +290,8 @@ #if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif + +#if (ESP_PER_SOC_TCP_WND == 0) #if TCP_SND_BUF < (2 * TCP_MSS) #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif @@ -304,6 +307,8 @@ #if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif +#endif + #if !MEMP_MEM_MALLOC && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif @@ -328,13 +333,6 @@ void lwip_init(void) { -#ifdef LWIP_ESP8266 -// MEMP_NUM_TCP_PCB = 5; -// TCP_WND = (4 * TCP_MSS); -// TCP_MAXRTX = 12; -// TCP_SYNMAXRTX = 6; -#endif - /* Modules initialization */ stats_init(); #if !NO_SYS diff --git a/components/lwip/core/tcp.c b/components/lwip/core/tcp.c index e8fda52c8..e7ea56103 100755 --- a/components/lwip/core/tcp.c +++ b/components/lwip/core/tcp.c @@ -638,7 +638,7 @@ u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb) { u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd; - if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) { + if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND(pcb) / 2), pcb->mss))) { /* we can advertise more window */ pcb->rcv_ann_wnd = pcb->rcv_wnd; return new_right_edge - pcb->rcv_ann_right_edge; @@ -694,10 +694,10 @@ tcp_recved(struct tcp_pcb *pcb, u16_t len) wnd_inflation = tcp_update_rcv_ann_wnd(pcb); /* If the change in the right edge of window is significant (default - * watermark is TCP_WND/4), then send an explicit update now. + * watermark is TCP_WND(pcb)/4), then send an explicit update now. * Otherwise wait for a packet to be sent in the normal course of * events (or more window to be available later) */ - if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) { + if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD(pcb)) { tcp_ack_now(pcb); tcp_output(pcb); } @@ -827,9 +827,9 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, pcb->snd_lbb = iss - 1; /* Start with a window that does not need scaling. When window scaling is enabled and used, the window is enlarged when both sides agree on scaling. */ - pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND); + pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND(pcb)); pcb->rcv_ann_right_edge = pcb->rcv_nxt; - pcb->snd_wnd = TCP_WND; + pcb->snd_wnd = TCP_WND(pcb); /* As initial send MSS, we use TCP_MSS but limit it to 536. The send MSS is updated when an MSS option is received. */ pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; @@ -837,7 +837,7 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip); #endif /* TCP_CALCULATE_EFF_SEND_MSS */ pcb->cwnd = 1; - pcb->ssthresh = TCP_WND; + pcb->ssthresh = TCP_WND(pcb); #if LWIP_CALLBACK_API pcb->connected = connected; #else /* LWIP_CALLBACK_API */ @@ -1581,11 +1581,11 @@ tcp_alloc(u8_t prio) if (pcb != NULL) { memset(pcb, 0, sizeof(struct tcp_pcb)); pcb->prio = prio; - pcb->snd_buf = TCP_SND_BUF; + pcb->snd_buf = TCP_SND_BUF_DEFAULT; pcb->snd_queuelen = 0; /* Start with a window that does not need scaling. When window scaling is enabled and used, the window is enlarged when both sides agree on scaling. */ - pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND); + pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND(pcb)); #if LWIP_WND_SCALE /* snd_scale and rcv_scale are zero unless both sides agree to use scaling */ pcb->snd_scale = 0; @@ -1608,7 +1608,6 @@ tcp_alloc(u8_t prio) pcb->snd_lbb = iss; pcb->tmr = tcp_ticks; pcb->last_timer = tcp_timer_ctr; - pcb->polltmr = 0; #if LWIP_CALLBACK_API @@ -1624,7 +1623,13 @@ tcp_alloc(u8_t prio) #endif /* LWIP_TCP_KEEPALIVE */ pcb->keep_cnt_sent = 0; + +#if ESP_PER_SOC_TCP_WND + pcb->per_soc_tcp_wnd = TCP_WND_DEFAULT; + pcb->per_soc_tcp_snd_buf = TCP_SND_BUF_DEFAULT; +#endif } + return pcb; } diff --git a/components/lwip/core/tcp_in.c b/components/lwip/core/tcp_in.c index 25d740385..90ebf1723 100755 --- a/components/lwip/core/tcp_in.c +++ b/components/lwip/core/tcp_in.c @@ -1745,9 +1745,9 @@ tcp_parseopt(struct tcp_pcb *pcb) pcb->rcv_scale = TCP_RCV_SCALE; pcb->flags |= TF_WND_SCALE; /* window scaling is enabled, we can use the full receive window */ - LWIP_ASSERT("window not at default value", pcb->rcv_wnd == TCPWND_MIN16(TCP_WND)); - LWIP_ASSERT("window not at default value", pcb->rcv_ann_wnd == TCPWND_MIN16(TCP_WND)); - pcb->rcv_wnd = pcb->rcv_ann_wnd = TCP_WND; + LWIP_ASSERT("window not at default value", pcb->rcv_wnd == TCPWND_MIN16(TCP_WND(pcb))); + LWIP_ASSERT("window not at default value", pcb->rcv_ann_wnd == TCPWND_MIN16(TCP_WND(pcb))); + pcb->rcv_wnd = pcb->rcv_ann_wnd = TCP_WND(pcb); } break; #endif diff --git a/components/lwip/core/tcp_out.c b/components/lwip/core/tcp_out.c index aac02e4eb..5f10befae 100755 --- a/components/lwip/core/tcp_out.c +++ b/components/lwip/core/tcp_out.c @@ -336,9 +336,9 @@ tcp_write_checks(struct tcp_pcb *pcb, u16_t len) /* If total number of pbufs on the unsent/unacked queues exceeds the * configured maximum, return an error */ /* check for configured max queuelen and possible overflow */ - if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN(pcb)) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n", - pcb->snd_queuelen, TCP_SND_QUEUELEN)); + pcb->snd_queuelen, TCP_SND_QUEUELEN(pcb))); TCP_STATS_INC(tcp.memerr); pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; @@ -606,9 +606,9 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) /* Now that there are more segments queued, we check again if the * length of the queue exceeds the configured maximum or * overflows. */ - if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + if ((queuelen > TCP_SND_QUEUELEN(pcb)) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: queue too long %"U16_F" (%d)\n", - queuelen, (int)TCP_SND_QUEUELEN)); + queuelen, (int)TCP_SND_QUEUELEN(pcb))); pbuf_free(p); goto memerr; } @@ -766,10 +766,10 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags) (flags & (TCP_SYN | TCP_FIN)) != 0); /* check for configured max queuelen and possible overflow (FIN flag should always come through!) */ - if (((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) && + if (((pcb->snd_queuelen >= TCP_SND_QUEUELEN(pcb)) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) && ((flags & TCP_FIN) == 0)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n", - pcb->snd_queuelen, TCP_SND_QUEUELEN)); + pcb->snd_queuelen, TCP_SND_QUEUELEN(pcb))); TCP_STATS_INC(tcp.memerr); pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; @@ -1301,6 +1301,7 @@ tcp_rst(u32_t seqno, u32_t ackno, struct pbuf *p; struct tcp_hdr *tcphdr; struct netif *netif; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); if (p == NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); @@ -1315,10 +1316,18 @@ tcp_rst(u32_t seqno, u32_t ackno, tcphdr->seqno = htonl(seqno); tcphdr->ackno = htonl(ackno); TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK); +#if ESP_PER_SOC_TCP_WND +#if LWIP_WND_SCALE + tcphdr->wnd = PP_HTONS(((TCP_WND_DEFAULT >> TCP_RCV_SCALE) & 0xFFFF)); +#else + tcphdr->wnd = PP_HTONS(TCP_WND_DEFAULT); +#endif +#else #if LWIP_WND_SCALE tcphdr->wnd = PP_HTONS(((TCP_WND >> TCP_RCV_SCALE) & 0xFFFF)); #else tcphdr->wnd = PP_HTONS(TCP_WND); +#endif #endif tcphdr->chksum = 0; tcphdr->urgp = 0; diff --git a/components/lwip/include/lwip/lwip/opt.h b/components/lwip/include/lwip/lwip/opt.h index 76fff8805..c286712e0 100755 --- a/components/lwip/include/lwip/lwip/opt.h +++ b/components/lwip/include/lwip/lwip/opt.h @@ -986,7 +986,7 @@ * (2 * TCP_MSS) for things to work well */ #ifndef TCP_WND -#define TCP_WND (4 * TCP_MSS) +#define TCP_WND(pcb) (4 * TCP_MSS) #endif /** @@ -1040,7 +1040,7 @@ * To achieve good performance, this should be at least 2 * TCP_MSS. */ #ifndef TCP_SND_BUF -#define TCP_SND_BUF (2 * TCP_MSS) +#define TCP_SND_BUF(pcb) (2 * TCP_MSS) #endif /** @@ -1048,7 +1048,7 @@ * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */ #ifndef TCP_SND_QUEUELEN -#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) +#define TCP_SND_QUEUELEN(pcb) ((4 * (TCP_SND_BUF((pcb))) + (TCP_MSS - 1))/(TCP_MSS)) #endif /** @@ -1057,7 +1057,7 @@ * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT). */ #ifndef TCP_SNDLOWAT -#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) +#define TCP_SNDLOWAT(pcb) LWIP_MIN(LWIP_MAX(((TCP_SND_BUF((pcb)))/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF((pcb))) - 1) #endif /** @@ -1066,7 +1066,7 @@ * this number, select returns writable (combined with TCP_SNDLOWAT). */ #ifndef TCP_SNDQUEUELOWAT -#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5) +#define TCP_SNDQUEUELOWAT(pcb) LWIP_MAX(((TCP_SND_QUEUELEN((pcb)))/2), 5) #endif /** @@ -1134,7 +1134,7 @@ * explicit window update */ #ifndef TCP_WND_UPDATE_THRESHOLD -#define TCP_WND_UPDATE_THRESHOLD LWIP_MIN((TCP_WND / 4), (TCP_MSS * 4)) +#define TCP_WND_UPDATE_THRESHOLD(pcb) LWIP_MIN((TCP_WND((pcb)) / 4), (TCP_MSS * 4)) #endif /** diff --git a/components/lwip/include/lwip/lwip/priv/tcp_priv.h b/components/lwip/include/lwip/lwip/priv/tcp_priv.h index b5261b445..0c498944b 100755 --- a/components/lwip/include/lwip/lwip/priv/tcp_priv.h +++ b/components/lwip/include/lwip/lwip/priv/tcp_priv.h @@ -92,7 +92,7 @@ err_t tcp_process_refused_data(struct tcp_pcb *pcb); ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \ (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \ ((tpcb)->unsent->len >= (tpcb)->mss))) || \ - ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \ + ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN(tpcb))) \ ) ? 1 : 0) #define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) diff --git a/components/lwip/include/lwip/lwip/sockets.h b/components/lwip/include/lwip/lwip/sockets.h index aafb3d5cf..fc2b7e2d1 100755 --- a/components/lwip/include/lwip/lwip/sockets.h +++ b/components/lwip/include/lwip/lwip/sockets.h @@ -190,7 +190,6 @@ struct msghdr { #define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ #define SO_NO_CHECK 0x100a /* don't create UDP checksum */ - /* * Structure used for manipulating linger option. */ @@ -250,6 +249,11 @@ struct linger { #define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */ #define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */ #define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ +#if ESP_PER_SOC_TCP_WND +#define TCP_WINDOW 0x06 /* set pcb->per_soc_tcp_wnd */ +#define TCP_SNDBUF 0x07 /* set pcb->per_soc_tcp_snd_buf */ +#endif + #endif /* LWIP_TCP */ #if LWIP_IPV6 diff --git a/components/lwip/include/lwip/lwip/tcp.h b/components/lwip/include/lwip/lwip/tcp.h index d52040f99..6b8c4b6c4 100755 --- a/components/lwip/include/lwip/lwip/tcp.h +++ b/components/lwip/include/lwip/lwip/tcp.h @@ -129,14 +129,14 @@ typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err); #define RCV_WND_SCALE(pcb, wnd) (((wnd) >> (pcb)->rcv_scale)) #define SND_WND_SCALE(pcb, wnd) (((wnd) << (pcb)->snd_scale)) #define TCPWND16(x) ((u16_t)LWIP_MIN((x), 0xFFFF)) -#define TCP_WND_MAX(pcb) ((tcpwnd_size_t)(((pcb)->flags & TF_WND_SCALE) ? TCP_WND : TCPWND16(TCP_WND))) +#define TCP_WND_MAX(pcb) ((tcpwnd_size_t)(((pcb)->flags & TF_WND_SCALE) ? TCP_WND(pcb) : TCPWND16(TCP_WND(pcb)))) typedef u32_t tcpwnd_size_t; typedef u16_t tcpflags_t; #else #define RCV_WND_SCALE(pcb, wnd) (wnd) #define SND_WND_SCALE(pcb, wnd) (wnd) #define TCPWND16(x) (x) -#define TCP_WND_MAX(pcb) TCP_WND +#define TCP_WND_MAX(pcb) TCP_WND(pcb) typedef u16_t tcpwnd_size_t; typedef u8_t tcpflags_t; #endif @@ -236,6 +236,11 @@ struct tcp_pcb { u8_t dupacks; u32_t lastack; /* Highest acknowledged seqno. */ +#if ESP_PER_SOC_TCP_WND + tcpwnd_size_t per_soc_tcp_wnd; /* per tcp socket tcp window size */ + tcpwnd_size_t per_soc_tcp_snd_buf; /* per tcp socket tcp send buffer size */ +#endif + /* congestion avoidance/control variables */ tcpwnd_size_t cwnd; tcpwnd_size_t ssthresh; @@ -402,6 +407,10 @@ const char* tcp_debug_state_str(enum tcp_state s); /* for compatibility with older implementation */ #define tcp_new_ip6() tcp_new_ip_type(IPADDR_TYPE_V6) +#if ESP_PER_SOC_TCP_WND +#define PER_SOC_WND(pcb) (pcb->per_soc_wnd) +#endif + #ifdef __cplusplus } #endif diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 2c24b2be9..5667e2d20 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -225,18 +225,21 @@ extern unsigned long os_random(void); * TCP_WND: The size of a TCP window. This must be at least * (2 * TCP_MSS) for things to work well */ -#define PERF 1 + +#define ESP_PER_SOC_TCP_WND 1 +#if ESP_PER_SOC_TCP_WND +#define TCP_WND_DEFAULT (4*TCP_MSS) +#define TCP_SND_BUF_DEFAULT (2*TCP_MSS) + +#define TCP_WND(pcb) (pcb->per_soc_tcp_wnd) +#define TCP_SND_BUF(pcb) (pcb->per_soc_tcp_snd_buf) +#else #ifdef PERF extern unsigned char misc_prof_get_tcpw(void); extern unsigned char misc_prof_get_tcp_snd_buf(void); -#define TCP_WND (misc_prof_get_tcpw()*TCP_MSS) -#define TCP_SND_BUF (misc_prof_get_tcp_snd_buf()*TCP_MSS) - -#else - -#define TCP_WND (4 * TCP_MSS) -#define TCP_SND_BUF (2 * TCP_MSS) - +#define TCP_WND(pcb) (misc_prof_get_tcpw()*TCP_MSS) +#define TCP_SND_BUF(pcb) (misc_prof_get_tcp_snd_buf()*TCP_MSS) +#endif #endif From abd4dc7d438d51711913591fe6f4d88643015448 Mon Sep 17 00:00:00 2001 From: Yinling Date: Mon, 24 Oct 2016 18:42:40 +0800 Subject: [PATCH 049/110] add know issue ^WIFI_CONN_0601: in STA+AP mode, when STA reconencting to enternal AP, external STA can't connect to AP --- components/idf_test/integration_test/KnownIssues | 1 + 1 file changed, 1 insertion(+) diff --git a/components/idf_test/integration_test/KnownIssues b/components/idf_test/integration_test/KnownIssues index 85b5b2f21..e0991f39b 100644 --- a/components/idf_test/integration_test/KnownIssues +++ b/components/idf_test/integration_test/KnownIssues @@ -56,6 +56,7 @@ WIFI_CONN_0901 # Wifi connect issue WIFI_CONN_0104 ^WIFI_CONN_0104 +^WIFI_CONN_0601 # Wifi scan issue WIFI_SCAN_0303 From 3edcf5b096fa64ddca7772cf0cef8964d67a1bd9 Mon Sep 17 00:00:00 2001 From: Yinling Date: Mon, 24 Oct 2016 18:59:56 +0800 Subject: [PATCH 050/110] fix bug for case WIFI_CONN_0102: after set AP, STA will disconnected and do reconnect. scan could fail as reconnect also use scan --- components/idf_test/integration_test/TestCaseAll.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/idf_test/integration_test/TestCaseAll.yml b/components/idf_test/integration_test/TestCaseAll.yml index 3b2242787..9e4823a29 100644 --- a/components/idf_test/integration_test/TestCaseAll.yml +++ b/components/idf_test/integration_test/TestCaseAll.yml @@ -5868,6 +5868,8 @@ test cases: - ['R SSC2 RE "\+JAP:CONNECTED,%%s"%%()'] - - SSC SSC1 ap -S -s -n 15 - ['R SSC1 C +SAP:OK'] + - - SSC SSC2 sta -C -s -p + - ['R SSC2 RE "\+JAP:CONNECTED,%%s"%%()'] - - SSC SSC2 sta -S - ['R SSC2 RE "\+SCAN:%%s,.+,\d+,1"%%()'] comment: '' From 700ed6365123f30a15f5b80ffef9d3b489c160d4 Mon Sep 17 00:00:00 2001 From: liuzhifu Date: Tue, 25 Oct 2016 09:26:10 +0800 Subject: [PATCH 051/110] component/tcpip_adapter: not update dhcps status when it is stopped after mode switch When switch the mode from WIFI_MODE_STA/WIFI_MODE_NULL to WIFI_MODE_AP/WIFI_MODE_APSTA, if the dhcp server is STOPPED, then dhcp server will not start automatically. --- components/tcpip_adapter/tcpip_adapter_lwip.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 78fecf2cb..12cf05f95 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -103,7 +103,9 @@ esp_err_t tcpip_adapter_stop(tcpip_adapter_if_t tcpip_if) if (tcpip_if == TCPIP_ADAPTER_IF_AP) { dhcps_stop(esp_netif[tcpip_if]); // TODO: dhcps checks status by its self - dhcps_status = TCPIP_ADAPTER_DHCP_INIT; + if (TCPIP_ADAPTER_DHCP_STOPPED != dhcps_status){ + dhcps_status = TCPIP_ADAPTER_DHCP_INIT; + } } else if (tcpip_if == TCPIP_ADAPTER_IF_STA) { dhcp_release(esp_netif[tcpip_if]); dhcp_stop(esp_netif[tcpip_if]); From 75a11589a16a0f0a6e451ed2eecf9279ce2eb2bc Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 25 Oct 2016 17:05:13 +0800 Subject: [PATCH 052/110] Disable brown-out WDT, fix thread WDT, add panic reason indication to _xt_panic() --- components/esp32/Kconfig | 12 +++-- components/esp32/brownout.c | 10 +++- components/esp32/int_wdt.c | 12 +++-- components/esp32/task_wdt.c | 43 ++++++++++----- .../include/freertos/FreeRTOSConfig.h | 1 + components/freertos/include/freertos/panic.h | 13 +++++ components/freertos/panic.c | 54 ++++++++++++++++++- components/freertos/tasks.c | 1 - components/freertos/xtensa_vectors.S | 11 ++++ 9 files changed, 134 insertions(+), 23 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index a2faa926a..dbd1cfb6f 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -140,7 +140,6 @@ config ULP_COPROC_RESERVE_MEM default 0 depends on !ULP_COPROC_ENABLED -menu "Watchdogs & brown-out detection" config INT_WDT bool "Interrupt watchdog" @@ -155,7 +154,7 @@ config INT_WDT_TIMEOUT_MS int "Interrupt watchdog timeout (ms)" depends on INT_WDT default 10 - range INT_WDT_TIMEOUT_MIN 10000 + range 10 10000 help The timeout of the watchdog, in miliseconds. Make this higher than the FreeRTOS tick rate. @@ -190,11 +189,15 @@ config TASK_WDT_CHECK_IDLE_TASK With this turned on, the task WDT can detect if the idle task is not called within the task watchdog timeout period. The idle task not being called usually is a symptom of another task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the - idle task getting some runtime every now and then. + idle task getting some runtime every now and then. Take Care: With this disabled, this + watchdog will trigger if no tasks register themselves within the timeout value. +#The brownout detector code is disabled (by making it depend on a nonexisting symbol) because the current revision of ESP32 +#silicon has a bug in the brown-out detector, rendering it unusable for resetting the CPU. config BROWNOUT_DET bool "Hardware brownout detect & reset" default y + depends on NEEDS_ESP32_NEW_SILICON_REV help The ESP32 has a built-in brownout detector which can detect if the voltage is lower than a specific value. If this happens, it will reset the chip in order to prevent unintended @@ -207,6 +210,8 @@ choice BROWNOUT_DET_LVL_SEL help The brownout detector will reset the chip when the supply voltage is below this level. +#The voltage levels here are estimates, more work needs to be done to figure out the exact voltages +#of the brownout threshold levels. config BROWNOUT_DET_LVL_SEL_0 bool "2.1V" config BROWNOUT_DET_LVL_SEL_1 @@ -246,7 +251,6 @@ config BROWNOUT_DET_RESETDELAY The brownout detector can reset the chip after a certain delay, in order to make sure e.g. a voltage dip has entirely passed before trying to restart the chip. You can set the delay here. -endmenu diff --git a/components/esp32/brownout.c b/components/esp32/brownout.c index 8e8805b8e..97846ae8c 100644 --- a/components/esp32/brownout.c +++ b/components/esp32/brownout.c @@ -22,10 +22,18 @@ #include "soc/rtc_cntl_reg.h" +#if CONFIG_BROWNOUT_DET +/* +This file ins included in esp-idf, but the menuconfig option for this is disabled because a silicon bug +prohibits the brownout detector from functioning correctly on the ESP32. +*/ + void esp_brownout_init() { WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, RTC_CNTL_BROWN_OUT_ENA | (CONFIG_BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S) | RTC_CNTL_BROWN_OUT_RST_ENA | (((CONFIG_BROWNOUT_DET_RESETDELAY*150)/1000) << RTC_CNTL_BROWN_OUT_RST_WAIT_S) | RTC_CNTL_BROWN_OUT_PD_RF_ENA|RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA); -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c index 2b4d9e084..5fb7a63ba 100644 --- a/components/esp32/int_wdt.c +++ b/components/esp32/int_wdt.c @@ -44,7 +44,6 @@ This uses the TIMERG1 WDT. #define WDT_INT_NUM 24 - #define WDT_WRITE_KEY 0x50D83AA1 void int_wdt_init() { @@ -55,11 +54,15 @@ void int_wdt_init() { TIMERG1.wdt_config0.stg0=1; //1st stage timeout: interrupt TIMERG1.wdt_config0.stg1=3; //2nd stage timeout: reset system TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS - TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt - TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets + //it to their actual value. + TIMERG1.wdt_config2=10000; + TIMERG1.wdt_config3=10000; TIMERG1.wdt_config0.en=1; TIMERG1.wdt_feed=1; TIMERG1.wdt_wprotect=0; + TIMERG1.int_clr_timers.wdt=1; + TIMERG1.int_ena.wdt=1; ESP_INTR_DISABLE(WDT_INT_NUM); intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); //We do not register a handler for the interrupt because it is interrupt level 4 which @@ -69,9 +72,10 @@ void int_wdt_init() { } - void vApplicationTickHook(void) { TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset TIMERG1.wdt_feed=1; TIMERG1.wdt_wprotect=0; } diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 7ae3dfb2e..3e4c43639 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -33,6 +33,7 @@ This uses the TIMERG0 WDT. #include #include "esp_err.h" #include "esp_intr.h" +#include "esp_attr.h" #include "soc/timer_group_struct.h" #include "esp_log.h" @@ -42,7 +43,6 @@ This uses the TIMERG0 WDT. static const char* TAG = "task_wdt"; - typedef struct wdt_task_t wdt_task_t; struct wdt_task_t { TaskHandle_t task_handle; @@ -50,16 +50,35 @@ struct wdt_task_t { wdt_task_t *next; }; - static wdt_task_t *wdt_task_list=NULL; +//We use this interrupt number on whatever task calls task_wdt_init. #define WDT_INT_NUM 24 - #define WDT_WRITE_KEY 0x50D83AA1 -static void task_wdt_isr(void *arg) { +static void IRAM_ATTR task_wdt_isr(void *arg) { + wdt_task_t *wdttask; + const char *cpu; + //Feed the watchdog so we do not reset + TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; + //Ack interrupt + TIMERG0.int_clr_timers.wdt=1; + //Watchdog got triggered because at least one task did not report in. + ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n"); + for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { + if (!wdttask->fed_watchdog) { + cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1"; + if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1"; + printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); + } + } +#if CONFIG_TASK_WDT_PANIC + ets_printf("Aborting.\n"); abort(); +#endif } @@ -69,7 +88,7 @@ void task_wdt_feed() { TaskHandle_t handle=xTaskGetCurrentTaskHandle(); //Walk the linked list of wdt tasks to find this one, as well as see if we need to feed //the real watchdog timer. - while (wdttask!=NULL) { + for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { //See if we are at the current task. if (wdttask->task_handle == handle) { wdttask->fed_watchdog=true; @@ -77,8 +96,6 @@ void task_wdt_feed() { } //If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog. if (!wdttask->fed_watchdog) do_feed_wdt=false; - //Next entry. - wdttask=wdttask->next; } if (!found_task) { @@ -91,9 +108,8 @@ void task_wdt_feed() { if (wdt_task_list == NULL) { wdt_task_list=newtask; } else { - wdttask=wdt_task_list; - while (!(wdttask->next == NULL)) wdttask=wdttask->next; - wdttask->next=wdttask; + for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ; + wdttask->next=newtask; } } if (do_feed_wdt) { @@ -101,6 +117,8 @@ void task_wdt_feed() { TIMERG0.wdt_wprotect=WDT_WRITE_KEY; TIMERG0.wdt_feed=1; TIMERG0.wdt_wprotect=0; + //Reset fed_watchdog status + for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false; } } @@ -143,12 +161,13 @@ void task_wdt_init() { TIMERG0.wdt_feed=1; TIMERG0.wdt_wprotect=0; ESP_INTR_DISABLE(ETS_T0_WDT_INUM); - intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); + intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL); + TIMERG0.int_clr_timers.wdt=1; + TIMERG0.int_ena.wdt=1; ESP_INTR_ENABLE(ETS_T0_WDT_INUM); } - #if CONFIG_TASK_WDT_CHECK_IDLE_TASK void vApplicationIdleHook(void) { task_wdt_feed(); diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index a5483d6bb..b732d1c07 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -231,6 +231,7 @@ #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_pcTaskGetTaskName 1 #if CONFIG_ENABLE_MEMORY_DEBUG #define configENABLE_MEMORY_DEBUG 1 diff --git a/components/freertos/include/freertos/panic.h b/components/freertos/include/freertos/panic.h index 9e902ed20..ba47e3d43 100644 --- a/components/freertos/include/freertos/panic.h +++ b/components/freertos/include/freertos/panic.h @@ -1,7 +1,20 @@ #ifndef PANIC_H #define PANIC_H + +#define PANIC_RSN_NONE 0 +#define PANIC_RSN_DEBUGEXCEPTION 1 +#define PANIC_RSN_DOUBLEEXCEPTION 2 +#define PANIC_RSN_KERNELEXCEPTION 3 +#define PANIC_RSN_COPROCEXCEPTION 4 +#define PANIC_RSN_INTWDT 5 +#define PANIC_RSN_MAX 5 + + +#ifndef __ASSEMBLER__ + void setBreakpointIfJtag(void *fn); +#endif #endif \ No newline at end of file diff --git a/components/freertos/panic.c b/components/freertos/panic.c index 6a87679d1..0c912e56f 100644 --- a/components/freertos/panic.c +++ b/components/freertos/panic.c @@ -25,8 +25,13 @@ #include "soc/io_mux_reg.h" #include "soc/dport_reg.h" #include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_struct.h" #include "gdbstub.h" +#include "panic.h" + +#define WDT_WRITE_KEY 0x50D83AA1 + /* Panic handlers; these get called when an unhandled exception occurs or the assembly-level @@ -130,10 +135,25 @@ static int inOCDMode() { } void panicHandler(XtExcFrame *frame) { + int *regs=(int*)frame; + //Please keep in sync with PANIC_RSN_* defines + const char *reasons[]={ + "Unknown reason", + "Unhandled debug exception", + "Double exception", + "Unhandled kernel exception", + "Coprocessor exception", + "Interrupt wdt timeout" + }; + const char *reason=reasons[0]; + //The panic reason is stored in the EXCCAUSE register. + if (regs[20]<=PANIC_RSN_MAX) reason=reasons[regs[20]]; haltOtherCore(); panicPutStr("Guru Meditation Error: Core "); panicPutDec(xPortGetCoreID()); - panicPutStr(" panic'ed.\r\n"); + panicPutStr(" panic'ed ("); + panicPutStr(reason); + panicPutStr(")\r\n"); if (inOCDMode()) { asm("break.n 1"); @@ -175,6 +195,33 @@ void xt_unhandled_exception(XtExcFrame *frame) { } +//Disables all but one WDT, and allows enough time on that WDT to do what we need to do. +static void reconfigureAllWdts() { + TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG0.wdt_config0.stg0=3; //1st stage timeout: reset system + TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + TIMERG0.wdt_config2=2000; //1 second before reset + TIMERG0.wdt_config0.en=1; + TIMERG0.wdt_wprotect=0; + //Disable wdt 1 + TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_config0.en=0; + TIMERG1.wdt_wprotect=0; +} + +static void disableAllWdts() { + TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_config0.en=0; + TIMERG0.wdt_wprotect=0; + TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_config0.en=0; + TIMERG0.wdt_wprotect=0; +} + + /* We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the serial port and either jump to the gdb stub, halt the CPU or reboot. @@ -187,6 +234,9 @@ void commonErrorHandler(XtExcFrame *frame) { "A6 ","A7 ","A8 ","A9 ","A10 ","A11 ","A12 ","A13 ", "A14 ","A15 ","SAR ","EXCCAUSE","EXCVADDR","LBEG ","LEND ","LCOUNT "}; + //Feed the watchdogs, so they will give us time to print out debug info + reconfigureAllWdts(); + panicPutStr("Register dump:\r\n"); for (x=0; x<24; x+=4) { @@ -201,6 +251,7 @@ void commonErrorHandler(XtExcFrame *frame) { panicPutStr("\r\n"); } #if CONFIG_FREERTOS_PANIC_GDBSTUB + disableAllWdts(); panicPutStr("Entering gdb stub now.\r\n"); gdbstubPanicHandler(frame); #elif CONFIG_FREERTOS_PANIC_PRINT_REBOOT || CONFIG_FREERTOS_PANIC_SILENT_REBOOT @@ -208,6 +259,7 @@ void commonErrorHandler(XtExcFrame *frame) { for (x=0; x<100; x++) ets_delay_us(1000); software_reset(); #else + disableAllWdts(); panicPutStr("CPU halted.\r\n"); while(1); #endif diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 3cde3bf13..221bf476c 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -1832,7 +1832,6 @@ UBaseType_t uxTaskGetNumberOfTasks( void ) /*-----------------------------------------------------------*/ #if ( INCLUDE_pcTaskGetTaskName == 1 ) - char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { TCB_t *pxTCB; diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index 89a47f741..5b31eb0a8 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -91,6 +91,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *******************************************************************************/ #include "xtensa_rtos.h" +#include "panic.h" /* Define for workaround: pin no-cpu-affinity tasks to a cpu when fpu is used. @@ -462,6 +463,8 @@ _DebugExceptionVector: jx a3 #else wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */ + movi a0,PANIC_RSN_DEBUGEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* does not return */ rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */ #endif @@ -489,6 +492,8 @@ _DoubleExceptionVector: #if XCHAL_HAVE_DEBUG break 1, 4 /* unhandled double exception */ #endif + movi a0,PANIC_RSN_DOUBLEEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* does not return */ rfde /* make a0 point here not later */ @@ -522,6 +527,8 @@ _xt_kernel_exc: #if XCHAL_HAVE_DEBUG break 1, 0 /* unhandled kernel exception */ #endif + movi a0,PANIC_RSN_KERNELEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* does not return */ rfe /* make a0 point here not there */ @@ -1024,6 +1031,8 @@ _xt_coproc_exc: #if XCHAL_HAVE_DEBUG break 1, 1 /* unhandled user exception */ #endif + movi a0,PANIC_RSN_COPROCEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* not in a thread (invalid) */ /* never returns */ @@ -1611,6 +1620,8 @@ _xt_highint4: off and the CPU should panic. */ rsr a0, EXCSAVE_4 wsr a0, EXCSAVE_1 /* panic handler reads this register */ + movi a0,PANIC_RSN_INTWDT + wsr a0,EXCCAUSE call0 _xt_panic From 89f7752cddc2e2a30885292e4d0c9002c5958705 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 25 Oct 2016 18:08:55 +0800 Subject: [PATCH 053/110] Make CPU1 int wdt / idle task wdt configurable, panic now properly disables other cpu, tick handler now also is called on cpu1, task wdt prints currently running tasks. --- components/esp32/Kconfig | 16 ++++++++++- components/esp32/int_wdt.c | 23 +++++++++++++++ components/esp32/task_wdt.c | 8 ++++++ components/freertos/include/freertos/panic.h | 5 ++-- components/freertos/include/freertos/task.h | 11 +++++++ components/freertos/panic.c | 19 +++++++------ components/freertos/tasks.c | 30 ++++++++++++++++++++ components/freertos/xtensa_vectors.S | 17 +++++++++-- 8 files changed, 115 insertions(+), 14 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index dbd1cfb6f..6fb47ebd0 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -158,6 +158,13 @@ config INT_WDT_TIMEOUT_MS help The timeout of the watchdog, in miliseconds. Make this higher than the FreeRTOS tick rate. +config INT_WDT_CHECK_CPU1 + bool "Also watch CPU1 tick interrupt" + depends on INT_WDT && !FREERTOS_UNICORE + default y + help + Also detect if interrupts on CPU 1 are disabled for too long. + config TASK_WDT bool "Task watchdog" default y @@ -182,7 +189,7 @@ config TASK_WDT_TIMEOUT_S Timeout for the task WDT, in seconds. config TASK_WDT_CHECK_IDLE_TASK - bool "Task watchdog watches idle tasks" + bool "Task watchdog watches CPU0 idle task" depends on TASK_WDT default y help @@ -192,6 +199,13 @@ config TASK_WDT_CHECK_IDLE_TASK idle task getting some runtime every now and then. Take Care: With this disabled, this watchdog will trigger if no tasks register themselves within the timeout value. +config TASK_WDT_CHECK_IDLE_TASK_CPU1 + bool "Task watchdog also watches CPU1 idle task" + depends on TASK_WDT_CHECK_IDLE_TASK && !FREERTOS_UNICORE + default y + help + Also check the idle task that runs on CPU1. + #The brownout detector code is disabled (by making it depend on a nonexisting symbol) because the current revision of ESP32 #silicon has a bug in the brown-out detector, rendering it unusable for resetting the CPU. config BROWNOUT_DET diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c index 5fb7a63ba..8eadbeb8d 100644 --- a/components/esp32/int_wdt.c +++ b/components/esp32/int_wdt.c @@ -30,6 +30,7 @@ This uses the TIMERG1 WDT. #include #include #include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include @@ -72,12 +73,34 @@ void int_wdt_init() { } +#if CONFIG_INT_WDT_CHECK_CPU1 +//Not static; the ISR assembly checks this. +bool int_wdt_app_cpu_ticked=false; + void vApplicationTickHook(void) { + if (xPortGetCoreID()!=0) { + int_wdt_app_cpu_ticked=true; + } else { + //Only feed wdt if app cpu also ticked. + if (int_wdt_app_cpu_ticked) { + TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + int_wdt_app_cpu_ticked=false; + } + } +} +#else +void vApplicationTickHook(void) { + if (xPortGetCoreID()!=0) return; TIMERG1.wdt_wprotect=WDT_WRITE_KEY; TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset TIMERG1.wdt_feed=1; TIMERG1.wdt_wprotect=0; } +#endif #endif \ No newline at end of file diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 3e4c43639..6da0901fb 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -75,6 +75,11 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); } } + ets_printf("Tasks currently running:\n"); + for (int x=0; x Date: Tue, 25 Oct 2016 18:18:11 +0800 Subject: [PATCH 054/110] Add licenses, docbook, general cleanup --- components/esp32/brownout.c | 2 +- components/esp32/include/esp_brownout.h | 19 ++- components/esp32/include/esp_int_wdt.h | 44 ++++- components/esp32/include/esp_task_wdt.h | 73 +++++++- components/esp32/int_wdt.c | 82 ++++----- components/esp32/task_wdt.c | 210 ++++++++++++------------ 6 files changed, 275 insertions(+), 155 deletions(-) diff --git a/components/esp32/brownout.c b/components/esp32/brownout.c index 97846ae8c..1dcde078e 100644 --- a/components/esp32/brownout.c +++ b/components/esp32/brownout.c @@ -24,7 +24,7 @@ #if CONFIG_BROWNOUT_DET /* -This file ins included in esp-idf, but the menuconfig option for this is disabled because a silicon bug +This file is included in esp-idf, but the menuconfig option for this is disabled because a silicon bug prohibits the brownout detector from functioning correctly on the ESP32. */ diff --git a/components/esp32/include/esp_brownout.h b/components/esp32/include/esp_brownout.h index acce05e0d..5a0b1aec0 100644 --- a/components/esp32/include/esp_brownout.h +++ b/components/esp32/include/esp_brownout.h @@ -1,5 +1,20 @@ -#ifndef BROWNOUT_H -#define BROWNOUT_H +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#ifndef __ESP_BROWNOUT_H +#define __ESP_BROWNOUT_H void esp_brownout_init(); diff --git a/components/esp32/include/esp_int_wdt.h b/components/esp32/include/esp_int_wdt.h index 81404e305..dc5bd0dda 100644 --- a/components/esp32/include/esp_int_wdt.h +++ b/components/esp32/include/esp_int_wdt.h @@ -1,6 +1,46 @@ -#ifndef INT_WDT_H -#define INT_WDT_H +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_INT_WDT_H +#define __ESP_INT_WDT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup Watchdog_APIs + * @{ + */ + +/** + * @brief Initialize the interrupt watchdog. This is called in the init code, no need to + * call it explicitly. + * + * @param null + * + * @return null + */ void int_wdt_init(); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file diff --git a/components/esp32/include/esp_task_wdt.h b/components/esp32/include/esp_task_wdt.h index 9163e6907..c050616af 100644 --- a/components/esp32/include/esp_task_wdt.h +++ b/components/esp32/include/esp_task_wdt.h @@ -1,8 +1,73 @@ -#ifndef TASK_WDT_H -#define TASK_WDT_H +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at -void task_wdt_feed(); -void task_wdt_delete(); +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_TASK_WDT_H +#define __ESP_TASK_WDT_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup Watchdog_APIs Watchdog APIs + * @brief Watchdog APIs + */ + +/** @addtogroup Watchdog_APIs + * @{ + */ + +/** + * @brief Initialize the task watchdog. This is called in the init code, no need to + * call it explicitly. + * + * @param null + * + * @return null + */ void task_wdt_init(); +/** + * @brief Feed the watchdog. After the first feeding session, the watchdog will expect the calling + * task to keep feeding the watchdog until task_wdt_delete() is called. + * + * @param null + * + * @return null + */ + +void task_wdt_feed(); + + +/** + * @brief Delete the watchdog for the current task. + * + * @param null + * + * @return null + */ +void task_wdt_delete(); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + + + #endif \ No newline at end of file diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c index 8eadbeb8d..8c506bde1 100644 --- a/components/esp32/int_wdt.c +++ b/components/esp32/int_wdt.c @@ -48,28 +48,28 @@ This uses the TIMERG1 WDT. #define WDT_WRITE_KEY 0x50D83AA1 void int_wdt_init() { - TIMERG1.wdt_wprotect=WDT_WRITE_KEY; - TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS - TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS - TIMERG1.wdt_config0.level_int_en=1; - TIMERG1.wdt_config0.stg0=1; //1st stage timeout: interrupt - TIMERG1.wdt_config0.stg1=3; //2nd stage timeout: reset system - TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS - //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets - //it to their actual value. - TIMERG1.wdt_config2=10000; - TIMERG1.wdt_config3=10000; - TIMERG1.wdt_config0.en=1; - TIMERG1.wdt_feed=1; - TIMERG1.wdt_wprotect=0; - TIMERG1.int_clr_timers.wdt=1; - TIMERG1.int_ena.wdt=1; - ESP_INTR_DISABLE(WDT_INT_NUM); - intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); - //We do not register a handler for the interrupt because it is interrupt level 4 which - //is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for - //this interrupt. - ESP_INTR_ENABLE(WDT_INT_NUM); + TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG1.wdt_config0.level_int_en=1; + TIMERG1.wdt_config0.stg0=1; //1st stage timeout: interrupt + TIMERG1.wdt_config0.stg1=3; //2nd stage timeout: reset system + TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets + //it to their actual value. + TIMERG1.wdt_config2=10000; + TIMERG1.wdt_config3=10000; + TIMERG1.wdt_config0.en=1; + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + TIMERG1.int_clr_timers.wdt=1; + TIMERG1.int_ena.wdt=1; + ESP_INTR_DISABLE(WDT_INT_NUM); + intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); + //We do not register a handler for the interrupt because it is interrupt level 4 which + //is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for + //this interrupt. + ESP_INTR_ENABLE(WDT_INT_NUM); } @@ -78,28 +78,28 @@ void int_wdt_init() { bool int_wdt_app_cpu_ticked=false; void vApplicationTickHook(void) { - if (xPortGetCoreID()!=0) { - int_wdt_app_cpu_ticked=true; - } else { - //Only feed wdt if app cpu also ticked. - if (int_wdt_app_cpu_ticked) { - TIMERG1.wdt_wprotect=WDT_WRITE_KEY; - TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt - TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset - TIMERG1.wdt_feed=1; - TIMERG1.wdt_wprotect=0; - int_wdt_app_cpu_ticked=false; - } - } + if (xPortGetCoreID()!=0) { + int_wdt_app_cpu_ticked=true; + } else { + //Only feed wdt if app cpu also ticked. + if (int_wdt_app_cpu_ticked) { + TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + int_wdt_app_cpu_ticked=false; + } + } } #else void vApplicationTickHook(void) { - if (xPortGetCoreID()!=0) return; - TIMERG1.wdt_wprotect=WDT_WRITE_KEY; - TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt - TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset - TIMERG1.wdt_feed=1; - TIMERG1.wdt_wprotect=0; + if (xPortGetCoreID()!=0) return; + TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; } #endif diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 6da0901fb..e6a462088 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -45,9 +45,9 @@ static const char* TAG = "task_wdt"; typedef struct wdt_task_t wdt_task_t; struct wdt_task_t { - TaskHandle_t task_handle; - bool fed_watchdog; - wdt_task_t *next; + TaskHandle_t task_handle; + bool fed_watchdog; + wdt_task_t *next; }; static wdt_task_t *wdt_task_list=NULL; @@ -58,127 +58,127 @@ static wdt_task_t *wdt_task_list=NULL; #define WDT_WRITE_KEY 0x50D83AA1 static void IRAM_ATTR task_wdt_isr(void *arg) { - wdt_task_t *wdttask; - const char *cpu; - //Feed the watchdog so we do not reset - TIMERG0.wdt_wprotect=WDT_WRITE_KEY; - TIMERG0.wdt_feed=1; - TIMERG0.wdt_wprotect=0; - //Ack interrupt - TIMERG0.int_clr_timers.wdt=1; - //Watchdog got triggered because at least one task did not report in. - ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n"); - for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { - if (!wdttask->fed_watchdog) { - cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1"; - if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1"; - printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); - } - } - ets_printf("Tasks currently running:\n"); - for (int x=0; xnext) { + if (!wdttask->fed_watchdog) { + cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1"; + if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1"; + printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); + } + } + ets_printf("Tasks currently running:\n"); + for (int x=0; xnext) { - //See if we are at the current task. - if (wdttask->task_handle == handle) { - wdttask->fed_watchdog=true; - found_task=true; - } - //If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog. - if (!wdttask->fed_watchdog) do_feed_wdt=false; - } - - if (!found_task) { - //This is the first time the task calls the task_wdt_feed function. Create a new entry for it in - //the linked list. - wdt_task_t *newtask=malloc(sizeof(wdt_task_t)); - memset(newtask, 0, sizeof(wdt_task_t)); - newtask->task_handle=handle; - newtask->fed_watchdog=true; - if (wdt_task_list == NULL) { - wdt_task_list=newtask; - } else { - for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ; - wdttask->next=newtask; - } - } - if (do_feed_wdt) { - //All tasks have checked in; time to feed the hw watchdog. - TIMERG0.wdt_wprotect=WDT_WRITE_KEY; - TIMERG0.wdt_feed=1; - TIMERG0.wdt_wprotect=0; - //Reset fed_watchdog status - for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false; - } + wdt_task_t *wdttask=wdt_task_list; + bool found_task=false, do_feed_wdt=true; + TaskHandle_t handle=xTaskGetCurrentTaskHandle(); + //Walk the linked list of wdt tasks to find this one, as well as see if we need to feed + //the real watchdog timer. + for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { + //See if we are at the current task. + if (wdttask->task_handle == handle) { + wdttask->fed_watchdog=true; + found_task=true; + } + //If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog. + if (!wdttask->fed_watchdog) do_feed_wdt=false; + } + + if (!found_task) { + //This is the first time the task calls the task_wdt_feed function. Create a new entry for it in + //the linked list. + wdt_task_t *newtask=malloc(sizeof(wdt_task_t)); + memset(newtask, 0, sizeof(wdt_task_t)); + newtask->task_handle=handle; + newtask->fed_watchdog=true; + if (wdt_task_list == NULL) { + wdt_task_list=newtask; + } else { + for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ; + wdttask->next=newtask; + } + } + if (do_feed_wdt) { + //All tasks have checked in; time to feed the hw watchdog. + TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; + //Reset fed_watchdog status + for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false; + } } void task_wdt_delete() { - TaskHandle_t handle=xTaskGetCurrentTaskHandle(); - wdt_task_t *wdttask=wdt_task_list; - //Wdt task list can't be empty - if (!wdt_task_list) { - ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?"); - return; - } - if (handle==wdt_task_list) { - //Current task is first on list. - wdt_task_list=wdt_task_list->next; - free(wdttask); - } else { - //Find current task in list - while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next; - if (!wdttask->next) { - ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!"); - return; - } - wdt_task_t *freeme=wdttask->next; - wdttask->next=wdttask->next->next; - free(freeme); - } + TaskHandle_t handle=xTaskGetCurrentTaskHandle(); + wdt_task_t *wdttask=wdt_task_list; + //Wdt task list can't be empty + if (!wdt_task_list) { + ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?"); + return; + } + if (handle==wdt_task_list) { + //Current task is first on list. + wdt_task_list=wdt_task_list->next; + free(wdttask); + } else { + //Find current task in list + while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next; + if (!wdttask->next) { + ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!"); + return; + } + wdt_task_t *freeme=wdttask->next; + wdttask->next=wdttask->next->next; + free(freeme); + } } void task_wdt_init() { - TIMERG0.wdt_wprotect=WDT_WRITE_KEY; - TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS - TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS - TIMERG0.wdt_config0.level_int_en=1; - TIMERG0.wdt_config0.stg0=1; //1st stage timeout: interrupt - TIMERG0.wdt_config0.stg1=3; //2nd stage timeout: reset system - TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS - TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000; //Set timeout before interrupt - TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000; //Set timeout before reset - TIMERG0.wdt_config0.en=1; - TIMERG0.wdt_feed=1; - TIMERG0.wdt_wprotect=0; - ESP_INTR_DISABLE(ETS_T0_WDT_INUM); - intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); - xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL); - TIMERG0.int_clr_timers.wdt=1; - TIMERG0.int_ena.wdt=1; - ESP_INTR_ENABLE(ETS_T0_WDT_INUM); + TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG0.wdt_config0.level_int_en=1; + TIMERG0.wdt_config0.stg0=1; //1st stage timeout: interrupt + TIMERG0.wdt_config0.stg1=3; //2nd stage timeout: reset system + TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000; //Set timeout before interrupt + TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000; //Set timeout before reset + TIMERG0.wdt_config0.en=1; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; + ESP_INTR_DISABLE(ETS_T0_WDT_INUM); + intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); + xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL); + TIMERG0.int_clr_timers.wdt=1; + TIMERG0.int_ena.wdt=1; + ESP_INTR_ENABLE(ETS_T0_WDT_INUM); } #if CONFIG_TASK_WDT_CHECK_IDLE_TASK void vApplicationIdleHook(void) { #if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 - if (xPortGetCoreID()!=0) return; + if (xPortGetCoreID()!=0) return; #endif - task_wdt_feed(); + task_wdt_feed(); } #endif From 1ca97f5adbc426e2968f446685de4e365586bf2a Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 26 Oct 2016 11:06:53 +0800 Subject: [PATCH 055/110] Move panic handler code from FreeRTOS to esp32 component --- .../include/freertos/panic.h => esp32/include/esp_panic.h} | 0 components/{freertos => esp32}/panic.c | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename components/{freertos/include/freertos/panic.h => esp32/include/esp_panic.h} (100%) rename components/{freertos => esp32}/panic.c (100%) diff --git a/components/freertos/include/freertos/panic.h b/components/esp32/include/esp_panic.h similarity index 100% rename from components/freertos/include/freertos/panic.h rename to components/esp32/include/esp_panic.h diff --git a/components/freertos/panic.c b/components/esp32/panic.c similarity index 100% rename from components/freertos/panic.c rename to components/esp32/panic.c From 7d254eb3f0486b9c81d02f769080325336d996a0 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 26 Oct 2016 12:23:01 +0800 Subject: [PATCH 056/110] Move panic handler and gdbstub into esp32 component, clean up wdt according to merge req suggestions --- components/esp32/Kconfig | 41 +++++++ components/esp32/cpu_start.c | 12 +-- components/{freertos => esp32}/gdbstub.c | 2 +- .../gdbstub.h => esp32/include/esp_gdbstub.h} | 2 +- components/esp32/include/esp_int_wdt.h | 20 +++- components/esp32/include/esp_panic.h | 2 +- components/esp32/include/esp_task_wdt.h | 20 +++- components/esp32/include/soc/rtc_cntl_reg.h | 2 + .../esp32/include/soc/timer_group_reg.h | 2 + components/esp32/int_wdt.c | 17 +-- components/esp32/panic.c | 22 ++-- components/esp32/task_wdt.c | 22 +--- components/freertos/Kconfig | 39 ------- components/freertos/port.c | 4 +- components/freertos/xtensa_vectors.S | 100 +++++++++--------- 15 files changed, 161 insertions(+), 146 deletions(-) rename components/{freertos => esp32}/gdbstub.c (99%) rename components/{freertos/gdbstub.h => esp32/include/esp_gdbstub.h} (93%) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 7fc5090f4..9e141529b 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -81,8 +81,10 @@ config TRACEMEM_RESERVE_DRAM default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS default 0x0 +# Not implemented and/or needs new silicon rev to work config MEMMAP_SPISRAM bool "Use external SPI SRAM chip as main memory" + depends on ESP32_NEEDS_NEW_SILICON_REV default "n" help The ESP32 can control an external SPI SRAM chip, adding the memory it contains to the @@ -153,6 +155,45 @@ config ULP_COPROC_RESERVE_MEM depends on !ULP_COPROC_ENABLED +choice ESP32_PANIC + prompt "Panic handler behaviour" + default FREERTOS_PANIC_PRINT_REBOOT + help + If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is + invoked. Configure the panic handlers action here. + +config ESP32_PANIC_PRINT_HALT + bool "Print registers and halt" + help + Outputs the relevant registers over the serial port and halt the + processor. Needs a manual reset to restart. + +config ESP32_PANIC_PRINT_REBOOT + bool "Print registers and reboot" + help + Outputs the relevant registers over the serial port and immediately + reset the processor. + +config ESP32_PANIC_SILENT_REBOOT + bool "Silent reboot" + help + Just resets the processor without outputting anything + +config ESP32_PANIC_GDBSTUB + bool "Invoke GDBStub" + help + Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem + of the crash. +endchoice + +config ESP32_DEBUG_OCDAWARE + bool "Make exception and panic handlers JTAG/OCD aware" + default y + help + The FreeRTOS panic and unhandled exception handers can detect a JTAG OCD debugger and + instead of panicking, have the debugger stop on the offending instruction. + + config INT_WDT bool "Interrupt watchdog" default y diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index aecea66ef..37205dcd9 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -147,19 +147,19 @@ void start_cpu0_default(void) #endif esp_set_cpu_freq(); // set CPU frequency configured in menuconfig uart_div_modify(0, (APB_CLK_FREQ << 4) / 115200); - ets_setup_syscalls(); - do_global_ctors(); - esp_ipc_init(); - spi_flash_init(); #if CONFIG_BROWNOUT_DET esp_brownout_init(); #endif #if CONFIG_INT_WDT - int_wdt_init(); + esp_int_wdt_init(); #endif #if CONFIG_TASK_WDT - task_wdt_init(); + esp_task_wdt_init(); #endif + ets_setup_syscalls(); + do_global_ctors(); + esp_ipc_init(); + spi_flash_init(); xTaskCreatePinnedToCore(&main_task, "main", ESP_TASK_MAIN_STACK, NULL, diff --git a/components/freertos/gdbstub.c b/components/esp32/gdbstub.c similarity index 99% rename from components/freertos/gdbstub.c rename to components/esp32/gdbstub.c index 1a92299bd..d75fced4c 100644 --- a/components/freertos/gdbstub.c +++ b/components/esp32/gdbstub.c @@ -24,7 +24,7 @@ #include "soc/uart_reg.h" #include "soc/io_mux_reg.h" -#include "gdbstub.h" +#include "esp_gdbstub.h" //Length of buffer used to reserve GDB commands. Has to be at least able to fit the G command, which //implies a minimum size of about 320 bytes. diff --git a/components/freertos/gdbstub.h b/components/esp32/include/esp_gdbstub.h similarity index 93% rename from components/freertos/gdbstub.h rename to components/esp32/include/esp_gdbstub.h index 5e97213c9..bc26f2941 100644 --- a/components/freertos/gdbstub.h +++ b/components/esp32/include/esp_gdbstub.h @@ -17,6 +17,6 @@ #include #include "freertos/xtensa_api.h" -void gdbstubPanicHandler(XtExcFrame *frame); +void esp_gdbstub_panic_handler(XtExcFrame *frame); #endif \ No newline at end of file diff --git a/components/esp32/include/esp_int_wdt.h b/components/esp32/include/esp_int_wdt.h index dc5bd0dda..438740039 100644 --- a/components/esp32/include/esp_int_wdt.h +++ b/components/esp32/include/esp_int_wdt.h @@ -23,15 +23,29 @@ extern "C" { * @{ */ +/* +This routine enables a watchdog to catch instances of processes disabling +interrupts for too long, or code within interrupt handlers taking too long. +It does this by setting up a watchdog which gets fed from the FreeRTOS +task switch interrupt. When this watchdog times out, initially it will call +a high-level interrupt routine that will panic FreeRTOS in order to allow +for forensic examination of the state of the CPU. When this interrupt +handler is not called and the watchdog times out a second time, it will +reset the SoC. + +This uses the TIMERG1 WDT. +*/ + + /** - * @brief Initialize the interrupt watchdog. This is called in the init code, no need to - * call it explicitly. + * @brief Initialize the interrupt watchdog. This is called in the init code if + * the interrupt watchdog is enabled in menuconfig. * * @param null * * @return null */ -void int_wdt_init(); +void esp_int_wdt_init(); /** diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h index 89dee5b24..6aba6c5f4 100644 --- a/components/esp32/include/esp_panic.h +++ b/components/esp32/include/esp_panic.h @@ -14,7 +14,7 @@ #ifndef __ASSEMBLER__ -void setBreakpointIfJtag(void *fn); +void esp_set_breakpoint_if_jtag(void *fn); #endif diff --git a/components/esp32/include/esp_task_wdt.h b/components/esp32/include/esp_task_wdt.h index c050616af..bbc499567 100644 --- a/components/esp32/include/esp_task_wdt.h +++ b/components/esp32/include/esp_task_wdt.h @@ -28,15 +28,25 @@ extern "C" { * @{ */ +/* +This routine enables a more general-purpose task watchdog: tasks can individually +feed the watchdog and the watchdog will bark if one or more tasks haven't fed the +watchdog within the specified time. Optionally, the idle tasks can also configured +to feed the watchdog in a similar fashion, to detect CPU starvation. + +This uses the TIMERG0 WDT. +*/ + + /** - * @brief Initialize the task watchdog. This is called in the init code, no need to - * call it explicitly. + * @brief Initialize the task watchdog. This is called in the init code, if the + * task watchdog is enabled in menuconfig. * * @param null * * @return null */ -void task_wdt_init(); +void esp_task_wdt_init(); /** * @brief Feed the watchdog. After the first feeding session, the watchdog will expect the calling @@ -47,7 +57,7 @@ void task_wdt_init(); * @return null */ -void task_wdt_feed(); +void esp_task_wdt_feed(); /** @@ -57,7 +67,7 @@ void task_wdt_feed(); * * @return null */ -void task_wdt_delete(); +void esp_task_wdt_delete(); /** * @} diff --git a/components/esp32/include/soc/rtc_cntl_reg.h b/components/esp32/include/soc/rtc_cntl_reg.h index 47328611e..24e0f1403 100644 --- a/components/esp32/include/soc/rtc_cntl_reg.h +++ b/components/esp32/include/soc/rtc_cntl_reg.h @@ -14,6 +14,8 @@ #ifndef _SOC_RTC_CNTL_REG_H_ #define _SOC_RTC_CNTL_REG_H_ +#define WDT_WRITE_KEY 0x50D83AA1 + #include "soc.h" #define RTC_CNTL_OPTIONS0_REG (DR_REG_RTCCNTL_BASE + 0x0) diff --git a/components/esp32/include/soc/timer_group_reg.h b/components/esp32/include/soc/timer_group_reg.h index 96a5eb790..0d67cab51 100644 --- a/components/esp32/include/soc/timer_group_reg.h +++ b/components/esp32/include/soc/timer_group_reg.h @@ -15,6 +15,8 @@ #define __TIMG_REG_H__ #include "soc.h" +#define WDT_WRITE_KEY 0x50D83AA1 + #define REG_TIMG_BASE(i) (DR_REG_TIMERGROUP0_BASE + i*0x1000) #define TIMG_T0CONFIG_REG(i) (REG_TIMG_BASE(i) + 0x0000) /* TIMG_T0_EN : R/W ;bitpos:[31] ;default: 1'h0 ; */ diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c index 8c506bde1..5f123ee36 100644 --- a/components/esp32/int_wdt.c +++ b/components/esp32/int_wdt.c @@ -13,18 +13,6 @@ // limitations under the License. -/* -This routine enables a watchdog to catch instances of processes disabling -interrupts for too long, or code within interrupt handlers taking too long. -It does this by setting up a watchdog which gets fed from the FreeRTOS -task switch interrupt. When this watchdog times out, initially it will call -a high-level interrupt routine that will panic FreeRTOS in order to allow -for forensic examination of the state of the CPU. When this interrupt -handler is not called and the watchdog times out a second time, it will -reset the SoC. - -This uses the TIMERG1 WDT. -*/ #include "sdkconfig.h" #include @@ -37,6 +25,7 @@ This uses the TIMERG1 WDT. #include "esp_err.h" #include "esp_intr.h" #include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" #include "esp_int_wdt.h" @@ -45,9 +34,8 @@ This uses the TIMERG1 WDT. #define WDT_INT_NUM 24 -#define WDT_WRITE_KEY 0x50D83AA1 -void int_wdt_init() { +void esp_int_wdt_init() { TIMERG1.wdt_wprotect=WDT_WRITE_KEY; TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS @@ -73,6 +61,7 @@ void int_wdt_init() { } +//Take care: the tick hook can also be called before esp_int_wdt_init() is called. #if CONFIG_INT_WDT_CHECK_CPU1 //Not static; the ISR assembly checks this. bool int_wdt_app_cpu_ticked=false; diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 0735e6b04..758d581d0 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -26,11 +26,10 @@ #include "soc/dport_reg.h" #include "soc/rtc_cntl_reg.h" #include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" -#include "gdbstub.h" -#include "panic.h" - -#define WDT_WRITE_KEY 0x50D83AA1 +#include "esp_gdbstub.h" +#include "esp_panic.h" /* @@ -196,7 +195,13 @@ void xt_unhandled_exception(XtExcFrame *frame) { } -//Disables all but one WDT, and allows enough time on that WDT to do what we need to do. +/* +If watchdogs are enabled, the panic handler runs the risk of getting aborted pre-emptively because +an overzealous watchdog decides to reset it. On the other hand, if we disable all watchdogs, we run +the risk of somehow halting in the panic handler and not resetting. That is why this routine kills +all watchdogs except the timer group 0 watchdog, and it reconfigures that to reset the chip after +one second. +*/ static void reconfigureAllWdts() { TIMERG0.wdt_wprotect=WDT_WRITE_KEY; TIMERG0.wdt_feed=1; @@ -213,6 +218,9 @@ static void reconfigureAllWdts() { TIMERG1.wdt_wprotect=0; } +/* +This disables all the watchdogs for when we call the gdbstub. +*/ static void disableAllWdts() { TIMERG0.wdt_wprotect=WDT_WRITE_KEY; TIMERG0.wdt_config0.en=0; @@ -254,7 +262,7 @@ void commonErrorHandler(XtExcFrame *frame) { #if CONFIG_FREERTOS_PANIC_GDBSTUB disableAllWdts(); panicPutStr("Entering gdb stub now.\r\n"); - gdbstubPanicHandler(frame); + esp_gdbstub_panic_handler(frame); #elif CONFIG_FREERTOS_PANIC_PRINT_REBOOT || CONFIG_FREERTOS_PANIC_SILENT_REBOOT panicPutStr("Rebooting...\r\n"); for (x=0; x<100; x++) ets_delay_us(1000); @@ -267,7 +275,7 @@ void commonErrorHandler(XtExcFrame *frame) { } -void setBreakpointIfJtag(void *fn) { +void esp_set_breakpoint_if_jtag(void *fn) { if (!inOCDMode()) return; setFirstBreakpoint((uint32_t)fn); } diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index e6a462088..24d3977b1 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -13,14 +13,6 @@ // limitations under the License. -/* -This routine enables a more general-purpose task watchdog: tasks can individually -feed the watchdog and the watchdog will bark if one or more tasks haven't fed the -watchdog within the specified time. Optionally, the idle tasks can also configured -to feed the watchdog in a similar fashion, to detect CPU starvation. - -This uses the TIMERG0 WDT. -*/ #include #include @@ -35,6 +27,7 @@ This uses the TIMERG0 WDT. #include "esp_intr.h" #include "esp_attr.h" #include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" #include "esp_log.h" #include "esp_task_wdt.h" @@ -52,11 +45,6 @@ struct wdt_task_t { static wdt_task_t *wdt_task_list=NULL; -//We use this interrupt number on whatever task calls task_wdt_init. -#define WDT_INT_NUM 24 - -#define WDT_WRITE_KEY 0x50D83AA1 - static void IRAM_ATTR task_wdt_isr(void *arg) { wdt_task_t *wdttask; const char *cpu; @@ -87,7 +75,7 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { } -void task_wdt_feed() { +void esp_task_wdt_feed() { wdt_task_t *wdttask=wdt_task_list; bool found_task=false, do_feed_wdt=true; TaskHandle_t handle=xTaskGetCurrentTaskHandle(); @@ -127,7 +115,7 @@ void task_wdt_feed() { } } -void task_wdt_delete() { +void esp_task_wdt_delete() { TaskHandle_t handle=xTaskGetCurrentTaskHandle(); wdt_task_t *wdttask=wdt_task_list; //Wdt task list can't be empty @@ -152,7 +140,7 @@ void task_wdt_delete() { } } -void task_wdt_init() { +void esp_task_wdt_init() { TIMERG0.wdt_wprotect=WDT_WRITE_KEY; TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS @@ -178,7 +166,7 @@ void vApplicationIdleHook(void) { #if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 if (xPortGetCoreID()!=0) return; #endif - task_wdt_feed(); + esp_task_wdt_feed(); } #endif diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 1a65e1eeb..413c710d2 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -93,45 +93,6 @@ config FREERTOS_THREAD_LOCAL_STORAGE_POINTERS If using the WiFi stack, this value must be at least 1. -#This still needs to be implemented. -choice FREERTOS_PANIC - prompt "Panic handler behaviour" - default FREERTOS_PANIC_PRINT_REBOOT - help - If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is - invoked. Configure the panic handlers action here. - -config FREERTOS_PANIC_PRINT_HALT - bool "Print registers and halt" - help - Outputs the relevant registers over the serial port and halt the - processor. Needs a manual reset to restart. - -config FREERTOS_PANIC_PRINT_REBOOT - bool "Print registers and reboot" - help - Outputs the relevant registers over the serial port and immediately - reset the processor. - -config FREERTOS_PANIC_SILENT_REBOOT - bool "Silent reboot" - help - Just resets the processor without outputting anything - -config FREERTOS_PANIC_GDBSTUB - bool "Invoke GDBStub" - help - Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem - of the crash. -endchoice - -config FREERTOS_DEBUG_OCDAWARE - bool "Make exception and panic handlers JTAG/OCD aware" - default y - help - The FreeRTOS panic and unhandled exception handers can detect a JTAG OCD debugger and - instead of panicking, have the debugger stop on the offending instruction. - choice FREERTOS_ASSERT prompt "FreeRTOS assertions" default FREERTOS_ASSERT_FAIL_ABORT diff --git a/components/freertos/port.c b/components/freertos/port.c index a982db7d4..834d787be 100644 --- a/components/freertos/port.c +++ b/components/freertos/port.c @@ -101,7 +101,7 @@ #include "FreeRTOS.h" #include "task.h" -#include "panic.h" +#include "esp_panic.h" /* Defined in portasm.h */ extern void _frxt_tick_timer_init(void); @@ -375,7 +375,7 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) { #if CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG void vPortFirstTaskHook(TaskFunction_t function) { - setBreakpointIfJtag(function); + esp_set_breakpoint_if_jtag(function); } #endif diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index fcf9af1cd..94acbd896 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -91,7 +91,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *******************************************************************************/ #include "xtensa_rtos.h" -#include "panic.h" +#include "esp_panic.h" #include "sdkconfig.h" /* Define for workaround: pin no-cpu-affinity tasks to a cpu when fpu is used. @@ -303,12 +303,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ .section .iram1,"ax" - .global panicHandler + .global panicHandler .global _xt_panic .type _xt_panic,@function .align 4 - .literal_position + .literal_position .align 4 _xt_panic: @@ -344,41 +344,41 @@ _xt_panic: movi a0, PS_INTLEVEL(7) | PS_UM | PS_WOE wsr a0, PS - //Call panic handler - mov a6,sp - call4 panicHandler + //Call panic handler + mov a6,sp + call4 panicHandler 1: j 1b /* loop infinitely */ - retw + retw - .align 4 + .align 4 //Call using call0. Prints the hex char in a2. Kills a3, a4, a5 panic_print_hex: - movi a3,0x60000000 - movi a4,8 + movi a3,0x60000000 + movi a4,8 panic_print_hex_loop: - l32i a5, a3, 0x1c - extui a5, a5, 16, 8 - bgei a5,64,panic_print_hex_loop + l32i a5, a3, 0x1c + extui a5, a5, 16, 8 + bgei a5,64,panic_print_hex_loop - srli a5,a2,28 - bgei a5,10,panic_print_hex_a - addi a5,a5,'0' - j panic_print_hex_ok + srli a5,a2,28 + bgei a5,10,panic_print_hex_a + addi a5,a5,'0' + j panic_print_hex_ok panic_print_hex_a: - addi a5,a5,'A'-10 + addi a5,a5,'A'-10 panic_print_hex_ok: - s32i a5,a3,0 - slli a2,a2,4 - - addi a4,a4,-1 - bnei a4,0,panic_print_hex_loop - movi a5,' ' - s32i a5,a3,0 + s32i a5,a3,0 + slli a2,a2,4 + + addi a4,a4,-1 + bnei a4,0,panic_print_hex_loop + movi a5,' ' + s32i a5,a3,0 - ret + ret @@ -463,8 +463,8 @@ _DebugExceptionVector: jx a3 #else wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */ - movi a0,PANIC_RSN_DEBUGEXCEPTION - wsr a0,EXCCAUSE + movi a0,PANIC_RSN_DEBUGEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* does not return */ rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */ #endif @@ -492,8 +492,8 @@ _DoubleExceptionVector: #if XCHAL_HAVE_DEBUG break 1, 4 /* unhandled double exception */ #endif - movi a0,PANIC_RSN_DOUBLEEXCEPTION - wsr a0,EXCCAUSE + movi a0,PANIC_RSN_DOUBLEEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* does not return */ rfde /* make a0 point here not later */ @@ -527,8 +527,8 @@ _xt_kernel_exc: #if XCHAL_HAVE_DEBUG break 1, 0 /* unhandled kernel exception */ #endif - movi a0,PANIC_RSN_KERNELEXCEPTION - wsr a0,EXCCAUSE + movi a0,PANIC_RSN_KERNELEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* does not return */ rfe /* make a0 point here not there */ @@ -916,11 +916,11 @@ _xt_coproc_exc: addi a2, a2, TASKTCB_XCOREID_OFFSET /* offset to xCoreID in tcb struct */ s32i a3, a2, 0 /* store current cpuid */ - /* Grab correct xt_coproc_owner_sa for this core */ - movi a2, XCHAL_CP_MAX << 2 - mull a2, a2, a3 + /* Grab correct xt_coproc_owner_sa for this core */ + movi a2, XCHAL_CP_MAX << 2 + mull a2, a2, a3 movi a3, _xt_coproc_owner_sa /* a3 = base of owner array */ - add a3, a3, a2 + add a3, a3, a2 extui a2, a0, 0, 16 /* coprocessor bitmask portion */ or a4, a4, a2 /* a4 = CPENABLE | (1 << n) */ @@ -1031,8 +1031,8 @@ _xt_coproc_exc: #if XCHAL_HAVE_DEBUG break 1, 1 /* unhandled user exception */ #endif - movi a0,PANIC_RSN_COPROCEXCEPTION - wsr a0,EXCCAUSE + movi a0,PANIC_RSN_COPROCEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* not in a thread (invalid) */ /* never returns */ @@ -1620,19 +1620,19 @@ _xt_highint4: /* On the ESP32, this level is used for the INT_WDT handler. If that triggers, the program is stuck with interrupts off and the CPU should panic. */ - rsr a0, EXCSAVE_4 - wsr a0, EXCSAVE_1 /* panic handler reads this register */ - /* Set EXCCAUSE to reflect cause of the wdt int trigger */ - movi a0,PANIC_RSN_INTWDT_CPU0 - wsr a0,EXCCAUSE + rsr a0, EXCSAVE_4 + wsr a0, EXCSAVE_1 /* panic handler reads this register */ + /* Set EXCCAUSE to reflect cause of the wdt int trigger */ + movi a0,PANIC_RSN_INTWDT_CPU0 + wsr a0,EXCCAUSE #if CONFIG_INT_WDT_CHECK_CPU1 - /* Check if the cause is the app cpu failing to tick.*/ - movi a0, int_wdt_app_cpu_ticked - l32i a0, a0, 0 - bnez a0, 1f - /* It is. Modify cause. */ - movi a0,PANIC_RSN_INTWDT_CPU1 - wsr a0,EXCCAUSE + /* Check if the cause is the app cpu failing to tick.*/ + movi a0, int_wdt_app_cpu_ticked + l32i a0, a0, 0 + bnez a0, 1f + /* It is. Modify cause. */ + movi a0,PANIC_RSN_INTWDT_CPU1 + wsr a0,EXCCAUSE 1: #endif call0 _xt_panic From bb1efe50c3c86c6d466834cef8f5110c0163f404 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 26 Oct 2016 12:23:35 +0800 Subject: [PATCH 057/110] Remove superfluous backup files --- components/freertos/xtensa_vectors.S-new | 1915 -------------------- components/freertos/xtensa_vectors.S-old | 2064 ---------------------- 2 files changed, 3979 deletions(-) delete mode 100644 components/freertos/xtensa_vectors.S-new delete mode 100644 components/freertos/xtensa_vectors.S-old diff --git a/components/freertos/xtensa_vectors.S-new b/components/freertos/xtensa_vectors.S-new deleted file mode 100644 index 88349eee9..000000000 --- a/components/freertos/xtensa_vectors.S-new +++ /dev/null @@ -1,1915 +0,0 @@ -/******************************************************************************* -Copyright (c) 2006-2015 Cadence Design Systems Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------- - - XTENSA VECTORS AND LOW LEVEL HANDLERS FOR AN RTOS - - Xtensa low level exception and interrupt vectors and handlers for an RTOS. - - Interrupt handlers and user exception handlers support interaction with - the RTOS by calling XT_RTOS_INT_ENTER and XT_RTOS_INT_EXIT before and - after user's specific interrupt handlers. These macros are defined in - xtensa_.h to call suitable functions in a specific RTOS. - - Users can install application-specific interrupt handlers for low and - medium level interrupts, by calling xt_set_interrupt_handler(). These - handlers can be written in C, and must obey C calling convention. The - handler table is indexed by the interrupt number. Each handler may be - provided with an argument. - - Note that the system timer interrupt is handled specially, and is - dispatched to the RTOS-specific handler. This timer cannot be hooked - by application code. - - Optional hooks are also provided to install a handler per level at - run-time, made available by compiling this source file with - '-DXT_INTEXC_HOOKS' (useful for automated testing). - -!! This file is a template that usually needs to be modified to handle !! -!! application specific interrupts. Search USER_EDIT for helpful comments !! -!! on where to insert handlers and how to write them. !! - - Users can also install application-specific exception handlers in the - same way, by calling xt_set_exception_handler(). One handler slot is - provided for each exception type. Note that some exceptions are handled - by the porting layer itself, and cannot be taken over by application - code in this manner. These are the alloca, syscall, and coprocessor - exceptions. - - The exception handlers can be written in C, and must follow C calling - convention. Each handler is passed a pointer to an exception frame as - its single argument. The exception frame is created on the stack, and - holds the saved context of the thread that took the exception. If the - handler returns, the context will be restored and the instruction that - caused the exception will be retried. If the handler makes any changes - to the saved state in the exception frame, the changes will be applied - when restoring the context. - - Because Xtensa is a configurable architecture, this port supports all user - generated configurations (except restrictions stated in the release notes). - This is accomplished by conditional compilation using macros and functions - defined in the Xtensa HAL (hardware adaptation layer) for your configuration. - Only the relevant parts of this file will be included in your RTOS build. - For example, this file provides interrupt vector templates for all types and - all priority levels, but only the ones in your configuration are built. - - NOTES on the use of 'call0' for long jumps instead of 'j': - 1. This file should be assembled with the -mlongcalls option to xt-xcc. - 2. The -mlongcalls compiler option causes 'call0 dest' to be expanded to - a sequence 'l32r a0, dest' 'callx0 a0' which works regardless of the - distance from the call to the destination. The linker then relaxes - it back to 'call0 dest' if it determines that dest is within range. - This allows more flexibility in locating code without the performance - overhead of the 'l32r' literal data load in cases where the destination - is in range of 'call0'. There is an additional benefit in that 'call0' - has a longer range than 'j' due to the target being word-aligned, so - the 'l32r' sequence is less likely needed. - 3. The use of 'call0' with -mlongcalls requires that register a0 not be - live at the time of the call, which is always the case for a function - call but needs to be ensured if 'call0' is used as a jump in lieu of 'j'. - 4. This use of 'call0' is independent of the C function call ABI. - -*******************************************************************************/ - -#include "xtensa_rtos.h" - - -/* Enable stack backtrace across exception/interrupt - see below */ -#define XT_DEBUG_BACKTRACE 1 - - -/* --------------------------------------------------------------------------------- - Defines used to access _xtos_interrupt_table. --------------------------------------------------------------------------------- -*/ -#define XIE_HANDLER 0 -#define XIE_ARG 4 -#define XIE_SIZE 8 - -/* --------------------------------------------------------------------------------- - Macro extract_msb - return the input with only the highest bit set. - - Input : "ain" - Input value, clobbered. - Output : "aout" - Output value, has only one bit set, MSB of "ain". - The two arguments must be different AR registers. --------------------------------------------------------------------------------- -*/ - - .macro extract_msb aout ain -1: - addi \aout, \ain, -1 /* aout = ain - 1 */ - and \ain, \ain, \aout /* ain = ain & aout */ - bnez \ain, 1b /* repeat until ain == 0 */ - addi \aout, \aout, 1 /* return aout + 1 */ - .endm - -/* --------------------------------------------------------------------------------- - Macro dispatch_c_isr - dispatch interrupts to user ISRs. - This will dispatch to user handlers (if any) that are registered in the - XTOS dispatch table (_xtos_interrupt_table). These handlers would have - been registered by calling _xtos_set_interrupt_handler(). There is one - exception - the timer interrupt used by the OS will not be dispatched - to a user handler - this must be handled by the caller of this macro. - - Level triggered and software interrupts are automatically deasserted by - this code. - - ASSUMPTIONS: - -- PS.INTLEVEL is set to "level" at entry - -- PS.EXCM = 0, C calling enabled - - NOTE: For CALL0 ABI, a12-a15 have not yet been saved. - - NOTE: This macro will use registers a0 and a2-a6. The arguments are: - level -- interrupt level - mask -- interrupt bitmask for this level --------------------------------------------------------------------------------- -*/ - - .macro dispatch_c_isr level mask - - /* Get mask of pending, enabled interrupts at this level into a2. */ - -.L_xt_user_int_&level&: - rsr a2, INTENABLE - rsr a3, INTERRUPT - movi a4, \mask - and a2, a2, a3 - and a2, a2, a4 - beqz a2, 9f /* nothing to do */ - - /* This bit of code provides a nice debug backtrace in the debugger. - It does take a few more instructions, so undef XT_DEBUG_BACKTRACE - if you want to save the cycles. - */ - #if XT_DEBUG_BACKTRACE - #ifndef __XTENSA_CALL0_ABI__ - rsr a0, EPC_1 + \level - 1 /* return address */ - movi a4, 0xC0000000 /* constant with top 2 bits set (call size) */ - or a0, a0, a4 /* set top 2 bits */ - addx2 a0, a4, a0 /* clear top bit -- simulating call4 size */ - #endif - #endif - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a4, _xt_intexc_hooks - l32i a4, a4, \level << 2 - beqz a4, 2f - #ifdef __XTENSA_CALL0_ABI__ - callx0 a4 - beqz a2, 9f - #else - mov a6, a2 - callx4 a4 - beqz a6, 9f - mov a2, a6 - #endif -2: - #endif - - /* Now look up in the dispatch table and call user ISR if any. */ - /* If multiple bits are set then MSB has highest priority. */ - - extract_msb a4, a2 /* a4 = MSB of a2, a2 trashed */ - - #ifdef XT_USE_SWPRI - /* Enable all interrupts at this level that are numerically higher - than the one we just selected, since they are treated as higher - priority. - */ - movi a3, \mask /* a3 = all interrupts at this level */ - add a2, a4, a4 /* a2 = a4 << 1 */ - addi a2, a2, -1 /* a2 = mask of 1's <= a4 bit */ - and a2, a2, a3 /* a2 = mask of all bits <= a4 at this level */ - movi a3, _xt_intdata - l32i a6, a3, 4 /* a6 = _xt_vpri_mask */ - neg a2, a2 - addi a2, a2, -1 /* a2 = mask to apply */ - and a5, a6, a2 /* mask off all bits <= a4 bit */ - s32i a5, a3, 4 /* update _xt_vpri_mask */ - rsr a3, INTENABLE - and a3, a3, a2 /* mask off all bits <= a4 bit */ - wsr a3, INTENABLE - rsil a3, \level - 1 /* lower interrupt level by 1 */ - #endif - - movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */ - wsr a4, INTCLEAR /* clear sw or edge-triggered interrupt */ - beq a3, a4, 7f /* if timer interrupt then skip table */ - - find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */ - - movi a4, _xt_interrupt_table - addx8 a3, a3, a4 /* a3 = address of interrupt table entry */ - l32i a4, a3, XIE_HANDLER /* a4 = handler address */ - #ifdef __XTENSA_CALL0_ABI__ - mov a12, a6 /* save in callee-saved reg */ - l32i a2, a3, XIE_ARG /* a2 = handler arg */ - callx0 a4 /* call handler */ - mov a2, a12 - #else - mov a2, a6 /* save in windowed reg */ - l32i a6, a3, XIE_ARG /* a6 = handler arg */ - callx4 a4 /* call handler */ - #endif - - #ifdef XT_USE_SWPRI - j 8f - #else - j .L_xt_user_int_&level& /* check for more interrupts */ - #endif - -7: - - .ifeq XT_TIMER_INTPRI - \level -.L_xt_user_int_timer_&level&: - /* - Interrupt handler for the RTOS tick timer if at this level. - We'll be reading the interrupt state again after this call - so no need to preserve any registers except a6 (vpri_mask). - */ - - #ifdef __XTENSA_CALL0_ABI__ - mov a12, a6 - call0 XT_RTOS_TIMER_INT - mov a2, a12 - #else - mov a2, a6 - call4 XT_RTOS_TIMER_INT - #endif - .endif - - #ifdef XT_USE_SWPRI - j 8f - #else - j .L_xt_user_int_&level& /* check for more interrupts */ - #endif - - #ifdef XT_USE_SWPRI -8: - /* Restore old value of _xt_vpri_mask from a2. Also update INTENABLE from - virtual _xt_intenable which _could_ have changed during interrupt - processing. */ - - movi a3, _xt_intdata - l32i a4, a3, 0 /* a4 = _xt_intenable */ - s32i a2, a3, 4 /* update _xt_vpri_mask */ - and a4, a4, a2 /* a4 = masked intenable */ - wsr a4, INTENABLE /* update INTENABLE */ - #endif - -9: - /* done */ - - .endm - - -/* --------------------------------------------------------------------------------- - Panic handler. - Should be reached by call0 (preferable) or jump only. If call0, a0 says where - from. If on simulator, display panic message and abort, else loop indefinitely. --------------------------------------------------------------------------------- -*/ - - .text - .global _xt_panic - .type _xt_panic,@function - .align 4 - -_xt_panic: - #ifdef XT_SIMULATOR - addi a4, a0, -3 /* point to call0 */ - movi a3, _xt_panic_message - movi a2, SYS_log_msg - simcall - movi a2, SYS_gdb_abort - simcall - #else - rsil a2, XCHAL_EXCM_LEVEL /* disable all low & med ints */ -1: j 1b /* loop infinitely */ - #endif - - .section .rodata, "a" - .align 4 - -_xt_panic_message: - .string "\n*** _xt_panic() was called from 0x%08x or jumped to. ***\n" - - -/* --------------------------------------------------------------------------------- - Hooks to dynamically install handlers for exceptions and interrupts. - Allows automated regression frameworks to install handlers per test. - Consists of an array of function pointers indexed by interrupt level, - with index 0 containing the entry for user exceptions. - Initialized with all 0s, meaning no handler is installed at each level. - See comment in xtensa_rtos.h for more details. --------------------------------------------------------------------------------- -*/ - - #ifdef XT_INTEXC_HOOKS - .data - .global _xt_intexc_hooks - .type _xt_intexc_hooks,@object - .align 4 - -_xt_intexc_hooks: - .fill XT_INTEXC_HOOK_NUM, 4, 0 - #endif - - -/* --------------------------------------------------------------------------------- - EXCEPTION AND LEVEL 1 INTERRUPT VECTORS AND LOW LEVEL HANDLERS - (except window exception vectors). - - Each vector goes at a predetermined location according to the Xtensa - hardware configuration, which is ensured by its placement in a special - section known to the Xtensa linker support package (LSP). It performs - the minimum necessary before jumping to the handler in the .text section. - - The corresponding handler goes in the normal .text section. It sets up - the appropriate stack frame, saves a few vector-specific registers and - calls XT_RTOS_INT_ENTER to save the rest of the interrupted context - and enter the RTOS, then sets up a C environment. It then calls the - user's interrupt handler code (which may be coded in C) and finally - calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling. - - While XT_RTOS_INT_EXIT does not return directly to the interruptee, - eventually the RTOS scheduler will want to dispatch the interrupted - task or handler. The scheduler will return to the exit point that was - saved in the interrupt stack frame at XT_STK_EXIT. --------------------------------------------------------------------------------- -*/ - - -/* --------------------------------------------------------------------------------- -Debug Exception. --------------------------------------------------------------------------------- -*/ - -#if XCHAL_HAVE_DEBUG - - .begin literal_prefix .DebugExceptionVector - .section .DebugExceptionVector.text, "ax" - .global _DebugExceptionVector - .align 4 - -_DebugExceptionVector: - - #ifdef XT_SIMULATOR - /* - In the simulator, let the debugger (if any) handle the debug exception, - or simply stop the simulation: - */ - wsr a2, EXCSAVE+XCHAL_DEBUGLEVEL /* save a2 where sim expects it */ - movi a2, SYS_gdb_enter_sktloop - simcall /* have ISS handle debug exc. */ - #elif 0 /* change condition to 1 to use the HAL minimal debug handler */ - wsr a3, EXCSAVE+XCHAL_DEBUGLEVEL - movi a3, xthal_debugexc_defhndlr_nw /* use default debug handler */ - jx a3 - #else - wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */ - call0 _xt_panic /* does not return */ - rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */ - #endif - - .end literal_prefix - -#endif - -/* --------------------------------------------------------------------------------- -Double Exception. -Double exceptions are not a normal occurrence. They indicate a bug of some kind. --------------------------------------------------------------------------------- -*/ - -#ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR - - .begin literal_prefix .DoubleExceptionVector - .section .DoubleExceptionVector.text, "ax" - .global _DoubleExceptionVector - .align 4 - -_DoubleExceptionVector: - - #if XCHAL_HAVE_DEBUG - break 1, 4 /* unhandled double exception */ - #endif - call0 _xt_panic /* does not return */ - rfde /* make a0 point here not later */ - - .end literal_prefix - -#endif /* XCHAL_DOUBLEEXC_VECTOR_VADDR */ - -/* --------------------------------------------------------------------------------- -Kernel Exception (including Level 1 Interrupt from kernel mode). --------------------------------------------------------------------------------- -*/ - - .begin literal_prefix .KernelExceptionVector - .section .KernelExceptionVector.text, "ax" - .global _KernelExceptionVector - .align 4 - -_KernelExceptionVector: - - wsr a0, EXCSAVE_1 /* preserve a0 */ - call0 _xt_kernel_exc /* kernel exception handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .align 4 - -_xt_kernel_exc: - #if XCHAL_HAVE_DEBUG - break 1, 0 /* unhandled kernel exception */ - #endif - call0 _xt_panic /* does not return */ - rfe /* make a0 point here not there */ - - -/* --------------------------------------------------------------------------------- -User Exception (including Level 1 Interrupt from user mode). --------------------------------------------------------------------------------- -*/ - - .begin literal_prefix .UserExceptionVector - .section .UserExceptionVector.text, "ax" - .global _UserExceptionVector - .type _UserExceptionVector,@function - .align 4 - -_UserExceptionVector: - - wsr a0, EXCSAVE_1 /* preserve a0 */ - call0 _xt_user_exc /* user exception handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - -/* --------------------------------------------------------------------------------- - Insert some waypoints for jumping beyond the signed 8-bit range of - conditional branch instructions, so the conditional branchces to specific - exception handlers are not taken in the mainline. Saves some cycles in the - mainline. --------------------------------------------------------------------------------- -*/ - - .text - - #if XCHAL_HAVE_WINDOWED - .align 4 -_xt_to_alloca_exc: - call0 _xt_alloca_exc /* in window vectors section */ - /* never returns here - call0 is used as a jump (see note at top) */ - #endif - - .align 4 -_xt_to_syscall_exc: - call0 _xt_syscall_exc - /* never returns here - call0 is used as a jump (see note at top) */ - - #if XCHAL_CP_NUM > 0 - .align 4 -_xt_to_coproc_exc: - call0 _xt_coproc_exc - /* never returns here - call0 is used as a jump (see note at top) */ - #endif - - -/* --------------------------------------------------------------------------------- - User exception handler. --------------------------------------------------------------------------------- -*/ - - .type _xt_user_exc,@function - .align 4 - -_xt_user_exc: - - /* If level 1 interrupt then jump to the dispatcher */ - rsr a0, EXCCAUSE - beqi a0, EXCCAUSE_LEVEL1INTERRUPT, _xt_lowint1 - - /* Handle any coprocessor exceptions. Rely on the fact that exception - numbers above EXCCAUSE_CP0_DISABLED all relate to the coprocessors. - */ - #if XCHAL_CP_NUM > 0 - bgeui a0, EXCCAUSE_CP0_DISABLED, _xt_to_coproc_exc - #endif - - /* Handle alloca and syscall exceptions */ - #if XCHAL_HAVE_WINDOWED - beqi a0, EXCCAUSE_ALLOCA, _xt_to_alloca_exc - #endif - beqi a0, EXCCAUSE_SYSCALL, _xt_to_syscall_exc - - /* Handle all other exceptions. All can have user-defined handlers. */ - /* NOTE: we'll stay on the user stack for exception handling. */ - - /* Allocate exception frame and save minimal context. */ - mov a0, sp - addi sp, sp, -XT_STK_FRMSZ - s32i a0, sp, XT_STK_A1 - #if XCHAL_HAVE_WINDOWED - s32e a0, sp, -12 /* for debug backtrace */ - #endif - rsr a0, PS /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_1 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_1 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - #if XCHAL_HAVE_WINDOWED - s32e a0, sp, -16 /* for debug backtrace */ - #endif - s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ - s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ - call0 _xt_context_save - - /* Save exc cause and vaddr into exception frame */ - rsr a0, EXCCAUSE - s32i a0, sp, XT_STK_EXCCAUSE - rsr a0, EXCVADDR - s32i a0, sp, XT_STK_EXCVADDR - - /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM - #else - movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE - #endif - wsr a0, PS - - #ifdef XT_DEBUG_BACKTRACE - #ifndef __XTENSA_CALL0_ABI__ - rsr a0, EPC_1 /* return address for debug backtrace */ - movi a5, 0xC0000000 /* constant with top 2 bits set (call size) */ - rsync /* wait for WSR.PS to complete */ - or a0, a0, a5 /* set top 2 bits */ - addx2 a0, a5, a0 /* clear top bit -- thus simulating call4 size */ - #else - rsync /* wait for WSR.PS to complete */ - #endif - #endif - - rsr a2, EXCCAUSE /* recover exc cause */ - - #ifdef XT_INTEXC_HOOKS - /* - Call exception hook to pre-handle exceptions (if installed). - Pass EXCCAUSE in a2, and check result in a2 (if -1, skip default handling). - */ - movi a4, _xt_intexc_hooks - l32i a4, a4, 0 /* user exception hook index 0 */ - beqz a4, 1f -.Ln_xt_user_exc_call_hook: - #ifdef __XTENSA_CALL0_ABI__ - callx0 a4 - beqi a2, -1, .L_xt_user_done - #else - mov a6, a2 - callx4 a4 - beqi a6, -1, .L_xt_user_done - mov a2, a6 - #endif -1: - #endif - - rsr a2, EXCCAUSE /* recover exc cause */ - movi a3, _xt_exception_table - addx4 a4, a2, a3 /* a4 = address of exception table entry */ - l32i a4, a4, 0 /* a4 = handler address */ - #ifdef __XTENSA_CALL0_ABI__ - mov a2, sp /* a2 = pointer to exc frame */ - callx0 a4 /* call handler */ - #else - mov a6, sp /* a6 = pointer to exc frame */ - callx4 a4 /* call handler */ - #endif - -.L_xt_user_done: - - /* Restore context and return */ - call0 _xt_context_restore - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, PS - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_1 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove exception frame */ - rsync /* ensure PS and EPC written */ - rfe /* PS.EXCM is cleared */ - - -/* --------------------------------------------------------------------------------- - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. --------------------------------------------------------------------------------- -*/ - - .global _xt_user_exit - .type _xt_user_exit,@function - .align 4 -_xt_user_exit: - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, PS - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_1 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure PS and EPC written */ - rfe /* PS.EXCM is cleared */ - - -/* --------------------------------------------------------------------------------- -Syscall Exception Handler (jumped to from User Exception Handler). -Syscall 0 is required to spill the register windows (no-op in Call 0 ABI). -Only syscall 0 is handled here. Other syscalls return -1 to caller in a2. --------------------------------------------------------------------------------- -*/ - - .text - .type _xt_syscall_exc,@function - .align 4 -_xt_syscall_exc: - - #ifdef __XTENSA_CALL0_ABI__ - /* - Save minimal regs for scratch. Syscall 0 does nothing in Call0 ABI. - Use a minimal stack frame (16B) to save A2 & A3 for scratch. - PS.EXCM could be cleared here, but unlikely to improve worst-case latency. - rsr a0, PS - addi a0, a0, -PS_EXCM_MASK - wsr a0, PS - */ - addi sp, sp, -16 - s32i a2, sp, 8 - s32i a3, sp, 12 - #else /* Windowed ABI */ - /* - Save necessary context and spill the register windows. - PS.EXCM is still set and must remain set until after the spill. - Reuse context save function though it saves more than necessary. - For this reason, a full interrupt stack frame is allocated. - */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ - s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ - call0 _xt_context_save - #endif - - /* - Grab the interruptee's PC and skip over the 'syscall' instruction. - If it's at the end of a zero-overhead loop and it's not on the last - iteration, decrement loop counter and skip to beginning of loop. - */ - rsr a2, EPC_1 /* a2 = PC of 'syscall' */ - addi a3, a2, 3 /* ++PC */ - #if XCHAL_HAVE_LOOPS - rsr a0, LEND /* if (PC == LEND */ - bne a3, a0, 1f - rsr a0, LCOUNT /* && LCOUNT != 0) */ - beqz a0, 1f /* { */ - addi a0, a0, -1 /* --LCOUNT */ - rsr a3, LBEG /* PC = LBEG */ - wsr a0, LCOUNT /* } */ - #endif -1: wsr a3, EPC_1 /* update PC */ - - /* Restore interruptee's context and return from exception. */ - #ifdef __XTENSA_CALL0_ABI__ - l32i a2, sp, 8 - l32i a3, sp, 12 - addi sp, sp, 16 - #else - call0 _xt_context_restore - addi sp, sp, XT_STK_FRMSZ - #endif - movi a0, -1 - movnez a2, a0, a2 /* return -1 if not syscall 0 */ - rsr a0, EXCSAVE_1 - rfe - -/* --------------------------------------------------------------------------------- -Co-Processor Exception Handler (jumped to from User Exception Handler). -These exceptions are generated by co-processor instructions, which are only -allowed in thread code (not in interrupts or kernel code). This restriction is -deliberately imposed to reduce the burden of state-save/restore in interrupts. --------------------------------------------------------------------------------- -*/ -#if XCHAL_CP_NUM > 0 - - .section .rodata, "a" - -/* Offset to CP n save area in thread's CP save area. */ - .global _xt_coproc_sa_offset - .type _xt_coproc_sa_offset,@object - .align 16 /* minimize crossing cache boundaries */ -_xt_coproc_sa_offset: - .word XT_CP0_SA, XT_CP1_SA, XT_CP2_SA, XT_CP3_SA - .word XT_CP4_SA, XT_CP5_SA, XT_CP6_SA, XT_CP7_SA - -/* Bitmask for CP n's CPENABLE bit. */ - .type _xt_coproc_mask,@object - .align 16,,8 /* try to keep it all in one cache line */ - .set i, 0 -_xt_coproc_mask: - .rept XCHAL_CP_MAX - .long (i<<16) | (1<= 2 - - .begin literal_prefix .Level2InterruptVector - .section .Level2InterruptVector.text, "ax" - .global _Level2Vector - .type _Level2Vector,@function - .align 4 -_Level2Vector: - wsr a0, EXCSAVE_2 /* preserve a0 */ - call0 _xt_medint2 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_medint2,@function - .align 4 -_xt_medint2: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_2 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_2 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_2 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint2_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(2) | PS_UM - #else - movi a0, PS_INTLEVEL(2) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 2 XCHAL_INTLEVEL2_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint2_exit - .type _xt_medint2_exit,@function - .align 4 -_xt_medint2_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_2 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_2 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 2 - -#endif /* Level 2 */ - -#if XCHAL_EXCM_LEVEL >= 3 - - .begin literal_prefix .Level3InterruptVector - .section .Level3InterruptVector.text, "ax" - .global _Level3Vector - .type _Level3Vector,@function - .align 4 -_Level3Vector: - wsr a0, EXCSAVE_3 /* preserve a0 */ - call0 _xt_medint3 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_medint3,@function - .align 4 -_xt_medint3: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_3 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_3 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_3 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint3_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(3) | PS_UM - #else - movi a0, PS_INTLEVEL(3) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 3 XCHAL_INTLEVEL3_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint3_exit - .type _xt_medint3_exit,@function - .align 4 -_xt_medint3_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_3 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_3 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 3 - -#endif /* Level 3 */ - -#if XCHAL_EXCM_LEVEL >= 4 - - .begin literal_prefix .Level4InterruptVector - .section .Level4InterruptVector.text, "ax" - .global _Level4Vector - .type _Level4Vector,@function - .align 4 -_Level4Vector: - wsr a0, EXCSAVE_4 /* preserve a0 */ - call0 _xt_medint4 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint4,@function - .align 4 -_xt_medint4: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_4 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_4 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_4 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint4_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(4) | PS_UM - #else - movi a0, PS_INTLEVEL(4) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 4 XCHAL_INTLEVEL4_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint4_exit - .type _xt_medint4_exit,@function - .align 4 -_xt_medint4_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_4 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_4 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 4 - -#endif /* Level 4 */ - -#if XCHAL_EXCM_LEVEL >= 5 - - .begin literal_prefix .Level5InterruptVector - .section .Level5InterruptVector.text, "ax" - .global _Level5Vector - .type _Level5Vector,@function - .align 4 -_Level5Vector: - wsr a0, EXCSAVE_5 /* preserve a0 */ - call0 _xt_medint5 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint5,@function - .align 4 -_xt_medint5: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_5 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_5 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_5 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint5_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(5) | PS_UM - #else - movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 5 XCHAL_INTLEVEL5_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint5_exit - .type _xt_medint5_exit,@function - .align 4 -_xt_medint5_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_5 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_5 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 5 - -#endif /* Level 5 */ - -#if XCHAL_EXCM_LEVEL >= 6 - - .begin literal_prefix .Level6InterruptVector - .section .Level6InterruptVector.text, "ax" - .global _Level6Vector - .type _Level6Vector,@function - .align 4 -_Level6Vector: - wsr a0, EXCSAVE_6 /* preserve a0 */ - call0 _xt_medint6 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint6,@function - .align 4 -_xt_medint6: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_6 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_6 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_6 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint6_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(6) | PS_UM - #else - movi a0, PS_INTLEVEL(6) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 6 XCHAL_INTLEVEL6_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint6_exit - .type _xt_medint6_exit,@function - .align 4 -_xt_medint6_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_6 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_6 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 6 - -#endif /* Level 6 */ - - -/******************************************************************************* - -HIGH PRIORITY (LEVEL > XCHAL_EXCM_LEVEL) INTERRUPT VECTORS AND HANDLERS - -High priority interrupts are by definition those with priorities greater -than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority -interrupts cannot interact with the RTOS, that is they must save all regs -they use and not call any RTOS function. - -A further restriction imposed by the Xtensa windowed architecture is that -high priority interrupts must not modify the stack area even logically -"above" the top of the interrupted stack (they need to provide their -own stack or static save area). - -Cadence Design Systems recommends high priority interrupt handlers be coded in assembly -and used for purposes requiring very short service times. - -Here are templates for high priority (level 2+) interrupt vectors. -They assume only one interrupt per level to avoid the burden of identifying -which interrupts at this level are pending and enabled. This allows for -minimum latency and avoids having to save/restore a2 in addition to a0. -If more than one interrupt per high priority level is configured, this burden -is on the handler which in any case must provide a way to save and restore -registers it uses without touching the interrupted stack. - -Each vector goes at a predetermined location according to the Xtensa -hardware configuration, which is ensured by its placement in a special -section known to the Xtensa linker support package (LSP). It performs -the minimum necessary before jumping to the handler in the .text section. - -*******************************************************************************/ - -/* -Currently only shells for high priority interrupt handlers are provided -here. However a template and example can be found in the Cadence Design Systems tools -documentation: "Microprocessor Programmer's Guide". -*/ - -#if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2 - - .begin literal_prefix .Level2InterruptVector - .section .Level2InterruptVector.text, "ax" - .global _Level2Vector - .type _Level2Vector,@function - .align 4 -_Level2Vector: - wsr a0, EXCSAVE_2 /* preserve a0 */ - call0 _xt_highint2 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_highint2,@function - .align 4 -_xt_highint2: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 2<<2 - beqz a0, 1f -.Ln_xt_highint2_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 2 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint2_exit: - rsr a0, EXCSAVE_2 /* restore a0 */ - rfi 2 - -#endif /* Level 2 */ - -#if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3 - - .begin literal_prefix .Level3InterruptVector - .section .Level3InterruptVector.text, "ax" - .global _Level3Vector - .type _Level3Vector,@function - .align 4 -_Level3Vector: - wsr a0, EXCSAVE_3 /* preserve a0 */ - call0 _xt_highint3 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint3,@function - .align 4 -_xt_highint3: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 3<<2 - beqz a0, 1f -.Ln_xt_highint3_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 3 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint3_exit: - rsr a0, EXCSAVE_3 /* restore a0 */ - rfi 3 - -#endif /* Level 3 */ - -#if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4 - - .begin literal_prefix .Level4InterruptVector - .section .Level4InterruptVector.text, "ax" - .global _Level4Vector - .type _Level4Vector,@function - .align 4 -_Level4Vector: - wsr a0, EXCSAVE_4 /* preserve a0 */ - call0 _xt_highint4 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint4,@function - .align 4 -_xt_highint4: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 4<<2 - beqz a0, 1f -.Ln_xt_highint4_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 4 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint4_exit: - rsr a0, EXCSAVE_4 /* restore a0 */ - rfi 4 - -#endif /* Level 4 */ - -#if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5 - - .begin literal_prefix .Level5InterruptVector - .section .Level5InterruptVector.text, "ax" - .global _Level5Vector - .type _Level5Vector,@function - .align 4 -_Level5Vector: - wsr a0, EXCSAVE_5 /* preserve a0 */ - call0 _xt_highint5 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint5,@function - .align 4 -_xt_highint5: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 5<<2 - beqz a0, 1f -.Ln_xt_highint5_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 5 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint5_exit: - rsr a0, EXCSAVE_5 /* restore a0 */ - rfi 5 - -#endif /* Level 5 */ - -#if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6 - - .begin literal_prefix .Level6InterruptVector - .section .Level6InterruptVector.text, "ax" - .global _Level6Vector - .type _Level6Vector,@function - .align 4 -_Level6Vector: - wsr a0, EXCSAVE_6 /* preserve a0 */ - call0 _xt_highint6 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint6,@function - .align 4 -_xt_highint6: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 6<<2 - beqz a0, 1f -.Ln_xt_highint6_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 6 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint6_exit: - rsr a0, EXCSAVE_6 /* restore a0 */ - rfi 6 - -#endif /* Level 6 */ - -#if XCHAL_HAVE_NMI - - .begin literal_prefix .NMIExceptionVector - .section .NMIExceptionVector.text, "ax" - .global _NMIExceptionVector - .type _NMIExceptionVector,@function - .align 4 -_NMIExceptionVector: - wsr a0, EXCSAVE + XCHAL_NMILEVEL _ /* preserve a0 */ - call0 _xt_nmi /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_nmi,@function - .align 4 -_xt_nmi: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, XCHAL_NMILEVEL<<2 - beqz a0, 1f -.Ln_xt_nmi_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY NON-MASKABLE INTERRUPT (NMI) HANDLER CODE HERE. - */ - - .align 4 -.L_xt_nmi_exit: - rsr a0, EXCSAVE + XCHAL_NMILEVEL /* restore a0 */ - rfi XCHAL_NMILEVEL - -#endif /* NMI */ - - -/******************************************************************************* - -WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION HANDLER - -Here is the code for each window overflow/underflow exception vector and -(interspersed) efficient code for handling the alloca exception cause. -Window exceptions are handled entirely in the vector area and are very -tight for performance. The alloca exception is also handled entirely in -the window vector area so comes at essentially no cost in code size. -Users should never need to modify them and Cadence Design Systems recommends -they do not. - -Window handlers go at predetermined vector locations according to the -Xtensa hardware configuration, which is ensured by their placement in a -special section known to the Xtensa linker support package (LSP). Since -their offsets in that section are always the same, the LSPs do not define -a section per vector. - -These things are coded for XEA2 only (XEA1 is not supported). - -Note on Underflow Handlers: -The underflow handler for returning from call[i+1] to call[i] -must preserve all the registers from call[i+1]'s window. -In particular, a0 and a1 must be preserved because the RETW instruction -will be reexecuted (and may even underflow if an intervening exception -has flushed call[i]'s registers). -Registers a2 and up may contain return values. - -*******************************************************************************/ - -#if XCHAL_HAVE_WINDOWED - - .section .WindowVectors.text, "ax" - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call4. - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call4 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a3 are registers to be saved; - a4-a15 must be preserved; - a5 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x0 - .global _WindowOverflow4 -_WindowOverflow4: - - s32e a0, a5, -16 /* save a0 to call[j+1]'s stack frame */ - s32e a1, a5, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a5, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a5, -4 /* save a3 to call[j+1]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call4 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call4 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a3 are undefined, must be reloaded with call[i].reg[0..3]; - a4-a15 must be preserved (they are call[i+1].reg[0..11]); - a5 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x40 - .global _WindowUnderflow4 -_WindowUnderflow4: - - l32e a0, a5, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a5, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a5, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a3, a5, -4 /* restore a3 from call[i+1]'s stack frame */ - rfwu - -/* --------------------------------------------------------------------------------- -Handle alloca exception generated by interruptee executing 'movsp'. -This uses space between the window vectors, so is essentially "free". -All interruptee's regs are intact except a0 which is saved in EXCSAVE_1, -and PS.EXCM has been set by the exception hardware (can't be interrupted). -The fact the alloca exception was taken means the registers associated with -the base-save area have been spilled and will be restored by the underflow -handler, so those 4 registers are available for scratch. -The code is optimized to avoid unaligned branches and minimize cache misses. --------------------------------------------------------------------------------- -*/ - - .align 4 - .global _xt_alloca_exc -_xt_alloca_exc: - - rsr a0, WINDOWBASE /* grab WINDOWBASE before rotw changes it */ - rotw -1 /* WINDOWBASE goes to a4, new a0-a3 are scratch */ - rsr a2, PS - extui a3, a2, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS - xor a3, a3, a4 /* bits changed from old to current windowbase */ - rsr a4, EXCSAVE_1 /* restore original a0 (now in a4) */ - slli a3, a3, XCHAL_PS_OWB_SHIFT - xor a2, a2, a3 /* flip changed bits in old window base */ - wsr a2, PS /* update PS.OWB to new window base */ - rsync - - _bbci.l a4, 31, _WindowUnderflow4 - rotw -1 /* original a0 goes to a8 */ - _bbci.l a8, 30, _WindowUnderflow8 - rotw -1 - j _WindowUnderflow12 - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call8 - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call8 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a7 are registers to be saved; - a8-a15 must be preserved; - a9 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x80 - .global _WindowOverflow8 -_WindowOverflow8: - - s32e a0, a9, -16 /* save a0 to call[j+1]'s stack frame */ - l32e a0, a1, -12 /* a0 <- call[j-1]'s sp - (used to find end of call[j]'s frame) */ - s32e a1, a9, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a9, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a9, -4 /* save a3 to call[j+1]'s stack frame */ - s32e a4, a0, -32 /* save a4 to call[j]'s stack frame */ - s32e a5, a0, -28 /* save a5 to call[j]'s stack frame */ - s32e a6, a0, -24 /* save a6 to call[j]'s stack frame */ - s32e a7, a0, -20 /* save a7 to call[j]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call8 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call8 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a7 are undefined, must be reloaded with call[i].reg[0..7]; - a8-a15 must be preserved (they are call[i+1].reg[0..7]); - a9 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0xC0 - .global _WindowUnderflow8 -_WindowUnderflow8: - - l32e a0, a9, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a9, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a9, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a7, a1, -12 /* a7 <- call[i-1]'s sp - (used to find end of call[i]'s frame) */ - l32e a3, a9, -4 /* restore a3 from call[i+1]'s stack frame */ - l32e a4, a7, -32 /* restore a4 from call[i]'s stack frame */ - l32e a5, a7, -28 /* restore a5 from call[i]'s stack frame */ - l32e a6, a7, -24 /* restore a6 from call[i]'s stack frame */ - l32e a7, a7, -20 /* restore a7 from call[i]'s stack frame */ - rfwu - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call12 - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call12 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a11 are registers to be saved; - a12-a15 must be preserved; - a13 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x100 - .global _WindowOverflow12 -_WindowOverflow12: - - s32e a0, a13, -16 /* save a0 to call[j+1]'s stack frame */ - l32e a0, a1, -12 /* a0 <- call[j-1]'s sp - (used to find end of call[j]'s frame) */ - s32e a1, a13, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a13, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a13, -4 /* save a3 to call[j+1]'s stack frame */ - s32e a4, a0, -48 /* save a4 to end of call[j]'s stack frame */ - s32e a5, a0, -44 /* save a5 to end of call[j]'s stack frame */ - s32e a6, a0, -40 /* save a6 to end of call[j]'s stack frame */ - s32e a7, a0, -36 /* save a7 to end of call[j]'s stack frame */ - s32e a8, a0, -32 /* save a8 to end of call[j]'s stack frame */ - s32e a9, a0, -28 /* save a9 to end of call[j]'s stack frame */ - s32e a10, a0, -24 /* save a10 to end of call[j]'s stack frame */ - s32e a11, a0, -20 /* save a11 to end of call[j]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call12 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call12 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a11 are undefined, must be reloaded with call[i].reg[0..11]; - a12-a15 must be preserved (they are call[i+1].reg[0..3]); - a13 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x140 - .global _WindowUnderflow12 -_WindowUnderflow12: - - l32e a0, a13, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a13, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a13, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a11, a1, -12 /* a11 <- call[i-1]'s sp - (used to find end of call[i]'s frame) */ - l32e a3, a13, -4 /* restore a3 from call[i+1]'s stack frame */ - l32e a4, a11, -48 /* restore a4 from end of call[i]'s stack frame */ - l32e a5, a11, -44 /* restore a5 from end of call[i]'s stack frame */ - l32e a6, a11, -40 /* restore a6 from end of call[i]'s stack frame */ - l32e a7, a11, -36 /* restore a7 from end of call[i]'s stack frame */ - l32e a8, a11, -32 /* restore a8 from end of call[i]'s stack frame */ - l32e a9, a11, -28 /* restore a9 from end of call[i]'s stack frame */ - l32e a10, a11, -24 /* restore a10 from end of call[i]'s stack frame */ - l32e a11, a11, -20 /* restore a11 from end of call[i]'s stack frame */ - rfwu - -#endif /* XCHAL_HAVE_WINDOWED */ - - .section .UserEnter.text, "ax" - .global call_user_start - .type call_user_start,@function - .align 4 - .literal_position - - -call_user_start: - - movi a2, 0x40040000 /* note: absolute symbol, not a ptr */ - wsr a2, vecbase - call0 user_start /* user exception handler */ diff --git a/components/freertos/xtensa_vectors.S-old b/components/freertos/xtensa_vectors.S-old deleted file mode 100644 index 2d0f7a99d..000000000 --- a/components/freertos/xtensa_vectors.S-old +++ /dev/null @@ -1,2064 +0,0 @@ -/******************************************************************************* -Copyright (c) 2006-2015 Cadence Design Systems Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------- - - XTENSA VECTORS AND LOW LEVEL HANDLERS FOR AN RTOS - - Xtensa low level exception and interrupt vectors and handlers for an RTOS. - - Interrupt handlers and user exception handlers support interaction with - the RTOS by calling XT_RTOS_INT_ENTER and XT_RTOS_INT_EXIT before and - after user's specific interrupt handlers. These macros are defined in - xtensa_.h to call suitable functions in a specific RTOS. - - Users can install application-specific interrupt handlers for low and - medium level interrupts, by calling xt_set_interrupt_handler(). These - handlers can be written in C, and must obey C calling convention. The - handler table is indexed by the interrupt number. Each handler may be - provided with an argument. - - Note that the system timer interrupt is handled specially, and is - dispatched to the RTOS-specific handler. This timer cannot be hooked - by application code. - - Optional hooks are also provided to install a handler per level at - run-time, made available by compiling this source file with - '-DXT_INTEXC_HOOKS' (useful for automated testing). - -!! This file is a template that usually needs to be modified to handle !! -!! application specific interrupts. Search USER_EDIT for helpful comments !! -!! on where to insert handlers and how to write them. !! - - Users can also install application-specific exception handlers in the - same way, by calling xt_set_exception_handler(). One handler slot is - provided for each exception type. Note that some exceptions are handled - by the porting layer itself, and cannot be taken over by application - code in this manner. These are the alloca, syscall, and coprocessor - exceptions. - - The exception handlers can be written in C, and must follow C calling - convention. Each handler is passed a pointer to an exception frame as - its single argument. The exception frame is created on the stack, and - holds the saved context of the thread that took the exception. If the - handler returns, the context will be restored and the instruction that - caused the exception will be retried. If the handler makes any changes - to the saved state in the exception frame, the changes will be applied - when restoring the context. - - Because Xtensa is a configurable architecture, this port supports all user - generated configurations (except restrictions stated in the release notes). - This is accomplished by conditional compilation using macros and functions - defined in the Xtensa HAL (hardware adaptation layer) for your configuration. - Only the relevant parts of this file will be included in your RTOS build. - For example, this file provides interrupt vector templates for all types and - all priority levels, but only the ones in your configuration are built. - - NOTES on the use of 'call0' for long jumps instead of 'j': - 1. This file should be assembled with the -mlongcalls option to xt-xcc. - 2. The -mlongcalls compiler option causes 'call0 dest' to be expanded to - a sequence 'l32r a0, dest' 'callx0 a0' which works regardless of the - distance from the call to the destination. The linker then relaxes - it back to 'call0 dest' if it determines that dest is within range. - This allows more flexibility in locating code without the performance - overhead of the 'l32r' literal data load in cases where the destination - is in range of 'call0'. There is an additional benefit in that 'call0' - has a longer range than 'j' due to the target being word-aligned, so - the 'l32r' sequence is less likely needed. - 3. The use of 'call0' with -mlongcalls requires that register a0 not be - live at the time of the call, which is always the case for a function - call but needs to be ensured if 'call0' is used as a jump in lieu of 'j'. - 4. This use of 'call0' is independent of the C function call ABI. - -*******************************************************************************/ - -#include "xtensa_rtos.h" - - -/* Enable stack backtrace across exception/interrupt - see below */ -#define XT_DEBUG_BACKTRACE 0 - - -/* --------------------------------------------------------------------------------- - Defines used to access _xtos_interrupt_table. --------------------------------------------------------------------------------- -*/ -#define XIE_HANDLER 0 -#define XIE_ARG 4 -#define XIE_SIZE 8 - -/* --------------------------------------------------------------------------------- - Macro extract_msb - return the input with only the highest bit set. - - Input : "ain" - Input value, clobbered. - Output : "aout" - Output value, has only one bit set, MSB of "ain". - The two arguments must be different AR registers. --------------------------------------------------------------------------------- -*/ - - .macro extract_msb aout ain -1: - addi \aout, \ain, -1 /* aout = ain - 1 */ - and \ain, \ain, \aout /* ain = ain & aout */ - bnez \ain, 1b /* repeat until ain == 0 */ - addi \aout, \aout, 1 /* return aout + 1 */ - .endm - -/* --------------------------------------------------------------------------------- - Macro dispatch_c_isr - dispatch interrupts to user ISRs. - This will dispatch to user handlers (if any) that are registered in the - XTOS dispatch table (_xtos_interrupt_table). These handlers would have - been registered by calling _xtos_set_interrupt_handler(). There is one - exception - the timer interrupt used by the OS will not be dispatched - to a user handler - this must be handled by the caller of this macro. - - Level triggered and software interrupts are automatically deasserted by - this code. - - ASSUMPTIONS: - -- PS.INTLEVEL is set to "level" at entry - -- PS.EXCM = 0, C calling enabled - - NOTE: For CALL0 ABI, a12-a15 have not yet been saved. - - NOTE: This macro will use registers a0 and a2-a6. The arguments are: - level -- interrupt level - mask -- interrupt bitmask for this level --------------------------------------------------------------------------------- -*/ - - .macro dispatch_c_isr level mask - - /* Get mask of pending, enabled interrupts at this level into a2. */ - -.L_xt_user_int_&level&: - rsr a2, INTENABLE - rsr a3, INTERRUPT - movi a4, \mask - and a2, a2, a3 - and a2, a2, a4 - beqz a2, 9f /* nothing to do */ - - /* This bit of code provides a nice debug backtrace in the debugger. - It does take a few more instructions, so undef XT_DEBUG_BACKTRACE - if you want to save the cycles. - */ - #if XT_DEBUG_BACKTRACE - #ifndef __XTENSA_CALL0_ABI__ - rsr a0, EPC_1 + \level - 1 /* return address */ - movi a4, 0xC0000000 /* constant with top 2 bits set (call size) */ - or a0, a0, a4 /* set top 2 bits */ - addx2 a0, a4, a0 /* clear top bit -- simulating call4 size */ - #endif - #endif - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a4, _xt_intexc_hooks - l32i a4, a4, \level << 2 - beqz a4, 2f - #ifdef __XTENSA_CALL0_ABI__ - callx0 a4 - beqz a2, 9f - #else - mov a6, a2 - callx4 a4 - beqz a6, 9f - mov a2, a6 - #endif -2: - #endif - - /* Now look up in the dispatch table and call user ISR if any. */ - /* If multiple bits are set then MSB has highest priority. */ - - extract_msb a4, a2 /* a4 = MSB of a2, a2 trashed */ - - #ifdef XT_USE_SWPRI - /* Enable all interrupts at this level that are numerically higher - than the one we just selected, since they are treated as higher - priority. - */ - movi a3, \mask /* a3 = all interrupts at this level */ - add a2, a4, a4 /* a2 = a4 << 1 */ - addi a2, a2, -1 /* a2 = mask of 1's <= a4 bit */ - and a2, a2, a3 /* a2 = mask of all bits <= a4 at this level */ - movi a3, _xt_intdata - l32i a6, a3, 4 /* a6 = _xt_vpri_mask */ - neg a2, a2 - addi a2, a2, -1 /* a2 = mask to apply */ - and a5, a6, a2 /* mask off all bits <= a4 bit */ - s32i a5, a3, 4 /* update _xt_vpri_mask */ - rsr a3, INTENABLE - and a3, a3, a2 /* mask off all bits <= a4 bit */ - wsr a3, INTENABLE - rsil a3, \level - 1 /* lower interrupt level by 1 */ - #endif - - movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */ - wsr a4, INTCLEAR /* clear sw or edge-triggered interrupt */ - beq a3, a4, 7f /* if timer interrupt then skip table */ - - find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */ - - movi a4, _xt_interrupt_table - addx8 a3, a3, a4 /* a3 = address of interrupt table entry */ - l32i a4, a3, XIE_HANDLER /* a4 = handler address */ - #ifdef __XTENSA_CALL0_ABI__ - mov a12, a6 /* save in callee-saved reg */ - l32i a2, a3, XIE_ARG /* a2 = handler arg */ - callx0 a4 /* call handler */ - mov a2, a12 - #else - mov a2, a6 /* save in windowed reg */ - l32i a6, a3, XIE_ARG /* a6 = handler arg */ - callx4 a4 /* call handler */ - #endif - - #ifdef XT_USE_SWPRI - j 8f - #else - j .L_xt_user_int_&level& /* check for more interrupts */ - #endif - -7: - - .ifeq XT_TIMER_INTPRI - \level -.L_xt_user_int_timer_&level&: - /* - Interrupt handler for the RTOS tick timer if at this level. - We'll be reading the interrupt state again after this call - so no need to preserve any registers except a6 (vpri_mask). - */ - - #ifdef __XTENSA_CALL0_ABI__ - mov a12, a6 - call0 XT_RTOS_TIMER_INT - mov a2, a12 - #else - mov a2, a6 - call4 XT_RTOS_TIMER_INT - #endif - .endif - - #ifdef XT_USE_SWPRI - j 8f - #else - j .L_xt_user_int_&level& /* check for more interrupts */ - #endif - - #ifdef XT_USE_SWPRI -8: - /* Restore old value of _xt_vpri_mask from a2. Also update INTENABLE from - virtual _xt_intenable which _could_ have changed during interrupt - processing. */ - - movi a3, _xt_intdata - l32i a4, a3, 0 /* a4 = _xt_intenable */ - s32i a2, a3, 4 /* update _xt_vpri_mask */ - and a4, a4, a2 /* a4 = masked intenable */ - wsr a4, INTENABLE /* update INTENABLE */ - #endif - -9: - /* done */ - - .endm - - -/* --------------------------------------------------------------------------------- - Panic handler. - Should be reached by call0 (preferable) or jump only. If call0, a0 says where - from. If on simulator, display panic message and abort, else loop indefinitely. --------------------------------------------------------------------------------- -*/ - - .text - .global panicHandlerRegs - .global panicHandler - .global panicStack - - .global _xt_panic - .type _xt_panic,@function - .align 4 - -_xt_panic: - //ToDo: save registers - movi a2, panicHandlerRegs - s32i a0,a2,0 - s32i a1,a2,4 - s32i a2,a2,8 - s32i a3,a2,12 - s32i a4,a2,16 - s32i a5,a2,20 - s32i a6,a2,24 - s32i a7,a2,28 - s32i a8,a2,32 - s32i a9,a2,36 - s32i a10,a2,40 - s32i a11,a2,44 - s32i a12,a2,48 - s32i a13,a2,52 - s32i a14,a2,56 - s32i a15,a2,60 - - rsr a3, EXCSAVE - s32i a3,a2,64 - rsr a3, EXCSAVE+1 - s32i a3,a2,68 - rsr a3, EXCSAVE+2 - s32i a3,a2,72 - rsr a3, EXCSAVE+3 - s32i a3,a2,76 - rsr a3, EXCSAVE+4 - s32i a3,a2,80 - rsr a3, EXCSAVE+5 - s32i a3,a2,84 - rsr a3, EXCSAVE+6 - s32i a3,a2,88 - rsr a3, EXCSAVE+7 - s32i a3,a2,92 - - rsr a3, EPC - s32i a3,a2,96 - rsr a3, EPC+1 - s32i a3,a2,100 - rsr a3, EPC+2 - s32i a3,a2,104 - rsr a3, EPC+3 - s32i a3,a2,108 - rsr a3, EPC+4 - s32i a3,a2,112 - rsr a3, EPC+5 - s32i a3,a2,116 - rsr a3, EPC+6 - s32i a3,a2,120 - rsr a3, EPC+7 - s32i a3,a2,124 - - rsr a3, DEPC - s32i a3,a2,128 - rsr a3, EXCVADDR - s32i a3,a2,132 - - //Reset window start / base to 0 - movi a2, 1 - wsr a2, windowstart - movi a2, 0 - wsr a2, windowbase - rsync - - //Clear EXCM, set WOE flags - rsr a2, ps - movi a3, ~(PS_EXCM_MASK) - and a2, a2, a3 - movi a3, PS_WOE_MASK - or a2, a2, a3 - wsr a2, ps - rsync - - //Switch to private stack - movi a1,panicStack+(255*4) - rsil a2, XCHAL_EXCM_LEVEL /* disable all low & med ints */ - - - // Debug: output '!' on serport - movi a2,0x60000000 - movi a3,'!' - s32i a3,a2,0 - - movi a3, panicHandlerRegs - l32i a2,a3,0 - call0 panic_print_hex - rsr a2, EXCCAUSE - call0 panic_print_hex - rsr a2, EXCVADDR - call0 panic_print_hex - rsr a2,EPC+1 - call0 panic_print_hex - rsr a2,EPC+2 - call0 panic_print_hex - rsr a2,EPC+3 - call0 panic_print_hex - rsr a2,EPC+6 - call0 panic_print_hex - rsr a2,EXCSAVE_1 - call0 panic_print_hex - rsr a2,DEPC - call0 panic_print_hex - - //Call panic handler -// movi a4, panicHandler -// callx4 a4 - call4 panicHandler - - movi a2,0x60000000 - movi a3,'d' - s32i a3,a2,0 - - -1: j 1b /* loop infinitely */ - - .align 4 -panicHandlerz: - entry a1,32 - movi a2,0x60000000 - movi a3,'h' - memw - s32i a3,a2,0 - - retw - - - .align 4 -//Call using call0. Prints the hex char in a2. Kills a3, a4, a5 -panic_print_hex: - movi a3,0x60000000 - movi a4,8 -panic_print_hex_loop: - l32i a5, a3, 0x1c - extui a5, a5, 16, 8 - bgei a5,64,panic_print_hex_loop - - srli a5,a2,28 - bgei a5,10,panic_print_hex_a - addi a5,a5,'0' - j panic_print_hex_ok -panic_print_hex_a: - addi a5,a5,'A'-10 -panic_print_hex_ok: - s32i a5,a3,0 - slli a2,a2,4 - - addi a4,a4,-1 - bnei a4,0,panic_print_hex_loop - movi a5,' ' - s32i a5,a3,0 - - ret - - - - .section .rodata, "a" - .align 4 - - - -/* --------------------------------------------------------------------------------- - Hooks to dynamically install handlers for exceptions and interrupts. - Allows automated regression frameworks to install handlers per test. - Consists of an array of function pointers indexed by interrupt level, - with index 0 containing the entry for user exceptions. - Initialized with all 0s, meaning no handler is installed at each level. - See comment in xtensa_rtos.h for more details. --------------------------------------------------------------------------------- -*/ - - #ifdef XT_INTEXC_HOOKS - .data - .global _xt_intexc_hooks - .type _xt_intexc_hooks,@object - .align 4 - -_xt_intexc_hooks: - .fill XT_INTEXC_HOOK_NUM, 4, 0 - #endif - - -/* --------------------------------------------------------------------------------- - EXCEPTION AND LEVEL 1 INTERRUPT VECTORS AND LOW LEVEL HANDLERS - (except window exception vectors). - - Each vector goes at a predetermined location according to the Xtensa - hardware configuration, which is ensured by its placement in a special - section known to the Xtensa linker support package (LSP). It performs - the minimum necessary before jumping to the handler in the .text section. - - The corresponding handler goes in the normal .text section. It sets up - the appropriate stack frame, saves a few vector-specific registers and - calls XT_RTOS_INT_ENTER to save the rest of the interrupted context - and enter the RTOS, then sets up a C environment. It then calls the - user's interrupt handler code (which may be coded in C) and finally - calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling. - - While XT_RTOS_INT_EXIT does not return directly to the interruptee, - eventually the RTOS scheduler will want to dispatch the interrupted - task or handler. The scheduler will return to the exit point that was - saved in the interrupt stack frame at XT_STK_EXIT. --------------------------------------------------------------------------------- -*/ - - -/* --------------------------------------------------------------------------------- -Debug Exception. --------------------------------------------------------------------------------- -*/ - -#if XCHAL_HAVE_DEBUG - - .begin literal_prefix .DebugExceptionVector - .section .DebugExceptionVector.text, "ax" - .global _DebugExceptionVector - .align 4 - -_DebugExceptionVector: - - #ifdef XT_SIMULATOR - /* - In the simulator, let the debugger (if any) handle the debug exception, - or simply stop the simulation: - */ - wsr a2, EXCSAVE+XCHAL_DEBUGLEVEL /* save a2 where sim expects it */ - movi a2, SYS_gdb_enter_sktloop - simcall /* have ISS handle debug exc. */ - #elif 0 /* change condition to 1 to use the HAL minimal debug handler */ - wsr a3, EXCSAVE+XCHAL_DEBUGLEVEL - movi a3, xthal_debugexc_defhndlr_nw /* use default debug handler */ - jx a3 - #else - wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */ - call0 _xt_panic /* does not return */ - rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */ - #endif - - .end literal_prefix - -#endif - -/* --------------------------------------------------------------------------------- -Double Exception. -Double exceptions are not a normal occurrence. They indicate a bug of some kind. --------------------------------------------------------------------------------- -*/ - -#ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR - - .begin literal_prefix .DoubleExceptionVector - .section .DoubleExceptionVector.text, "ax" - .global _DoubleExceptionVector - .align 4 - -_DoubleExceptionVector: - - #if XCHAL_HAVE_DEBUG - break 1, 4 /* unhandled double exception */ - #endif - call0 _xt_panic /* does not return */ - rfde /* make a0 point here not later */ - - .end literal_prefix - -#endif /* XCHAL_DOUBLEEXC_VECTOR_VADDR */ - -/* --------------------------------------------------------------------------------- -Kernel Exception (including Level 1 Interrupt from kernel mode). --------------------------------------------------------------------------------- -*/ - - .begin literal_prefix .KernelExceptionVector - .section .KernelExceptionVector.text, "ax" - .global _KernelExceptionVector - .align 4 - -_KernelExceptionVector: - - wsr a0, EXCSAVE_1 /* preserve a0 */ - call0 _xt_kernel_exc /* kernel exception handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .align 4 - -_xt_kernel_exc: - #if XCHAL_HAVE_DEBUG - break 1, 0 /* unhandled kernel exception */ - #endif - call0 _xt_panic /* does not return */ - rfe /* make a0 point here not there */ - - -/* --------------------------------------------------------------------------------- -User Exception (including Level 1 Interrupt from user mode). --------------------------------------------------------------------------------- -*/ - - .begin literal_prefix .UserExceptionVector - .section .UserExceptionVector.text, "ax" - .global _UserExceptionVector - .type _UserExceptionVector,@function - .align 4 - -_UserExceptionVector: - - wsr a0, EXCSAVE_1 /* preserve a0 */ - call0 _xt_user_exc /* user exception handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - -/* --------------------------------------------------------------------------------- - Insert some waypoints for jumping beyond the signed 8-bit range of - conditional branch instructions, so the conditional branchces to specific - exception handlers are not taken in the mainline. Saves some cycles in the - mainline. --------------------------------------------------------------------------------- -*/ - - .text - - #if XCHAL_HAVE_WINDOWED - .align 4 -_xt_to_alloca_exc: - call0 _xt_alloca_exc /* in window vectors section */ - /* never returns here - call0 is used as a jump (see note at top) */ - #endif - - .align 4 -_xt_to_syscall_exc: - call0 _xt_syscall_exc - /* never returns here - call0 is used as a jump (see note at top) */ - - #if XCHAL_CP_NUM > 0 - .align 4 -_xt_to_coproc_exc: - call0 _xt_coproc_exc - /* never returns here - call0 is used as a jump (see note at top) */ - #endif - - -/* --------------------------------------------------------------------------------- - User exception handler. --------------------------------------------------------------------------------- -*/ - - .type _xt_user_exc,@function - .align 4 - -_xt_user_exc: - - /* If level 1 interrupt then jump to the dispatcher */ - rsr a0, EXCCAUSE - beqi a0, EXCCAUSE_LEVEL1INTERRUPT, _xt_lowint1 - - /* Handle any coprocessor exceptions. Rely on the fact that exception - numbers above EXCCAUSE_CP0_DISABLED all relate to the coprocessors. - */ - #if XCHAL_CP_NUM > 0 - bgeui a0, EXCCAUSE_CP0_DISABLED, _xt_to_coproc_exc - #endif - - /* Handle alloca and syscall exceptions */ - #if XCHAL_HAVE_WINDOWED - beqi a0, EXCCAUSE_ALLOCA, _xt_to_alloca_exc - #endif - beqi a0, EXCCAUSE_SYSCALL, _xt_to_syscall_exc - - //HACK! No custom vector stuff, just call panic handler directly. ToDo: remove this when possible. -JD - call0 _xt_panic - - /* Handle all other exceptions. All can have user-defined handlers. */ - /* NOTE: we'll stay on the user stack for exception handling. */ - - /* Allocate exception frame and save minimal context. */ - mov a0, sp - addi sp, sp, -XT_STK_FRMSZ - s32i a0, sp, XT_STK_A1 - #if XCHAL_HAVE_WINDOWED - s32e a0, sp, -12 /* for debug backtrace */ - #endif - rsr a0, PS /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_1 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_1 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - #if XCHAL_HAVE_WINDOWED - s32e a0, sp, -16 /* for debug backtrace */ - #endif - s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ - s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ - call0 _xt_context_save - - /* Save exc cause and vaddr into exception frame */ - rsr a0, EXCCAUSE - s32i a0, sp, XT_STK_EXCCAUSE - rsr a0, EXCVADDR - s32i a0, sp, XT_STK_EXCVADDR - - /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM - #else - movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE - #endif - wsr a0, PS - - #ifdef XT_DEBUG_BACKTRACE - #ifndef __XTENSA_CALL0_ABI__ - rsr a0, EPC_1 /* return address for debug backtrace */ - movi a5, 0xC0000000 /* constant with top 2 bits set (call size) */ - rsync /* wait for WSR.PS to complete */ - or a0, a0, a5 /* set top 2 bits */ - addx2 a0, a5, a0 /* clear top bit -- thus simulating call4 size */ - #else - rsync /* wait for WSR.PS to complete */ - #endif - #endif - - rsr a2, EXCCAUSE /* recover exc cause */ - - #ifdef XT_INTEXC_HOOKS - /* - Call exception hook to pre-handle exceptions (if installed). - Pass EXCCAUSE in a2, and check result in a2 (if -1, skip default handling). - */ - movi a4, _xt_intexc_hooks - l32i a4, a4, 0 /* user exception hook index 0 */ - beqz a4, 1f -.Ln_xt_user_exc_call_hook: - #ifdef __XTENSA_CALL0_ABI__ - callx0 a4 - beqi a2, -1, .L_xt_user_done - #else - mov a6, a2 - callx4 a4 - beqi a6, -1, .L_xt_user_done - mov a2, a6 - #endif -1: - #endif - - rsr a2, EXCCAUSE /* recover exc cause */ - movi a3, _xt_exception_table - addx4 a4, a2, a3 /* a4 = address of exception table entry */ - l32i a4, a4, 0 /* a4 = handler address */ - #ifdef __XTENSA_CALL0_ABI__ - mov a2, sp /* a2 = pointer to exc frame */ - callx0 a4 /* call handler */ - #else - mov a6, sp /* a6 = pointer to exc frame */ - callx4 a4 /* call handler */ - #endif - -.L_xt_user_done: - - /* Restore context and return */ - call0 _xt_context_restore - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, PS - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_1 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove exception frame */ - rsync /* ensure PS and EPC written */ - rfe /* PS.EXCM is cleared */ - - -/* --------------------------------------------------------------------------------- - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. --------------------------------------------------------------------------------- -*/ - - .global _xt_user_exit - .type _xt_user_exit,@function - .align 4 -_xt_user_exit: - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, PS - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_1 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure PS and EPC written */ - rfe /* PS.EXCM is cleared */ - - -/* --------------------------------------------------------------------------------- -Syscall Exception Handler (jumped to from User Exception Handler). -Syscall 0 is required to spill the register windows (no-op in Call 0 ABI). -Only syscall 0 is handled here. Other syscalls return -1 to caller in a2. --------------------------------------------------------------------------------- -*/ - - .text - .type _xt_syscall_exc,@function - .align 4 -_xt_syscall_exc: - - #ifdef __XTENSA_CALL0_ABI__ - /* - Save minimal regs for scratch. Syscall 0 does nothing in Call0 ABI. - Use a minimal stack frame (16B) to save A2 & A3 for scratch. - PS.EXCM could be cleared here, but unlikely to improve worst-case latency. - rsr a0, PS - addi a0, a0, -PS_EXCM_MASK - wsr a0, PS - */ - addi sp, sp, -16 - s32i a2, sp, 8 - s32i a3, sp, 12 - #else /* Windowed ABI */ - /* - Save necessary context and spill the register windows. - PS.EXCM is still set and must remain set until after the spill. - Reuse context save function though it saves more than necessary. - For this reason, a full interrupt stack frame is allocated. - */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ - s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ - call0 _xt_context_save - #endif - - /* - Grab the interruptee's PC and skip over the 'syscall' instruction. - If it's at the end of a zero-overhead loop and it's not on the last - iteration, decrement loop counter and skip to beginning of loop. - */ - rsr a2, EPC_1 /* a2 = PC of 'syscall' */ - addi a3, a2, 3 /* ++PC */ - #if XCHAL_HAVE_LOOPS - rsr a0, LEND /* if (PC == LEND */ - bne a3, a0, 1f - rsr a0, LCOUNT /* && LCOUNT != 0) */ - beqz a0, 1f /* { */ - addi a0, a0, -1 /* --LCOUNT */ - rsr a3, LBEG /* PC = LBEG */ - wsr a0, LCOUNT /* } */ - #endif -1: wsr a3, EPC_1 /* update PC */ - - /* Restore interruptee's context and return from exception. */ - #ifdef __XTENSA_CALL0_ABI__ - l32i a2, sp, 8 - l32i a3, sp, 12 - addi sp, sp, 16 - #else - call0 _xt_context_restore - addi sp, sp, XT_STK_FRMSZ - #endif - movi a0, -1 - movnez a2, a0, a2 /* return -1 if not syscall 0 */ - rsr a0, EXCSAVE_1 - rfe - -/* --------------------------------------------------------------------------------- -Co-Processor Exception Handler (jumped to from User Exception Handler). -These exceptions are generated by co-processor instructions, which are only -allowed in thread code (not in interrupts or kernel code). This restriction is -deliberately imposed to reduce the burden of state-save/restore in interrupts. --------------------------------------------------------------------------------- -*/ -#if XCHAL_CP_NUM > 0 - - .section .rodata, "a" - -/* Offset to CP n save area in thread's CP save area. */ - .global _xt_coproc_sa_offset - .type _xt_coproc_sa_offset,@object - .align 16 /* minimize crossing cache boundaries */ -_xt_coproc_sa_offset: - .word XT_CP0_SA, XT_CP1_SA, XT_CP2_SA, XT_CP3_SA - .word XT_CP4_SA, XT_CP5_SA, XT_CP6_SA, XT_CP7_SA - -/* Bitmask for CP n's CPENABLE bit. */ - .type _xt_coproc_mask,@object - .align 16,,8 /* try to keep it all in one cache line */ - .set i, 0 -_xt_coproc_mask: - .rept XCHAL_CP_MAX - .long (i<<16) | (1<= 2 - - .begin literal_prefix .Level2InterruptVector - .section .Level2InterruptVector.text, "ax" - .global _Level2Vector - .type _Level2Vector,@function - .align 4 -_Level2Vector: - wsr a0, EXCSAVE_2 /* preserve a0 */ - call0 _xt_medint2 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_medint2,@function - .align 4 -_xt_medint2: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_2 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_2 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_2 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint2_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(2) | PS_UM - #else - movi a0, PS_INTLEVEL(2) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 2 XCHAL_INTLEVEL2_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint2_exit - .type _xt_medint2_exit,@function - .align 4 -_xt_medint2_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_2 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_2 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 2 - -#endif /* Level 2 */ - -#if XCHAL_EXCM_LEVEL >= 3 - - .begin literal_prefix .Level3InterruptVector - .section .Level3InterruptVector.text, "ax" - .global _Level3Vector - .type _Level3Vector,@function - .align 4 -_Level3Vector: - wsr a0, EXCSAVE_3 /* preserve a0 */ - call0 _xt_medint3 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_medint3,@function - .align 4 -_xt_medint3: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_3 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_3 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_3 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint3_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(3) | PS_UM - #else - movi a0, PS_INTLEVEL(3) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 3 XCHAL_INTLEVEL3_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint3_exit - .type _xt_medint3_exit,@function - .align 4 -_xt_medint3_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_3 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_3 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 3 - -#endif /* Level 3 */ - -#if XCHAL_EXCM_LEVEL >= 4 - - .begin literal_prefix .Level4InterruptVector - .section .Level4InterruptVector.text, "ax" - .global _Level4Vector - .type _Level4Vector,@function - .align 4 -_Level4Vector: - wsr a0, EXCSAVE_4 /* preserve a0 */ - call0 _xt_medint4 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint4,@function - .align 4 -_xt_medint4: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_4 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_4 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_4 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint4_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(4) | PS_UM - #else - movi a0, PS_INTLEVEL(4) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 4 XCHAL_INTLEVEL4_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint4_exit - .type _xt_medint4_exit,@function - .align 4 -_xt_medint4_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_4 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_4 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 4 - -#endif /* Level 4 */ - -#if XCHAL_EXCM_LEVEL >= 5 - - .begin literal_prefix .Level5InterruptVector - .section .Level5InterruptVector.text, "ax" - .global _Level5Vector - .type _Level5Vector,@function - .align 4 -_Level5Vector: - wsr a0, EXCSAVE_5 /* preserve a0 */ - call0 _xt_medint5 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint5,@function - .align 4 -_xt_medint5: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_5 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_5 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_5 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint5_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(5) | PS_UM - #else - movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 5 XCHAL_INTLEVEL5_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint5_exit - .type _xt_medint5_exit,@function - .align 4 -_xt_medint5_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_5 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_5 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 5 - -#endif /* Level 5 */ - -#if XCHAL_EXCM_LEVEL >= 6 - - .begin literal_prefix .Level6InterruptVector - .section .Level6InterruptVector.text, "ax" - .global _Level6Vector - .type _Level6Vector,@function - .align 4 -_Level6Vector: - wsr a0, EXCSAVE_6 /* preserve a0 */ - call0 _xt_medint6 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint6,@function - .align 4 -_xt_medint6: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_6 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_6 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_6 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint6_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(6) | PS_UM - #else - movi a0, PS_INTLEVEL(6) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 6 XCHAL_INTLEVEL6_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint6_exit - .type _xt_medint6_exit,@function - .align 4 -_xt_medint6_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_6 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_6 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 6 - -#endif /* Level 6 */ - - -/******************************************************************************* - -HIGH PRIORITY (LEVEL > XCHAL_EXCM_LEVEL) INTERRUPT VECTORS AND HANDLERS - -High priority interrupts are by definition those with priorities greater -than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority -interrupts cannot interact with the RTOS, that is they must save all regs -they use and not call any RTOS function. - -A further restriction imposed by the Xtensa windowed architecture is that -high priority interrupts must not modify the stack area even logically -"above" the top of the interrupted stack (they need to provide their -own stack or static save area). - -Cadence Design Systems recommends high priority interrupt handlers be coded in assembly -and used for purposes requiring very short service times. - -Here are templates for high priority (level 2+) interrupt vectors. -They assume only one interrupt per level to avoid the burden of identifying -which interrupts at this level are pending and enabled. This allows for -minimum latency and avoids having to save/restore a2 in addition to a0. -If more than one interrupt per high priority level is configured, this burden -is on the handler which in any case must provide a way to save and restore -registers it uses without touching the interrupted stack. - -Each vector goes at a predetermined location according to the Xtensa -hardware configuration, which is ensured by its placement in a special -section known to the Xtensa linker support package (LSP). It performs -the minimum necessary before jumping to the handler in the .text section. - -*******************************************************************************/ - -/* -Currently only shells for high priority interrupt handlers are provided -here. However a template and example can be found in the Cadence Design Systems tools -documentation: "Microprocessor Programmer's Guide". -*/ - -#if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2 - - .begin literal_prefix .Level2InterruptVector - .section .Level2InterruptVector.text, "ax" - .global _Level2Vector - .type _Level2Vector,@function - .align 4 -_Level2Vector: - wsr a0, EXCSAVE_2 /* preserve a0 */ - call0 _xt_highint2 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_highint2,@function - .align 4 -_xt_highint2: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 2<<2 - beqz a0, 1f -.Ln_xt_highint2_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 2 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint2_exit: - rsr a0, EXCSAVE_2 /* restore a0 */ - rfi 2 - -#endif /* Level 2 */ - -#if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3 - - .begin literal_prefix .Level3InterruptVector - .section .Level3InterruptVector.text, "ax" - .global _Level3Vector - .type _Level3Vector,@function - .align 4 -_Level3Vector: - wsr a0, EXCSAVE_3 /* preserve a0 */ - call0 _xt_highint3 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint3,@function - .align 4 -_xt_highint3: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 3<<2 - beqz a0, 1f -.Ln_xt_highint3_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 3 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint3_exit: - rsr a0, EXCSAVE_3 /* restore a0 */ - rfi 3 - -#endif /* Level 3 */ - -#if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4 - - .begin literal_prefix .Level4InterruptVector - .section .Level4InterruptVector.text, "ax" - .global _Level4Vector - .type _Level4Vector,@function - .align 4 -_Level4Vector: - wsr a0, EXCSAVE_4 /* preserve a0 */ - call0 _xt_highint4 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint4,@function - .align 4 -_xt_highint4: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 4<<2 - beqz a0, 1f -.Ln_xt_highint4_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 4 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint4_exit: - rsr a0, EXCSAVE_4 /* restore a0 */ - rfi 4 - -#endif /* Level 4 */ - -#if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5 - - .begin literal_prefix .Level5InterruptVector - .section .Level5InterruptVector.text, "ax" - .global _Level5Vector - .type _Level5Vector,@function - .align 4 -_Level5Vector: - wsr a0, EXCSAVE_5 /* preserve a0 */ - call0 _xt_highint5 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint5,@function - .align 4 -_xt_highint5: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 5<<2 - beqz a0, 1f -.Ln_xt_highint5_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 5 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint5_exit: - rsr a0, EXCSAVE_5 /* restore a0 */ - rfi 5 - -#endif /* Level 5 */ - -#if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6 - - .begin literal_prefix .Level6InterruptVector - .section .Level6InterruptVector.text, "ax" - .global _Level6Vector - .type _Level6Vector,@function - .align 4 -_Level6Vector: - wsr a0, EXCSAVE_6 /* preserve a0 */ - call0 _xt_highint6 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint6,@function - .align 4 -_xt_highint6: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 6<<2 - beqz a0, 1f -.Ln_xt_highint6_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 6 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint6_exit: - rsr a0, EXCSAVE_6 /* restore a0 */ - rfi 6 - -#endif /* Level 6 */ - -#if XCHAL_HAVE_NMI - - .begin literal_prefix .NMIExceptionVector - .section .NMIExceptionVector.text, "ax" - .global _NMIExceptionVector - .type _NMIExceptionVector,@function - .align 4 -_NMIExceptionVector: - wsr a0, EXCSAVE + XCHAL_NMILEVEL _ /* preserve a0 */ - call0 _xt_nmi /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_nmi,@function - .align 4 -_xt_nmi: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, XCHAL_NMILEVEL<<2 - beqz a0, 1f -.Ln_xt_nmi_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY NON-MASKABLE INTERRUPT (NMI) HANDLER CODE HERE. - */ - - .align 4 -.L_xt_nmi_exit: - rsr a0, EXCSAVE + XCHAL_NMILEVEL /* restore a0 */ - rfi XCHAL_NMILEVEL - -#endif /* NMI */ - - -/******************************************************************************* - -WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION HANDLER - -Here is the code for each window overflow/underflow exception vector and -(interspersed) efficient code for handling the alloca exception cause. -Window exceptions are handled entirely in the vector area and are very -tight for performance. The alloca exception is also handled entirely in -the window vector area so comes at essentially no cost in code size. -Users should never need to modify them and Cadence Design Systems recommends -they do not. - -Window handlers go at predetermined vector locations according to the -Xtensa hardware configuration, which is ensured by their placement in a -special section known to the Xtensa linker support package (LSP). Since -their offsets in that section are always the same, the LSPs do not define -a section per vector. - -These things are coded for XEA2 only (XEA1 is not supported). - -Note on Underflow Handlers: -The underflow handler for returning from call[i+1] to call[i] -must preserve all the registers from call[i+1]'s window. -In particular, a0 and a1 must be preserved because the RETW instruction -will be reexecuted (and may even underflow if an intervening exception -has flushed call[i]'s registers). -Registers a2 and up may contain return values. - -*******************************************************************************/ - -#if XCHAL_HAVE_WINDOWED - - .section .WindowVectors.text, "ax" - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call4. - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call4 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a3 are registers to be saved; - a4-a15 must be preserved; - a5 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x0 - .global _WindowOverflow4 -_WindowOverflow4: - - s32e a0, a5, -16 /* save a0 to call[j+1]'s stack frame */ - s32e a1, a5, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a5, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a5, -4 /* save a3 to call[j+1]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call4 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call4 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a3 are undefined, must be reloaded with call[i].reg[0..3]; - a4-a15 must be preserved (they are call[i+1].reg[0..11]); - a5 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x40 - .global _WindowUnderflow4 -_WindowUnderflow4: - - l32e a0, a5, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a5, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a5, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a3, a5, -4 /* restore a3 from call[i+1]'s stack frame */ - rfwu - -/* --------------------------------------------------------------------------------- -Handle alloca exception generated by interruptee executing 'movsp'. -This uses space between the window vectors, so is essentially "free". -All interruptee's regs are intact except a0 which is saved in EXCSAVE_1, -and PS.EXCM has been set by the exception hardware (can't be interrupted). -The fact the alloca exception was taken means the registers associated with -the base-save area have been spilled and will be restored by the underflow -handler, so those 4 registers are available for scratch. -The code is optimized to avoid unaligned branches and minimize cache misses. --------------------------------------------------------------------------------- -*/ - - .align 4 - .global _xt_alloca_exc -_xt_alloca_exc: - - rsr a0, WINDOWBASE /* grab WINDOWBASE before rotw changes it */ - rotw -1 /* WINDOWBASE goes to a4, new a0-a3 are scratch */ - rsr a2, PS - extui a3, a2, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS - xor a3, a3, a4 /* bits changed from old to current windowbase */ - rsr a4, EXCSAVE_1 /* restore original a0 (now in a4) */ - slli a3, a3, XCHAL_PS_OWB_SHIFT - xor a2, a2, a3 /* flip changed bits in old window base */ - wsr a2, PS /* update PS.OWB to new window base */ - rsync - - _bbci.l a4, 31, _WindowUnderflow4 - rotw -1 /* original a0 goes to a8 */ - _bbci.l a8, 30, _WindowUnderflow8 - rotw -1 - j _WindowUnderflow12 - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call8 - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call8 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a7 are registers to be saved; - a8-a15 must be preserved; - a9 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x80 - .global _WindowOverflow8 -_WindowOverflow8: - - s32e a0, a9, -16 /* save a0 to call[j+1]'s stack frame */ - l32e a0, a1, -12 /* a0 <- call[j-1]'s sp - (used to find end of call[j]'s frame) */ - s32e a1, a9, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a9, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a9, -4 /* save a3 to call[j+1]'s stack frame */ - s32e a4, a0, -32 /* save a4 to call[j]'s stack frame */ - s32e a5, a0, -28 /* save a5 to call[j]'s stack frame */ - s32e a6, a0, -24 /* save a6 to call[j]'s stack frame */ - s32e a7, a0, -20 /* save a7 to call[j]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call8 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call8 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a7 are undefined, must be reloaded with call[i].reg[0..7]; - a8-a15 must be preserved (they are call[i+1].reg[0..7]); - a9 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0xC0 - .global _WindowUnderflow8 -_WindowUnderflow8: - - l32e a0, a9, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a9, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a9, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a7, a1, -12 /* a7 <- call[i-1]'s sp - (used to find end of call[i]'s frame) */ - l32e a3, a9, -4 /* restore a3 from call[i+1]'s stack frame */ - l32e a4, a7, -32 /* restore a4 from call[i]'s stack frame */ - l32e a5, a7, -28 /* restore a5 from call[i]'s stack frame */ - l32e a6, a7, -24 /* restore a6 from call[i]'s stack frame */ - l32e a7, a7, -20 /* restore a7 from call[i]'s stack frame */ - rfwu - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call12 - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call12 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a11 are registers to be saved; - a12-a15 must be preserved; - a13 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x100 - .global _WindowOverflow12 -_WindowOverflow12: - - s32e a0, a13, -16 /* save a0 to call[j+1]'s stack frame */ - l32e a0, a1, -12 /* a0 <- call[j-1]'s sp - (used to find end of call[j]'s frame) */ - s32e a1, a13, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a13, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a13, -4 /* save a3 to call[j+1]'s stack frame */ - s32e a4, a0, -48 /* save a4 to end of call[j]'s stack frame */ - s32e a5, a0, -44 /* save a5 to end of call[j]'s stack frame */ - s32e a6, a0, -40 /* save a6 to end of call[j]'s stack frame */ - s32e a7, a0, -36 /* save a7 to end of call[j]'s stack frame */ - s32e a8, a0, -32 /* save a8 to end of call[j]'s stack frame */ - s32e a9, a0, -28 /* save a9 to end of call[j]'s stack frame */ - s32e a10, a0, -24 /* save a10 to end of call[j]'s stack frame */ - s32e a11, a0, -20 /* save a11 to end of call[j]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call12 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call12 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a11 are undefined, must be reloaded with call[i].reg[0..11]; - a12-a15 must be preserved (they are call[i+1].reg[0..3]); - a13 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x140 - .global _WindowUnderflow12 -_WindowUnderflow12: - - l32e a0, a13, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a13, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a13, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a11, a1, -12 /* a11 <- call[i-1]'s sp - (used to find end of call[i]'s frame) */ - l32e a3, a13, -4 /* restore a3 from call[i+1]'s stack frame */ - l32e a4, a11, -48 /* restore a4 from end of call[i]'s stack frame */ - l32e a5, a11, -44 /* restore a5 from end of call[i]'s stack frame */ - l32e a6, a11, -40 /* restore a6 from end of call[i]'s stack frame */ - l32e a7, a11, -36 /* restore a7 from end of call[i]'s stack frame */ - l32e a8, a11, -32 /* restore a8 from end of call[i]'s stack frame */ - l32e a9, a11, -28 /* restore a9 from end of call[i]'s stack frame */ - l32e a10, a11, -24 /* restore a10 from end of call[i]'s stack frame */ - l32e a11, a11, -20 /* restore a11 from end of call[i]'s stack frame */ - rfwu - -#endif /* XCHAL_HAVE_WINDOWED */ - - .section .UserEnter.text, "ax" - .global call_user_start - .type call_user_start,@function - .align 4 - .literal_position - - -call_user_start: - - movi a2, 0x40040000 /* note: absolute symbol, not a ptr */ - wsr a2, vecbase - call0 user_start /* user exception handler */ From 74a658c76596eed678f7595867606d2a23ca38ad Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 26 Oct 2016 12:25:53 +0800 Subject: [PATCH 058/110] nvs: fix memory leaks in HashList and nvs_close Fixes TW8162. Associated test case is run under Instruments on macOS, until I set up valgrind to test this automatically on Linux. --- components/nvs_flash/src/nvs_api.cpp | 1 + .../nvs_flash/src/nvs_item_hash_list.cpp | 11 ++++++++++- .../nvs_flash/src/nvs_item_hash_list.hpp | 10 ++++++++-- components/nvs_flash/src/nvs_page.cpp | 2 +- components/nvs_flash/test/test_nvs.cpp | 18 ++++++++++++++++++ 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index 0a56925c0..c1a910260 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -116,6 +116,7 @@ extern "C" void nvs_close(nvs_handle handle) return; } s_nvs_handles.erase(it); + delete static_cast(it); } extern "C" esp_err_t nvs_erase_key(nvs_handle handle, const char* key) diff --git a/components/nvs_flash/src/nvs_item_hash_list.cpp b/components/nvs_flash/src/nvs_item_hash_list.cpp index 7fa019dff..cf48477d6 100644 --- a/components/nvs_flash/src/nvs_item_hash_list.cpp +++ b/components/nvs_flash/src/nvs_item_hash_list.cpp @@ -17,7 +17,11 @@ namespace nvs { -HashList::~HashList() +HashList::HashList() +{ +} + +void HashList::clear() { for (auto it = mBlockList.begin(); it != mBlockList.end();) { auto tmp = it; @@ -26,6 +30,11 @@ HashList::~HashList() delete static_cast(tmp); } } + +HashList::~HashList() +{ + clear(); +} HashList::HashListBlock::HashListBlock() { diff --git a/components/nvs_flash/src/nvs_item_hash_list.hpp b/components/nvs_flash/src/nvs_item_hash_list.hpp index b40a53d61..3f8dcc850 100644 --- a/components/nvs_flash/src/nvs_item_hash_list.hpp +++ b/components/nvs_flash/src/nvs_item_hash_list.hpp @@ -25,11 +25,18 @@ namespace nvs class HashList { public: + HashList(); ~HashList(); + void insert(const Item& item, size_t index); void erase(const size_t index); size_t find(size_t start, const Item& item); - + void clear(); + +private: + HashList(const HashList& other); + const HashList& operator= (const HashList& rhs); + protected: struct HashListNode { @@ -57,7 +64,6 @@ protected: HashListNode mNodes[ENTRY_COUNT]; }; - typedef intrusive_list TBlockList; TBlockList mBlockList; }; // class HashList diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index f4fc5430c..64f7bd985 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -741,7 +741,7 @@ esp_err_t Page::erase() mFirstUsedEntry = INVALID_ENTRY; mNextFreeEntry = INVALID_ENTRY; mState = PageState::UNINITIALIZED; - mHashList = HashList(); + mHashList.clear(); return ESP_OK; } diff --git a/components/nvs_flash/test/test_nvs.cpp b/components/nvs_flash/test/test_nvs.cpp index 3db9b45ae..ce552578d 100644 --- a/components/nvs_flash/test/test_nvs.cpp +++ b/components/nvs_flash/test/test_nvs.cpp @@ -933,6 +933,24 @@ TEST_CASE("test recovery from sudden poweroff", "[.][long][nvs][recovery][monkey } } +TEST_CASE("test for memory leaks in open/set", "[leaks]") +{ + SpiFlashEmulator emu(10); + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + TEST_ESP_OK(nvs_flash_init_custom(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + + for (int i = 0; i < 100000; ++i) { + nvs_handle light_handle = 0; + char lightbulb[1024] = {12, 13, 14, 15, 16}; + TEST_ESP_OK(nvs_open("light", NVS_READWRITE, &light_handle)); + TEST_ESP_OK(nvs_set_blob(light_handle, "key", lightbulb, sizeof(lightbulb))); + TEST_ESP_OK(nvs_commit(light_handle)); + nvs_close(light_handle); + } +} + TEST_CASE("dump all performance data", "[nvs]") { std::cout << "====================" << std::endl << "Dumping benchmarks" << std::endl; From 612aaa69e4927baa9ce413237006bb5123361954 Mon Sep 17 00:00:00 2001 From: liuzhifu Date: Wed, 26 Oct 2016 13:23:35 +0800 Subject: [PATCH 059/110] lwip/esp32: move the extern wifi calls into esp_wifi_internal.h 1. Add esp_wifi_internal.h 2. Rename system_pp_recycle_rx_pkt to esp_wifi_internal_free_rx_buffer 3. rename esp_wifi_tx_is_stop to esp_wifi_internal_tx_is_stop 4. rename ieee80211_output to esp_wifi_internal_tx --- components/esp32/include/esp_wifi_internal.h | 80 +++++++++++++++++++ components/lwip/api/sockets.c | 8 +- components/lwip/core/pbuf.c | 4 +- .../lwip/include/lwip/port/netif/wlanif.h | 4 +- components/lwip/port/netif/wlanif.c | 4 +- 5 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 components/esp32/include/esp_wifi_internal.h diff --git a/components/esp32/include/esp_wifi_internal.h b/components/esp32/include/esp_wifi_internal.h new file mode 100644 index 000000000..5100010e5 --- /dev/null +++ b/components/esp32/include/esp_wifi_internal.h @@ -0,0 +1,80 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * All the APIs declared here are internal only APIs, it can only be used by + * espressif internal modules, such as SSC, LWIP, TCPIP adapter etc, espressif + * customers are not recommended to use them. + * + * If someone really want to use specified APIs declared in here, please contact + * espressif AE/developer to make sure you know the limitations or risk of + * the API, otherwise you may get unexpected behavior!!! + * + */ + + +#ifndef __ESP_WIFI_INTERNAL_H__ +#define __ESP_WIFI_INTERNAL_H__ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "rom/queue.h" +#include "esp_err.h" +#include "esp_wifi_types.h" +#include "esp_event.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief get whether the wifi driver is allowed to transmit data or not + * + * @param none + * + * @return true : upper layer should stop to transmit data to wifi driver + * @return false : upper layer can transmit data to wifi driver + */ +bool esp_wifi_internal_tx_is_stop(void); + +/** + * @brief free the rx buffer which allocated by wifi driver + * + * @param void* buffer: rx buffer pointer + * + * @return nonoe + */ +void esp_wifi_internal_free_rx_buffer(void* buffer); + +/** + * @brief free the rx buffer which allocated by wifi driver + * + * @attention1 TODO should modify the return type from bool to int + * + * @param wifi_interface_t wifi_if : wifi interface id + * @param void *buffer : the buffer to be tansmit + * @param u16_t len : the length of buffer + * + * @return True : success transmit the buffer to wifi driver + * False : failed to transmit the buffer to wifi driver + */ +bool esp_wifi_internal_tx(wifi_interface_t wifi_if, void *buffer, u16_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_WIFI_H__ */ diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index 350847b57..47f25bb07 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -388,10 +388,8 @@ static void lwip_socket_drop_registered_memberships(int s); #endif /* LWIP_IGMP */ #ifdef LWIP_ESP8266 - -/* Since esp_wifi_tx_is_stop/system_get_free_heap_size are not an public wifi API, so extern them here*/ -extern size_t system_get_free_heap_size(void); -extern bool esp_wifi_tx_is_stop(void); +#include "esp_wifi_internal.h" +#include "esp_system.h" /* Please be notified that this flow control is just a workaround for fixing wifi Q full issue. * Under UDP/TCP pressure test, we found that the sockets may cause wifi tx queue full if the socket @@ -404,7 +402,7 @@ static inline void esp32_tx_flow_ctrl(void) { uint8_t _wait_delay = 0; - while ((system_get_free_heap_size() < HEAP_HIGHWAT) || esp_wifi_tx_is_stop()){ + while ((system_get_free_heap_size() < HEAP_HIGHWAT) || esp_wifi_internal_tx_is_stop()){ vTaskDelay(_wait_delay/portTICK_RATE_MS); if (_wait_delay < 64) _wait_delay *= 2; } diff --git a/components/lwip/core/pbuf.c b/components/lwip/core/pbuf.c index e35f8a6b7..044d765cd 100755 --- a/components/lwip/core/pbuf.c +++ b/components/lwip/core/pbuf.c @@ -83,6 +83,7 @@ static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; #endif #ifdef LWIP_ESP8266 +#include "esp_wifi_internal.h" #define EP_OFFSET 0 #endif @@ -764,8 +765,7 @@ pbuf_free(struct pbuf *p) } else if (type == PBUF_ROM || type == PBUF_REF) { #ifdef LWIP_ESP8266 - extern void system_pp_recycle_rx_pkt(void*); - if (type == PBUF_REF && p->eb != NULL ) system_pp_recycle_rx_pkt(p->eb); + if (type == PBUF_REF && p->eb != NULL ) esp_wifi_internal_free_rx_buffer(p->eb); #endif memp_free(MEMP_PBUF, p); diff --git a/components/lwip/include/lwip/port/netif/wlanif.h b/components/lwip/include/lwip/port/netif/wlanif.h index 7eb303eab..c6f7831b3 100755 --- a/components/lwip/include/lwip/port/netif/wlanif.h +++ b/components/lwip/include/lwip/port/netif/wlanif.h @@ -8,6 +8,8 @@ #include "esp_wifi.h" +#include "esp_wifi_internal.h" + #include "lwip/err.h" #ifdef __cplusplus @@ -18,8 +20,6 @@ err_t wlanif_init(struct netif *netif); void wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb); -bool ieee80211_output(wifi_interface_t wifi_if, void *buffer, u16_t len); - wifi_interface_t wifi_get_interface(void *dev); void netif_reg_addr_change_cb(void* cb); diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index 9832c41af..0b4fe70ba 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -150,12 +150,12 @@ low_level_output(struct netif *netif, struct pbuf *p) } } - ieee80211_output(wifi_if, q->payload, pbuf_x_len); + esp_wifi_internal_tx(wifi_if, q->payload, pbuf_x_len); return ERR_OK; #else for(q = p; q != NULL; q = q->next) { - ieee80211_output(wifi_if, q->payload, q->len); + esp_wifi_internal_tx(wifi_if, q->payload, q->len); } #endif From 48301909eba52562f60ad5a1aa8657a9d42709ba Mon Sep 17 00:00:00 2001 From: liuzhifu Date: Wed, 26 Oct 2016 13:53:00 +0800 Subject: [PATCH 060/110] components/esp32: update wifi lib 6ce01d76: rename some wifi internal APIs aa4d2aa9: last rx buffer is reserved for mgmt frame bb0ff4a8: tw7775 fix assert when rx auth frame before create bss --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index a1e5f8b95..d3920845c 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit a1e5f8b953c7934677ba7a6ed0a6dd2da0e6bd0f +Subproject commit d3920845c9501f6ebae178186c3f63a80fab1ed1 From a90217e201a2340ddfb3b5adc2c29c777b8bfd82 Mon Sep 17 00:00:00 2001 From: liuzhifu Date: Wed, 26 Oct 2016 14:09:54 +0800 Subject: [PATCH 061/110] components esp32/lwip: modify code according to review comments 1. Modify comments for esp_wifi_internal_tx 2. Fix delay time error in esp32_tx_flow_ctrl which is found in code review, modify _wait_delay init value from 0 to 1 --- components/esp32/include/esp_wifi_internal.h | 2 +- components/lwip/api/sockets.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp32/include/esp_wifi_internal.h b/components/esp32/include/esp_wifi_internal.h index 5100010e5..217d5f6d1 100644 --- a/components/esp32/include/esp_wifi_internal.h +++ b/components/esp32/include/esp_wifi_internal.h @@ -60,7 +60,7 @@ bool esp_wifi_internal_tx_is_stop(void); void esp_wifi_internal_free_rx_buffer(void* buffer); /** - * @brief free the rx buffer which allocated by wifi driver + * @brief transmit the buffer via wifi driver * * @attention1 TODO should modify the return type from bool to int * diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index 47f25bb07..487bec8e8 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -400,7 +400,7 @@ static void lwip_socket_drop_registered_memberships(int s); */ static inline void esp32_tx_flow_ctrl(void) { - uint8_t _wait_delay = 0; + uint8_t _wait_delay = 1; while ((system_get_free_heap_size() < HEAP_HIGHWAT) || esp_wifi_internal_tx_is_stop()){ vTaskDelay(_wait_delay/portTICK_RATE_MS); From 9546ad5b5ebaef3230c73b6fc248f1dbad1138d0 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 26 Oct 2016 14:54:50 +0800 Subject: [PATCH 062/110] Move write key and stage action select constants into headers --- components/esp32/include/soc/rtc_cntl_reg.h | 3 ++- components/esp32/include/soc/timer_group_reg.h | 10 +++++++++- components/esp32/int_wdt.c | 16 ++++++++-------- components/esp32/panic.c | 10 +++++----- components/esp32/task_wdt.c | 16 ++++++++-------- 5 files changed, 32 insertions(+), 23 deletions(-) diff --git a/components/esp32/include/soc/rtc_cntl_reg.h b/components/esp32/include/soc/rtc_cntl_reg.h index 24e0f1403..bb4e2afce 100644 --- a/components/esp32/include/soc/rtc_cntl_reg.h +++ b/components/esp32/include/soc/rtc_cntl_reg.h @@ -14,7 +14,8 @@ #ifndef _SOC_RTC_CNTL_REG_H_ #define _SOC_RTC_CNTL_REG_H_ -#define WDT_WRITE_KEY 0x50D83AA1 +/* The value that needs to be written to RTC_CNTL_WDT_WKEY to write-enable the wdt registers */ +#define RTC_CNTL_WDT_WKEY_VALUE 0x50D83AA1 #include "soc.h" diff --git a/components/esp32/include/soc/timer_group_reg.h b/components/esp32/include/soc/timer_group_reg.h index 0d67cab51..2db2a7e3f 100644 --- a/components/esp32/include/soc/timer_group_reg.h +++ b/components/esp32/include/soc/timer_group_reg.h @@ -15,7 +15,15 @@ #define __TIMG_REG_H__ #include "soc.h" -#define WDT_WRITE_KEY 0x50D83AA1 +/* The value that needs to be written to TIMG_WDT_WKEY to write-enable the wdt registers */ +#define TIMG_WDT_WKEY_VALUE 0x50D83AA1 + +/* Possible values for TIMG_WDT_STGx */ +#define TIMG_WDT_STG_SEL_OFF 0 +#define TIMG_WDT_STG_SEL_INT 1 +#define TIMG_WDT_STG_SEL_RESET_CPU 2 +#define TIMG_WDT_STG_SEL_RESET_SYSTEM 3 + #define REG_TIMG_BASE(i) (DR_REG_TIMERGROUP0_BASE + i*0x1000) #define TIMG_T0CONFIG_REG(i) (REG_TIMG_BASE(i) + 0x0000) diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c index 5f123ee36..93a4d9fe6 100644 --- a/components/esp32/int_wdt.c +++ b/components/esp32/int_wdt.c @@ -36,13 +36,13 @@ void esp_int_wdt_init() { - TIMERG1.wdt_wprotect=WDT_WRITE_KEY; - TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS - TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS TIMERG1.wdt_config0.level_int_en=1; - TIMERG1.wdt_config0.stg0=1; //1st stage timeout: interrupt - TIMERG1.wdt_config0.stg1=3; //2nd stage timeout: reset system - TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + TIMERG1.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt + TIMERG1.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system + TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets //it to their actual value. TIMERG1.wdt_config2=10000; @@ -72,7 +72,7 @@ void vApplicationTickHook(void) { } else { //Only feed wdt if app cpu also ticked. if (int_wdt_app_cpu_ticked) { - TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset TIMERG1.wdt_feed=1; @@ -84,7 +84,7 @@ void vApplicationTickHook(void) { #else void vApplicationTickHook(void) { if (xPortGetCoreID()!=0) return; - TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset TIMERG1.wdt_feed=1; diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 758d581d0..274364008 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -203,17 +203,17 @@ all watchdogs except the timer group 0 watchdog, and it reconfigures that to res one second. */ static void reconfigureAllWdts() { - TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_feed=1; TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS - TIMERG0.wdt_config0.stg0=3; //1st stage timeout: reset system + TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_RESET_SYSTEM; //1st stage timeout: reset system TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS TIMERG0.wdt_config2=2000; //1 second before reset TIMERG0.wdt_config0.en=1; TIMERG0.wdt_wprotect=0; //Disable wdt 1 - TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG1.wdt_config0.en=0; TIMERG1.wdt_wprotect=0; } @@ -222,10 +222,10 @@ static void reconfigureAllWdts() { This disables all the watchdogs for when we call the gdbstub. */ static void disableAllWdts() { - TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en=0; TIMERG0.wdt_wprotect=0; - TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG1.wdt_config0.en=0; TIMERG0.wdt_wprotect=0; } diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 24d3977b1..bec1cadaa 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -49,7 +49,7 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { wdt_task_t *wdttask; const char *cpu; //Feed the watchdog so we do not reset - TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_feed=1; TIMERG0.wdt_wprotect=0; //Ack interrupt @@ -107,7 +107,7 @@ void esp_task_wdt_feed() { } if (do_feed_wdt) { //All tasks have checked in; time to feed the hw watchdog. - TIMERG0.wdt_wprotect=WDT_WRITE_KEY; + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_feed=1; TIMERG0.wdt_wprotect=0; //Reset fed_watchdog status @@ -141,13 +141,13 @@ void esp_task_wdt_delete() { } void esp_task_wdt_init() { - TIMERG0.wdt_wprotect=WDT_WRITE_KEY; - TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS - TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS TIMERG0.wdt_config0.level_int_en=1; - TIMERG0.wdt_config0.stg0=1; //1st stage timeout: interrupt - TIMERG0.wdt_config0.stg1=3; //2nd stage timeout: reset system - TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt + TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system + TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000; //Set timeout before interrupt TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000; //Set timeout before reset TIMERG0.wdt_config0.en=1; From 3cca62dfa46bc0888b45e7a848657f0387e386dc Mon Sep 17 00:00:00 2001 From: liuzhifu Date: Wed, 26 Oct 2016 18:16:40 +0800 Subject: [PATCH 063/110] esp32/tcpip_adapter: refractor for some wifi APIs 1. Modify esp_wifi_get_station_list to esp_wifi_ap_get_sta_list 2. Modify tcpip_adapter_get_station_list to tcpip_adapter_get_sta_list 3. Remove esp_wifi_free_station_list 4. Remove tcpip_adapter_free_station_list 5. Modify related data struct accordingly --- components/esp32/include/esp_wifi.h | 7 ++-- components/esp32/include/esp_wifi_types.h | 13 ++++-- .../tcpip_adapter/include/tcpip_adapter.h | 28 +++++-------- components/tcpip_adapter/tcpip_adapter_lwip.c | 42 ++++--------------- 4 files changed, 29 insertions(+), 61 deletions(-) diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 12378f334..3898bfec7 100644 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -198,7 +198,7 @@ esp_err_t esp_wifi_clear_fast_connect(void); * @return ESP_OK : succeed * @return others : fail */ -esp_err_t esp_wifi_kick_station(uint16_t aid); +esp_err_t esp_wifi_kick_sta(uint16_t aid); /** * @brief Scan all available APs. @@ -471,14 +471,13 @@ esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf); * * @attention SSC only API * - * @param struct station_info **station : station list + * @param wifi_sta_list_t *sta: sta list * * @return ESP_OK : succeed * @return others : fail */ -esp_err_t esp_wifi_get_station_list(struct station_info **station); +esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t *sta); -esp_err_t esp_wifi_free_station_list(void); /** * @brief Set the WiFi API configuration storage type diff --git a/components/esp32/include/esp_wifi_types.h b/components/esp32/include/esp_wifi_types.h index b3474769e..a607ad9e9 100644 --- a/components/esp32/include/esp_wifi_types.h +++ b/components/esp32/include/esp_wifi_types.h @@ -150,10 +150,15 @@ typedef union { wifi_sta_config_t sta; /**< configuration of STA */ } wifi_config_t; -struct station_info { - STAILQ_ENTRY(station_info) next; - uint8_t bssid[6]; -}; +typedef struct { + uint8_t mac[6]; /**< mac address of sta that associated with ESP32 soft-AP */ +}wifi_sta_info_t; + +#define ESP_WIFI_MAX_CONN_NUM 8 /**< max number of sta the eSP32 soft-AP can connect */ +typedef struct { + wifi_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM+2]; /**< sta list */ + uint8_t num; /**< number of sta that associated with ESP32 soft-AP */ +}wifi_sta_list_t; typedef enum { WIFI_STORAGE_FLASH, /**< all configuration will strore in both memory and flash */ diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index 5b0fc4c62..bbcf33727 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -67,11 +67,15 @@ typedef struct { typedef dhcps_lease_t tcpip_adapter_dhcps_lease_t; #if CONFIG_DHCP_STA_LIST -struct station_list { - STAILQ_ENTRY(station_list) next; +typedef struct { uint8_t mac[6]; ip4_addr_t ip; -}; +}tcpip_adapter_sta_info_t; + +typedef struct { + tcpip_adapter_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM+2]; + uint8_t num; +}tcpip_adapter_sta_list_t; #endif #endif @@ -359,26 +363,14 @@ wifi_interface_t tcpip_adapter_get_wifi_if(void *dev); /** * @brief Get the station information list * - * @note This function should be called in AP mode and dhcp server started, and the list should - * be by using tcpip_adapter_free_sta_list. - * - * @param[in] sta_info: station information - * @param[out] sta_list: station information list + * @param[in] wifi_sta_list_t *wifi_sta_list: sta list info + * @param[out] tcpip_adapter_sta_list_t *tcpip_sta_list: sta list info * * @return ESP_OK * ESP_ERR_TCPIP_ADAPTER_NO_MEM * ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS */ -esp_err_t tcpip_adapter_get_sta_list(struct station_info *sta_info, struct station_list **sta_list); - -/** - * @brief Free the station information list's memory - * - * @param sta_list: station information list - * - * @return ESP_OK - */ -esp_err_t tcpip_adapter_free_sta_list(struct station_list *sta_list); +esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapter_sta_list_t *tcpip_sta_list); #ifdef __cplusplus } diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 12cf05f95..677368008 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -590,45 +590,17 @@ wifi_interface_t tcpip_adapter_get_wifi_if(void *dev) return WIFI_IF_MAX; } -esp_err_t tcpip_adapter_get_sta_list(struct station_info *sta_info, struct station_list **sta_list) +esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapter_sta_list_t *tcpip_sta_list) { - struct station_info *info = sta_info; - struct station_list *list; - STAILQ_HEAD(, station_list) list_head; + int i; - if (sta_list == NULL) + if ((wifi_sta_list == NULL) || (tcpip_sta_list == NULL)) return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; - STAILQ_INIT(&list_head); - - while (info != NULL) { - list = (struct station_list *)malloc(sizeof(struct station_list)); - memset(list, 0, sizeof (struct station_list)); - - if (list == NULL) { - return ESP_ERR_TCPIP_ADAPTER_NO_MEM; - } - - memcpy(list->mac, info->bssid, 6); - dhcp_search_ip_on_mac(list->mac, &list->ip); - STAILQ_INSERT_TAIL(&list_head, list, next); - - info = STAILQ_NEXT(info, next); - } - - *sta_list = STAILQ_FIRST(&list_head); - - return ESP_OK; -} - -esp_err_t tcpip_adapter_free_sta_list(struct station_list *sta_list) -{ - struct station_list *list = sta_list; - - while (sta_list != NULL) { - list = sta_list; - sta_list = STAILQ_NEXT(sta_list, next); - free(list); + memset(tcpip_sta_list, 0, sizeof(tcpip_adapter_sta_list_t)); + for (i=0; inum; i++){ + memcpy(tcpip_sta_list->sta[i].mac, wifi_sta_list->sta[i].mac, 6); + dhcp_search_ip_on_mac(tcpip_sta_list->sta[i].mac, &tcpip_sta_list->sta[i].ip); } return ESP_OK; From 345cf333a89b2e1f9d5383e5b66b6b923afb20b1 Mon Sep 17 00:00:00 2001 From: liuzhifu Date: Wed, 26 Oct 2016 18:18:58 +0800 Subject: [PATCH 064/110] components/esp32: udpate wifi lib 1. cc5a5e29 - refractor for some wifi APIs 2. 8d787147 - move soft wdt to idf --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index d3920845c..0ee0776b5 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit d3920845c9501f6ebae178186c3f63a80fab1ed1 +Subproject commit 0ee0776b539db3e42a06a5b1ff8e4ea102f8630b From 750d6faf512184803bd9016ea612b4ecf99e7bc8 Mon Sep 17 00:00:00 2001 From: liuzhifu Date: Wed, 26 Oct 2016 20:02:39 +0800 Subject: [PATCH 065/110] esp32/tcpip_adapter: rework according to review comments 1. Modify sta to station in comments 2. Modify esp_wifi_get_ap_num to esp_wifi_scan_get_ap_num 3. Modify esp_wifi_get_ap_list to esp_wifi_scan_get_ap_records --- components/esp32/include/esp_wifi.h | 10 +++++----- components/esp32/include/esp_wifi_types.h | 8 ++++---- components/esp32/lib | 2 +- components/tcpip_adapter/include/tcpip_adapter.h | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 3898bfec7..6fc9d896a 100644 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -235,19 +235,19 @@ esp_err_t esp_wifi_scan_stop(void); * @return ESP_OK : succeed * @return others : fail */ -esp_err_t esp_wifi_get_ap_num(uint16_t *number); +esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); /** * @brief Get AP list found in last scan * - * @param uint16_t *number : as input param, it stores max AP number ap_list can hold, as output param, it store + * @param uint16_t *number : as input param, it stores max AP number ap_records can hold, as output param, it store the actual AP number this API returns - * @param wifi_ap_list_t *ap_list : a list to hold the found APs + * @param wifi_ap_record_t *ap_records: an wifi_ap_record_t array to hold the found APs * * @return ESP_OK : succeed * @return others : fail */ -esp_err_t esp_wifi_get_ap_list(uint16_t *number, wifi_ap_list_t *ap_list); +esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records); /** * @brief Set current power save type @@ -471,7 +471,7 @@ esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf); * * @attention SSC only API * - * @param wifi_sta_list_t *sta: sta list + * @param wifi_sta_list_t *sta: station list * * @return ESP_OK : succeed * @return others : fail diff --git a/components/esp32/include/esp_wifi_types.h b/components/esp32/include/esp_wifi_types.h index a607ad9e9..0ea719a65 100644 --- a/components/esp32/include/esp_wifi_types.h +++ b/components/esp32/include/esp_wifi_types.h @@ -109,7 +109,7 @@ typedef struct { wifi_second_chan_t second; /**< second channel of AP */ int8_t rssi; /**< signal strength of AP */ wifi_auth_mode_t authmode; /**< authmode of AP */ -} wifi_ap_list_t; +} wifi_ap_record_t; typedef enum { WIFI_PS_NONE, /**< No power save */ @@ -154,10 +154,10 @@ typedef struct { uint8_t mac[6]; /**< mac address of sta that associated with ESP32 soft-AP */ }wifi_sta_info_t; -#define ESP_WIFI_MAX_CONN_NUM 8 /**< max number of sta the eSP32 soft-AP can connect */ +#define ESP_WIFI_MAX_CONN_NUM (8+2) /**< max number of sta the eSP32 soft-AP can connect */ typedef struct { - wifi_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM+2]; /**< sta list */ - uint8_t num; /**< number of sta that associated with ESP32 soft-AP */ + wifi_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM]; /**< station list */ + uint8_t num; /**< number of station that associated with ESP32 soft-AP */ }wifi_sta_list_t; typedef enum { diff --git a/components/esp32/lib b/components/esp32/lib index 0ee0776b5..02063e8d4 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 0ee0776b539db3e42a06a5b1ff8e4ea102f8630b +Subproject commit 02063e8d40f72933622b2eafd78ce968085b0047 diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index bbcf33727..218325320 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -363,8 +363,8 @@ wifi_interface_t tcpip_adapter_get_wifi_if(void *dev); /** * @brief Get the station information list * - * @param[in] wifi_sta_list_t *wifi_sta_list: sta list info - * @param[out] tcpip_adapter_sta_list_t *tcpip_sta_list: sta list info + * @param[in] wifi_sta_list_t *wifi_sta_list: station list info + * @param[out] tcpip_adapter_sta_list_t *tcpip_sta_list: station list info * * @return ESP_OK * ESP_ERR_TCPIP_ADAPTER_NO_MEM From d3d9a8bc280900ffac9b61a3a1bc8532fa9aa8c6 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 26 Oct 2016 21:09:55 +0800 Subject: [PATCH 066/110] Most code written. Interrupts still do not fire yet. --- components/esp32/crosscore_int.c | 107 +++++++++++++ components/esp32/include/esp_crosscore_int.h | 8 + .../freertos/include/freertos/portable.h | 8 + components/freertos/port.c | 8 + components/freertos/tasks.c | 147 ++++++++++++------ 5 files changed, 233 insertions(+), 45 deletions(-) create mode 100644 components/esp32/crosscore_int.c create mode 100644 components/esp32/include/esp_crosscore_int.h diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c new file mode 100644 index 000000000..95f2762f2 --- /dev/null +++ b/components/esp32/crosscore_int.c @@ -0,0 +1,107 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_intr.h" + +#include "rom/ets_sys.h" +#include "rom/uart.h" + +#include "soc/cpu.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/rtc_cntl_reg.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/portmacro.h" + + +#define REASON_YIELD (1<<0) + +static portMUX_TYPE reasonSpinlock = portMUX_INITIALIZER_UNLOCKED; +static volatile uint32_t reason[ portNUM_PROCESSORS ]; + + +/* +ToDo: There is a small chance the CPU already has yielded when this ISR is serviced. In that case, it's running the intended task but +the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that. +*/ +static void esp_crosscore_isr(void *arg) { + volatile uint32_t myReasonVal; +#if 0 + //A pointer to the correct reason array item is passed to this ISR. + volatile uint32_t *myReason=arg; +#else + //Does not work yet, the interrupt code needs work to understand two separate interrupt and argument + //tables... + volatile uint32_t *myReason=&reason[xPortGetCoreID()]; +#endif + //Clear the interrupt first. + if (xPortGetCoreID()==0) { + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); + } else { + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 1); + } + //Grab the reason and clear it. + portENTER_CRITICAL(&reasonSpinlock); + myReasonVal=*myReason; + *myReason=0; + portEXIT_CRITICAL(&reasonSpinlock); + + //Check what we need to do. + if (myReasonVal&REASON_YIELD) { + portYIELD_FROM_ISR(); + } + + ets_printf("recv yield\n"); +} + +//Initialize the crosscore interrupt on this core. Call this once +//on each active core. +void esp_crosscore_int_init() { + portENTER_CRITICAL(&reasonSpinlock); + reason[xPortGetCoreID()]=0; + portEXIT_CRITICAL(&reasonSpinlock); + ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); + if (xPortGetCoreID()==0) { + intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR0_SOURCE, ETS_FROM_CPU_INUM); + } else { + intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM); + } + xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); + ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); + +} + +void esp_crosscore_int_send_yield(int coreId) { + ets_printf("send yield\n"); + assert(coreIduxPriority ] ), &( ( pxTCB )->xGenericListItem ) ) /*-----------------------------------------------------------*/ + +#define tskCAN_RUN_HERE( cpuid ) ( cpuid==xPortGetCoreID() || cpuid==tskNO_AFFINITY ) + /* * Several functions take an TaskHandle_t parameter that can optionally be NULL, * where NULL is used to indicate that the handle of the currently executing @@ -581,6 +584,35 @@ static void prvResetNextTaskUnblockTime( void ); /*-----------------------------------------------------------*/ +/* + * This routine tries to send an interrupt to another core if needed to make it execute a task + * of higher priority. We try to figure out if needed first by inspecting the pxTCB of the + * other CPU first. Specifically for Xtensa, we can do this because pxTCB is an atomic pointer. It + * is possible that it is inaccurate because the other CPU just did a task switch, but in that case + * at most a superfluous interrupt is generated. +*/ +static void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority ) +{ + BaseType_t i; + if (xCoreID != tskNO_AFFINITY) { + if ( pxCurrentTCB[ xCoreID ]->uxPriority < uxPriority ) { + vPortYieldOtherCore( xCoreID ); + } + } + else + { + /* The task has no affinity. See if we can find a CPU to put it on.*/ + for (i=0; iuxPriority < uxPriority) + { + vPortYieldOtherCore( i ); + break; + } + } + } +} + + BaseType_t xTaskGenericCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, StackType_t * const puxStackBuffer, const MemoryRegion_t * const xRegions, const BaseType_t xCoreID) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { BaseType_t xReturn; @@ -753,7 +785,10 @@ BaseType_t i; the other processor will keep running the task it's working on, and only switch to the newer task on a timer interrupt. */ //No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. - if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < uxPriority ) + if( xCoreID != xPortGetCoreID() ) { + taskYIELD_OTHER_CORE(xCoreID, uxPriority); + } + else if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); } @@ -834,7 +869,7 @@ BaseType_t i; after which it is not possible to yield away from this task - hence xYieldPending is used to latch that a context switch is required. */ - portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); + portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[xPortGetCoreID()] ); portYIELD_WITHIN_API(); } else @@ -1188,10 +1223,14 @@ BaseType_t i; /* The priority of a task other than the currently running task is being raised. Is the priority being raised above that of the running task? */ - if( uxNewPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxTCB->xCoreID) && uxNewPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { xYieldRequired = pdTRUE; } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, uxNewPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1470,13 +1509,17 @@ BaseType_t i; prvAddTaskToReadyList( pxTCB ); /* We may have just resumed a higher priority task. */ - if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* This yield may not cause the task just resumed to run, but will leave the lists in the correct state for the next yield. */ taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex); } + else if( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1521,7 +1564,14 @@ BaseType_t i; { /* Ready lists can be accessed so move the task from the suspended list to the ready list directly. */ - if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + if ( pxTCB->xCoreID == xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority); + } + else if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { xYieldRequired = pdTRUE; } @@ -1529,9 +1579,6 @@ BaseType_t i; { mtCOVERAGE_TEST_MARKER(); } - - ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); - prvAddTaskToReadyList( pxTCB ); } else { @@ -1732,11 +1779,16 @@ BaseType_t xAlreadyYielded = pdFALSE; /* If the moved task has a priority higher than the current task then a yield must be performed. */ - if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - xYieldPending = pdTRUE; + /* We can schedule the awoken task on this CPU. */ + xYieldPending[xPortGetCoreID()] = pdTRUE; break; } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1753,7 +1805,7 @@ BaseType_t xAlreadyYielded = pdFALSE; { if( xTaskIncrementTick() != pdFALSE ) { - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } else { @@ -1767,7 +1819,7 @@ BaseType_t xAlreadyYielded = pdFALSE; mtCOVERAGE_TEST_MARKER(); } - if( xYieldPending == pdTRUE ) + if( xYieldPending[ xPortGetCoreID() ] == pdTRUE ) { #if( configUSE_PREEMPTION != 0 ) { @@ -2135,7 +2187,7 @@ BaseType_t xSwitchRequired = pdFALSE; #if ( configUSE_PREEMPTION == 1 ) { - if( xYieldPending != pdFALSE ) + if( xYieldPending [ xPortGetCoreID() ] != pdFALSE ) { xSwitchRequired = pdTRUE; } @@ -2251,11 +2303,11 @@ void vTaskSwitchContext( void ) { /* The scheduler is currently suspended - do not allow a context switch. */ - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } else { - xYieldPending = pdFALSE; + xYieldPending[ xPortGetCoreID() ] = pdFALSE; traceTASK_SWITCHED_OUT(); #if ( configGENERATE_RUN_TIME_STATS == 1 ) @@ -2610,16 +2662,16 @@ BaseType_t xReturn; taskEXIT_CRITICAL(&xTaskQueueMutex); } - if( pxUnblockedTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - /* Return true if the task removed from the event list has a higher - priority than the calling task. This allows the calling task to know if - it should force a context switch now. */ + /* We can schedule the awoken task on this CPU. */ + xYieldPending[xPortGetCoreID()] = pdTRUE; xReturn = pdTRUE; - - /* Mark that a yield is pending in case the user is not using the - "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ - xYieldPending = pdTRUE; + } + else if ( pxUnblockedTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxUnblockedTCB->xCoreID, pxUnblockedTCB->uxPriority ); + xReturn = pdFALSE; } else { @@ -2670,17 +2722,16 @@ BaseType_t xReturn; ( void ) uxListRemove( &( pxUnblockedTCB->xGenericListItem ) ); prvAddTaskToReadyList( pxUnblockedTCB ); - if( pxUnblockedTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - /* Return true if the task removed from the event list has - a higher priority than the calling task. This allows - the calling task to know if it should force a context - switch now. */ + /* We can schedule the awoken task on this CPU. */ + xYieldPending[xPortGetCoreID()] = pdTRUE; xReturn = pdTRUE; - - /* Mark that a yield is pending in case the user is not using the - "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ - xYieldPending = pdTRUE; + } + else if ( pxUnblockedTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxUnblockedTCB->xCoreID, pxUnblockedTCB->uxPriority ); + xReturn = pdFALSE; } else { @@ -2751,7 +2802,7 @@ BaseType_t xReturn; void vTaskMissedYield( void ) { - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } /*-----------------------------------------------------------*/ @@ -2921,7 +2972,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) /* A task was made ready while the scheduler was suspended. */ eReturn = eAbortSleep; } - else if( xYieldPending != pdFALSE ) + else if( xYieldPending[ xPortGetCoreID() ] != pdFALSE ) { /* A yield was pended while the scheduler was suspended. */ eReturn = eAbortSleep; @@ -3597,12 +3648,6 @@ TCB_t *pxTCB; #endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) */ /*-----------------------------------------------------------*/ -/* -ToDo: Mutexes haven't been tested or adapted to multicore at all. - -In fact, nothing below this line has/is. -*/ - #if ( configUSE_MUTEXES == 1 ) void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) @@ -4434,12 +4479,16 @@ TickType_t uxReturn; /* The task should not have been on an event list. */ configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); - if( pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* The notified task has a priority above the currently executing task so a yield is required. */ portYIELD_WITHIN_API(); } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE(pxTCB->xCoreID, pxTCB->uxPriority); + } else { mtCOVERAGE_TEST_MARKER(); @@ -4530,7 +4579,7 @@ TickType_t uxReturn; vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); } - if( pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* The notified task has a priority above the currently executing task so a yield is required. */ @@ -4539,6 +4588,10 @@ TickType_t uxReturn; *pxHigherPriorityTaskWoken = pdTRUE; } } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -4593,8 +4646,8 @@ TickType_t uxReturn; this task pending until the scheduler is resumed. */ vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); } - - if( pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* The notified task has a priority above the currently executing task so a yield is required. */ @@ -4603,6 +4656,10 @@ TickType_t uxReturn; *pxHigherPriorityTaskWoken = pdTRUE; } } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); From df1c2f0da5e9b1a00622e5eb5f81d2ab772967c2 Mon Sep 17 00:00:00 2001 From: liuzhifu Date: Wed, 26 Oct 2016 21:50:15 +0800 Subject: [PATCH 067/110] components/esp32: refractor according to review comments modify esp_wifi_kick_sta to esp_wifi_deauth_sta --- components/esp32/include/esp_wifi.h | 6 +++--- components/esp32/lib | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 6fc9d896a..7cfff6ee0 100644 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -191,14 +191,14 @@ esp_err_t esp_wifi_disconnect(void); esp_err_t esp_wifi_clear_fast_connect(void); /** - * @brief Kick the all station or associated id equals to aid + * @brief deauthenticate all stations or associated id equals to aid * - * @param uint16_t aid : when aid is 0, kick all stations, otherwise kick station whose associated id is aid + * @param uint16_t aid : when aid is 0, deauthenticate all stations, otherwise deauthenticate station whose associated id is aid * * @return ESP_OK : succeed * @return others : fail */ -esp_err_t esp_wifi_kick_sta(uint16_t aid); +esp_err_t esp_wifi_deauth_sta(uint16_t aid); /** * @brief Scan all available APs. diff --git a/components/esp32/lib b/components/esp32/lib index 02063e8d4..b9561aa5d 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 02063e8d40f72933622b2eafd78ce968085b0047 +Subproject commit b9561aa5db15443d11f8bb5aefdfc5da540d8f2d From 6d54fb004d3ab2c85ac80184a5f1f2ae9a513639 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 27 Oct 2016 09:15:43 +0800 Subject: [PATCH 068/110] Change inline to static inline functions. Ref Github issue 62. --- components/freertos/include/freertos/portable.h | 2 +- components/freertos/include/freertos/portmacro.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/freertos/include/freertos/portable.h b/components/freertos/include/freertos/portable.h index a3d39bd5a..f3474d49e 100644 --- a/components/freertos/include/freertos/portable.h +++ b/components/freertos/include/freertos/portable.h @@ -192,7 +192,7 @@ void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; #endif /* Multi-core: get current core ID */ -inline uint32_t xPortGetCoreID() { +static inline uint32_t xPortGetCoreID() { int id; asm volatile( "rsr.prid %0\n" diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index 5e2386d72..f20a4a1e2 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -234,7 +234,7 @@ static inline unsigned portENTER_CRITICAL_NESTED() { unsigned state = XTOS_SET_I * *bitwise inverse* of the old mem if the mem wasn't written. This doesn't seem to happen on the * ESP32, though. (Would show up directly if it did because the magic wouldn't match.) */ -inline void uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) { +static inline void uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) { __asm__ __volatile__( "WSR %2,SCOMPARE1 \n" "ISYNC \n" From e0f49c2221de4face091f8a98085e395f6c0cd4f Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 27 Oct 2016 10:42:01 +0800 Subject: [PATCH 069/110] esp32: add esp_wifi_sta_get_ap_info The customers need to get information about AP that associated with ESP32 station, these information includes RSSI, channel number etc, so add this new API --- components/esp32/include/esp_wifi.h | 13 ++++++++++++- components/esp32/lib | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 7cfff6ee0..80ced5dc6 100644 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -242,13 +242,24 @@ esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); * * @param uint16_t *number : as input param, it stores max AP number ap_records can hold, as output param, it store the actual AP number this API returns - * @param wifi_ap_record_t *ap_records: an wifi_ap_record_t array to hold the found APs + * @param wifi_ap_record_t *ap_records: wifi_ap_record_t array to hold the found APs * * @return ESP_OK : succeed * @return others : fail */ esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records); + +/** + * @brief Get information of AP associated with ESP32 station + * + * @param wifi_ap_record_t *ap_info: the wifi_ap_record_t to hold station assocated AP + * + * @return ESP_OK : succeed + * @return others : fail + */ +esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info); + /** * @brief Set current power save type * diff --git a/components/esp32/lib b/components/esp32/lib index b9561aa5d..774f6073d 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit b9561aa5db15443d11f8bb5aefdfc5da540d8f2d +Subproject commit 774f6073dee1b01da5f420c5d7513b3d88cd5729 From ff6b8addd90bc0da34fb83b914ea74d779b93089 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 27 Oct 2016 11:17:24 +0800 Subject: [PATCH 070/110] Fix panic config ifdefs, un-stall app cpu on boot so it restarts after panic --- components/esp32/cpu_start.c | 4 ++++ components/esp32/panic.c | 10 ++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 37205dcd9..a649764ab 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -102,6 +102,10 @@ void IRAM_ATTR call_start_cpu0() #if !CONFIG_FREERTOS_UNICORE ESP_EARLY_LOGI(TAG, "Starting app cpu, entry point is %p", call_start_cpu1); + //Un-stall the app cpu; the panic handler may have stalled it. + CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_APPCPU_C1_M); + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_APPCPU_C0_M); + //Enable clock gating and reset the app cpu. SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN); CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_C_REG, DPORT_APPCPU_RUNSTALL); SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_A_REG, DPORT_APPCPU_RESETTING); diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 274364008..c806dace2 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -38,7 +38,7 @@ task switching / interrupt code runs into an unrecoverable error. The default ta overflow handler also is in here. */ -#if !CONFIG_FREERTOS_PANIC_SILENT_REBOOT +#if !CONFIG_ESP32_PANIC_SILENT_REBOOT //printf may be broken, so we fix our own printing fns... inline static void panicPutchar(char c) { while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ; @@ -123,7 +123,7 @@ static void haltOtherCore() { //Returns true when a debugger is attached using JTAG. static int inOCDMode() { -#if CONFIG_FREERTOS_DEBUG_OCDAWARE +#if CONFIG_ESP32_DEBUG_OCDAWARE int dcr; int reg=0x10200C; //DSRSET register asm("rer %0,%1":"=r"(dcr):"r"(reg)); @@ -218,6 +218,7 @@ static void reconfigureAllWdts() { TIMERG1.wdt_wprotect=0; } +#if CONFIG_ESP32_PANIC_GDBSTUB || CONFIG_ESP32_PANIC_PRINT_HALT /* This disables all the watchdogs for when we call the gdbstub. */ @@ -230,6 +231,7 @@ static void disableAllWdts() { TIMERG0.wdt_wprotect=0; } +#endif /* We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the @@ -259,11 +261,11 @@ void commonErrorHandler(XtExcFrame *frame) { } panicPutStr("\r\n"); } -#if CONFIG_FREERTOS_PANIC_GDBSTUB +#if CONFIG_ESP32_PANIC_GDBSTUB disableAllWdts(); panicPutStr("Entering gdb stub now.\r\n"); esp_gdbstub_panic_handler(frame); -#elif CONFIG_FREERTOS_PANIC_PRINT_REBOOT || CONFIG_FREERTOS_PANIC_SILENT_REBOOT +#elif CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT panicPutStr("Rebooting...\r\n"); for (x=0; x<100; x++) ets_delay_us(1000); software_reset(); From c6477ff10d983aa8dcb152be2bc673352d5bd69c Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 27 Oct 2016 12:37:19 +0800 Subject: [PATCH 071/110] Fix int clear, actually call int init code --- components/esp32/cpu_start.c | 3 +++ components/esp32/crosscore_int.c | 4 ++-- components/esp32/lib | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 5c7a411c8..0c94f39e3 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -41,6 +41,7 @@ #include "esp_event.h" #include "esp_spi_flash.h" #include "esp_ipc.h" +#include "esp_crosscore_int.h" #include "esp_log.h" #include "trax.h" @@ -146,6 +147,7 @@ void start_cpu0_default(void) uart_div_modify(0, (APB_CLK_FREQ << 4) / 115200); ets_setup_syscalls(); do_global_ctors(); + esp_crosscore_int_init(); esp_ipc_init(); spi_flash_init(); xTaskCreatePinnedToCore(&main_task, "main", @@ -165,6 +167,7 @@ void start_cpu1_default(void) while (port_xSchedulerRunning[0] == 0) { ; } + esp_crosscore_int_init(); ESP_LOGI(TAG, "Starting scheduler on APP CPU."); xPortStartScheduler(); } diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 95f2762f2..a98b13583 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -57,7 +57,7 @@ static void esp_crosscore_isr(void *arg) { if (xPortGetCoreID()==0) { WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); } else { - WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 1); + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0); } //Grab the reason and clear it. portENTER_CRITICAL(&reasonSpinlock); @@ -77,6 +77,7 @@ static void esp_crosscore_isr(void *arg) { //on each active core. void esp_crosscore_int_init() { portENTER_CRITICAL(&reasonSpinlock); + ets_printf("init cpu %d\n", xPortGetCoreID()); reason[xPortGetCoreID()]=0; portEXIT_CRITICAL(&reasonSpinlock); ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); @@ -87,7 +88,6 @@ void esp_crosscore_int_init() { } xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); - } void esp_crosscore_int_send_yield(int coreId) { diff --git a/components/esp32/lib b/components/esp32/lib index a1e5f8b95..b9561aa5d 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit a1e5f8b953c7934677ba7a6ed0a6dd2da0e6bd0f +Subproject commit b9561aa5db15443d11f8bb5aefdfc5da540d8f2d From 329ad14b8c79ac1ae0e30850909b2ef1f12b7621 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 27 Oct 2016 12:37:34 +0800 Subject: [PATCH 072/110] esp32: update libs for changes in FreeRTOS header files --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index aac7d416a..12b3435fc 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit aac7d416a702215f2501a6237caf034ec7e8e80a +Subproject commit 12b3435fc0cd04efc249d52d71efb1cdecda50f8 From 6e6e51426f2a5b2837d4bd9e81e54340aea226ee Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 27 Oct 2016 14:11:01 +0800 Subject: [PATCH 073/110] lwip: refractor for lwip 1. All espressif specific code are prefix with ESP_ 2. Define all ESP_ options in lwipopts.h 3. Remove useless code added in 8266 --- components/lwip/api/api_lib.c | 21 +---- components/lwip/api/api_msg.c | 22 +---- components/lwip/api/netbuf.c | 5 -- components/lwip/api/netdb.c | 5 -- components/lwip/api/sockets.c | 86 +++++++------------ components/lwip/api/tcpip.c | 20 ++--- components/lwip/apps/dhcpserver.c | 6 +- components/lwip/core/dns.c | 8 +- components/lwip/core/init.c | 4 +- components/lwip/core/ipv4/autoip.c | 5 -- components/lwip/core/ipv4/dhcp.c | 10 +-- components/lwip/core/ipv4/icmp.c | 5 -- components/lwip/core/ipv4/igmp.c | 5 -- components/lwip/core/ipv4/ip4.c | 9 +- components/lwip/core/ipv4/ip4_addr.c | 14 +-- components/lwip/core/ipv4/ip_frag.c | 5 -- components/lwip/core/ipv6/icmp6.c | 4 - components/lwip/core/ipv6/ip6.c | 4 - components/lwip/core/ipv6/ip6_frag.c | 5 -- components/lwip/core/ipv6/mld6.c | 5 -- components/lwip/core/ipv6/nd6.c | 5 -- components/lwip/core/mem.c | 4 - components/lwip/core/memp.c | 4 - components/lwip/core/netif.c | 13 +-- components/lwip/core/pbuf.c | 55 ++---------- components/lwip/core/raw.c | 4 - components/lwip/core/stats.c | 4 - components/lwip/core/tcp.c | 61 +------------ components/lwip/core/tcp_in.c | 19 ---- components/lwip/core/tcp_out.c | 4 - components/lwip/core/timers.c | 17 +--- components/lwip/core/udp.c | 5 -- components/lwip/include/lwip/lwip/api.h | 4 - components/lwip/include/lwip/lwip/dhcp.h | 2 +- components/lwip/include/lwip/lwip/dns.h | 2 +- components/lwip/include/lwip/lwip/err.h | 2 +- components/lwip/include/lwip/lwip/mem.h | 37 -------- components/lwip/include/lwip/lwip/netif.h | 6 +- components/lwip/include/lwip/lwip/opt.h | 4 +- components/lwip/include/lwip/lwip/pbuf.h | 2 +- .../lwip/include/lwip/lwip/priv/api_msg.h | 6 +- components/lwip/include/lwip/lwip/sockets.h | 4 +- components/lwip/include/lwip/port/lwipopts.h | 33 ++++++- components/lwip/netif/etharp.c | 4 - components/lwip/port/freertos/sys_arch.c | 44 +++++----- components/lwip/port/netif/wlanif.c | 24 ++---- 46 files changed, 142 insertions(+), 475 deletions(-) diff --git a/components/lwip/api/api_lib.c b/components/lwip/api/api_lib.c index c38c76081..ecebf4f81 100755 --- a/components/lwip/api/api_lib.c +++ b/components/lwip/api/api_lib.c @@ -55,11 +55,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - #define API_MSG_VAR_REF(name) API_VAR_REF(name) #define API_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct api_msg, name) #define API_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name) @@ -178,8 +173,8 @@ netconn_delete(struct netconn *conn) return err; } -#if !LWIP_THREAD_SAFE - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n")); +#if !ESP_THREAD_SAFE + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n")); netconn_free(conn); #endif @@ -502,7 +497,7 @@ netconn_recv_data(struct netconn *conn, void **new_buf) #endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ #if (LWIP_UDP || LWIP_RAW) { -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE if (buf == NULL){ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); return ERR_CLSD; @@ -710,17 +705,7 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, } dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK); -#ifdef LWIP_ESP8266 - -#ifdef FOR_XIAOMI - if (dontblock && bytes_written) { -#else - if (dontblock && !bytes_written) { -#endif - -#else if (dontblock && !bytes_written) { -#endif /* This implies netconn_write() cannot be used for non-blocking send, since it has no way to return the number of bytes written. */ return ERR_VAL; diff --git a/components/lwip/api/api_msg.c b/components/lwip/api/api_msg.c index 9cac2e0e9..d504bfb87 100755 --- a/components/lwip/api/api_msg.c +++ b/components/lwip/api/api_msg.c @@ -55,10 +55,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /* netconns are polled once per second (e.g. continue write on memory error) */ #define NETCONN_TCP_POLL_INTERVAL 2 @@ -1216,16 +1212,7 @@ lwip_netconn_do_connect(void *m) if (msg->conn->state == NETCONN_CONNECT) { msg->err = ERR_ALREADY; } else if (msg->conn->state != NETCONN_NONE) { - -#ifdef LWIP_ESP8266 - if( msg->conn->pcb.tcp->state == ESTABLISHED ) msg->err = ERR_ISCONN; - else - msg->err = ERR_ALREADY; -#else - msg->err = ERR_ISCONN; -#endif - } else { setup_tcp(msg->conn); msg->err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), @@ -1646,14 +1633,7 @@ lwip_netconn_do_write(void *m) if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) { LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); UNLOCK_TCPIP_CORE(); - -#ifdef LWIP_ESP8266 -//#if 0 - sys_arch_sem_wait( LWIP_API_MSG_SND_SEM(msg), 0); -#else - sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); -#endif - + sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); LOCK_TCPIP_CORE(); LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE); } diff --git a/components/lwip/api/netbuf.c b/components/lwip/api/netbuf.c index 6c6dc69cc..9ab76a463 100755 --- a/components/lwip/api/netbuf.c +++ b/components/lwip/api/netbuf.c @@ -45,11 +45,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** * Create (allocate) and initialize a new netbuf. * The netbuf doesn't yet contain a packet buffer! diff --git a/components/lwip/api/netdb.c b/components/lwip/api/netdb.c index 8fd3f4186..65510f55e 100755 --- a/components/lwip/api/netdb.c +++ b/components/lwip/api/netdb.c @@ -47,11 +47,6 @@ #include #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** helper struct for gethostbyname_r to access the char* buffer */ struct gethostbyname_r_helper { ip_addr_t *addr_list[2]; diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index 1cc5fbf7b..455d007ea 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -61,11 +61,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /* If the netconn API is not required publicly, then we include the necessary files here to get the implementation */ #if !LWIP_NETCONN @@ -216,7 +211,7 @@ struct lwip_sock { /** last error that occurred on this socket (in fact, all our errnos fit into an u8_t) */ u8_t err; -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE /* lock is used to protect state/ref field, however this lock is not a perfect lock, e.g * taskA and taskB can access sock X, then taskA freed sock X, before taskB detect * this, taskC reuse sock X, then when taskB try to access sock X, problem may happen. @@ -239,7 +234,7 @@ struct lwip_sock { SELWAIT_T select_waiting; }; -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE #define LWIP_SOCK_OPEN 0 #define LWIP_SOCK_CLOSING 1 @@ -247,25 +242,25 @@ struct lwip_sock { #define LWIP_SOCK_LOCK(sock) \ do{\ - /*LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("l\n"));*/\ + /*LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("l\n"));*/\ sys_mutex_lock(&sock->lock);\ - /*LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("l ok\n"));*/\ + /*LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("l ok\n"));*/\ }while(0) #define LWIP_SOCK_UNLOCK(sock) \ do{\ sys_mutex_unlock(&sock->lock);\ - /*LWIP_DEBUGF(THREAD_SAFE_DEBUG1, ("unl\n"));*/\ + /*LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG1, ("unl\n"));*/\ }while(0) #define LWIP_FREE_SOCK(sock) \ do{\ if(sock->conn && NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP){\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("LWIP_FREE_SOCK:free tcp sock\n"));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_FREE_SOCK:free tcp sock\n"));\ free_socket(sock, 1);\ } else {\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("LWIP_FREE_SOCK:free non-tcp sock\n"));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_FREE_SOCK:free non-tcp sock\n"));\ free_socket(sock, 0);\ }\ }while(0) @@ -273,7 +268,7 @@ do{\ #define LWIP_SET_CLOSE_FLAG() \ do{\ LWIP_SOCK_LOCK(__sock);\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("mark sock closing\n"));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("mark sock closing\n"));\ __sock->state = LWIP_SOCK_CLOSING;\ LWIP_SOCK_UNLOCK(__sock);\ }while(0) @@ -291,7 +286,7 @@ do{\ LWIP_SOCK_LOCK(__sock);\ __sock->ref ++;\ if (__sock->state != LWIP_SOCK_OPEN) {\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("LWIP_API_LOCK:soc is %d, return\n", __sock->state));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_API_LOCK:soc is %d, return\n", __sock->state));\ __sock->ref --;\ LWIP_SOCK_UNLOCK(__sock);\ return -1;\ @@ -306,12 +301,12 @@ do{\ __sock->ref --;\ if (__sock->state == LWIP_SOCK_CLOSING) {\ if (__sock->ref == 0){\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("LWIP_API_UNLOCK:ref 0, free __sock\n"));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_API_UNLOCK:ref 0, free __sock\n"));\ LWIP_FREE_SOCK(__sock);\ LWIP_SOCK_UNLOCK(__sock);\ return __ret;\ }\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("LWIP_API_UNLOCK: soc state is closing, return\n"));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_API_UNLOCK: soc state is closing, return\n"));\ LWIP_SOCK_UNLOCK(__sock);\ return __ret;\ }\ @@ -387,7 +382,7 @@ static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, static void lwip_socket_drop_registered_memberships(int s); #endif /* LWIP_IGMP */ -#ifdef LWIP_ESP8266 +#if ESP_LWIP #include "esp_wifi_internal.h" #include "esp_system.h" @@ -414,7 +409,7 @@ static inline void esp32_tx_flow_ctrl(void) /** The global array of available sockets */ static struct lwip_sock sockets[NUM_SOCKETS]; -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE static bool sockets_init_flag = false; #endif /** The global list of tasks waiting for select */ @@ -425,13 +420,7 @@ static volatile int select_cb_ctr; /** Table to quickly map an lwIP error (err_t) to a socket error * by using -err as an index */ -#ifdef LWIP_ESP8266 -//TO_DO -//static const int err_to_errno_table[] ICACHE_RODATA_ATTR STORE_ATTR = { static const int err_to_errno_table[] = { -#else -static const int err_to_errno_table[] = { -#endif 0, /* ERR_OK 0 No error, everything OK. */ ENOMEM, /* ERR_MEM -1 Out of memory error. */ ENOBUFS, /* ERR_BUF -2 Buffer error. */ @@ -442,7 +431,7 @@ static const int err_to_errno_table[] = { EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */ EADDRINUSE, /* ERR_USE -8 Address in use. */ -#ifdef LWIP_ESP8266 +#if ESP_LWIP EALREADY, /* ERR_ALREADY -9 Already connected. */ EISCONN, /* ERR_ISCONN -10 Conn already established */ ECONNABORTED, /* ERR_ABRT -11 Connection aborted. */ @@ -583,7 +572,7 @@ alloc_socket(struct netconn *newconn, int accepted) int i; SYS_ARCH_DECL_PROTECT(lev); -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE bool found = false; int oldest = -1; @@ -639,16 +628,16 @@ alloc_socket(struct netconn *newconn, int accepted) if (!sockets[oldest].lock){ /* one time init and never free */ if (sys_mutex_new(&sockets[oldest].lock) != ERR_OK){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("new sock lock fail\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("new sock lock fail\n")); return -1; } } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("alloc_socket: alloc %d ok\n", oldest)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("alloc_socket: alloc %d ok\n", oldest)); return oldest + LWIP_SOCKET_OFFSET; } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("alloc_socket: failed\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("alloc_socket: failed\n")); #else @@ -693,12 +682,12 @@ free_socket(struct lwip_sock *sock, int is_tcp) void *lastdata; SYS_ARCH_DECL_PROTECT(lev); - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("free_sockset:free socket s=%p is_tcp=%d\n", sock, is_tcp)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("free_sockset:free socket s=%p is_tcp=%d\n", sock, is_tcp)); lastdata = sock->lastdata; sock->lastdata = NULL; sock->lastoffset = 0; sock->err = 0; -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE if (sock->conn){ netconn_free(sock->conn); } @@ -716,10 +705,10 @@ free_socket(struct lwip_sock *sock, int is_tcp) if (lastdata != NULL) { if (is_tcp) { - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("free_sockset:free lastdata pbuf=%p\n", lastdata)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("free_sockset:free lastdata pbuf=%p\n", lastdata)); pbuf_free((struct pbuf *)lastdata); } else { - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("free_sockset:free lastdata, netbuf=%p\n", lastdata)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("free_sockset:free lastdata, netbuf=%p\n", lastdata)); netbuf_delete((struct netbuf *)lastdata); } } @@ -872,19 +861,19 @@ lwip_close(int s) int is_tcp = 0; err_t err; - LWIP_DEBUGF(SOCKETS_DEBUG|THREAD_SAFE_DEBUG, ("lwip_close: (%d)\n", s)); + LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("lwip_close: (%d)\n", s)); sock = get_socket(s); if (!sock) { - LWIP_DEBUGF(SOCKETS_DEBUG|THREAD_SAFE_DEBUG, ("lwip_close: sock is null, return -1\n")); + LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("lwip_close: sock is null, return -1\n")); return -1; } if (sock->conn != NULL) { is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP; - LWIP_DEBUGF(SOCKETS_DEBUG|THREAD_SAFE_DEBUG, ("lwip_close: is_tcp=%d\n", is_tcp)); + LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("lwip_close: is_tcp=%d\n", is_tcp)); } else { - LWIP_DEBUGF(SOCKETS_DEBUG|THREAD_SAFE_DEBUG, ("conn is null\n")); + LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("conn is null\n")); LWIP_ASSERT("lwip_close: sock->lastdata == NULL", sock->lastdata == NULL); } @@ -895,12 +884,12 @@ lwip_close(int s) err = netconn_delete(sock->conn); if (err != ERR_OK) { - LWIP_DEBUGF(SOCKETS_DEBUG|THREAD_SAFE_DEBUG, ("netconn_delete fail, ret=%d\n", err)); + LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("netconn_delete fail, ret=%d\n", err)); sock_set_errno(sock, err_to_errno(err)); return -1; } -#if !LWIP_THREAD_SAFE +#if !ESP_THREAD_SAFE free_socket(sock, is_tcp); #endif @@ -1130,22 +1119,13 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, ip_addr_debug_print(SOCKETS_DEBUG, fromaddr); LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); -#ifdef LWIP_ESP8266 if (from && fromlen) -#else - -#if SOCKETS_DEBUG - if (from && fromlen) -#endif /* SOCKETS_DEBUG */ - -#endif { if (*fromlen > saddr.sa.sa_len) { *fromlen = saddr.sa.sa_len; } MEMCPY(from, &saddr, *fromlen); - -#ifdef LWIP_ESP8266 +#if ESP_LWIP } else { /*fix the code for setting the UDP PROTO's remote infomation by liuh at 2014.8.27*/ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP){ @@ -1437,7 +1417,7 @@ lwip_sendto(int s, const void *data, size_t size, int flags, SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port); } else { -#ifdef LWIP_ESP8266 +#if ESP_LWIP /*fix the code for getting the UDP proto's remote information by liuh at 2014.8.27*/ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP){ if(NETCONNTYPE_ISIPV6(netconn_type(sock->conn))) { @@ -1453,7 +1433,7 @@ lwip_sendto(int s, const void *data, size_t size, int flags, #endif remote_port = 0; ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr); -#ifdef LWIP_ESP8266 +#if ESP_LWIP } #endif @@ -1986,7 +1966,7 @@ again: int lwip_shutdown(int s, int how) { -#ifndef LWIP_ESP8266 +#if ! ESP_LWIP struct lwip_sock *sock; err_t err; @@ -3126,7 +3106,7 @@ static void lwip_socket_drop_registered_memberships(int s) } #endif /* LWIP_IGMP */ -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE int lwip_sendto_r(int s, const void *data, size_t size, int flags, diff --git a/components/lwip/api/tcpip.c b/components/lwip/api/tcpip.c index 9df3c38a1..0ad60721e 100755 --- a/components/lwip/api/tcpip.c +++ b/components/lwip/api/tcpip.c @@ -50,18 +50,13 @@ #include "lwip/pbuf.h" #include "netif/etharp.h" -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - #define TCPIP_MSG_VAR_REF(name) API_VAR_REF(name) #define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name) #define TCPIP_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name) #define TCPIP_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_TCPIP_MSG_API, name) /* global variables */ -#ifdef PERF +#if ESP_PERF uint32_t g_rx_post_mbox_fail_cnt = 0; #endif static tcpip_init_done_fn tcpip_init_done; @@ -144,13 +139,11 @@ tcpip_thread(void *arg) case TCPIP_MSG_INPKT: LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); -#ifdef LWIP_ESP8266 -//#if 0 +#if ESP_LWIP if(msg->msg.inp.p != NULL && msg->msg.inp.netif != NULL) { #endif msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif); -#ifdef LWIP_ESP8266 -//#if 0 +#if ESP_LWIP } #endif @@ -230,7 +223,7 @@ tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn) msg->msg.inp.netif = inp; msg->msg.inp.input_fn = input_fn; if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { -#ifdef PERF +#if ESP_PERF g_rx_post_mbox_fail_cnt ++; #endif memp_free(MEMP_TCPIP_MSG_INPKT, msg); @@ -503,7 +496,7 @@ tcpip_init(tcpip_init_done_fn initfunc, void *arg) #endif /* LWIP_TCPIP_CORE_LOCKING */ -#ifdef LWIP_ESP8266 +#if ESP_LWIP sys_thread_t xLwipTaskHandle = sys_thread_new(TCPIP_THREAD_NAME , tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); @@ -548,8 +541,7 @@ pbuf_free_callback(struct pbuf *p) * @return ERR_OK if callback could be enqueued, an err_t if not */ -#ifdef LWIP_ESP8266 -//#if 0 +#if ESP_LWIP static void mem_free_local(void *arg) { mem_free(arg); diff --git a/components/lwip/apps/dhcpserver.c b/components/lwip/apps/dhcpserver.c index 4cdef4123..22443e8cd 100644 --- a/components/lwip/apps/dhcpserver.c +++ b/components/lwip/apps/dhcpserver.c @@ -24,7 +24,7 @@ #include "apps/dhcpserver.h" -#ifdef LWIP_ESP8266 +#if ESP_DHCP #define BOOTP_BROADCAST 0x8000 @@ -71,10 +71,6 @@ #define DHCPS_STATE_IDLE 5 #define DHCPS_STATE_RELEASE 6 -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - //////////////////////////////////////////////////////////////////////////////////// static const u32_t magic_cookie = 0x63538263; diff --git a/components/lwip/core/dns.c b/components/lwip/core/dns.c index da8ac95b8..8f0ac5cc8 100755 --- a/components/lwip/core/dns.c +++ b/components/lwip/core/dns.c @@ -85,10 +85,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /** Random generator function to create random TXIDs and source ports for queries */ #ifndef DNS_RAND_TXID #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0) @@ -1091,7 +1087,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u8_t dns_err; /* This entry is now completed. */ -#ifndef LWIP_ESP8266 +#if ! ESP_DNS entry->state = DNS_STATE_DONE; #endif dns_err = hdr.flags2 & DNS_FLAG2_ERR_MASK; @@ -1105,7 +1101,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, if (((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) || (dns_err != 0) || (nquestions != 1)) { LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name)); /* call callback to indicate error, clean up memory and return */ -#ifndef LWIP_ESP8266 +#if ! ESP_DNS goto responseerr; } #else diff --git a/components/lwip/core/init.c b/components/lwip/core/init.c index f974aedaf..774e9a2be 100755 --- a/components/lwip/core/init.c +++ b/components/lwip/core/init.c @@ -61,7 +61,7 @@ #include "lwip/api.h" #include "netif/ppp/ppp_impl.h" -#ifndef PERF +#if ! ESP_PERF /* Compile-time sanity checks for configuration errors. * These can be done independently of LWIP_DEBUG, without penalty. */ @@ -313,7 +313,7 @@ #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif -#ifndef LWIP_ESP8266 +#if ! ESP_LWIP #if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)))) #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif diff --git a/components/lwip/core/ipv4/autoip.c b/components/lwip/core/ipv4/autoip.c index 391e8eeae..19b192836 100755 --- a/components/lwip/core/ipv4/autoip.c +++ b/components/lwip/core/ipv4/autoip.c @@ -76,11 +76,6 @@ #include #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /* 169.254.0.0 */ #define AUTOIP_NET 0xA9FE0000 /* 169.254.1.0 */ diff --git a/components/lwip/core/ipv4/dhcp.c b/components/lwip/core/ipv4/dhcp.c index 1f3758fa9..33d13fb32 100755 --- a/components/lwip/core/ipv4/dhcp.c +++ b/components/lwip/core/ipv4/dhcp.c @@ -82,10 +82,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using * LWIP_RAND() (this overrides DHCP_GLOBAL_XID) */ @@ -146,7 +142,7 @@ static u8_t dhcp_discover_select_options[] = { DHCP_OPTION_BROADCAST, DHCP_OPTION_DNS_SERVER -#ifdef LWIP_ESP8266 +#if ESP_DHCP /**add options for support more router by liuHan**/ , DHCP_OPTION_DOMAIN_NAME, DHCP_OPTION_NB_TINS, @@ -454,7 +450,7 @@ dhcp_fine_tmr(void) /* only act on DHCP configured interfaces */ if (netif->dhcp != NULL) { -//#ifdef LWIP_ESP8266 +//#if ESP_DHCP /*add DHCP retries processing by LiuHan*/ #if 0 if (DHCP_MAXRTX != 0) { @@ -997,7 +993,7 @@ dhcp_discover(struct netif *netif) dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); -#ifdef LWIP_ESP8266 +#if ESP_DHCP #if LWIP_NETIF_HOSTNAME dhcp_option_hostname(dhcp, netif); #endif /* LWIP_NETIF_HOSTNAME */ diff --git a/components/lwip/core/ipv4/icmp.c b/components/lwip/core/ipv4/icmp.c index c492ed75f..9202bb650 100755 --- a/components/lwip/core/ipv4/icmp.c +++ b/components/lwip/core/ipv4/icmp.c @@ -51,11 +51,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be * used to modify and send a response packet (and to 1 if this is not the case, * e.g. when link header is stripped of when receiving) */ diff --git a/components/lwip/core/ipv4/igmp.c b/components/lwip/core/ipv4/igmp.c index d75fe15fd..03f3ae384 100755 --- a/components/lwip/core/ipv4/igmp.c +++ b/components/lwip/core/ipv4/igmp.c @@ -92,11 +92,6 @@ Steve Reynolds #include "string.h" -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /* * IGMP constants */ diff --git a/components/lwip/core/ipv4/ip4.c b/components/lwip/core/ipv4/ip4.c index 5f1e77a5e..1d581d4d8 100755 --- a/components/lwip/core/ipv4/ip4.c +++ b/components/lwip/core/ipv4/ip4.c @@ -59,11 +59,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** Set this to 0 in the rare case of wanting to call an extra function to * generate the IP checksum (in contrast to calculating it on-the-fly). */ #ifndef LWIP_INLINE_IP_CHKSUM @@ -150,7 +145,7 @@ ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src) struct netif * ip4_route(const ip4_addr_t *dest) { -#ifdef LWIP_ESP8266 +#if ESP_LWIP struct netif *non_default_netif = NULL; #endif struct netif *netif; @@ -183,7 +178,7 @@ ip4_route(const ip4_addr_t *dest) } } -#ifdef LWIP_ESP8266 +#if ESP_LWIP if (non_default_netif && !ip4_addr_isbroadcast(dest, non_default_netif)){ return non_default_netif; } diff --git a/components/lwip/core/ipv4/ip4_addr.c b/components/lwip/core/ipv4/ip4_addr.c index 3053cf087..0501b84e5 100755 --- a/components/lwip/core/ipv4/ip4_addr.c +++ b/components/lwip/core/ipv4/ip4_addr.c @@ -45,17 +45,8 @@ /* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ -#ifdef LWIP_ESP8266 -//TO_DO -//const ip_addr_t ip_addr_any ICACHE_RODATA_ATTR STORE_ATTR = IPADDR4_INIT(IPADDR_ANY); -//const ip_addr_t ip_addr_broadcast ICACHE_RODATA_ATTR STORE_ATTR = IPADDR4_INIT(IPADDR_BROADCAST); const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY); const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST); -#else -const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY); -const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST); -#endif - /** * Determine if an address is a broadcast address on a network interface @@ -170,7 +161,7 @@ ip4addr_aton(const char *cp, ip4_addr_t *addr) u32_t parts[4]; u32_t *pp = parts; -#ifdef LWIP_ESP8266 +#if ESP_LWIP //#if 0 char ch; unsigned long cutoff; @@ -199,8 +190,7 @@ ip4addr_aton(const char *cp, ip4_addr_t *addr) } } -#ifdef LWIP_ESP8266 -//#if 0 +#if ESP_IP4_ATON cutoff =(unsigned long)0xffffffff / (unsigned long)base; cutlim =(unsigned long)0xffffffff % (unsigned long)base; for (;;) { diff --git a/components/lwip/core/ipv4/ip_frag.c b/components/lwip/core/ipv4/ip_frag.c index 1e6b053e6..a64743350 100755 --- a/components/lwip/core/ipv4/ip_frag.c +++ b/components/lwip/core/ipv4/ip_frag.c @@ -51,11 +51,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - #if IP_REASSEMBLY /** * The IP reassembly code currently has the following limitations: diff --git a/components/lwip/core/ipv6/icmp6.c b/components/lwip/core/ipv6/icmp6.c index 0a17da33e..013983bde 100755 --- a/components/lwip/core/ipv6/icmp6.c +++ b/components/lwip/core/ipv6/icmp6.c @@ -56,10 +56,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - #ifndef LWIP_ICMP6_DATASIZE #define LWIP_ICMP6_DATASIZE 8 #endif diff --git a/components/lwip/core/ipv6/ip6.c b/components/lwip/core/ipv6/ip6.c index 056d33355..380bc290c 100755 --- a/components/lwip/core/ipv6/ip6.c +++ b/components/lwip/core/ipv6/ip6.c @@ -59,10 +59,6 @@ #include "lwip/debug.h" #include "lwip/stats.h" -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /** * Finds the appropriate network interface for a given IPv6 address. It tries to select * a netif following a sequence of heuristics: diff --git a/components/lwip/core/ipv6/ip6_frag.c b/components/lwip/core/ipv6/ip6_frag.c index 0792c2e1b..c9e13cd20 100755 --- a/components/lwip/core/ipv6/ip6_frag.c +++ b/components/lwip/core/ipv6/ip6_frag.c @@ -52,11 +52,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - #if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ diff --git a/components/lwip/core/ipv6/mld6.c b/components/lwip/core/ipv6/mld6.c index 6a2d55c54..489c5063a 100755 --- a/components/lwip/core/ipv6/mld6.c +++ b/components/lwip/core/ipv6/mld6.c @@ -59,11 +59,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /* * MLD constants */ diff --git a/components/lwip/core/ipv6/nd6.c b/components/lwip/core/ipv6/nd6.c index 39e7bfed0..36f8f78c3 100755 --- a/components/lwip/core/ipv6/nd6.c +++ b/components/lwip/core/ipv6/nd6.c @@ -60,11 +60,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /* Router tables. */ struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS]; struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS]; diff --git a/components/lwip/core/mem.c b/components/lwip/core/mem.c index 42df6daeb..9ca9e3c4f 100755 --- a/components/lwip/core/mem.c +++ b/components/lwip/core/mem.c @@ -65,10 +65,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - #if MEM_USE_POOLS #if MEMP_MEM_MALLOC diff --git a/components/lwip/core/memp.c b/components/lwip/core/memp.c index a5169abc8..789553365 100755 --- a/components/lwip/core/memp.c +++ b/components/lwip/core/memp.c @@ -70,10 +70,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - #define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc) #include "lwip/priv/memp_std.h" diff --git a/components/lwip/core/netif.c b/components/lwip/core/netif.c index 33e030412..5c308a957 100755 --- a/components/lwip/core/netif.c +++ b/components/lwip/core/netif.c @@ -81,10 +81,6 @@ #define NETIF_LINK_CALLBACK(n) #endif /* LWIP_NETIF_LINK_CALLBACK */ -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - struct netif *netif_list; struct netif *netif_default; @@ -220,7 +216,7 @@ netif_add(struct netif *netif, /* netif not under DHCP control by default */ netif->dhcp = NULL; -#ifdef LWIP_ESP8266 +#if ESP_DHCP netif->dhcps_pcb = NULL; #endif @@ -233,8 +229,7 @@ netif_add(struct netif *netif, #endif /* LWIP_AUTOIP */ #if LWIP_IPV6_AUTOCONFIG -#ifdef LWIP_ESP8266 -//#if 0 +#if ESP_IPV6_AUTOCONFIG netif->ip6_autoconfig_enabled = 1; #else /* IPv6 address autoconfiguration not enabled by default */ @@ -973,7 +968,7 @@ netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit) } } -#ifdef LWIP_ESP8266 +#if ESP_LWIP ip6_addr_set( ip_2_ip6(&netif->link_local_addr), ip_2_ip6(&netif->ip6_addr[0]) ); #endif @@ -1028,7 +1023,7 @@ netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chos } -#ifdef LWIP_ESP8266 +#if ESP_LWIP void netif_create_ip4_linklocal_address(struct netif * netif) { diff --git a/components/lwip/core/pbuf.c b/components/lwip/core/pbuf.c index 044d765cd..29e24ef2b 100755 --- a/components/lwip/core/pbuf.c +++ b/components/lwip/core/pbuf.c @@ -78,13 +78,8 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - -#ifdef LWIP_ESP8266 +#if ESP_LWIP #include "esp_wifi_internal.h" -#define EP_OFFSET 0 #endif #define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) @@ -208,12 +203,7 @@ struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p, *q, *r; - -#ifdef LWIP_ESP8266 - u16_t offset = 0; -#else - u16_t offset; -#endif + u16_t offset = 0; s32_t rem_len; /* remaining length */ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); @@ -224,48 +214,16 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) /* add room for transport (often TCP) layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; -#ifdef LWIP_ESP8266 //TO_DO - offset += EP_OFFSET; -#endif - break; case PBUF_IP: /* add room for IP layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN; -#ifdef LWIP_ESP8266 //TO_DO - offset += EP_OFFSET; -#endif - break; case PBUF_LINK: /* add room for link layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN; -#ifdef LWIP_ESP8266 //TO_DO - /* - * 1. LINK_HLEN 14Byte will be remove in WLAN layer - * 2. IEEE80211_HDR_MAX_LEN needs 40 bytes. - * 3. encryption needs exra 4 bytes ahead of actual data payload, and require - * DAddr and SAddr to be 4-byte aligned. - * 4. TRANSPORT and IP are all 20, 4 bytes aligned, nice... - * 5. LCC add 6 bytes more, We don't consider WAPI yet... - * 6. define LWIP_MEM_ALIGN to be 4 Byte aligned, pbuf struct is 16B, Only thing may be - * matter is ether_hdr is not 4B aligned. - * - * So, we need extra (40 + 4 - 14) = 30 and it's happen to be 4-Byte aligned - * - * 1. lwip - * | empty 30B | eth_hdr (14B) | payload ...| - * total: 44B ahead payload - * 2. net80211 - * | max 80211 hdr, 32B | ccmp/tkip iv (8B) | sec rsv(4B) | payload ...| - * total: 40B ahead sec_rsv and 44B ahead payload - * - */ - offset += EP_OFFSET; //remove LINK hdr in wlan -#endif - break; case PBUF_RAW_TX: /* add room for encapsulating link layer headers (e.g. 802.11) */ @@ -274,10 +232,6 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) case PBUF_RAW: offset = 0; -#ifdef LWIP_ESP8266 //TO_DO - offset += EP_OFFSET; -#endif - break; default: LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); @@ -396,9 +350,10 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) /* set flags */ p->flags = 0; -#ifdef LWIP_ESP8266 +#if ESP_LWIP p->eb = NULL; #endif + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); return p; } @@ -764,7 +719,7 @@ pbuf_free(struct pbuf *p) /* is this a ROM or RAM referencing pbuf? */ } else if (type == PBUF_ROM || type == PBUF_REF) { -#ifdef LWIP_ESP8266 +#if ESP_LWIP if (type == PBUF_REF && p->eb != NULL ) esp_wifi_internal_free_rx_buffer(p->eb); #endif diff --git a/components/lwip/core/raw.c b/components/lwip/core/raw.c index 72a58d381..82ce4e3a7 100755 --- a/components/lwip/core/raw.c +++ b/components/lwip/core/raw.c @@ -54,10 +54,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /** The list of RAW PCBs */ static struct raw_pcb *raw_pcbs; diff --git a/components/lwip/core/stats.c b/components/lwip/core/stats.c index 77ac3c675..b47ab0b7f 100755 --- a/components/lwip/core/stats.c +++ b/components/lwip/core/stats.c @@ -47,10 +47,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - struct stats_ lwip_stats; #if defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY diff --git a/components/lwip/core/tcp.c b/components/lwip/core/tcp.c index e7ea56103..87ddf5f1a 100755 --- a/components/lwip/core/tcp.c +++ b/components/lwip/core/tcp.c @@ -57,10 +57,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - #ifndef TCP_LOCAL_PORT_RANGE_START /* From http://www.iana.org/assignments/port-numbers: "The Dynamic and/or Private Ports are those from 49152 through 65535" */ @@ -77,14 +73,7 @@ static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; #define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT #endif /* LWIP_TCP_KEEPALIVE */ -#ifdef LWIP_ESP8266 -//TO_DO -//char tcp_state_str[12]; -//const char tcp_state_str_rodata[][12] ICACHE_RODATA_ATTR STORE_ATTR = { const char * const tcp_state_str[] = { -#else -const char * const tcp_state_str[] = { -#endif "CLOSED", "LISTEN", "SYN_SENT", @@ -100,27 +89,14 @@ const char * const tcp_state_str[] = { /* last local TCP port */ -#ifdef LWIP_ESP8266 static s16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; -#else -static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; -#endif /* Incremented every coarse grained timer shot (typically every 500 ms). */ u32_t tcp_ticks; -#ifdef LWIP_ESP8266 -//TO_DO -//const u8_t tcp_backoff[13] ICACHE_RODATA_ATTR STORE_ATTR ={ 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; -//const u8_t tcp_persist_backoff[7] ICACHE_RODATA_ATTR STORE_ATTR = { 3, 6, 12, 24, 48, 96, 120 }; - -const u8_t tcp_backoff[13] = { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; -const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; -#else const u8_t tcp_backoff[13] = { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; /* Times per slowtmr hits */ const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; -#endif /* The TCP PCB lists. */ @@ -136,19 +112,9 @@ struct tcp_pcb *tcp_active_pcbs; struct tcp_pcb *tcp_tw_pcbs; /** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ -#ifdef LWIP_ESP8266 -//TO_DO -//struct tcp_pcb ** const tcp_pcb_lists[] ICACHE_RODATA_ATTR STORE_ATTR = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, - // &tcp_active_pcbs, &tcp_tw_pcbs}; struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs}; -#else -struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, - &tcp_active_pcbs, &tcp_tw_pcbs}; -#endif - - u8_t tcp_active_pcbs_changed; /** Timer counter to handle calling slow-timer from tcp_tmr() */ @@ -720,7 +686,7 @@ tcp_new_port(void) again: -#ifdef LWIP_ESP8266 +#if ESP_RANDOM_TCP_PORT tcp_port = system_get_time(); if (tcp_port < 0) tcp_port = LWIP_RAND() - tcp_port; @@ -915,13 +881,7 @@ tcp_slowtmr_start: /* If snd_wnd is zero, use persist timer to send 1 byte probes * instead of using the standard retransmission mechanism. */ -#ifdef LWIP_ESP8266 -//NEED TO DO - //u8_t backoff_cnt = system_get_data_of_array_8(tcp_persist_backoff, pcb->persist_backoff-1); u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1]; -#else - u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1]; -#endif if (pcb->persist_cnt < backoff_cnt) { pcb->persist_cnt++; @@ -949,15 +909,7 @@ tcp_slowtmr_start: /* Double retransmission time-out unless we are trying to * connect to somebody (i.e., we are in SYN_SENT). */ if (pcb->state != SYN_SENT) { - -#ifdef LWIP_ESP8266 -//TO_DO -// pcb->rto = ((pcb->sa >> 3) + pcb->sv) << system_get_data_of_array_8(tcp_backoff, pcb->nrtx); pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; -#else - pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; -#endif - } /* Reset the retransmission timer. */ @@ -1436,7 +1388,7 @@ tcp_kill_timewait(void) } } -#ifdef LWIP_ESP8266 +#if ESP_LWIP /** * Kills the oldest connection that is in FIN_WAIT_2 state. * Called from tcp_alloc() if no more connections are available. @@ -1502,7 +1454,7 @@ tcp_alloc(u8_t prio) struct tcp_pcb *pcb; u32_t iss; -#ifdef LWIP_ESP8266 +#if ESP_LWIP /*Kills the oldest connection that is in TIME_WAIT state.*/ u8_t time_wait_num = 0; for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { @@ -2015,14 +1967,7 @@ void tcp_netif_ipv4_addr_changed(const ip4_addr_t* old_addr, const ip4_addr_t* n const char* tcp_debug_state_str(enum tcp_state s) { -#ifdef LWIP_ESP8266 -//TO_DO - //system_get_string_from_flash(tcp_state_str_rodata[s], tcp_state_str, 12); - //return tcp_state_str; return tcp_state_str[s]; -#else - return tcp_state_str[s]; -#endif } #if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG diff --git a/components/lwip/core/tcp_in.c b/components/lwip/core/tcp_in.c index 90ebf1723..f3284233e 100755 --- a/components/lwip/core/tcp_in.c +++ b/components/lwip/core/tcp_in.c @@ -60,11 +60,6 @@ #include "lwip/nd6.h" #endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** Initial CWND calculation as defined RFC 2581 */ #define LWIP_TCP_CALC_INITIAL_CWND(mss) LWIP_MIN((4U * (mss)), LWIP_MAX((2U * (mss)), 4380U)); /** Initial slow start threshold value: we use the full window */ @@ -329,20 +324,6 @@ tcp_input(struct pbuf *p, struct netif *inp) if (pcb != NULL) { - -#ifdef LWIP_ESP8266 -//No Need Any more -/* - extern char RxNodeNum(void); - if(RxNodeNum() <= 2) - { -extern void pbuf_free_ooseq(void); - pbuf_free_ooseq(); - } -*/ -#endif - - /* The incoming segment belongs to a connection. */ #if TCP_INPUT_DEBUG tcp_debug_print_state(pcb->state); diff --git a/components/lwip/core/tcp_out.c b/components/lwip/core/tcp_out.c index 5f10befae..f189623f5 100755 --- a/components/lwip/core/tcp_out.c +++ b/components/lwip/core/tcp_out.c @@ -59,10 +59,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /* Define some copy-macros for checksum-on-copy so that the code looks nicer by preventing too many ifdef's. */ #if TCP_CHECKSUM_ON_COPY diff --git a/components/lwip/core/timers.c b/components/lwip/core/timers.c index 0a361474e..ef47b2e18 100755 --- a/components/lwip/core/timers.c +++ b/components/lwip/core/timers.c @@ -62,11 +62,6 @@ #include "lwip/sys.h" #include "lwip/pbuf.h" -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** The one and only timeout list */ static struct sys_timeo *next_timeout; #if NO_SYS @@ -162,7 +157,7 @@ dhcp_timer_coarse(void *arg) LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); dhcp_coarse_tmr(); -#ifdef LWIP_ESP8266 +#if ESP_DHCP extern void dhcps_coarse_tmr(void); dhcps_coarse_tmr(); #endif @@ -294,12 +289,6 @@ void sys_timeouts_init(void) #endif /* LWIP_ARP */ #if LWIP_DHCP -#ifdef LWIP_ESP8266 - // DHCP_MAXRTX = 0; -#endif - - - sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); #endif /* LWIP_DHCP */ @@ -346,7 +335,7 @@ void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) #else /* LWIP_DEBUG_TIMERNAMES */ -#ifdef LWIP_ESP8266 +#if ESP_LIGHT_SLEEP u32_t LwipTimOutLim = 0; // For light sleep. time out. limit is 3000ms #endif @@ -379,7 +368,7 @@ sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) timeout->h = handler; timeout->arg = arg; -#ifdef LWIP_ESP8266 +#if ESP_LIGHT_SLEEP if(msecs < LwipTimOutLim) msecs = LwipTimOutLim; #endif diff --git a/components/lwip/core/udp.c b/components/lwip/core/udp.c index e44ab7e73..37ae2c179 100755 --- a/components/lwip/core/udp.c +++ b/components/lwip/core/udp.c @@ -67,11 +67,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - #ifndef UDP_LOCAL_PORT_RANGE_START /* From http://www.iana.org/assignments/port-numbers: "The Dynamic and/or Private Ports are those from 49152 through 65535" */ diff --git a/components/lwip/include/lwip/lwip/api.h b/components/lwip/include/lwip/lwip/api.h index 985eb76d4..5b6a21ecf 100755 --- a/components/lwip/include/lwip/lwip/api.h +++ b/components/lwip/include/lwip/lwip/api.h @@ -185,10 +185,6 @@ struct netconn { /** sem that is used to synchronously execute functions in the core context */ sys_sem_t op_completed; -#ifdef LWIP_ESP8266 - sys_sem_t snd_op_completed; //only for snd semphore -#endif - #endif /** mbox where received packets are stored until they are fetched diff --git a/components/lwip/include/lwip/lwip/dhcp.h b/components/lwip/include/lwip/lwip/dhcp.h index 2d8926eca..76ce1543f 100755 --- a/components/lwip/include/lwip/lwip/dhcp.h +++ b/components/lwip/include/lwip/lwip/dhcp.h @@ -249,7 +249,7 @@ void dhcp_fine_tmr(void); #define DHCP_OPTION_NTP 42 #define DHCP_OPTION_END 255 -#ifdef LWIP_ESP8266 +#if ESP_LWIP /**add options for support more router by liuHan**/ #define DHCP_OPTION_DOMAIN_NAME 15 #define DHCP_OPTION_PRD 31 diff --git a/components/lwip/include/lwip/lwip/dns.h b/components/lwip/include/lwip/lwip/dns.h index 1ceed0d88..5ef12e56c 100755 --- a/components/lwip/include/lwip/lwip/dns.h +++ b/components/lwip/include/lwip/lwip/dns.h @@ -36,7 +36,7 @@ #include "lwip/opt.h" -#ifdef LWIP_ESP8266 +#if ESP_DNS #include "lwip/err.h" #endif diff --git a/components/lwip/include/lwip/lwip/err.h b/components/lwip/include/lwip/lwip/err.h index 26fb91db9..a766ee186 100755 --- a/components/lwip/include/lwip/lwip/err.h +++ b/components/lwip/include/lwip/lwip/err.h @@ -60,7 +60,7 @@ typedef s8_t err_t; #define ERR_USE -8 /* Address in use. */ -#ifdef LWIP_ESP8266 +#if ESP_LWIP #define ERR_ALREADY -9 /* Already connected. */ #define ERR_ISCONN -10 /* Conn already established.*/ #define ERR_IS_FATAL(e) ((e) < ERR_ISCONN) diff --git a/components/lwip/include/lwip/lwip/mem.h b/components/lwip/include/lwip/lwip/mem.h index ca76f6632..a90d07256 100755 --- a/components/lwip/include/lwip/lwip/mem.h +++ b/components/lwip/include/lwip/lwip/mem.h @@ -51,8 +51,6 @@ typedef size_t mem_size_t; * allow these defines to be overridden. */ -#ifndef MEMLEAK_DEBUG - #ifndef mem_free #define mem_free free #endif @@ -63,41 +61,6 @@ typedef size_t mem_size_t; #define mem_calloc calloc #endif -/* DYC_NEED_TO_DO_LATER -#ifndef mem_realloc -#define mem_realloc -#endif -#ifndef mem_zalloc -#define mem_zalloc -#endif -*/ - -#else -/* -#ifndef mem_free -#define mem_free(s) \ - do{\ - const char *file = mem_debug_file;\ - vPortFree(s, file, __LINE__);\ - }while(0) -#endif -#ifndef mem_malloc -#define mem_malloc(s) ({const char *file = mem_debug_file; pvPortMalloc(s, file, __LINE__);}) -#endif -#ifndef mem_calloc -#define mem_calloc(s) ({const char *file = mem_debug_file; pvPortCalloc(s, file, __LINE__);}) -#endif -#ifndef mem_realloc -#define mem_realloc(p, s) ({const char *file = mem_debug_file; pvPortRealloc(p, s, file, __LINE__);}) -#endif -#ifndef mem_zalloc -#define mem_zalloc(s) ({const char *file = mem_debug_file; pvPortZalloc(s, file, __LINE__);}) -#endif -*/ -#endif - - - /* Since there is no C library allocation function to shrink memory without moving it, define this to nothing. */ #ifndef mem_trim diff --git a/components/lwip/include/lwip/lwip/netif.h b/components/lwip/include/lwip/lwip/netif.h index 99066a5a1..666f77eb9 100755 --- a/components/lwip/include/lwip/lwip/netif.h +++ b/components/lwip/include/lwip/lwip/netif.h @@ -177,7 +177,7 @@ typedef err_t (*netif_mld_mac_filter_fn)(struct netif *netif, #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ -#ifdef LWIP_ESP8266 +#if ESP_DHCP /*add DHCP event processing by LiuHan*/ typedef void (*dhcp_event_fn)(void); #endif @@ -190,7 +190,7 @@ struct netif { /** pointer to next in linked list */ struct netif *next; -#ifdef LWIP_ESP8266 +#if ESP_LWIP //ip_addr_t is changed by marco IPV4, IPV6 ip_addr_t link_local_addr; #endif @@ -248,7 +248,7 @@ struct netif { /** the DHCP client state information for this netif */ struct dhcp *dhcp; -#ifdef LWIP_ESP8266 +#if ESP_LWIP struct udp_pcb *dhcps_pcb; dhcp_event_fn dhcp_event; #endif diff --git a/components/lwip/include/lwip/lwip/opt.h b/components/lwip/include/lwip/lwip/opt.h index c286712e0..51d340e00 100755 --- a/components/lwip/include/lwip/lwip/opt.h +++ b/components/lwip/include/lwip/lwip/opt.h @@ -3008,8 +3008,8 @@ #define LWIP_PERF 0 #endif -#ifndef THREAD_SAFE_DEBUG -#define THREAD_SAFE_DEBUG 0 +#ifndef ESP_THREAD_SAFE_DEBUG +#define ESP_THREAD_SAFE_DEBUG 0 #endif #endif /* LWIP_HDR_OPT_H */ diff --git a/components/lwip/include/lwip/lwip/pbuf.h b/components/lwip/include/lwip/lwip/pbuf.h index aaf5e294a..1834c4e04 100755 --- a/components/lwip/include/lwip/lwip/pbuf.h +++ b/components/lwip/include/lwip/lwip/pbuf.h @@ -137,7 +137,7 @@ struct pbuf { */ u16_t ref; -#ifdef LWIP_ESP8266 +#if ESP_LWIP void *eb; #endif }; diff --git a/components/lwip/include/lwip/lwip/priv/api_msg.h b/components/lwip/include/lwip/lwip/priv/api_msg.h index 329fa0de3..02d191a53 100755 --- a/components/lwip/include/lwip/lwip/priv/api_msg.h +++ b/components/lwip/include/lwip/lwip/priv/api_msg.h @@ -187,7 +187,7 @@ struct dns_api_msg { #endif /* LWIP_DNS */ #if LWIP_NETCONN_SEM_PER_THREAD -#ifdef LWIP_ESP8266 +#if ESP_THREAD_SAFE #define LWIP_NETCONN_THREAD_SEM_GET() sys_thread_sem_get() #define LWIP_NETCONN_THREAD_SEM_ALLOC() sys_thread_sem_init() #define LWIP_NETCONN_THREAD_SEM_FREE() sys_thread_sem_deinit() @@ -222,10 +222,6 @@ struct dns_api_msg { #define TCPIP_APIMSG(m,f,e) do { (m)->function = f; (e) = tcpip_apimsg(m); } while(0) #define TCPIP_APIMSG_ACK(m) do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0) -#ifdef LWIP_ESP8266 -#define TCPIP_APIMSG_ACK_SND(m) do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SND_SEM(m)); } while(0) -#endif - #endif /* LWIP_TCPIP_CORE_LOCKING */ void lwip_netconn_do_newconn (void *m); diff --git a/components/lwip/include/lwip/lwip/sockets.h b/components/lwip/include/lwip/lwip/sockets.h index fc2b7e2d1..d9622ea03 100755 --- a/components/lwip/include/lwip/lwip/sockets.h +++ b/components/lwip/include/lwip/lwip/sockets.h @@ -509,7 +509,7 @@ int lwip_fcntl(int s, int cmd, int val); #if LWIP_COMPAT_SOCKETS #if LWIP_COMPAT_SOCKETS != 2 -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE int lwip_accept_r(int s, struct sockaddr *addr, socklen_t *addrlen); int lwip_bind_r(int s, const struct sockaddr *name, socklen_t namelen); @@ -594,7 +594,7 @@ int lwip_fcntl_r(int s, int cmd, int val); #define fcntl(s,cmd,val) lwip_fcntl(s,cmd,val) #define ioctl(s,cmd,argp) lwip_ioctl(s,cmd,argp) #endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ -#endif /* LWIP_THREAD_SAFE */ +#endif /* ESP_THREAD_SAFE */ #endif /* LWIP_COMPAT_SOCKETS != 2 */ diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 5667e2d20..75f349280 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -37,7 +37,6 @@ #include "sdkconfig.h" /* Enable all Espressif-only options */ -#define LWIP_ESP8266 /* ----------------------------------------------- @@ -510,14 +509,42 @@ extern unsigned char misc_prof_get_tcp_snd_buf(void); */ #define TCPIP_DEBUG LWIP_DBG_OFF +/* Enable all Espressif-only options */ + +#define ESP_LWIP 1 +#define ESP_PER_SOC_TCP_WND 1 +#define ESP_THREAD_SAFE 1 +#define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF +#define ESP_DHCP 1 +#define ESP_DNS 1 +#define ESP_IPV6_AUTOCONFIG 1 +#define ESP_PERF 0 +#define ESP_RANDOM_TCP_PORT 1 +#define ESP_IP4_ATON 1 +#define ESP_LIGHT_SLEEP 1 + + +#if ESP_PER_SOC_TCP_WND +#define TCP_WND_DEFAULT (4*TCP_MSS) +#define TCP_SND_BUF_DEFAULT (2*TCP_MSS) +#define TCP_WND(pcb) (pcb->per_soc_tcp_wnd) +#define TCP_SND_BUF(pcb) (pcb->per_soc_tcp_snd_buf) +#else +#if ESP_PERF +extern unsigned char misc_prof_get_tcpw(void); +extern unsigned char misc_prof_get_tcp_snd_buf(void); +#define TCP_WND(pcb) (misc_prof_get_tcpw()*TCP_MSS) +#define TCP_SND_BUF(pcb) (misc_prof_get_tcp_snd_buf()*TCP_MSS) +#endif +#endif + /** * DHCP_DEBUG: Enable debugging in dhcp.c. */ #define DHCP_DEBUG LWIP_DBG_OFF #define LWIP_DEBUG 0 #define TCP_DEBUG LWIP_DBG_OFF -#define THREAD_SAFE_DEBUG LWIP_DBG_OFF -#define LWIP_THREAD_SAFE 1 +#define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF #define CHECKSUM_CHECK_UDP 0 #define CHECKSUM_CHECK_IP 0 diff --git a/components/lwip/netif/etharp.c b/components/lwip/netif/etharp.c index 5891c5cfd..776e949f7 100755 --- a/components/lwip/netif/etharp.c +++ b/components/lwip/netif/etharp.c @@ -55,10 +55,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - #if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */ /** Re-request a used ARP entry 1 minute before it would expire to prevent diff --git a/components/lwip/port/freertos/sys_arch.c b/components/lwip/port/freertos/sys_arch.c index 74a4a996a..15ba3011d 100755 --- a/components/lwip/port/freertos/sys_arch.c +++ b/components/lwip/port/freertos/sys_arch.c @@ -56,7 +56,7 @@ sys_mutex_new(sys_mutex_t *pxMutex) xReturn = ERR_OK; } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mutex_new: m=%p\n", *pxMutex)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_new: m=%p\n", *pxMutex)); return xReturn; } @@ -89,7 +89,7 @@ sys_mutex_unlock(sys_mutex_t *pxMutex) void sys_mutex_free(sys_mutex_t *pxMutex) { - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mutex_free: m=%p\n", *pxMutex)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_free: m=%p\n", *pxMutex)); vQueueDelete(*pxMutex); } #endif @@ -192,20 +192,20 @@ sys_mbox_new(sys_mbox_t *mbox, int size) { *mbox = malloc(sizeof(struct sys_mbox_s)); if (*mbox == NULL){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("fail to new *mbox\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new *mbox\n")); return ERR_MEM; } (*mbox)->os_mbox = xQueueCreate(size, sizeof(void *)); if ((*mbox)->os_mbox == NULL) { - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("fail to new *mbox->os_mbox\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new *mbox->os_mbox\n")); free(*mbox); return ERR_MEM; } if (sys_mutex_new(&((*mbox)->lock)) != ERR_OK){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("fail to new *mbox->lock\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new *mbox->lock\n")); vQueueDelete((*mbox)->os_mbox); free(*mbox); return ERR_MEM; @@ -213,7 +213,7 @@ sys_mbox_new(sys_mbox_t *mbox, int size) (*mbox)->alive = true; - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("new *mbox ok mbox=%p os_mbox=%p mbox_lock=%p\n", *mbox, (*mbox)->os_mbox, (*mbox)->lock)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("new *mbox ok mbox=%p os_mbox=%p mbox_lock=%p\n", *mbox, (*mbox)->os_mbox, (*mbox)->lock)); return ERR_OK; } @@ -234,7 +234,7 @@ sys_mbox_trypost(sys_mbox_t *mbox, void *msg) if (xQueueSend((*mbox)->os_mbox, &msg, (portTickType)0) == pdPASS) { xReturn = ERR_OK; } else { - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("trypost mbox=%p fail\n", (*mbox)->os_mbox)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("trypost mbox=%p fail\n", (*mbox)->os_mbox)); xReturn = ERR_MEM; } @@ -271,7 +271,7 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) if (*mbox == NULL){ *msg = NULL; - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch: null mbox\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch: null mbox\n")); return -1; } @@ -294,14 +294,14 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) } else { // block forever for a message. while (1){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch: fetch mbox=%p os_mbox=%p lock=%p\n", mbox, (*mbox)->os_mbox, (*mbox)->lock)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch: fetch mbox=%p os_mbox=%p lock=%p\n", mbox, (*mbox)->os_mbox, (*mbox)->lock)); if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), portMAX_DELAY)){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch:mbox rx msg=%p\n", (*msg))); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch:mbox rx msg=%p\n", (*msg))); break; } if ((*mbox)->alive == false){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch:mbox not alive\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch:mbox not alive\n")); *msg = NULL; break; } @@ -356,24 +356,24 @@ sys_mbox_free(sys_mbox_t *mbox) uint16_t count = 0; bool post_null = true; - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free: set alive false\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free: set alive false\n")); (*mbox)->alive = false; while ( count++ < MAX_POLL_CNT ){ //ESP32_WORKAROUND - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free:try lock=%d\n", count)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free:try lock=%d\n", count)); if (!sys_mutex_trylock( &(*mbox)->lock )){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free:get lock ok %d\n", count)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free:get lock ok %d\n", count)); sys_mutex_unlock( &(*mbox)->lock ); break; } if (post_null){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free: post null to mbox\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free: post null to mbox\n")); if (sys_mbox_trypost( mbox, NULL) != ERR_OK){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free: post null mbox fail\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free: post null mbox fail\n")); } else { post_null = false; - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free: post null mbox ok\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free: post null mbox ok\n")); } } @@ -383,7 +383,7 @@ sys_mbox_free(sys_mbox_t *mbox) sys_delay_ms(PER_POLL_DELAY); } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free:free mbox\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free:free mbox\n")); if (uxQueueMessagesWaiting((*mbox)->os_mbox)) { xQueueReset((*mbox)->os_mbox); @@ -491,7 +491,7 @@ sys_sem_t* sys_thread_sem_get(void) if (!sem){ sem = sys_thread_sem_init(); } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem_get s=%p\n", sem)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem_get s=%p\n", sem)); return sem; } @@ -500,12 +500,12 @@ static void sys_thread_tls_free(int index, void* data) sys_sem_t *sem = (sys_sem_t*)(data); if (sem && *sem){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem del, i=%d sem=%p\n", index, *sem)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem del, i=%d sem=%p\n", index, *sem)); vSemaphoreDelete(*sem); } if (sem){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem pointer del, i=%d sem_p=%p\n", index, sem)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem pointer del, i=%d sem_p=%p\n", index, sem)); free(sem); } } @@ -526,7 +526,7 @@ sys_sem_t* sys_thread_sem_init(void) return 0; } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem init sem_p=%p sem=%p cb=%p\n", sem, *sem, sys_thread_tls_free)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem init sem_p=%p sem=%p cb=%p\n", sem, *sem, sys_thread_tls_free)); vTaskSetThreadLocalStoragePointerAndDelCallback(xTaskGetCurrentTaskHandle(), SYS_TLS_INDEX, sem, (TlsDeleteCallbackFunction_t)sys_thread_tls_free); return sem; diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index 0b4fe70ba..548bb7f97 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -56,16 +56,8 @@ #define IFNAME0 'e' #define IFNAME1 'n' -#ifdef LWIP_ESP8266 -//TO_DO -//char *hostname; -//bool default_hostname = 1; - static char hostname[16]; -#else -static char hostname[16]; -#endif -#ifdef PERF +#if ESP_PERF uint32_t g_rx_alloc_pbuf_fail_cnt = 0; #endif @@ -95,7 +87,7 @@ low_level_init(struct netif *netif) /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; -#ifdef LWIP_ESP8266 +#if ESP_LWIP #if LWIP_IGMP @@ -133,7 +125,7 @@ low_level_output(struct netif *netif, struct pbuf *p) return ERR_IF; } -#ifdef LWIP_ESP8266 +#if ESP_LWIP q = p; u16_t pbuf_x_len = 0; pbuf_x_len = q->len; @@ -172,7 +164,7 @@ low_level_output(struct netif *netif, struct pbuf *p) * @param netif the lwip network interface structure for this ethernetif */ void -#ifdef LWIP_ESP8266 +#if ESP_LWIP wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) #else wlanif_input(struct netif *netif, void *buffer, uint16 len) @@ -180,17 +172,17 @@ wlanif_input(struct netif *netif, void *buffer, uint16 len) { struct pbuf *p; -#ifdef LWIP_ESP8266 +#if ESP_LWIP if(buffer== NULL) goto _exit; if(netif == NULL) goto _exit; #endif -#ifdef LWIP_ESP8266 +#if ESP_LWIP p = pbuf_alloc(PBUF_RAW, len, PBUF_REF); if (p == NULL){ -#ifdef PERF +#if ESP_PERF g_rx_alloc_pbuf_fail_cnt++; #endif return; @@ -236,7 +228,7 @@ wlanif_init(struct netif *netif) #if LWIP_NETIF_HOSTNAME /* Initialize interface hostname */ -#ifdef LWIP_ESP8266 +#if ESP_LWIP //TO_DO /* if ((struct netif *)wifi_get_netif(STATION_IF) == netif) { From 3371083c1611fb7a5b0f389739599146af51047c Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 27 Oct 2016 16:07:47 +0800 Subject: [PATCH 074/110] Add checks for tasks woken up on other CPUs where needed, make xYieldPending and xPendingReadyList per-processor, add configurable ISR stack size to Kconfig, in general fix the entire wake-up-task-on-other-cpu-by-interrupt implementation --- components/esp32/crosscore_int.c | 90 +++++++++---------- components/esp32/include/esp_crosscore_int.h | 27 ++++++ components/freertos/Kconfig | 9 ++ .../include/freertos/FreeRTOSConfig.h | 2 +- components/freertos/tasks.c | 70 ++++++++------- 5 files changed, 117 insertions(+), 81 deletions(-) diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index a98b13583..56fe6fe9a 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -44,64 +44,60 @@ ToDo: There is a small chance the CPU already has yielded when this ISR is servi the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that. */ static void esp_crosscore_isr(void *arg) { - volatile uint32_t myReasonVal; + volatile uint32_t myReasonVal; #if 0 - //A pointer to the correct reason array item is passed to this ISR. - volatile uint32_t *myReason=arg; + //A pointer to the correct reason array item is passed to this ISR. + volatile uint32_t *myReason=arg; #else - //Does not work yet, the interrupt code needs work to understand two separate interrupt and argument - //tables... - volatile uint32_t *myReason=&reason[xPortGetCoreID()]; + //Does not work yet, the interrupt code needs work to understand two separate interrupt and argument + //tables... + volatile uint32_t *myReason=&reason[xPortGetCoreID()]; #endif - //Clear the interrupt first. - if (xPortGetCoreID()==0) { - WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); - } else { - WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0); - } - //Grab the reason and clear it. - portENTER_CRITICAL(&reasonSpinlock); - myReasonVal=*myReason; - *myReason=0; - portEXIT_CRITICAL(&reasonSpinlock); + //Clear the interrupt first. + if (xPortGetCoreID()==0) { + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); + } else { + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0); + } + //Grab the reason and clear it. + portENTER_CRITICAL(&reasonSpinlock); + myReasonVal=*myReason; + *myReason=0; + portEXIT_CRITICAL(&reasonSpinlock); - //Check what we need to do. - if (myReasonVal&REASON_YIELD) { - portYIELD_FROM_ISR(); - } - - ets_printf("recv yield\n"); + //Check what we need to do. + if (myReasonVal&REASON_YIELD) { + portYIELD_FROM_ISR(); + } } //Initialize the crosscore interrupt on this core. Call this once //on each active core. void esp_crosscore_int_init() { - portENTER_CRITICAL(&reasonSpinlock); - ets_printf("init cpu %d\n", xPortGetCoreID()); - reason[xPortGetCoreID()]=0; - portEXIT_CRITICAL(&reasonSpinlock); - ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); - if (xPortGetCoreID()==0) { - intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR0_SOURCE, ETS_FROM_CPU_INUM); - } else { - intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM); - } - xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); - ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); + portENTER_CRITICAL(&reasonSpinlock); + reason[xPortGetCoreID()]=0; + portEXIT_CRITICAL(&reasonSpinlock); + ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); + if (xPortGetCoreID()==0) { + intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR0_SOURCE, ETS_FROM_CPU_INUM); + } else { + intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM); + } + xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); + ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); } void esp_crosscore_int_send_yield(int coreId) { - ets_printf("send yield\n"); - assert(coreIduxPriority < uxPriority ) + No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. + */ + if( tskCAN_RUN_HERE( xCoreID ) && pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); } + else if( xCoreID != xPortGetCoreID() ) { + taskYIELD_OTHER_CORE(xCoreID, uxPriority); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1452,7 +1450,7 @@ BaseType_t i; if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ) != pdFALSE ) { /* Has the task already been resumed from within an ISR? */ - if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) + if( listIS_CONTAINED_WITHIN( &xPendingReadyList[ xPortGetCoreID() ], &( pxTCB->xEventListItem ) ) == pdFALSE ) { /* Is it in the suspended list because it is in the Suspended state, or because is is blocked with no timeout? */ @@ -1544,7 +1542,6 @@ BaseType_t i; #if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) -/* ToDo: Make this multicore-compatible. */ BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) { BaseType_t xYieldRequired = pdFALSE; @@ -1567,14 +1564,14 @@ BaseType_t i; ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); prvAddTaskToReadyList( pxTCB ); - if ( pxTCB->xCoreID == xPortGetCoreID() ) - { - taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority); - } - else if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE( pxTCB->xCoreID ) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { xYieldRequired = pdTRUE; } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1585,7 +1582,7 @@ BaseType_t i; /* The delayed or ready lists cannot be accessed so the task is held in the pending ready list until the scheduler is unsuspended. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxTCB->xEventListItem ) ); } } else @@ -1770,9 +1767,9 @@ BaseType_t xAlreadyYielded = pdFALSE; { /* Move any readied tasks from the pending list into the appropriate ready list. */ - while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) + while( listLIST_IS_EMPTY( &xPendingReadyList[ xPortGetCoreID() ] ) == pdFALSE ) { - pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); + pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList[ xPortGetCoreID() ] ) ); ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); prvAddTaskToReadyList( pxTCB ); @@ -1785,10 +1782,6 @@ BaseType_t xAlreadyYielded = pdFALSE; xYieldPending[xPortGetCoreID()] = pdTRUE; break; } - else if ( pxTCB->xCoreID != xPortGetCoreID() ) - { - taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); - } else { mtCOVERAGE_TEST_MARKER(); @@ -2658,15 +2651,20 @@ BaseType_t xReturn; /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed. */ taskENTER_CRITICAL(&xTaskQueueMutex); - vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxUnblockedTCB->xEventListItem ) ); taskEXIT_CRITICAL(&xTaskQueueMutex); } if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - /* We can schedule the awoken task on this CPU. */ - xYieldPending[xPortGetCoreID()] = pdTRUE; + /* Return true if the task removed from the event list has a higher + priority than the calling task. This allows the calling task to know if + it should force a context switch now. */ xReturn = pdTRUE; + + /* Mark that a yield is pending in case the user is not using the + "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } else if ( pxUnblockedTCB->xCoreID != xPortGetCoreID() ) { @@ -2724,9 +2722,15 @@ BaseType_t xReturn; if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - /* We can schedule the awoken task on this CPU. */ - xYieldPending[xPortGetCoreID()] = pdTRUE; + /* Return true if the task removed from the event list has + a higher priority than the calling task. This allows + the calling task to know if it should force a context + switch now. */ xReturn = pdTRUE; + + /* Mark that a yield is pending in case the user is not using the + "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } else if ( pxUnblockedTCB->xCoreID != xPortGetCoreID() ) { @@ -2967,7 +2971,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) eSleepModeStatus eReturn = eStandardSleep; taskENTER_CRITICAL(&xTaskQueueMutex); - if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 ) + if( listCURRENT_LIST_LENGTH( &xPendingReadyList[ xPortGetCoreID() ] ) != 0 ) { /* A task was made ready while the scheduler was suspended. */ eReturn = eAbortSleep; @@ -3210,7 +3214,7 @@ UBaseType_t uxPriority; vListInitialise( &xDelayedTaskList1 ); vListInitialise( &xDelayedTaskList2 ); - vListInitialise( &xPendingReadyList ); + vListInitialise( &xPendingReadyList[ xPortGetCoreID() ] ); #if ( INCLUDE_vTaskDelete == 1 ) { @@ -4576,7 +4580,7 @@ TickType_t uxReturn; { /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxTCB->xEventListItem ) ); } if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) @@ -4644,7 +4648,7 @@ TickType_t uxReturn; { /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxTCB->xEventListItem ) ); } if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) From 68f39c1ed90bc948ad46af74869d26f5bef9806c Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 27 Oct 2016 16:50:28 +0800 Subject: [PATCH 075/110] Only init crosscore when FreeRTOS runs in multicore mode, add warnings that cross_int calls are private. --- components/esp32/cpu_start.c | 2 ++ components/esp32/crosscore_int.c | 4 ++-- components/esp32/include/esp_crosscore_int.h | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 0c94f39e3..bfc751d45 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -147,7 +147,9 @@ void start_cpu0_default(void) uart_div_modify(0, (APB_CLK_FREQ << 4) / 115200); ets_setup_syscalls(); do_global_ctors(); +#if !CONFIG_FREERTOS_UNICORE esp_crosscore_int_init(); +#endif esp_ipc_init(); spi_flash_init(); xTaskCreatePinnedToCore(&main_task, "main", diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 56fe6fe9a..78287d405 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -49,8 +49,8 @@ static void esp_crosscore_isr(void *arg) { //A pointer to the correct reason array item is passed to this ISR. volatile uint32_t *myReason=arg; #else - //Does not work yet, the interrupt code needs work to understand two separate interrupt and argument - //tables... + //The previous line does not work yet, the interrupt code needs work to understand two separate interrupt and argument + //tables... this is a valid but slightly less optimal replacement. volatile uint32_t *myReason=&reason[xPortGetCoreID()]; #endif //Clear the interrupt first. diff --git a/components/esp32/include/esp_crosscore_int.h b/components/esp32/include/esp_crosscore_int.h index ca5f3901e..0e4b2b838 100644 --- a/components/esp32/include/esp_crosscore_int.h +++ b/components/esp32/include/esp_crosscore_int.h @@ -19,6 +19,10 @@ * Initialize the crosscore interrupt system for this CPU. * This needs to be called once on every CPU that is used * by FreeRTOS. + * + * If multicore FreeRTOS support is enabled, this will be + * called automatically by the startup code and should not + * be called manually. */ void esp_crosscore_int_init(); @@ -28,6 +32,9 @@ void esp_crosscore_int_init(); * currently running task in favour of a higher-priority task * that presumably just woke up. * + * This is used internally by FreeRTOS in multicore mode + * and should not be called by the user. + * * @param coreID Core that should do the yielding */ void esp_crosscore_int_send_yield(int coreId); From 401f6e47134417708a4654cac150f15e0c70819a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 25 Oct 2016 21:02:39 +0800 Subject: [PATCH 076/110] vfs: initial version of virtual filesystem API --- components/vfs/README.rst | 2 + components/vfs/component.mk | 1 + components/vfs/include/esp_vfs.h | 157 +++++++++++++++++ components/vfs/vfs.c | 285 +++++++++++++++++++++++++++++++ 4 files changed, 445 insertions(+) create mode 100644 components/vfs/README.rst create mode 100755 components/vfs/component.mk create mode 100644 components/vfs/include/esp_vfs.h create mode 100644 components/vfs/vfs.c diff --git a/components/vfs/README.rst b/components/vfs/README.rst new file mode 100644 index 000000000..b32e22ccf --- /dev/null +++ b/components/vfs/README.rst @@ -0,0 +1,2 @@ +Virtual filesystem (VFS) is a driver which can perform operations on file-like objects. This can be a real filesystem (FAT, SPIFFS, etc.), or a device driver which exposes file-like interface. + \ No newline at end of file diff --git a/components/vfs/component.mk b/components/vfs/component.mk new file mode 100755 index 000000000..fccf88db8 --- /dev/null +++ b/components/vfs/component.mk @@ -0,0 +1 @@ +include $(IDF_PATH)/make/component_common.mk diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h new file mode 100644 index 000000000..0d1c3d147 --- /dev/null +++ b/components/vfs/include/esp_vfs.h @@ -0,0 +1,157 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_VFS_H__ +#define __ESP_VFS_H__ + +#include +#include +#include "esp_err.h" +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Maximum length of path prefix (not including zero terminator) + */ +#define ESP_VFS_PATH_MAX 15 + +/** + * Default value of flags member in esp_vfs_t structure. + */ +#define ESP_VFS_FLAG_DEFAULT 0 + +/** + * Flag which indicates that FS needs extra context pointer in syscalls. + */ +#define ESP_VFS_FLAG_CONTEXT_PTR 1 + +/** + * @brief VFS definition structure + * + * This structure should be filled with pointers to corresponding + * FS driver functions. + * + * If the FS implementation has an option to use certain offset for + * all file descriptors, this value should be passed into fd_offset + * field. Otherwise VFS component will translate all FDs to start + * at zero offset. + * + * Some FS implementations expect some state (e.g. pointer to some structure) + * to be passed in as a first argument. For these implementations, + * populate the members of this structure which have _p suffix, set + * flags member to ESP_VFS_FLAG_CONTEXT_PTR and provide the context pointer + * to esp_vfs_register function. + * If the implementation doesn't use this extra argument, populate the + * members without _p suffix and set flags memeber to ESP_VFS_FLAG_DEFAULT. + * + * If the FS driver doesn't provide some of the functions, set corresponding + * members to NULL. + */ +typedef struct +{ + int fd_offset; + int flags; + union { + size_t (*write_p)(void* p, int fd, const void * data, size_t size); + size_t (*write)(int fd, const void * data, size_t size); + }; + union { + _off_t (*lseek_p)(void* p, int fd, _off_t size, int mode); + _off_t (*lseek)(int fd, _off_t size, int mode); + }; + union { + ssize_t (*read_p)(void* ctx, int fd, void * dst, size_t size); + ssize_t (*read)(int fd, void * dst, size_t size); + }; + union { + int (*open_p)(void* ctx, const char * path, int flags, int mode); + int (*open)(const char * path, int flags, int mode); + }; + union { + int (*close_p)(void* ctx, int fd); + int (*close)(int fd); + }; + union { + int (*fstat_p)(void* ctx, int fd, struct stat * st); + int (*fstat)(int fd, struct stat * st); + }; + union { + int (*stat_p)(void* ctx, const char * path, struct stat * st); + int (*stat)(const char * path, struct stat * st); + }; + union { + int (*link_p)(void* ctx, const char* n1, const char* n2); + int (*link)(const char* n1, const char* n2); + }; + union { + int (*unlink_p)(void* ctx, const char *path); + int (*unlink)(const char *path); + }; + union { + int (*rename_p)(void* ctx, const char *src, const char *dst); + int (*rename)(const char *src, const char *dst); + }; +} esp_vfs_t; + + +/** + * Register a virtual filesystem for given path prefix. + * + * @param base_path file path prefix associated with the filesystem. + * Must be a zero-terminated C string, up to ESP_VFS_PATH_MAX + * characters long, and at least 2 characters long. + * Name must start with a "/" and must not end with "/". + * For example, "/data" or "/dev/spi" are valid. + * These VFSes would then be called to handle file paths such as + * "/data/myfile.txt" or "/dev/spi/0". + * @param vfs Pointer to esp_vfs_t, a structure which maps syscalls to + * the filesystem driver functions. VFS component doesn't + * assume ownership of this pointer. + * @param ctx If vfs->flags has ESP_VFS_FLAG_CONTEXT_PTR set, a pointer + * which should be passed to VFS functions. Otherwise, NULL. + * + * @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are + * registered. + */ +esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx); + + +/** + * These functions are to be used in newlib syscall table. They will be called by + * newlib when it needs to use any of the syscalls. + */ + +ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size); +_off_t esp_vfs_lseek(struct _reent *r, int fd, _off_t size, int mode); +ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size); +int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode); +int esp_vfs_close(struct _reent *r, int fd); +int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st); +int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st); +int esp_vfs_link(struct _reent *r, const char* n1, const char* n2); +int esp_vfs_unlink(struct _reent *r, const char *path); +int esp_vfs_rename(struct _reent *r, const char *src, const char *dst); + + + +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif //__ESP_VFS_H__ diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c new file mode 100644 index 000000000..afc7149ba --- /dev/null +++ b/components/vfs/vfs.c @@ -0,0 +1,285 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include "esp_vfs.h" +#include "esp_log.h" + +/* + * File descriptors visible by the applications are composed of two parts. + * Lower CONFIG_MAX_FD_BITS bits are used for the actual file descriptor. + * Next (16 - CONFIG_MAX_FD_BITS - 1) bits are used to identify the VFS this + * descriptor corresponds to. + * Highest bit is zero. + * We can only use 16 bits because newlib stores file descriptor as short int. + */ + +#ifndef CONFIG_MAX_FD_BITS +#define CONFIG_MAX_FD_BITS 12 +#endif + +#define MAX_VFS_ID_BITS (16 - CONFIG_MAX_FD_BITS - 1) +// mask of actual file descriptor (e.g. 0x00000fff) +#define VFS_FD_MASK ((1 << CONFIG_MAX_FD_BITS) - 1) +// max number of VFS entries +#define VFS_MAX_COUNT ((1 << MAX_VFS_ID_BITS) - 1) +// mask of VFS id (e.g. 0x00007000) +#define VFS_INDEX_MASK (VFS_MAX_COUNT << CONFIG_MAX_FD_BITS) +#define VFS_INDEX_S CONFIG_MAX_FD_BITS + +typedef struct vfs_entry_ { + esp_vfs_t vfs; // contains pointers to VFS functions + char path_prefix[ESP_VFS_PATH_MAX]; // path prefix mapped to this VFS + size_t path_prefix_len; // micro-optimization to avoid doing extra strlen + void* ctx; // optional pointer which can be passed to VFS + int offset; // index of this structure in s_vfs array +} vfs_entry_t; + +static vfs_entry_t* s_vfs[VFS_MAX_COUNT] = { 0 }; +static size_t s_vfs_count = 0; + +esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx) +{ + if (s_vfs_count >= VFS_MAX_COUNT) { + return ESP_ERR_NO_MEM; + } + size_t len = strlen(base_path); + if (len < 2 || len > ESP_VFS_PATH_MAX) { + return ESP_ERR_INVALID_ARG; + } + if (base_path[0] != '/' || base_path[len - 1] == '/') { + return ESP_ERR_INVALID_ARG; + } + vfs_entry_t *entry = (vfs_entry_t*) malloc(sizeof(vfs_entry_t)); + if (entry == NULL) { + return ESP_ERR_NO_MEM; + } + strcpy(entry->path_prefix, base_path); // we have already verified argument length + memcpy(&entry->vfs, vfs, sizeof(esp_vfs_t)); + entry->path_prefix_len = len; + entry->ctx = ctx; + entry->offset = s_vfs_count; + s_vfs[s_vfs_count] = entry; + ++s_vfs_count; + return ESP_OK; +} + +static const vfs_entry_t* get_vfs_for_fd(int fd) +{ + int index = ((fd & VFS_INDEX_MASK) >> VFS_INDEX_S); + if (index >= s_vfs_count) { + return NULL; + } + return s_vfs[index]; +} + +static int translate_fd(const vfs_entry_t* vfs, int fd) +{ + return (fd & VFS_FD_MASK) + vfs->vfs.fd_offset; +} + +static const char* translate_path(const vfs_entry_t* vfs, const char* src_path) +{ + assert(strncmp(src_path, vfs->path_prefix, vfs->path_prefix_len) == 0); + return src_path + vfs->path_prefix_len; +} + +static const vfs_entry_t* get_vfs_for_path(const char* path) +{ + size_t len = strlen(path); + for (size_t i = 0; i < s_vfs_count; ++i) { + const vfs_entry_t* vfs = s_vfs[i]; + if (len < vfs->path_prefix_len + 1) { // +1 is for the trailing slash after base path + continue; + } + if (memcmp(path, vfs->path_prefix, vfs->path_prefix_len) != 0) { // match prefix + continue; + } + if (path[vfs->path_prefix_len] != '/') { // don't match "/data" prefix for "/data1/foo.txt" + continue; + } + return vfs; + } + return NULL; +} + +/* + * Using huge multi-line macros is never nice, but in this case + * the only alternative is to repeat this chunk of code (with different function names) + * for each syscall being implemented. Given that this define is contained within a single + * file, this looks like a good tradeoff. + * + * First we check if syscall is implemented by VFS (corresponding member is not NULL), + * then call the right flavor of the method (e.g. open or open_p) depending on + * ESP_VFS_FLAG_CONTEXT_PTR flag. If ESP_VFS_FLAG_CONTEXT_PTR is set, context is passed + * in as first argument and _p variant is used for the call. + * It is enough to check just one of them for NULL, as both variants are part of a union. + */ +#define CHECK_AND_CALL(ret, r, pvfs, func, ...) \ + if (pvfs->vfs.func == NULL) { \ + __errno_r(r) = ENOSYS; \ + return -1; \ + } \ + if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ + ret = (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \ + } else { \ + ret = (*pvfs->vfs.func)(__VA_ARGS__);\ + } + + +int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode) +{ + const vfs_entry_t* vfs = get_vfs_for_path(path); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, path); + int ret; + CHECK_AND_CALL(ret, r, vfs, open, path_within_vfs, flags, mode); + return ret - vfs->vfs.fd_offset + (vfs->offset << VFS_INDEX_S); +} + +ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return -1; + } + int local_fd = translate_fd(vfs, fd); + int ret; + CHECK_AND_CALL(ret, r, vfs, write, local_fd, data, size); + return ret; +} + +_off_t esp_vfs_lseek(struct _reent *r, int fd, _off_t size, int mode) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return -1; + } + int local_fd = translate_fd(vfs, fd); + int ret; + CHECK_AND_CALL(ret, r, vfs, lseek, local_fd, size, mode); + return ret; +} + +ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return -1; + } + int local_fd = translate_fd(vfs, fd); + int ret; + CHECK_AND_CALL(ret, r, vfs, read, local_fd, dst, size); + return ret; +} + + +int esp_vfs_close(struct _reent *r, int fd) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return -1; + } + int local_fd = translate_fd(vfs, fd); + int ret; + CHECK_AND_CALL(ret, r, vfs, close, local_fd); + return ret; +} + +int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return -1; + } + int local_fd = translate_fd(vfs, fd); + int ret; + CHECK_AND_CALL(ret, r, vfs, fstat, local_fd, st); + return ret; +} + +int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st) +{ + const vfs_entry_t* vfs = get_vfs_for_path(path); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, path); + int ret; + CHECK_AND_CALL(ret, r, vfs, stat, path_within_vfs, st); + return ret; +} + +int esp_vfs_link(struct _reent *r, const char* n1, const char* n2) +{ + const vfs_entry_t* vfs = get_vfs_for_path(n1); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const vfs_entry_t* vfs2 = get_vfs_for_path(n2); + if (vfs != vfs2) { + __errno_r(r) = EXDEV; + return -1; + } + const char* path1_within_vfs = translate_path(vfs, n1); + const char* path2_within_vfs = translate_path(vfs, n2); + int ret; + CHECK_AND_CALL(ret, r, vfs, link, path1_within_vfs, path2_within_vfs); + return ret; +} + +int esp_vfs_unlink(struct _reent *r, const char *path) +{ + const vfs_entry_t* vfs = get_vfs_for_path(path); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, path); + int ret; + CHECK_AND_CALL(ret, r, vfs, unlink, path_within_vfs); + return ret; +} + +int esp_vfs_rename(struct _reent *r, const char *src, const char *dst) +{ + const vfs_entry_t* vfs = get_vfs_for_path(src); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const vfs_entry_t* vfs_dst = get_vfs_for_path(dst); + if (vfs != vfs_dst) { + __errno_r(r) = EXDEV; + return -1; + } + const char* src_within_vfs = translate_path(vfs, src); + const char* dst_within_vfs = translate_path(vfs, dst); + int ret; + CHECK_AND_CALL(ret, r, vfs, rename, src_within_vfs, dst_within_vfs); + return ret; +} From 587360363c121d266b66271b10a60831b6ff9d40 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 25 Oct 2016 22:01:50 +0800 Subject: [PATCH 077/110] vfs: add UART device This should be integrated with UART driver once everything is merged --- components/vfs/include/esp_vfs_dev.h | 28 ++++++++ components/vfs/vfs_uart.c | 95 ++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 components/vfs/include/esp_vfs_dev.h create mode 100644 components/vfs/vfs_uart.c diff --git a/components/vfs/include/esp_vfs_dev.h b/components/vfs/include/esp_vfs_dev.h new file mode 100644 index 000000000..6eb63d852 --- /dev/null +++ b/components/vfs/include/esp_vfs_dev.h @@ -0,0 +1,28 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_VFS_H__ +#define __ESP_VFS_H__ + +#include "esp_vfs.h" + +/** + * @brief add /dev/uart virtual filesystem driver + * + * This function is called from startup code to enable serial output + */ +void esp_vfs_dev_uart_register(); + + +#endif //__ESP_VFS_H__ diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c new file mode 100644 index 000000000..179c5e258 --- /dev/null +++ b/components/vfs/vfs_uart.c @@ -0,0 +1,95 @@ +#include +#include "esp_vfs.h" +#include "esp_attr.h" +#include "sys/errno.h" +#include "sys/lock.h" +#include "soc/uart_struct.h" + +static uart_dev_t* s_uarts[3] = {&UART0, &UART1, &UART2}; + +static int IRAM_ATTR uart_open(const char * path, int flags, int mode) +{ + // this is fairly primitive, we should check if file is opened read only, + // and error out if write is requested + if (strcmp(path, "/0") == 0) { + return 0; + } else if (strcmp(path, "/1") == 0) { + return 1; + } else if (strcmp(path, "/2") == 0) { + return 2; + } + errno = ENOENT; + return -1; +} + +static void IRAM_ATTR uart_tx_char(uart_dev_t* uart, int c) +{ + while (uart->status.txfifo_cnt >= 127) { + ; + } + uart->fifo.rw_byte = c; +} + + +static size_t IRAM_ATTR uart_write(int fd, const void * data, size_t size) +{ + assert(fd >=0 && fd < 3); + const char *data_c = (const char *)data; + uart_dev_t* uart = s_uarts[fd]; + static _lock_t stdout_lock; /* lazily initialised */ + /* Even though newlib does stream locking on stdout, we need + a dedicated stdout UART lock... + + This is because each task has its own _reent structure with + unique FILEs for stdin/stdout/stderr, so these are + per-thread (lazily initialised by __sinit the first time a + stdio function is used, see findfp.c:235. + + It seems like overkill to allocate a FILE-per-task and lock + a thread-local stream, but I see no easy way to fix this + (pre-__sinit_, tasks have "fake" FILEs ie __sf_fake_stdout + which aren't fully valid.) + */ + _lock_acquire_recursive(&stdout_lock); + for (size_t i = 0; i < size; i++) { +#if CONFIG_NEWLIB_STDOUT_ADDCR + if (data_c[i]=='\n') { + uart_tx_char(uart, '\r'); + } +#endif + uart_tx_char(uart, data_c[i]); + } + _lock_release_recursive(&stdout_lock); + return size; +} + +static int IRAM_ATTR uart_fstat(int fd, struct stat * st) +{ + assert(fd >=0 && fd < 3); + st->st_mode = S_IFCHR; + return 0; +} + +static int IRAM_ATTR uart_close(int fd) +{ + assert(fd >=0 && fd < 3); + return 0; +} + +void esp_vfs_dev_uart_register() +{ + esp_vfs_t vfs = { + .fd_offset = 0, + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &uart_write, + .open = &uart_open, + .fstat = &uart_fstat, + .close = &uart_close, + .read = NULL, // TODO: implement reading from UART + .stat = NULL, + .link = NULL, + .unlink = NULL, + .rename = NULL + }; + ESP_ERROR_CHECK(esp_vfs_register("/dev/uart", &vfs, NULL)); +} From b3b8334d546fbcdb57629fc07b1cbaa67b65958e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 25 Oct 2016 22:12:07 +0800 Subject: [PATCH 078/110] vfs and newlib: small fixes - spaces->tabs in tasks.c - update vfs_uart.c to use per-UART locks - add license to vfs_uart.c - allocate separate streams for stdout, stdin, stderr, so that they can be independently reassigned - fix build system test failure --- components/esp32/cpu_start.c | 4 +- components/esp32/include/soc/cpu.h | 3 + components/newlib/component.mk | 7 +- .../{esp32/syscalls.c => newlib/locks.c} | 258 +----------------- .../newlib/platform_include/esp_newlib.h | 27 ++ components/newlib/syscall_table.c | 91 ++++++ components/newlib/syscalls.c | 105 +++++++ components/newlib/time.c | 35 +++ 8 files changed, 269 insertions(+), 261 deletions(-) rename components/{esp32/syscalls.c => newlib/locks.c} (54%) create mode 100644 components/newlib/platform_include/esp_newlib.h create mode 100644 components/newlib/syscall_table.c create mode 100644 components/newlib/syscalls.c create mode 100644 components/newlib/time.c diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index a649764ab..cbac54d7c 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -42,7 +42,7 @@ #include "esp_spi_flash.h" #include "esp_ipc.h" #include "esp_log.h" - +#include "esp_newlib.h" #include "esp_brownout.h" #include "esp_int_wdt.h" #include "esp_task_wdt.h" @@ -160,7 +160,7 @@ void start_cpu0_default(void) #if CONFIG_TASK_WDT esp_task_wdt_init(); #endif - ets_setup_syscalls(); + esp_setup_syscalls(); do_global_ctors(); esp_ipc_init(); spi_flash_init(); diff --git a/components/esp32/include/soc/cpu.h b/components/esp32/include/soc/cpu.h index c74ba317c..ee2907493 100644 --- a/components/esp32/include/soc/cpu.h +++ b/components/esp32/include/soc/cpu.h @@ -15,6 +15,9 @@ #ifndef _SOC_CPU_H #define _SOC_CPU_H +#include +#include +#include #include "xtensa/corebits.h" /* C macros for xtensa special register read/write/exchange */ diff --git a/components/newlib/component.mk b/components/newlib/component.mk index 7c8c74deb..3731b5ef0 100644 --- a/components/newlib/component.mk +++ b/components/newlib/component.mk @@ -1,8 +1,5 @@ -COMPONENT_ADD_LDFLAGS := $(abspath lib/libc.a) $(abspath lib/libm.a) +COMPONENT_ADD_LDFLAGS := $(abspath lib/libc.a) $(abspath lib/libm.a) -lnewlib - -define COMPONENT_BUILDRECIPE - #Nothing to do; this does not generate a library. -endef +COMPONENT_ADD_INCLUDEDIRS := include platform_include include $(IDF_PATH)/make/component_common.mk diff --git a/components/esp32/syscalls.c b/components/newlib/locks.c similarity index 54% rename from components/esp32/syscalls.c rename to components/newlib/locks.c index 052605ee3..21b974a1f 100644 --- a/components/esp32/syscalls.c +++ b/components/newlib/locks.c @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at - +// // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software @@ -11,181 +11,17 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#include -#include -#include -#include -#include -#include -#include + +#include #include +#include #include "esp_attr.h" -#include "rom/libc_stubs.h" -#include "rom/uart.h" #include "soc/cpu.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/portmacro.h" #include "freertos/task.h" -void abort() { - do - { - __asm__ ("break 0,0"); - *((int*) 0) = 0; - } while(true); -} - -void* _malloc_r(struct _reent *r, size_t size) { - return pvPortMalloc(size); -} - -void _free_r(struct _reent *r, void* ptr) { - return vPortFree(ptr); -} - -void* _realloc_r(struct _reent *r, void* ptr, size_t size) { - void* new_chunk; - if (size == 0) { - if (ptr) { - vPortFree(ptr); - } - return NULL; - } - - new_chunk = pvPortMalloc(size); - if (new_chunk && ptr) { - memcpy(new_chunk, ptr, size); - vPortFree(ptr); - } - // realloc behaviour: don't free original chunk if alloc failed - return new_chunk; -} - -void* _calloc_r(struct _reent *r, size_t count, size_t size) { - void* result = pvPortMalloc(count * size); - if (result) - { - memset(result, 0, count * size); - } - return result; -} - -int _system_r(struct _reent *r, const char *str) { - abort(); - return 0; -} - -int _rename_r(struct _reent *r, const char *src, const char *dst) { - abort(); - return 0; -} - -clock_t _times_r(struct _reent *r, struct tms *ptms) { - abort(); - return 0; -} - -// TODO: read time from RTC -int _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) { - abort(); - return 0; -} - -void _raise_r(struct _reent *r) { - abort(); -} - -int _unlink_r(struct _reent *r, const char *path) { - abort(); - return 0; -} - -int _link_r(struct _reent *r, const char* n1, const char* n2) { - abort(); - return 0; -} - -int _stat_r(struct _reent *r, const char * path, struct stat * st) { - return 0; -} - -int _fstat_r(struct _reent *r, int fd, struct stat * st) { - st->st_mode = S_IFCHR; - return 0; -} - -void* _sbrk_r(struct _reent *r, ptrdiff_t sz) { - abort(); - return 0; -} - -int _getpid_r(struct _reent *r) { - abort(); - return 0; -} - -int _kill_r(struct _reent *r, int pid, int sig) { - abort(); - return 0; -} - -void _exit_r(struct _reent *r, int e) { - abort(); -} - -int _close_r(struct _reent *r, int fd) { - return 0; -} - -int _open_r(struct _reent *r, const char * path, int flags, int mode) { - return 0; -} - -void _exit(int __status) { - abort(); -} - -ssize_t _write_r(struct _reent *r, int fd, const void * data, size_t size) { - const char *data_c = (const char *)data; - if (fd == STDOUT_FILENO) { - static _lock_t stdout_lock; /* lazily initialised */ - /* Even though newlib does stream locking on stdout, we need - a dedicated stdout UART lock... - - This is because each task has its own _reent structure with - unique FILEs for stdin/stdout/stderr, so these are - per-thread (lazily initialised by __sinit the first time a - stdio function is used, see findfp.c:235. - - It seems like overkill to allocate a FILE-per-task and lock - a thread-local stream, but I see no easy way to fix this - (pre-__sinit_, tasks have "fake" FILEs ie __sf_fake_stdout - which aren't fully valid.) - */ - _lock_acquire_recursive(&stdout_lock); - for (size_t i = 0; i < size; i++) { -#if CONFIG_NEWLIB_STDOUT_ADDCR - if (data_c[i]=='\n') { - uart_tx_one_char('\r'); - } -#endif - uart_tx_one_char(data_c[i]); - } - _lock_release_recursive(&stdout_lock); - } - return size; -} - -_off_t _lseek_r(struct _reent *r, int fd, _off_t size, int mode) { - return 0; -} - -// TODO: implement reading from UART -ssize_t _read_r(struct _reent *r, int fd, void * dst, size_t size) { - return 0; -} - /* Notes on our newlib lock implementation: * * - Use FreeRTOS mutex semaphores as locks. @@ -369,89 +205,3 @@ void IRAM_ATTR _lock_release(_lock_t *lock) { void IRAM_ATTR _lock_release_recursive(_lock_t *lock) { lock_release_generic(lock, queueQUEUE_TYPE_RECURSIVE_MUTEX); } - -// This function is not part on newlib API, it is defined in libc/stdio/local.h -// It is called as part of _reclaim_reent via a pointer in __cleanup member -// of struct _reent. -// This function doesn't call _fclose_r for _stdin, _stdout, _stderr members -// of struct reent. Not doing so causes a memory leak each time a task is -// terminated. We replace __cleanup member with _extra_cleanup_r (below) to work -// around this. -extern void _cleanup_r(struct _reent* r); - -void _extra_cleanup_r(struct _reent* r) -{ - _cleanup_r(r); - _fclose_r(r, r->_stdout); - _fclose_r(r, r->_stderr); - _fclose_r(r, r->_stdin); -} - -static struct _reent s_reent; - -/* - General ToDo that the Xtensa newlib support code did but we do not: Close every open fd a running task had when the task - is killed. Do we want that too? - JD -*/ - -extern int _printf_float(struct _reent *rptr, - void *pdata, - FILE * fp, - int (*pfunc) (struct _reent *, FILE *, _CONST char *, size_t len), - va_list * ap); - - -extern int _scanf_float(struct _reent *rptr, - void *pdata, - FILE *fp, - va_list *ap); - - -static struct syscall_stub_table s_stub_table = { - .__getreent = &__getreent, - ._malloc_r = &_malloc_r, - ._free_r = &_free_r, - ._realloc_r = &_realloc_r, - ._calloc_r = &_calloc_r, - ._abort = &abort, - ._system_r = &_system_r, - ._rename_r = &_rename_r, - ._times_r = &_times_r, - ._gettimeofday_r = &_gettimeofday_r, - ._raise_r = &_raise_r, - ._unlink_r = &_unlink_r, - ._link_r = &_link_r, - ._stat_r = &_stat_r, - ._fstat_r = &_fstat_r, - ._sbrk_r = &_sbrk_r, - ._getpid_r = &_getpid_r, - ._kill_r = &_kill_r, - ._exit_r = &_exit_r, - ._close_r = &_close_r, - ._open_r = &_open_r, - ._write_r = (int (*)(struct _reent *r, int, const void *, int)) &_write_r, - ._lseek_r = (int (*)(struct _reent *r, int, int, int)) &_lseek_r, - ._read_r = (int (*)(struct _reent *r, int, void *, int)) &_read_r, - ._lock_init = &_lock_init, - ._lock_init_recursive = &_lock_init_recursive, - ._lock_close = &_lock_close, - ._lock_close_recursive = &_lock_close, - ._lock_acquire = &_lock_acquire, - ._lock_acquire_recursive = &_lock_acquire_recursive, - ._lock_try_acquire = &_lock_try_acquire, - ._lock_try_acquire_recursive = &_lock_try_acquire_recursive, - ._lock_release = &_lock_release, - ._lock_release_recursive = &_lock_release_recursive, - ._printf_float = &_printf_float, - ._scanf_float = &_scanf_float, -}; - -void ets_setup_syscalls() { - syscall_table_ptr_pro = &s_stub_table; - syscall_table_ptr_app = &s_stub_table; - _GLOBAL_REENT = &s_reent; - environ = malloc(sizeof(char*)); - environ[0] = NULL; -} - - diff --git a/components/newlib/platform_include/esp_newlib.h b/components/newlib/platform_include/esp_newlib.h new file mode 100644 index 000000000..74d0cc5e5 --- /dev/null +++ b/components/newlib/platform_include/esp_newlib.h @@ -0,0 +1,27 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_NEWLIB_H__ +#define __ESP_NEWLIB_H__ + +/** + * Function which sets up syscall table used by newlib functions in ROM. + * + * Called from the startup code, not intended to be called from application + * code. + */ +void esp_setup_syscalls(); + + +#endif //__ESP_NEWLIB_H__ diff --git a/components/newlib/syscall_table.c b/components/newlib/syscall_table.c new file mode 100644 index 000000000..b6414af55 --- /dev/null +++ b/components/newlib/syscall_table.c @@ -0,0 +1,91 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rom/libc_stubs.h" +#include "esp_vfs.h" + +static struct _reent s_reent; + +extern int _printf_float(struct _reent *rptr, + void *pdata, + FILE * fp, + int (*pfunc) (struct _reent *, FILE *, _CONST char *, size_t len), + va_list * ap); + + +extern int _scanf_float(struct _reent *rptr, + void *pdata, + FILE *fp, + va_list *ap); + + +static struct syscall_stub_table s_stub_table = { + .__getreent = &__getreent, + ._malloc_r = &_malloc_r, + ._free_r = &_free_r, + ._realloc_r = &_realloc_r, + ._calloc_r = &_calloc_r, + ._abort = &abort, + ._system_r = &_system_r, + ._rename_r = &esp_vfs_rename, + ._times_r = &_times_r, + ._gettimeofday_r = &_gettimeofday_r, + ._raise_r = (void (*)(struct _reent *r)) &_raise_r, + ._unlink_r = &esp_vfs_unlink, + ._link_r = &esp_vfs_link, + ._stat_r = &esp_vfs_stat, + ._fstat_r = &esp_vfs_fstat, + ._sbrk_r = &_sbrk_r, + ._getpid_r = &_getpid_r, + ._kill_r = &_kill_r, + ._exit_r = NULL, // never called in ROM + ._close_r = &esp_vfs_close, + ._open_r = &esp_vfs_open, + ._write_r = (int (*)(struct _reent *r, int, const void *, int)) &esp_vfs_write, + ._lseek_r = (int (*)(struct _reent *r, int, int, int)) &esp_vfs_lseek, + ._read_r = (int (*)(struct _reent *r, int, void *, int)) &esp_vfs_read, + ._lock_init = &_lock_init, + ._lock_init_recursive = &_lock_init_recursive, + ._lock_close = &_lock_close, + ._lock_close_recursive = &_lock_close, + ._lock_acquire = &_lock_acquire, + ._lock_acquire_recursive = &_lock_acquire_recursive, + ._lock_try_acquire = &_lock_try_acquire, + ._lock_try_acquire_recursive = &_lock_try_acquire_recursive, + ._lock_release = &_lock_release, + ._lock_release_recursive = &_lock_release_recursive, + ._printf_float = &_printf_float, + ._scanf_float = &_scanf_float, +}; + +void esp_setup_syscalls() +{ + syscall_table_ptr_pro = &s_stub_table; + syscall_table_ptr_app = &s_stub_table; + _GLOBAL_REENT = &s_reent; + environ = malloc(sizeof(char*)); + environ[0] = NULL; +} + + diff --git a/components/newlib/syscalls.c b/components/newlib/syscalls.c new file mode 100644 index 000000000..4d70a39a8 --- /dev/null +++ b/components/newlib/syscalls.c @@ -0,0 +1,105 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include "esp_attr.h" +#include "freertos/FreeRTOS.h" + +void IRAM_ATTR abort() +{ + do + { + __asm__ ("break 0,0"); + *((int*) 0) = 0; + } while(true); +} + +void* IRAM_ATTR _malloc_r(struct _reent *r, size_t size) +{ + return pvPortMalloc(size); +} + +void IRAM_ATTR _free_r(struct _reent *r, void* ptr) +{ + return vPortFree(ptr); +} + +void* IRAM_ATTR _realloc_r(struct _reent *r, void* ptr, size_t size) +{ + void* new_chunk; + if (size == 0) { + if (ptr) { + vPortFree(ptr); + } + return NULL; + } + + new_chunk = pvPortMalloc(size); + if (new_chunk && ptr) { + memcpy(new_chunk, ptr, size); + vPortFree(ptr); + } + // realloc behaviour: don't free original chunk if alloc failed + return new_chunk; +} + +void* IRAM_ATTR _calloc_r(struct _reent *r, size_t count, size_t size) +{ + void* result = pvPortMalloc(count * size); + if (result) + { + memset(result, 0, count * size); + } + return result; +} + +int _system_r(struct _reent *r, const char *str) +{ + __errno_r(r) = ENOSYS; + return -1; +} + +void _raise_r(struct _reent *r) +{ + abort(); +} + +void* _sbrk_r(struct _reent *r, ptrdiff_t sz) +{ + abort(); +} + +int _getpid_r(struct _reent *r) +{ + __errno_r(r) = ENOSYS; + return -1; +} + +int _kill_r(struct _reent *r, int pid, int sig) +{ + __errno_r(r) = ENOSYS; + return -1; +} + +void _exit(int __status) +{ + abort(); +} + diff --git a/components/newlib/time.c b/components/newlib/time.c new file mode 100644 index 000000000..cb2efb4e1 --- /dev/null +++ b/components/newlib/time.c @@ -0,0 +1,35 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include "esp_attr.h" + + +clock_t _times_r(struct _reent *r, struct tms *ptms) +{ + __errno_r(r) = ENOSYS; + return (clock_t) -1; +} + +// TODO: read time from RTC +int _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) +{ + __errno_r(r) = ENOSYS; + return (clock_t) -1; +} From 0c130ecf19a8c1fe9803dba75148028965898f8b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 25 Oct 2016 22:16:08 +0800 Subject: [PATCH 079/110] vfs: code review fixes - fix typo in readme - remove unneeded extern declaration - fix header guard macro - tabs->spaces in syscalls.c (+1 squashed commit) - fix minor typos --- components/esp32/cpu_start.c | 6 +++ components/freertos/tasks.c | 8 ++-- .../newlib/platform_include/esp_newlib.h | 10 +++++ components/newlib/reent_init.c | 45 +++++++++++++++++++ 4 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 components/newlib/reent_init.c diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index cbac54d7c..769bd3eaf 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -42,6 +42,7 @@ #include "esp_spi_flash.h" #include "esp_ipc.h" #include "esp_log.h" +#include "esp_vfs_dev.h" #include "esp_newlib.h" #include "esp_brownout.h" #include "esp_int_wdt.h" @@ -161,6 +162,11 @@ void start_cpu0_default(void) esp_task_wdt_init(); #endif esp_setup_syscalls(); + esp_vfs_dev_uart_register(); + esp_reent_init(_GLOBAL_REENT); + _GLOBAL_REENT->_stdout = fopen("/dev/uart/0", "w"); // use fdopen here? + _GLOBAL_REENT->_stderr = _GLOBAL_REENT->_stdout; + _GLOBAL_REENT->_stdin = _GLOBAL_REENT->_stdout; do_global_ctors(); esp_ipc_init(); spi_flash_init(); diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 9f004002c..6a7eb5034 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -77,6 +77,7 @@ task.h is included from an application file. */ #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE #include "rom/ets_sys.h" +#include "esp_newlib.h" /* FreeRTOS includes. */ #include "FreeRTOS.h" @@ -962,8 +963,8 @@ UBaseType_t x; #if ( configUSE_NEWLIB_REENTRANT == 1 ) { - /* Initialise this task's Newlib reent structure. */ - _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); + /* Initialise this task's Newlib reent structure. */ + esp_reent_init(&pxNewTCB->xNewLib_reent); } #endif @@ -3643,8 +3644,6 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask ) #if ( INCLUDE_vTaskDelete == 1 ) - // TODO: move this to newlib component and provide a header file - extern void _extra_cleanup_r(struct _reent* r); static void prvDeleteTCB( TCB_t *pxTCB ) { @@ -3657,7 +3656,6 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask ) to the task to free any memory allocated at the application level. */ #if ( configUSE_NEWLIB_REENTRANT == 1 ) { - pxTCB->xNewLib_reent.__cleanup = &_extra_cleanup_r; _reclaim_reent( &( pxTCB->xNewLib_reent ) ); } #endif /* configUSE_NEWLIB_REENTRANT */ diff --git a/components/newlib/platform_include/esp_newlib.h b/components/newlib/platform_include/esp_newlib.h index 74d0cc5e5..468f2ae34 100644 --- a/components/newlib/platform_include/esp_newlib.h +++ b/components/newlib/platform_include/esp_newlib.h @@ -15,6 +15,16 @@ #ifndef __ESP_NEWLIB_H__ #define __ESP_NEWLIB_H__ +#include + +/** + * Replacement for newlib's _REENT_INIT_PTR and __sinit. + * + * Called from startup code and FreeRTOS, not intended to be called from + * application code. + */ +void esp_reent_init(struct _reent* r); + /** * Function which sets up syscall table used by newlib functions in ROM. * diff --git a/components/newlib/reent_init.c b/components/newlib/reent_init.c new file mode 100644 index 000000000..5c29e898c --- /dev/null +++ b/components/newlib/reent_init.c @@ -0,0 +1,45 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include "esp_attr.h" + +/* This function is not part on newlib API, it is defined in libc/stdio/local.h + * There is no nice way to get __cleanup member populated while avoiding __sinit, + * so extern declaration is used here. + */ +extern void _cleanup_r(struct _reent* r); + +/** + * This is the replacement for newlib's _REENT_INIT_PTR and __sinit. + * The problem with __sinit is that it allocates three FILE structures + * (stdin, stdout, stderr). Having individual standard streams for each task + * is a bit too much on a small embedded system. So we point streams + * to the streams of the global struct _reent, which are initialized in + * startup code. + */ +void IRAM_ATTR esp_reent_init(struct _reent* r) +{ + memset(r, 0, sizeof(*r)); + r->_stdout = _GLOBAL_REENT->_stdout; + r->_stderr = _GLOBAL_REENT->_stderr; + r->_stdin = _GLOBAL_REENT->_stdin; + r->__cleanup = &_cleanup_r; + r->__sdidinit = 1; + r->__sglue._next = NULL; + r->__sglue._niobs = 0; + r->__sglue._iobs = NULL; + r->_current_locale = "C"; +} From 7e201c55276649df496baed44a003fa9d958de60 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 26 Oct 2016 14:05:56 +0800 Subject: [PATCH 080/110] vfs and newlib: small fixes - spaces->tabs in tasks.c - update vfs_uart.c to use per-UART locks - add license to vfs_uart.c - allocate separate streams for stdout, stdin, stderr, so that they can be independently reassigned - fix build system test failure - use posix off_t instead of newlib internal _off_t --- components/esp32/cpu_start.c | 7 +++--- components/vfs/include/esp_vfs.h | 6 ++--- components/vfs/vfs.c | 2 +- components/vfs/vfs_uart.c | 38 ++++++++++++++++++-------------- make/test_build_system.sh | 4 ++-- 5 files changed, 32 insertions(+), 25 deletions(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 769bd3eaf..6998140af 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -164,9 +164,10 @@ void start_cpu0_default(void) esp_setup_syscalls(); esp_vfs_dev_uart_register(); esp_reent_init(_GLOBAL_REENT); - _GLOBAL_REENT->_stdout = fopen("/dev/uart/0", "w"); // use fdopen here? - _GLOBAL_REENT->_stderr = _GLOBAL_REENT->_stdout; - _GLOBAL_REENT->_stdin = _GLOBAL_REENT->_stdout; + const char* default_uart_dev = "/dev/uart/0"; + _GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w"); + _GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w"); + _GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r"); do_global_ctors(); esp_ipc_init(); spi_flash_init(); diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index 0d1c3d147..2d9e52c5a 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -71,8 +71,8 @@ typedef struct size_t (*write)(int fd, const void * data, size_t size); }; union { - _off_t (*lseek_p)(void* p, int fd, _off_t size, int mode); - _off_t (*lseek)(int fd, _off_t size, int mode); + off_t (*lseek_p)(void* p, int fd, off_t size, int mode); + off_t (*lseek)(int fd, off_t size, int mode); }; union { ssize_t (*read_p)(void* ctx, int fd, void * dst, size_t size); @@ -137,7 +137,7 @@ esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ct */ ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size); -_off_t esp_vfs_lseek(struct _reent *r, int fd, _off_t size, int mode); +off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode); ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size); int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode); int esp_vfs_close(struct _reent *r, int fd); diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index afc7149ba..bf26968ff 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -167,7 +167,7 @@ ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size) return ret; } -_off_t esp_vfs_lseek(struct _reent *r, int fd, _off_t size, int mode) +off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode) { const vfs_entry_t* vfs = get_vfs_for_fd(fd); if (vfs == NULL) { diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c index 179c5e258..6b6fad8e4 100644 --- a/components/vfs/vfs_uart.c +++ b/components/vfs/vfs_uart.c @@ -1,3 +1,17 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include #include "esp_vfs.h" #include "esp_attr.h" @@ -6,6 +20,7 @@ #include "soc/uart_struct.h" static uart_dev_t* s_uarts[3] = {&UART0, &UART1, &UART2}; +static _lock_t s_uart_locks[3]; // per-UART locks, lazily initialized static int IRAM_ATTR uart_open(const char * path, int flags, int mode) { @@ -36,21 +51,12 @@ static size_t IRAM_ATTR uart_write(int fd, const void * data, size_t size) assert(fd >=0 && fd < 3); const char *data_c = (const char *)data; uart_dev_t* uart = s_uarts[fd]; - static _lock_t stdout_lock; /* lazily initialised */ - /* Even though newlib does stream locking on stdout, we need - a dedicated stdout UART lock... - - This is because each task has its own _reent structure with - unique FILEs for stdin/stdout/stderr, so these are - per-thread (lazily initialised by __sinit the first time a - stdio function is used, see findfp.c:235. - - It seems like overkill to allocate a FILE-per-task and lock - a thread-local stream, but I see no easy way to fix this - (pre-__sinit_, tasks have "fake" FILEs ie __sf_fake_stdout - which aren't fully valid.) - */ - _lock_acquire_recursive(&stdout_lock); + /* + * Even though newlib does stream locking on each individual stream, we need + * a dedicated UART lock if two streams (stdout and stderr) point to the + * same UART. + */ + _lock_acquire_recursive(&s_uart_locks[fd]); for (size_t i = 0; i < size; i++) { #if CONFIG_NEWLIB_STDOUT_ADDCR if (data_c[i]=='\n') { @@ -59,7 +65,7 @@ static size_t IRAM_ATTR uart_write(int fd, const void * data, size_t size) #endif uart_tx_char(uart, data_c[i]); } - _lock_release_recursive(&stdout_lock); + _lock_release_recursive(&s_uart_locks[fd]); return size; } diff --git a/make/test_build_system.sh b/make/test_build_system.sh index 7c8cbc1f1..d08ae6c8a 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -59,9 +59,9 @@ function run_tests() print_status "Updating component source file rebuilds component" # touch a file & do a build take_build_snapshot - touch ${IDF_PATH}/components/esp32/syscalls.c + touch ${IDF_PATH}/components/esp32/cpu_start.c make || failure "Failed to partial build" - assert_rebuilt ${APP_BINS} esp32/libesp32.a esp32/syscalls.o + assert_rebuilt ${APP_BINS} esp32/libesp32.a esp32/cpu_start.o assert_not_rebuilt lwip/liblwip.a freertos/libfreertos.a ${BOOTLOADER_BINS} partitions_singleapp.bin print_status "Bootloader source file rebuilds bootloader" From da56e7625503773dba62d58c93c18fbd879cce76 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 26 Oct 2016 16:47:26 +0800 Subject: [PATCH 081/110] vfs: add readme file --- components/vfs/README.rst | 125 +++++++++++++++++++++++++++++++++++++- components/vfs/vfs_uart.c | 1 + 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/components/vfs/README.rst b/components/vfs/README.rst index b32e22ccf..21b687e78 100644 --- a/components/vfs/README.rst +++ b/components/vfs/README.rst @@ -1,2 +1,123 @@ -Virtual filesystem (VFS) is a driver which can perform operations on file-like objects. This can be a real filesystem (FAT, SPIFFS, etc.), or a device driver which exposes file-like interface. - \ No newline at end of file +Virtual filesystem component +============================ + +Overview +-------- + +Virtual filesystem (VFS) component provides a unified interface for drivers which can perform operations on file-like objects. This can be a real filesystems (FAT, SPIFFS, etc.), or device drivers which exposes file-like interface. + +This component allows C library functions, such as fopen and fprintf, to work with FS drivers. At high level, each FS driver is associated with some path prefix. When one of C library functions needs to open a file, VFS component searches for the FS driver associated with the file's path, and forwards the call to that driver. VFS also forwards read, write, and other calls for the given file to the same FS driver. + +For example, one can register a FAT filesystem driver with ``/fat`` prefix, and call ``fopen("/fat/file.txt", "w")``. VFS component will the call ``open`` function of FAT driver and pass ``/file.txt`` argument to it (and appropriate mode flags). All subsequent calls to C library functions for the returned ``FILE*`` stream will also be forwarded to the FAT driver. + +FS registration +--------------- + +To register an FS driver, application needs to define in instance of esp_vfs_t structure and populate it with function pointers to FS APIs:: + + esp_vfs_t myfs = { + .fd_offset = 0, + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &myfs_write, + .open = &myfs_open, + .fstat = &myfs_fstat, + .close = &myfs_close, + .read = &myfs_read, + .lseek = NULL, + .stat = NULL, + .link = NULL, + .unlink = NULL, + .rename = NULL + }; + + ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL)); + +Depending on the way FS driver declares it's APIs, either ``read``, ``write``, etc., or ``read_p``, ``write_p``, etc. should be used. + +Case 1: API functions are declared without an extra context pointer (FS driver is a singleton):: + + size_t myfs_write(int fd, const void * data, size_t size); + + // In definition of esp_vfs_t: + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &myfs_write, + // ... other member initialized + + // When registering FS, context pointer (third argument) is NULL: + ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL)); + +Case 2: API functions are declared with an extra context pointer (FS driver supports multiple instances):: + + size_t myfs_write(myfs_t* fs, int fd, const void * data, size_t size); + + // In definition of esp_vfs_t: + .flags = ESP_VFS_FLAG_CONTEXT_PTR, + .write_p = &myfs_write, + // ... other member initialized + + // When registering FS, pass the FS context pointer into the third argument + // (hypothetical myfs_mount function is used for illustrative purposes) + myfs_t* myfs_inst1 = myfs_mount(partition1->offset, parition1->size); + ESP_ERROR_CHECK(esp_vfs_register("/data1", &myfs, myfs_inst1)); + + // Can register another instance: + myfs_t* myfs_inst2 = myfs_mount(partition2->offset, parition2->size); + ESP_ERROR_CHECK(esp_vfs_register("/data2", &myfs, myfs_inst2)); + +Paths +----- + +Each registered FS has a path prefix associated with it. This prefix may be considered a "mount point" of this partition. + +Registering mount points which have another mount point as a prefix is not supported and results in undefined behavior. For instance, the following is correct and supported: + +- FS 1 on /data/fs1 +- FS 2 on /data/fs2 + +This **will not work** as expected: + +- FS 1 on /data +- FS 2 on /data/fs2 + +When opening files, FS driver will only be given relative path to files. If the ``myfs`` driver is registered with ``/data`` as prefix, and application calls ``fopen("/data/config.json", ...)``, VFS component will call ``myfs_open("/config.json", ...)``. + +VFS doesn't impose a limit on total file path length, but it does limit FS path prefix to ``ESP_VFS_PATH_MAX`` characters. Individual FS drivers may have their own filename length limitations. + + +File descriptors +---------------- + +It is suggested that filesystem drivers should use small positive integers as file descriptors. VFS component assumes that ``CONFIG_MAX_FD_BITS`` bits (12 by default) are sufficient to represent a file descriptor. + +If filesystem is configured with an option to offset all file descriptors by a constant value, such value should be passed to ``fd_offset`` field of ``esp_vfs_t`` structure. VFS component will then remove this offset when working with FDs of that specific FS, bringing them into the range of small positive integers. + +While file descriptors returned by VFS component to newlib library are rarely seen by the application, the following details may be useful for debugging purposes. File descriptors returned by VFS component are composed of two parts: FS driver ID, and the actual file descriptor. Because newlib stores file descriptors as 16-bit integers, VFS component is also limited by 16 bits to store both parts. + +Lower ``CONFIG_MAX_FD_BITS`` bits are used to store zero-based file descriptor. If FS driver has a non-zero ``fd_offset`` field, this ``fd_offset`` is subtracted FDs obtained from the FS ``open`` call, and the result is stored in the lower bits of the FD. Higher bits are used to save the index of FS in the internal table of registered filesystems. + +When VFS component receives a call from newlib which has a file descriptor, this file descriptor is translated back to the FS-specific file descriptor. First, higher bits of FD are used to identify the FS. Then ``fd_offset`` field of the FS is added to the lower ``CONFIG_MAX_FD_BITS`` bits of the fd, and resulting FD is passed to the FS driver. + +:: + + FD as seen by newlib FD as seen by FS driver + +-----+ + +-------+---------------+ | | +------------------------+ + | FS id | Zero—based FD | +---------------> sum +----> | + +---+---+------+--------+ | | | +------------------------+ + | | | +--^--+ + | +--------------+ | + | | + | +-------------+ | + | | Table of | | + | | registered | | + | | filesystems | | + | +-------------+ +-------------+ | + +-------> entry +----> esp_vfs_t | | + index +-------------+ | structure | | + | | | | | + | | | + fd_offset +---+ + +-------------+ | | + +-------------+ + + + diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c index 6b6fad8e4..bfccad896 100644 --- a/components/vfs/vfs_uart.c +++ b/components/vfs/vfs_uart.c @@ -92,6 +92,7 @@ void esp_vfs_dev_uart_register() .fstat = &uart_fstat, .close = &uart_close, .read = NULL, // TODO: implement reading from UART + .lseek = NULL, .stat = NULL, .link = NULL, .unlink = NULL, From 6f1d3ce4a7c782f8001574b96e37fc781f6f09c4 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 27 Oct 2016 11:47:41 +0800 Subject: [PATCH 082/110] vfs: code review fixes - fix typo in readme - remove unneeded extern declaration - fix header guard macro - tabs->spaces in syscalls.c - spaces->tabs in tasks.c --- components/esp32/cpu_start.c | 1 - components/freertos/tasks.c | 4 ++-- components/newlib/syscalls.c | 32 ++++++++++++++-------------- components/newlib/time.c | 2 +- components/vfs/README.rst | 19 +++++++++++------ components/vfs/include/esp_vfs_dev.h | 6 +++--- 6 files changed, 34 insertions(+), 30 deletions(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 6998140af..12189ccf6 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -60,7 +60,6 @@ static bool app_cpu_started = false; static void do_global_ctors(void); static void main_task(void* args); -extern void ets_setup_syscalls(void); extern void app_main(void); extern int _bss_start; diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 6a7eb5034..64cc3a65d 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -963,8 +963,8 @@ UBaseType_t x; #if ( configUSE_NEWLIB_REENTRANT == 1 ) { - /* Initialise this task's Newlib reent structure. */ - esp_reent_init(&pxNewTCB->xNewLib_reent); + /* Initialise this task's Newlib reent structure. */ + esp_reent_init(&pxNewTCB->xNewLib_reent); } #endif diff --git a/components/newlib/syscalls.c b/components/newlib/syscalls.c index 4d70a39a8..3b2fbf62c 100644 --- a/components/newlib/syscalls.c +++ b/components/newlib/syscalls.c @@ -38,26 +38,26 @@ void* IRAM_ATTR _malloc_r(struct _reent *r, size_t size) void IRAM_ATTR _free_r(struct _reent *r, void* ptr) { - return vPortFree(ptr); + vPortFree(ptr); } void* IRAM_ATTR _realloc_r(struct _reent *r, void* ptr, size_t size) { - void* new_chunk; - if (size == 0) { - if (ptr) { - vPortFree(ptr); - } - return NULL; - } + void* new_chunk; + if (size == 0) { + if (ptr) { + vPortFree(ptr); + } + return NULL; + } - new_chunk = pvPortMalloc(size); - if (new_chunk && ptr) { - memcpy(new_chunk, ptr, size); - vPortFree(ptr); - } - // realloc behaviour: don't free original chunk if alloc failed - return new_chunk; + new_chunk = pvPortMalloc(size); + if (new_chunk && ptr) { + memcpy(new_chunk, ptr, size); + vPortFree(ptr); + } + // realloc behaviour: don't free original chunk if alloc failed + return new_chunk; } void* IRAM_ATTR _calloc_r(struct _reent *r, size_t count, size_t size) @@ -65,7 +65,7 @@ void* IRAM_ATTR _calloc_r(struct _reent *r, size_t count, size_t size) void* result = pvPortMalloc(count * size); if (result) { - memset(result, 0, count * size); + memset(result, 0, count * size); } return result; } diff --git a/components/newlib/time.c b/components/newlib/time.c index cb2efb4e1..021b29545 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -31,5 +31,5 @@ clock_t _times_r(struct _reent *r, struct tms *ptms) int _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) { __errno_r(r) = ENOSYS; - return (clock_t) -1; + return -1; } diff --git a/components/vfs/README.rst b/components/vfs/README.rst index 21b687e78..c58161c90 100644 --- a/components/vfs/README.rst +++ b/components/vfs/README.rst @@ -8,7 +8,7 @@ Virtual filesystem (VFS) component provides a unified interface for drivers whic This component allows C library functions, such as fopen and fprintf, to work with FS drivers. At high level, each FS driver is associated with some path prefix. When one of C library functions needs to open a file, VFS component searches for the FS driver associated with the file's path, and forwards the call to that driver. VFS also forwards read, write, and other calls for the given file to the same FS driver. -For example, one can register a FAT filesystem driver with ``/fat`` prefix, and call ``fopen("/fat/file.txt", "w")``. VFS component will the call ``open`` function of FAT driver and pass ``/file.txt`` argument to it (and appropriate mode flags). All subsequent calls to C library functions for the returned ``FILE*`` stream will also be forwarded to the FAT driver. +For example, one can register a FAT filesystem driver with ``/fat`` prefix, and call ``fopen("/fat/file.txt", "w")``. VFS component will then call ``open`` function of FAT driver and pass ``/file.txt`` argument to it (and appropriate mode flags). All subsequent calls to C library functions for the returned ``FILE*`` stream will also be forwarded to the FAT driver. FS registration --------------- @@ -32,7 +32,7 @@ To register an FS driver, application needs to define in instance of esp_vfs_t s ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL)); -Depending on the way FS driver declares it's APIs, either ``read``, ``write``, etc., or ``read_p``, ``write_p``, etc. should be used. +Depending on the way FS driver declares its APIs, either ``read``, ``write``, etc., or ``read_p``, ``write_p``, etc. should be used. Case 1: API functions are declared without an extra context pointer (FS driver is a singleton):: @@ -41,7 +41,7 @@ Case 1: API functions are declared without an extra context pointer (FS driver i // In definition of esp_vfs_t: .flags = ESP_VFS_FLAG_DEFAULT, .write = &myfs_write, - // ... other member initialized + // ... other members initialized // When registering FS, context pointer (third argument) is NULL: ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL)); @@ -53,15 +53,15 @@ Case 2: API functions are declared with an extra context pointer (FS driver supp // In definition of esp_vfs_t: .flags = ESP_VFS_FLAG_CONTEXT_PTR, .write_p = &myfs_write, - // ... other member initialized + // ... other members initialized // When registering FS, pass the FS context pointer into the third argument // (hypothetical myfs_mount function is used for illustrative purposes) - myfs_t* myfs_inst1 = myfs_mount(partition1->offset, parition1->size); + myfs_t* myfs_inst1 = myfs_mount(partition1->offset, partition1->size); ESP_ERROR_CHECK(esp_vfs_register("/data1", &myfs, myfs_inst1)); // Can register another instance: - myfs_t* myfs_inst2 = myfs_mount(partition2->offset, parition2->size); + myfs_t* myfs_inst2 = myfs_mount(partition2->offset, partition2->size); ESP_ERROR_CHECK(esp_vfs_register("/data2", &myfs, myfs_inst2)); Paths @@ -79,7 +79,12 @@ This **will not work** as expected: - FS 1 on /data - FS 2 on /data/fs2 -When opening files, FS driver will only be given relative path to files. If the ``myfs`` driver is registered with ``/data`` as prefix, and application calls ``fopen("/data/config.json", ...)``, VFS component will call ``myfs_open("/config.json", ...)``. +When opening files, FS driver will only be given relative path to files. For example: + +- ``myfs`` driver is registered with ``/data`` as path prefix +- and application calls ``fopen("/data/config.json", ...)`` +- then VFS component will call ``myfs_open("/config.json", ...)``. +- ``myfs`` driver will open ``/config.json`` file VFS doesn't impose a limit on total file path length, but it does limit FS path prefix to ``ESP_VFS_PATH_MAX`` characters. Individual FS drivers may have their own filename length limitations. diff --git a/components/vfs/include/esp_vfs_dev.h b/components/vfs/include/esp_vfs_dev.h index 6eb63d852..bb2579ee0 100644 --- a/components/vfs/include/esp_vfs_dev.h +++ b/components/vfs/include/esp_vfs_dev.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef __ESP_VFS_H__ -#define __ESP_VFS_H__ +#ifndef __ESP_VFS_DEV_H__ +#define __ESP_VFS_DEV_H__ #include "esp_vfs.h" @@ -25,4 +25,4 @@ void esp_vfs_dev_uart_register(); -#endif //__ESP_VFS_H__ +#endif //__ESP_VFS_DEV_H__ From f6f23141b35dd3fbcf7fe4522766e732ea92df61 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 8 Sep 2016 19:18:03 +0800 Subject: [PATCH 083/110] components/spi_flash: add high level partition api header TW6701 --- components/spi_flash/include/esp_partition.h | 211 +++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 components/spi_flash/include/esp_partition.h diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h new file mode 100644 index 000000000..fd1223da5 --- /dev/null +++ b/components/spi_flash/include/esp_partition.h @@ -0,0 +1,211 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_PARTITION_H__ +#define __ESP_PARTITION_H__ + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum esp_partition_type_t { + PT_APP_MASK = 0x0000, + PT_APP_FACTORY = PT_APP_MASK | 0x00, + PT_APP_OTA_MIN = PT_APP_MASK | 0x10, + PT_APP_OTA_0 = PT_APP_OTA_MIN + 0, + PT_APP_OTA_1 = PT_APP_OTA_MIN + 1, + PT_APP_OTA_2 = PT_APP_OTA_MIN + 2, + PT_APP_OTA_3 = PT_APP_OTA_MIN + 3, + PT_APP_OTA_4 = PT_APP_OTA_MIN + 4, + PT_APP_OTA_5 = PT_APP_OTA_MIN + 5, + PT_APP_OTA_6 = PT_APP_OTA_MIN + 6, + PT_APP_OTA_7 = PT_APP_OTA_MIN + 7, + PT_APP_OTA_8 = PT_APP_OTA_MIN + 8, + PT_APP_OTA_9 = PT_APP_OTA_MIN + 9, + PT_APP_OTA_10 = PT_APP_OTA_MIN + 10, + PT_APP_OTA_11 = PT_APP_OTA_MIN + 11, + PT_APP_OTA_12 = PT_APP_OTA_MIN + 12, + PT_APP_OTA_13 = PT_APP_OTA_MIN + 13, + PT_APP_OTA_14 = PT_APP_OTA_MIN + 14, + PT_APP_OTA_15 = PT_APP_OTA_MIN + 15, + PT_APP_OTA_MAX = PT_APP_MASK | 0x1f, + PT_APP_TEST = PT_APP_MASK | 0x20, + PT_APP_ANY = PT_APP_MASK | 0xff, + + PT_DATA_MASK = 0x0100, + PT_DATA_OTA = PT_DATA_MASK | 0x00, + PT_DATA_RF = PT_DATA_MASK | 0x01, + PT_DATA_WIFI = PT_DATA_MASK | 0x02, + PT_DATA_ANY = PT_DATA_MASK | 0xff, + + PT_FILESYSTEM_MASK = 0x0200, + PT_FILESYSTEM_ESPHTTPD = 0x0200, + PT_FILESYSTEM_FAT = 0x0201, + PT_FILESYSTEM_SPIFFS = 0x0202, + PT_FILESYSTEM_ANY = 0x20ff, + + PT_END = 0xffff +}; + +#define PT_APP_OTA(i) ((esp_partition_type_t)(PT_APP_OTA_MIN + ((i) & 0xf))) + + +typedef struct esp_partition_iterator_opaque_t* esp_partition_iterator_t; + +/** + * @brief Find partition based on one or more parameters + * + * @param type Partition type, one of esp_partition_type_t values + * To find all app partitions or all filesystem partitions, + * use PT_APP_ANY or PT_FILESYSTEM_ANY, respectively. + * @param label (optional) Partition label. Set this value if looking + * for partition with a specific name. Pass NULL otherwise. + * + * @return iterator which can be used to enumerate all the partitions found, + * or NULL if no partitions were found. + * Iterator obtained through this function has to be released + * using esp_partition_iterator_release when not used any more. + */ +esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, const char* label); + + +/** + * @brief Move partition iterator to the next partition found + * + * Any pointers obtained using esp_partition_label function for this iterator + * will be invalid after this call. + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return iterator pointing to the next partition found, or NULL if no more + * partitions were found. + * + */ +esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t iterator); + +/** + * @brief Get partition type + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return esp_partition_type_t value for partition pointed to by the iterator. + */ +esp_partition_type_t esp_partition_type(esp_partition_iterator_t iterator); + +/** + * @brief Get partition size + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return partition size, in bytes + */ +uint32_t esp_partition_size(esp_partition_iterator_t iterator); + +/** + * @brief Get partition address + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return flash address of partition start + */ +uint32_t esp_partition_address(esp_partition_iterator_t iterator); + +/** + * @brief Get partition label + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return pointer to a zero-terminated string with partition label. + * The pointer is valid until the call to esp_partition_next or + * esp_partition_iterator_release for the given iterator. + */ +const char* esp_partition_label(esp_partition_iterator_t iterator); + +/** + * @brief Read data from the partition + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * @param src_offset Address of the data to be read, relative to the + * beginning of the partition. + * @param dst Pointer to the buffer where data should be stored. + * Must be non-NULL and at least 'size' bytes long. + * @param size Size of data to be read, in bytes. + * + * @return ESP_OK, if data was read successfully; + * ESP_INVALID_ARG, if iterator or src are NULL; + * ESP_INVALID_SIZE, if read would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_read(esp_partition_iterator_t iterator, + uint32_t src_offset, uint8_t* dst, uint32_t size); + +/** + * @brief Write data to the partition + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * @param src Pointer to the source buffer. Must be non-NULL and + * at least 'size' bytes long. + * @param dst_offset Address where the data should be written, relative to the + * beginning of the partition. + * @param size Size of data to be written, in bytes. + * + * @note Prior to writing to flash memory, make sure it has been erased with + * esp_partition_erase_range call. + * + * @return ESP_OK, if data was written successfully; + * ESP_INVALID_ARG, if iterator or dst are NULL; + * ESP_INVALID_SIZE, if write would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_write(esp_partition_iterator_t iterator, + const uint8_t* src, uint32_t dst_offset, uint32_t size); + +/** + * @brief Erase part of the partition + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * @param start_addr Address where erase operation should start. Must be aligned + * to 4 kilobytes. + * @param size Size of the range which should be erased, in bytes. + * Must be divisible by 4 kilobytes. + * + * @return ESP_OK, if the range was erased successfully; + * ESP_INVALID_ARG, if iterator or dst are NULL; + * ESP_INVALID_SIZE, if erase would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_erase_range(esp_partition_iterator_t iterator, + uint32_t start_addr, uint32_t size); + + +/** + * @brief Release partition iterator + * + * Any pointers obtained using esp_partition_label function will be invalid + * after this call. + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + */ +void esp_partition_iterator_release(esp_partition_iterator_t iterator); + + +#ifdef __cplusplus +} +#endif + + +#endif /* __ESP_PARTITION_H__ */ From 628bde2080a9b99023e3214a8d55aae5962d9eeb Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 17:05:37 +0800 Subject: [PATCH 084/110] bootloader: move useful structures to esp32 component --- .../bootloader/src/main/bootloader_config.h | 80 +---------- .../bootloader/src/main/bootloader_start.c | 124 ++++++++---------- .../bootloader/src/main/flash_encrypt.c | 2 +- .../esp32/include/esp_flash_data_types.h | 102 ++++++++++++++ 4 files changed, 162 insertions(+), 146 deletions(-) create mode 100644 components/esp32/include/esp_flash_data_types.h diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index f99a1c94e..8a837693c 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -20,12 +20,13 @@ extern "C" { #endif + +#include "esp_flash_data_types.h" + #define BOOT_VERSION "V0.1" #define SPI_SEC_SIZE 0x1000 #define MEM_CACHE(offset) (uint8_t *)(0x3f400000 + (offset)) #define CACHE_READ_32(offset) ((uint32_t *)(0x3f400000 + (offset))) -#define PARTITION_ADD 0x4000 -#define PARTITION_MAGIC 0x50AA #define IROM_LOW 0x400D0000 #define IROM_HIGH 0x40400000 #define DROM_LOW 0x3F400000 @@ -35,73 +36,6 @@ extern "C" #define RTC_DATA_LOW 0x50000000 #define RTC_DATA_HIGH 0x50002000 -/*spi mode,saved in third byte in flash */ -enum { - SPI_MODE_QIO, - SPI_MODE_QOUT, - SPI_MODE_DIO, - SPI_MODE_DOUT, - SPI_MODE_FAST_READ, - SPI_MODE_SLOW_READ -}; -/* spi speed*/ -enum { - SPI_SPEED_40M, - SPI_SPEED_26M, - SPI_SPEED_20M, - SPI_SPEED_80M = 0xF -}; -/*supported flash sizes*/ -enum { - SPI_SIZE_1MB = 0, - SPI_SIZE_2MB, - SPI_SIZE_4MB, - SPI_SIZE_8MB, - SPI_SIZE_16MB, - SPI_SIZE_MAX -}; - - -struct flash_hdr { - char magic; - char blocks; - char spi_mode; /* flag of flash read mode in unpackage and usage in future */ - char spi_speed: 4; /* low bit */ - char spi_size: 4; - unsigned int entry_addr; - uint8_t encrypt_flag; /* encrypt flag */ - uint8_t secury_boot_flag; /* secury boot flag */ - char extra_header[14]; /* ESP32 additional header, unused by second bootloader */ -}; - -/* each header of flash bin block */ -struct block_hdr { - unsigned int load_addr; - unsigned int data_len; -}; - -/* OTA selection structure (two copies in the OTA data partition.) - - Size of 32 bytes is friendly to flash encryption */ -typedef struct { - uint32_t ota_seq; - uint8_t seq_label[24]; - uint32_t crc; /* CRC32 of ota_seq field only */ -} ota_select; - -typedef struct { - uint32_t offset; - uint32_t size; -} partition_pos_t; - -typedef struct { - uint16_t magic; - uint8_t type; /* partition Type */ - uint8_t subtype; /* part_subtype */ - partition_pos_t pos; - uint8_t label[16]; /* label for the partition */ - uint8_t reserved[4]; /* reserved */ -} partition_info_t; #define PART_TYPE_APP 0x00 #define PART_SUBTYPE_FACTORY 0x00 @@ -120,10 +54,10 @@ typedef struct { #define SPI_ERROR_LOG "spi flash error" typedef struct { - partition_pos_t ota_info; - partition_pos_t factory; - partition_pos_t test; - partition_pos_t ota[16]; + esp_partition_pos_t ota_info; + esp_partition_pos_t factory; + esp_partition_pos_t test; + esp_partition_pos_t ota[16]; uint32_t app_count; uint32_t selected_subtype; } bootloader_state_t; diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index e87f579f4..5b1e15207 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at - +// // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software @@ -49,8 +49,8 @@ flash cache is down and the app CPU is in reset. We do have a stack, so we can d extern void Cache_Flush(int); void bootloader_main(); -void unpack_load_app(const partition_pos_t *app_node); -void print_flash_info(struct flash_hdr* pfhdr); +void unpack_load_app(const esp_partition_pos_t *app_node); +void print_flash_info(const esp_image_header_t* pfhdr); void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, uint32_t drom_load_addr, uint32_t drom_size, @@ -58,7 +58,7 @@ void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, uint32_t irom_load_addr, uint32_t irom_size, uint32_t entry_addr); -static void update_flash_config(struct flash_hdr* pfhdr); +static void update_flash_config(const esp_image_header_t* pfhdr); void IRAM_ATTR call_start_cpu0() @@ -154,7 +154,7 @@ void boot_cache_redirect( uint32_t pos, size_t size ) */ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) { - partition_info_t partition; + esp_partition_info_t partition; uint32_t end = addr + 0x1000; int index = 0; char *partition_usage; @@ -168,7 +168,7 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) ESP_LOGD(TAG, "type=%x subtype=%x", partition.type, partition.subtype); partition_usage = "unknown"; - if (partition.magic == PARTITION_MAGIC) { /* valid partition definition */ + if (partition.magic == ESP_PARTITION_MAGIC) { /* valid partition definition */ switch(partition.type) { case PART_TYPE_APP: /* app partition */ switch(partition.subtype) { @@ -231,12 +231,12 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) return true; } -static uint32_t ota_select_crc(const ota_select *s) +static uint32_t ota_select_crc(const esp_ota_select_entry_t *s) { return crc32_le(UINT32_MAX, (uint8_t*)&s->ota_seq, 4); } -static bool ota_select_valid(const ota_select *s) +static bool ota_select_valid(const esp_ota_select_entry_t *s) { return s->ota_seq != UINT32_MAX && s->crc == ota_select_crc(s); } @@ -252,10 +252,10 @@ void bootloader_main() { ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); - struct flash_hdr fhdr; + esp_image_header_t fhdr; bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; - ota_select sa,sb; + esp_ota_select_entry_t sa,sb; memset(&bs, 0, sizeof(bs)); ESP_LOGI(TAG, "compile time " __TIME__ ); @@ -266,18 +266,18 @@ void bootloader_main() /*register first sector in drom0 page 0 */ boot_cache_redirect( 0, 0x5000 ); - memcpy((unsigned int *) &fhdr, MEM_CACHE(0x1000), sizeof(struct flash_hdr) ); + memcpy((unsigned int *) &fhdr, MEM_CACHE(0x1000), sizeof(esp_image_header_t) ); print_flash_info(&fhdr); update_flash_config(&fhdr); - if (!load_partition_table(&bs, PARTITION_ADD)) { + if (!load_partition_table(&bs, ESP_PARTITION_TABLE_ADDR)) { ESP_LOGE(TAG, "load partition table error!"); return; } - partition_pos_t load_part_pos; + esp_partition_pos_t load_part_pos; if (bs.ota_info.offset != 0) { // check if partition table has OTA info partition //ESP_LOGE("OTA info sector handling is not implemented"); @@ -293,14 +293,14 @@ void bootloader_main() sb.crc = ota_select_crc(&sb); Cache_Read_Disable(0); - spiRet1 = SPIEraseSector(bs.ota_info.offset/0x1000); - spiRet2 = SPIEraseSector(bs.ota_info.offset/0x1000+1); + spiRet1 = SPIEraseSector(bs.ota_info.offset/0x1000); + spiRet2 = SPIEraseSector(bs.ota_info.offset/0x1000+1); if (spiRet1 != SPI_FLASH_RESULT_OK || spiRet2 != SPI_FLASH_RESULT_OK ) { ESP_LOGE(TAG, SPI_ERROR_LOG); return; } - spiRet1 = SPIWrite(bs.ota_info.offset,(uint32_t *)&sa,sizeof(ota_select)); - spiRet2 = SPIWrite(bs.ota_info.offset + 0x1000,(uint32_t *)&sb,sizeof(ota_select)); + spiRet1 = SPIWrite(bs.ota_info.offset,(uint32_t *)&sa,sizeof(esp_ota_select_entry_t)); + spiRet2 = SPIWrite(bs.ota_info.offset + 0x1000,(uint32_t *)&sb,sizeof(esp_ota_select_entry_t)); if (spiRet1 != SPI_FLASH_RESULT_OK || spiRet2 != SPI_FLASH_RESULT_OK ) { ESP_LOGE(TAG, SPI_ERROR_LOG); return; @@ -329,7 +329,7 @@ void bootloader_main() } ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); - if(fhdr.secury_boot_flag == 0x01) { + if(fhdr.secure_boot_flag == 0x01) { /* protect the 2nd_boot */ if(false == secure_boot()){ ESP_LOGE(TAG, "secure boot failed"); @@ -350,12 +350,12 @@ void bootloader_main() } -void unpack_load_app(const partition_pos_t* partition) +void unpack_load_app(const esp_partition_pos_t* partition) { boot_cache_redirect(partition->offset, partition->size); uint32_t pos = 0; - struct flash_hdr image_header; + esp_image_header_t image_header; memcpy(&image_header, MEM_CACHE(pos), sizeof(image_header)); pos += sizeof(image_header); @@ -379,7 +379,7 @@ void unpack_load_app(const partition_pos_t* partition) for (uint32_t section_index = 0; section_index < image_header.blocks; ++section_index) { - struct block_hdr section_header = {0}; + esp_image_section_header_t section_header = {0}; memcpy(§ion_header, MEM_CACHE(pos), sizeof(section_header)); pos += sizeof(section_header); @@ -485,23 +485,23 @@ void IRAM_ATTR set_cache_and_start_app( (*entry)(); } -static void update_flash_config(struct flash_hdr* pfhdr) +static void update_flash_config(const esp_image_header_t* pfhdr) { uint32_t size; switch(pfhdr->spi_size) { - case SPI_SIZE_1MB: + case ESP_IMAGE_FLASH_SIZE_1MB: size = 1; break; - case SPI_SIZE_2MB: + case ESP_IMAGE_FLASH_SIZE_2MB: size = 2; break; - case SPI_SIZE_4MB: + case ESP_IMAGE_FLASH_SIZE_4MB: size = 4; break; - case SPI_SIZE_8MB: + case ESP_IMAGE_FLASH_SIZE_8MB: size = 8; break; - case SPI_SIZE_16MB: + case ESP_IMAGE_FLASH_SIZE_16MB: size = 16; break; default: @@ -516,66 +516,53 @@ static void update_flash_config(struct flash_hdr* pfhdr) Cache_Read_Enable( 0 ); } -void print_flash_info(struct flash_hdr* pfhdr) +void print_flash_info(const esp_image_header_t* phdr) { #if (BOOT_LOG_LEVEL >= BOOT_LOG_LEVEL_NOTICE) - struct flash_hdr fhdr = *pfhdr; - - ESP_LOGD(TAG, "magic %02x", fhdr.magic ); - ESP_LOGD(TAG, "blocks %02x", fhdr.blocks ); - ESP_LOGD(TAG, "spi_mode %02x", fhdr.spi_mode ); - ESP_LOGD(TAG, "spi_speed %02x", fhdr.spi_speed ); - ESP_LOGD(TAG, "spi_size %02x", fhdr.spi_size ); + ESP_LOGD(TAG, "magic %02x", phdr->magic ); + ESP_LOGD(TAG, "blocks %02x", phdr->blocks ); + ESP_LOGD(TAG, "spi_mode %02x", phdr->spi_mode ); + ESP_LOGD(TAG, "spi_speed %02x", phdr->spi_speed ); + ESP_LOGD(TAG, "spi_size %02x", phdr->spi_size ); const char* str; - switch ( fhdr.spi_speed ) { - case SPI_SPEED_40M: + switch ( phdr->spi_speed ) { + case ESP_IMAGE_SPI_SPEED_40M: str = "40MHz"; break; - - case SPI_SPEED_26M: + case ESP_IMAGE_SPI_SPEED_26M: str = "26.7MHz"; break; - - case SPI_SPEED_20M: + case ESP_IMAGE_SPI_SPEED_20M: str = "20MHz"; break; - - case SPI_SPEED_80M: + case ESP_IMAGE_SPI_SPEED_80M: str = "80MHz"; break; - default: str = "20MHz"; break; } ESP_LOGI(TAG, "SPI Speed : %s", str ); - - - switch ( fhdr.spi_mode ) { - case SPI_MODE_QIO: + switch ( phdr->spi_mode ) { + case ESP_IMAGE_SPI_MODE_QIO: str = "QIO"; break; - - case SPI_MODE_QOUT: + case ESP_IMAGE_SPI_MODE_QOUT: str = "QOUT"; break; - - case SPI_MODE_DIO: + case ESP_IMAGE_SPI_MODE_DIO: str = "DIO"; break; - - case SPI_MODE_DOUT: + case ESP_IMAGE_SPI_MODE_DOUT: str = "DOUT"; break; - - case SPI_MODE_FAST_READ: + case ESP_IMAGE_SPI_MODE_FAST_READ: str = "FAST READ"; break; - - case SPI_MODE_SLOW_READ: + case ESP_IMAGE_SPI_MODE_SLOW_READ: str = "SLOW READ"; break; default: @@ -584,31 +571,24 @@ void print_flash_info(struct flash_hdr* pfhdr) } ESP_LOGI(TAG, "SPI Mode : %s", str ); - - - switch ( fhdr.spi_size ) { - case SPI_SIZE_1MB: + switch ( phdr->spi_size ) { + case ESP_IMAGE_FLASH_SIZE_1MB: str = "1MB"; break; - - case SPI_SIZE_2MB: + case ESP_IMAGE_FLASH_SIZE_2MB: str = "2MB"; break; - - case SPI_SIZE_4MB: + case ESP_IMAGE_FLASH_SIZE_4MB: str = "4MB"; break; - - case SPI_SIZE_8MB: + case ESP_IMAGE_FLASH_SIZE_8MB: str = "8MB"; break; - - case SPI_SIZE_16MB: + case ESP_IMAGE_FLASH_SIZE_16MB: str = "16MB"; break; - default: - str = "1MB"; + str = "2MB"; break; } ESP_LOGI(TAG, "SPI Flash Size : %s", str ); diff --git a/components/bootloader/src/main/flash_encrypt.c b/components/bootloader/src/main/flash_encrypt.c index 26e66aa03..2fb57a987 100644 --- a/components/bootloader/src/main/flash_encrypt.c +++ b/components/bootloader/src/main/flash_encrypt.c @@ -128,7 +128,7 @@ bool flash_encrypt(bootloader_state_t *bs) return false; } /* encrypt partition table */ - if (false == flash_encrypt_write(PARTITION_ADD, SPI_SEC_SIZE)) { + if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) { ESP_LOGE(TAG, "encrypt partition table error"); return false; } diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h new file mode 100644 index 000000000..b16ee59f5 --- /dev/null +++ b/components/esp32/include/esp_flash_data_types.h @@ -0,0 +1,102 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP_BIN_TYPES_H__ +#define __ESP_BIN_TYPES_H__ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define ESP_PARTITION_TABLE_ADDR 0x4000 +#define ESP_PARTITION_MAGIC 0x50AA + +/*spi mode,saved in third byte in flash */ +typedef enum { + ESP_IMAGE_SPI_MODE_QIO, + ESP_IMAGE_SPI_MODE_QOUT, + ESP_IMAGE_SPI_MODE_DIO, + ESP_IMAGE_SPI_MODE_DOUT, + ESP_IMAGE_SPI_MODE_FAST_READ, + ESP_IMAGE_SPI_MODE_SLOW_READ +} esp_image_spi_mode_t; + +/* spi speed*/ +enum { + ESP_IMAGE_SPI_SPEED_40M, + ESP_IMAGE_SPI_SPEED_26M, + ESP_IMAGE_SPI_SPEED_20M, + ESP_IMAGE_SPI_SPEED_80M = 0xF +} esp_image_spi_freq_t; + +/*supported flash sizes*/ +typedef enum { + ESP_IMAGE_FLASH_SIZE_1MB = 0, + ESP_IMAGE_FLASH_SIZE_2MB, + ESP_IMAGE_FLASH_SIZE_4MB, + ESP_IMAGE_FLASH_SIZE_8MB, + ESP_IMAGE_FLASH_SIZE_16MB, + ESP_IMAGE_FLASH_SIZE_MAX +} esp_image_flash_size_t; + +typedef struct { + char magic; + char blocks; + char spi_mode; /* flag of flash read mode in unpackage and usage in future */ + char spi_speed: 4; /* low bit */ + char spi_size: 4; + unsigned int entry_addr; + uint8_t encrypt_flag; /* encrypt flag */ + uint8_t secure_boot_flag; /* secure boot flag */ + char extra_header[14]; /* ESP32 additional header, unused by second bootloader */ +} esp_image_header_t; + +/* each header of flash bin block */ +typedef struct { + unsigned int load_addr; + unsigned int data_len; +} esp_image_section_header_t; + + +/* OTA selection structure (two copies in the OTA data partition.) + Size of 32 bytes is friendly to flash encryption */ +typedef struct { + uint32_t ota_seq; + uint8_t seq_label[24]; + uint32_t crc; /* CRC32 of ota_seq field only */ +} esp_ota_select_entry_t; + + +typedef struct { + uint32_t offset; + uint32_t size; +} esp_partition_pos_t; + +typedef struct { + uint16_t magic; + uint8_t type; /* partition Type */ + uint8_t subtype; /* part_subtype */ + esp_partition_pos_t pos; + uint8_t label[16]; /* label for the partition */ + uint8_t reserved[4]; /* reserved */ +} esp_partition_info_t; + + +#ifdef __cplusplus +} +#endif + +#endif //__ESP_BIN_TYPES_H__ From 54ca573ce465b4a12cad9cc804c6c80063dcab35 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 17:08:05 +0800 Subject: [PATCH 085/110] spi_flash: move cache operations into separate file --- components/spi_flash/README.rst | 33 +++ .../{esp_spi_flash.c => cache_utils.c} | 201 ++++-------------- components/spi_flash/cache_utils.h | 44 ++++ components/spi_flash/flash_ops.c | 158 ++++++++++++++ 4 files changed, 274 insertions(+), 162 deletions(-) create mode 100644 components/spi_flash/README.rst rename components/spi_flash/{esp_spi_flash.c => cache_utils.c} (53%) create mode 100644 components/spi_flash/cache_utils.h create mode 100644 components/spi_flash/flash_ops.c diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst new file mode 100644 index 000000000..22f98cf02 --- /dev/null +++ b/components/spi_flash/README.rst @@ -0,0 +1,33 @@ +Driver for SPI flash read/write/erase operations +================================================ + +Implementation notes +-------------------- + +In order to perform some flash operations, we need to make sure both CPUs +are not running any code from flash for the duration of the flash operation. +In a single-core setup this is easy: we disable interrupts/scheduler and do +the flash operation. In the dual-core setup this is slightly more complicated. +We need to make sure that the other CPU doesn't run any code from flash. + + +When SPI flash API is called on CPU A (can be PRO or APP), we start +spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API +wakes up high priority task on CPU B and tells it to execute given function, +in this case spi_flash_op_block_func. This function disables cache on CPU B and +signals that cache is disabled by setting s_flash_op_can_start flag. +Then the task on CPU A disables cache as well, and proceeds to execute flash +operation. + +While flash operation is running, interrupts can still run on CPU B. +We assume that all interrupt code is placed into RAM. + +Once flash operation is complete, function on CPU A sets another flag, +s_flash_op_complete, to let the task on CPU B know that it can re-enable +cache and release the CPU. Then the function on CPU A re-enables the cache on +CPU A as well and returns control to the calling code. + +Additionally, all API functions are protected with a mutex (s_flash_op_mutex). + +In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply +disable both caches, no inter-CPU communication takes place. diff --git a/components/spi_flash/esp_spi_flash.c b/components/spi_flash/cache_utils.c similarity index 53% rename from components/spi_flash/esp_spi_flash.c rename to components/spi_flash/cache_utils.c index d702f3b81..6ae47bdb3 100644 --- a/components/spi_flash/esp_spi_flash.c +++ b/components/spi_flash/cache_utils.c @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at - +// // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software @@ -30,39 +30,7 @@ #include "esp_spi_flash.h" #include "esp_log.h" -/* - Driver for SPI flash read/write/erase operations - In order to perform some flash operations, we need to make sure both CPUs - are not running any code from flash for the duration of the flash operation. - In a single-core setup this is easy: we disable interrupts/scheduler and do - the flash operation. In the dual-core setup this is slightly more complicated. - We need to make sure that the other CPU doesn't run any code from flash. - - - When SPI flash API is called on CPU A (can be PRO or APP), we start - spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API - wakes up high priority task on CPU B and tells it to execute given function, - in this case spi_flash_op_block_func. This function disables cache on CPU B and - signals that cache is disabled by setting s_flash_op_can_start flag. - Then the task on CPU A disables cache as well, and proceeds to execute flash - operation. - - While flash operation is running, interrupts can still run on CPU B. - We assume that all interrupt code is placed into RAM. - - Once flash operation is complete, function on CPU A sets another flag, - s_flash_op_complete, to let the task on CPU B know that it can re-enable - cache and release the CPU. Then the function on CPU A re-enables the cache on - CPU A as well and returns control to the calling code. - - Additionally, all API functions are protected with a mutex (s_flash_op_mutex). - - In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply - disable both caches, no inter-CPU communication takes place. -*/ - -static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); static void IRAM_ATTR spi_flash_disable_cache(uint32_t cpuid, uint32_t* saved_state); static void IRAM_ATTR spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_state); @@ -72,25 +40,23 @@ static uint32_t s_flash_op_cache_state[2]; static SemaphoreHandle_t s_flash_op_mutex; static bool s_flash_op_can_start = false; static bool s_flash_op_complete = false; -#endif //CONFIG_FREERTOS_UNICORE -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS -static const char* TAG = "spi_flash"; -static spi_flash_counters_t s_flash_stats; +void spi_flash_init_lock() +{ + s_flash_op_mutex = xSemaphoreCreateMutex(); +} -#define COUNTER_START() uint32_t ts_begin = xthal_get_ccount() -#define COUNTER_STOP(counter) do{ s_flash_stats.counter.count++; s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); } while(0) -#define COUNTER_ADD_BYTES(counter, size) do { s_flash_stats.counter.bytes += size; } while (0) -#else -#define COUNTER_START() -#define COUNTER_STOP(counter) -#define COUNTER_ADD_BYTES(counter, size) +void spi_flash_op_lock() +{ + xSemaphoreTake(s_flash_op_mutex, portMAX_DELAY); +} -#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS +void spi_flash_op_unlock() +{ + xSemaphoreGive(s_flash_op_mutex); +} -#ifndef CONFIG_FREERTOS_UNICORE - -static void IRAM_ATTR spi_flash_op_block_func(void* arg) +void IRAM_ATTR spi_flash_op_block_func(void* arg) { // Disable scheduler on this CPU vTaskSuspendAll(); @@ -108,19 +74,9 @@ static void IRAM_ATTR spi_flash_op_block_func(void* arg) xTaskResumeAll(); } -void spi_flash_init() +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() { - s_flash_op_mutex = xSemaphoreCreateMutex(); - -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS - spi_flash_reset_counters(); -#endif -} - -static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() -{ - // Take the API lock - xSemaphoreTake(s_flash_op_mutex, portMAX_DELAY); + spi_flash_op_lock(); const uint32_t cpuid = xPortGetCoreID(); const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; @@ -152,7 +108,7 @@ static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]); } -static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() +void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() { const uint32_t cpuid = xPortGetCoreID(); const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; @@ -173,98 +129,45 @@ static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() xTaskResumeAll(); } // Release API lock - xSemaphoreGive(s_flash_op_mutex); + spi_flash_op_unlock(); } -#else // CONFIG_FREERTOS_UNICORE +#else // CONFIG_FREERTOS_UNICORE -void spi_flash_init() +void spi_flash_init_lock() { -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS - spi_flash_reset_counters(); -#endif } -static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() +void spi_flash_op_lock() { vTaskSuspendAll(); +} + +void spi_flash_op_unlock() +{ + xTaskResumeAll(); +} + + +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() +{ + spi_flash_op_lock(); spi_flash_disable_cache(0, &s_flash_op_cache_state[0]); } -static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() +void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() { spi_flash_restore_cache(0, s_flash_op_cache_state[0]); - xTaskResumeAll(); + spi_flash_op_unlock(); } #endif // CONFIG_FREERTOS_UNICORE - -SpiFlashOpResult IRAM_ATTR spi_flash_unlock() -{ - static bool unlocked = false; - if (!unlocked) { - SpiFlashOpResult rc = SPIUnlock(); - if (rc != SPI_FLASH_RESULT_OK) { - return rc; - } - unlocked = true; - } - return SPI_FLASH_RESULT_OK; -} - -esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec) -{ - COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc; - rc = spi_flash_unlock(); - if (rc == SPI_FLASH_RESULT_OK) { - rc = SPIEraseSector(sec); - } - spi_flash_enable_interrupts_caches_and_other_cpu(); - COUNTER_STOP(erase); - return spi_flash_translate_rc(rc); -} - -esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size) -{ - COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc; - rc = spi_flash_unlock(); - if (rc == SPI_FLASH_RESULT_OK) { - rc = SPIWrite(dest_addr, src, (int32_t) size); - COUNTER_ADD_BYTES(write, size); - } - spi_flash_enable_interrupts_caches_and_other_cpu(); - COUNTER_STOP(write); - return spi_flash_translate_rc(rc); -} - -esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size) -{ - COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc = SPIRead(src_addr, dest, (int32_t) size); - COUNTER_ADD_BYTES(read, size); - spi_flash_enable_interrupts_caches_and_other_cpu(); - COUNTER_STOP(read); - return spi_flash_translate_rc(rc); -} - -static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc) -{ - switch (rc) { - case SPI_FLASH_RESULT_OK: - return ESP_OK; - case SPI_FLASH_RESULT_TIMEOUT: - return ESP_ERR_FLASH_OP_TIMEOUT; - case SPI_FLASH_RESULT_ERR: - default: - return ESP_ERR_FLASH_OP_FAIL; - } -} +/** + * The following two functions are replacements for Cache_Read_Disable and Cache_Read_Enable + * function in ROM. They are used to work around a bug where Cache_Read_Disable requires a call to + * Cache_Flush before Cache_Read_Enable, even if cached data was not modified. + */ static const uint32_t cache_mask = DPORT_APP_CACHE_MASK_OPSDRAM | DPORT_APP_CACHE_MASK_DROM0 | DPORT_APP_CACHE_MASK_DRAM1 | DPORT_APP_CACHE_MASK_IROM0 | @@ -300,29 +203,3 @@ static void IRAM_ATTR spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_sta } } -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS - -static inline void dump_counter(spi_flash_counter_t* counter, const char* name) -{ - ESP_LOGI(TAG, "%s count=%8d time=%8dms bytes=%8d\n", name, - counter->count, counter->time, counter->bytes); -} - -const spi_flash_counters_t* spi_flash_get_counters() -{ - return &s_flash_stats; -} - -void spi_flash_reset_counters() -{ - memset(&s_flash_stats, 0, sizeof(s_flash_stats)); -} - -void spi_flash_dump_counters() -{ - dump_counter(&s_flash_stats.read, "read "); - dump_counter(&s_flash_stats.write, "write"); - dump_counter(&s_flash_stats.erase, "erase"); -} - -#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h new file mode 100644 index 000000000..899a31c65 --- /dev/null +++ b/components/spi_flash/cache_utils.h @@ -0,0 +1,44 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ESP_SPI_FLASH_CACHE_UTILS_H +#define ESP_SPI_FLASH_CACHE_UTILS_H + +/** + * This header file contains declarations of cache manipulation functions + * used both in flash_ops.c and flash_mmap.c. + * + * These functions are considered internal and are not designed to be called from applications. + */ + +// Init mutex protecting access to spi_flash_* APIs +void spi_flash_init_lock(); + +// Take mutex protecting access to spi_flash_* APIs +void spi_flash_op_lock(); + +// Release said mutex +void spi_flash_op_unlock(); + +// Suspend the scheduler on both CPUs, disable cache. +// Contrary to its name this doesn't do anything with interrupts, yet. +// Interrupt disabling capability will be added once we implement +// interrupt allocation API. +void spi_flash_disable_interrupts_caches_and_other_cpu(); + +// Enable cache, enable interrupts (to be added in future), resume scheduler +void spi_flash_enable_interrupts_caches_and_other_cpu(); + + +#endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c new file mode 100644 index 000000000..99e8ef77f --- /dev/null +++ b/components/spi_flash/flash_ops.c @@ -0,0 +1,158 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_ipc.h" +#include "esp_attr.h" +#include "esp_spi_flash.h" +#include "esp_log.h" +#include "cache_utils.h" + +#if CONFIG_SPI_FLASH_ENABLE_COUNTERS +static const char* TAG = "spi_flash"; +static spi_flash_counters_t s_flash_stats; + +#define COUNTER_START() uint32_t ts_begin = xthal_get_ccount() +#define COUNTER_STOP(counter) \ + do{ \ + s_flash_stats.counter.count++; \ + s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); \\ + } while(0) + +#define COUNTER_ADD_BYTES(counter, size) \ + do { \ + s_flash_stats.counter.bytes += size; \ + } while (0) + +#else +#define COUNTER_START() +#define COUNTER_STOP(counter) +#define COUNTER_ADD_BYTES(counter, size) + +#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS + +static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); + +void spi_flash_init() +{ + spi_flash_init_lock(); +#if CONFIG_SPI_FLASH_ENABLE_COUNTERS + spi_flash_reset_counters(); +#endif +} + +SpiFlashOpResult IRAM_ATTR spi_flash_unlock() +{ + static bool unlocked = false; + if (!unlocked) { + SpiFlashOpResult rc = SPIUnlock(); + if (rc != SPI_FLASH_RESULT_OK) { + return rc; + } + unlocked = true; + } + return SPI_FLASH_RESULT_OK; +} + +esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec) +{ + COUNTER_START(); + spi_flash_disable_interrupts_caches_and_other_cpu(); + SpiFlashOpResult rc; + rc = spi_flash_unlock(); + if (rc == SPI_FLASH_RESULT_OK) { + rc = SPIEraseSector(sec); + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + COUNTER_STOP(erase); + return spi_flash_translate_rc(rc); +} + +esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size) +{ + COUNTER_START(); + spi_flash_disable_interrupts_caches_and_other_cpu(); + SpiFlashOpResult rc; + rc = spi_flash_unlock(); + if (rc == SPI_FLASH_RESULT_OK) { + rc = SPIWrite(dest_addr, src, (int32_t) size); + COUNTER_ADD_BYTES(write, size); + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + COUNTER_STOP(write); + return spi_flash_translate_rc(rc); +} + +esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size) +{ + COUNTER_START(); + spi_flash_disable_interrupts_caches_and_other_cpu(); + SpiFlashOpResult rc = SPIRead(src_addr, dest, (int32_t) size); + COUNTER_ADD_BYTES(read, size); + spi_flash_enable_interrupts_caches_and_other_cpu(); + COUNTER_STOP(read); + return spi_flash_translate_rc(rc); +} + +static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc) +{ + switch (rc) { + case SPI_FLASH_RESULT_OK: + return ESP_OK; + case SPI_FLASH_RESULT_TIMEOUT: + return ESP_ERR_FLASH_OP_TIMEOUT; + case SPI_FLASH_RESULT_ERR: + default: + return ESP_ERR_FLASH_OP_FAIL; + } +} + +#if CONFIG_SPI_FLASH_ENABLE_COUNTERS + +static inline void dump_counter(spi_flash_counter_t* counter, const char* name) +{ + ESP_LOGI(TAG, "%s count=%8d time=%8dms bytes=%8d\n", name, + counter->count, counter->time, counter->bytes); +} + +const spi_flash_counters_t* spi_flash_get_counters() +{ + return &s_flash_stats; +} + +void spi_flash_reset_counters() +{ + memset(&s_flash_stats, 0, sizeof(s_flash_stats)); +} + +void spi_flash_dump_counters() +{ + dump_counter(&s_flash_stats.read, "read "); + dump_counter(&s_flash_stats.write, "write"); + dump_counter(&s_flash_stats.erase, "erase"); +} + +#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS From 42068c3b36cb4b5898ea0f9b353057dd85af33ab Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 17:17:24 +0800 Subject: [PATCH 086/110] spi_flash: implement mmap/munmap --- components/esp32/include/esp_err.h | 2 + components/esp32/include/soc/dport_reg.h | 5 + components/spi_flash/flash_mmap.c | 211 +++++++++++++++++++ components/spi_flash/include/esp_spi_flash.h | 57 +++++ 4 files changed, 275 insertions(+) create mode 100644 components/spi_flash/flash_mmap.c diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index 4f013f91a..af9d2ec33 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -31,6 +31,8 @@ typedef int32_t esp_err_t; #define ESP_ERR_NO_MEM 0x101 #define ESP_ERR_INVALID_ARG 0x102 #define ESP_ERR_INVALID_STATE 0x103 +#define ESP_ERR_INVALID_SIZE 0x104 +#define ESP_ERR_NOT_FOUND 0x105 /** * Macro which can be used to check the error code, diff --git a/components/esp32/include/soc/dport_reg.h b/components/esp32/include/soc/dport_reg.h index d65d9edbc..0c43c0874 100644 --- a/components/esp32/include/soc/dport_reg.h +++ b/components/esp32/include/soc/dport_reg.h @@ -3830,6 +3830,11 @@ #define DPORT_DATE_S 0 #define DPORT_DPORT_DATE_VERSION 0x1605190 +/* Flash MMU table for PRO CPU */ +#define DPORT_PRO_FLASH_MMU_TABLE ((volatile uint32_t*) 0x3FF10000) + +/* Flash MMU table for APP CPU */ +#define DPORT_APP_FLASH_MMU_TABLE ((volatile uint32_t*) 0x3FF12000) diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c new file mode 100644 index 000000000..8636a2605 --- /dev/null +++ b/components/spi_flash/flash_mmap.c @@ -0,0 +1,211 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_ipc.h" +#include "esp_attr.h" +#include "esp_spi_flash.h" +#include "esp_log.h" +#include "cache_utils.h" + +#ifndef NDEBUG +// Enable built-in checks in queue.h in debug builds +#define INVARIANTS +#endif +#include "rom/queue.h" + +#define REGIONS_COUNT 4 +#define PAGES_PER_REGION 64 +#define FLASH_PAGE_SIZE 0x10000 +#define INVALID_ENTRY_VAL 0x100 +#define VADDR0_START_ADDR 0x3F400000 +#define VADDR1_START_ADDR 0x40000000 +#define VADDR1_FIRST_USABLE_ADDR 0x400D0000 +#define PRO_IRAM0_FIRST_USABLE_PAGE ((VADDR1_FIRST_USABLE_ADDR - VADDR1_START_ADDR) / FLASH_PAGE_SIZE + 64) + + +typedef struct mmap_entry_{ + uint32_t handle; + int page; + int count; + LIST_ENTRY(mmap_entry_) entries; +} mmap_entry_t; + + +static LIST_HEAD(mmap_entries_head, mmap_entry_) s_mmap_entries_head = + LIST_HEAD_INITIALIZER(s_mmap_entries_head); +static uint8_t s_mmap_page_refcnt[REGIONS_COUNT * PAGES_PER_REGION] = {0}; +static uint32_t s_mmap_last_handle = 0; + + +static void IRAM_ATTR spi_flash_mmap_init() +{ + for (int i = 0; i < REGIONS_COUNT * PAGES_PER_REGION; ++i) { + uint32_t entry_pro = DPORT_PRO_FLASH_MMU_TABLE[i]; + uint32_t entry_app = DPORT_APP_FLASH_MMU_TABLE[i]; + if (entry_pro != entry_app) { + // clean up entries used by boot loader + entry_pro = 0; + DPORT_PRO_FLASH_MMU_TABLE[i] = 0; + } + if ((entry_pro & 0x100) == 0 && (i == 0 || i == PRO_IRAM0_FIRST_USABLE_PAGE || entry_pro != 0)) { + s_mmap_page_refcnt[i] = 1; + } + } +} + +esp_err_t IRAM_ATTR spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle) +{ + esp_err_t ret; + mmap_entry_t* new_entry = (mmap_entry_t*) malloc(sizeof(mmap_entry_t)); + if (new_entry == 0) { + return ESP_ERR_NO_MEM; + } + if (src_addr & 0xffff) { + return ESP_ERR_INVALID_ARG; + } + spi_flash_disable_interrupts_caches_and_other_cpu(); + if (s_mmap_page_refcnt[0] == 0) { + spi_flash_mmap_init(); + } + // figure out the memory region where we should look for pages + int region_begin; // first page to check + int region_size; // number of pages to check + uint32_t region_addr; // base address of memory region + if (memory == SPI_FLASH_MMAP_DATA) { + // Vaddr0 + region_begin = 0; + region_size = 64; + region_addr = VADDR0_START_ADDR; + } else { + // only part of VAddr1 is usable, so adjust for that + region_begin = VADDR1_FIRST_USABLE_ADDR; + region_size = 3 * 64 - region_begin; + region_addr = VADDR1_FIRST_USABLE_ADDR; + } + // region which should be mapped + int phys_page = src_addr / FLASH_PAGE_SIZE; + int page_count = (size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE; + // The following part searches for a range of MMU entries which can be used. + // Algorithm is essentially naïve strstr algorithm, except that unused MMU + // entries are treated as wildcards. + int start; + int end = region_begin + region_size - page_count; + for (start = region_begin; start < end; ++start) { + int page = phys_page; + int pos; + for (pos = start; pos < start + page_count; ++pos, ++page) { + int table_val = (int) DPORT_PRO_FLASH_MMU_TABLE[pos]; + uint8_t refcnt = s_mmap_page_refcnt[pos]; + if (refcnt != 0 && table_val != page) { + break; + } + } + // whole mapping range matched, bail out + if (pos - start == page_count) { + break; + } + } + // checked all the region(s) and haven't found anything? + if (start == end) { + *out_handle = 0; + *out_ptr = NULL; + ret = ESP_ERR_NO_MEM; + } else { + // set up mapping using pages [start, start + page_count) + uint32_t entry_val = (uint32_t) phys_page; + for (int i = start; i != start + page_count; ++i, ++entry_val) { + // sanity check: we won't reconfigure entries with non-zero reference count + assert(s_mmap_page_refcnt[i] == 0 || + (DPORT_PRO_FLASH_MMU_TABLE[i] == entry_val && + DPORT_APP_FLASH_MMU_TABLE[i] == entry_val)); + if (s_mmap_page_refcnt[i] == 0) { + DPORT_PRO_FLASH_MMU_TABLE[i] = entry_val; + DPORT_APP_FLASH_MMU_TABLE[i] = entry_val; + } + ++s_mmap_page_refcnt[i]; + } + + LIST_INSERT_HEAD(&s_mmap_entries_head, new_entry, entries); + new_entry->page = start; + new_entry->count = page_count; + new_entry->handle = ++s_mmap_last_handle; + *out_handle = new_entry->handle; + *out_ptr = (void*) (region_addr + start * FLASH_PAGE_SIZE); + ret = ESP_OK; + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + if (*out_ptr == NULL) { + free(new_entry); + } + return ret; +} + +void IRAM_ATTR spi_flash_munmap(spi_flash_mmap_handle_t handle) +{ + spi_flash_disable_interrupts_caches_and_other_cpu(); + mmap_entry_t* it; + // look for handle in linked list + for (it = LIST_FIRST(&s_mmap_entries_head); it != NULL; it = LIST_NEXT(it, entries)) { + if (it->handle == handle) { + // for each page, decrement reference counter + // if reference count is zero, disable MMU table entry to + // facilitate debugging of use-after-free conditions + for (int i = it->page; i < it->page + it->count; ++i) { + assert(s_mmap_page_refcnt[i] > 0); + if (--s_mmap_page_refcnt[i] == 0) { + DPORT_PRO_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL; + DPORT_APP_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL; + } + } + LIST_REMOVE(it, entries); + break; + } + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + if (it == NULL) { + assert(0 && "invalid handle, or handle already unmapped"); + } + free(it); +} + +void spi_flash_mmap_dump() +{ + if (s_mmap_page_refcnt[0] == 0) { + spi_flash_mmap_init(); + } + mmap_entry_t* it; + for (it = LIST_FIRST(&s_mmap_entries_head); it != NULL; it = LIST_NEXT(it, entries)) { + printf("handle=%d page=%d count=%d\n", it->handle, it->page, it->count); + } + for (int i = 0; i < REGIONS_COUNT * PAGES_PER_REGION; ++i) { + if (s_mmap_page_refcnt[i] != 0) { + printf("page %d: refcnt=%d paddr=%d\n", + i, (int) s_mmap_page_refcnt[i], DPORT_PRO_FLASH_MMU_TABLE[i]); + } + } +} diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 6d635880e..597e41857 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -70,6 +70,63 @@ esp_err_t spi_flash_write(uint32_t des_addr, const uint32_t *src_addr, uint32_t esp_err_t spi_flash_read(uint32_t src_addr, uint32_t *des_addr, uint32_t size); +/** + * @brief Enumeration which specifies memory space requested in an mmap call + */ +typedef enum { + SPI_FLASH_MMAP_DATA, /**< map to data memory (Vaddr0), allows byte-aligned access, 4 MB total */ + SPI_FLASH_MMAP_INST, /**< map to instruction memory (Vaddr1-3), allows only 4-byte-aligned access, 11 MB total */ +} spi_flash_mmap_memory_t; + +/** + * @brief Opaque handle for memory region obtained from spi_flash_mmap. + */ +typedef uint32_t spi_flash_mmap_handle_t; + +/** + * @brief Map region of flash memory into data or instruction address space + * + * This function allocates sufficient number of 64k MMU pages and configures + * them to map request region of flash memory into data address space or into + * instruction address space. It may reuse MMU pages which already provide + * required mapping. As with any allocator, there is possibility of fragmentation + * of address space if mmap/munmap are heavily used. To troubleshoot issues with + * page allocation, use spi_flash_mmap_dump function. + * + * @param src_addr Physical address in flash where requested region starts. + * This address *must* be aligned to 64kB boundary. + * @param size Size of region which has to be mapped. This size will be rounded + * up to a 64k boundary. + * @param memory Memory space where the region should be mapped + * @param out_ptr Output, pointer to the mapped memory region + * @param out_handle Output, handle which should be used for spi_flash_munmap call + * + * @return ESP_OK on success, ESP_ERR_NO_MEM if pages can not be allocated + */ +esp_err_t spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle); + +/** + * @brief Release region previously obtained using spi_flash_mmap + * + * @note Calling this function will not necessarily unmap memory region. + * Region will only be unmapped when there are no other handles which + * reference this region. In case of partially overlapping regions + * it is possible that memory will be unmapped partially. + * + * @param handle Handle obtained from spi_flash_mmap + */ +void spi_flash_munmap(spi_flash_mmap_handle_t handle); + +/** + * @brief Display information about mapped regions + * + * This function lists handles obtained using spi_flash_mmap, along with range + * of pages allocated to each handle. It also lists all non-zero entries of + * MMU table and corresponding reference counts. + */ +void spi_flash_mmap_dump(); + #if CONFIG_SPI_FLASH_ENABLE_COUNTERS /** From 999e6d4e8f9035f8de54410a8e037ec7ada3e917 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 17:19:02 +0800 Subject: [PATCH 087/110] freertos: move panic handler data to DRAM Fixes https://ezredmine.espressif.com/issues/7817 --- components/esp32/ld/esp32.common.ld | 1 + 1 file changed, 1 insertion(+) diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index a3c636784..2226e9882 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -92,6 +92,7 @@ SECTIONS KEEP(*(.gnu.linkonce.s2.*)) KEEP(*(.jcr)) *(.dram1 .dram1.*) + *libfreertos.a:panic.o(.rodata .rodata.*) _data_end = ABSOLUTE(.); . = ALIGN(4); _heap_start = ABSOLUTE(.); From 079d9ea018729c95a9aea0ac759f40849cb13f5e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 18:04:25 +0800 Subject: [PATCH 088/110] spi_flash: implement partition API --- components/spi_flash/include/esp_partition.h | 156 ++++++++----- components/spi_flash/partition.c | 221 +++++++++++++++++++ 2 files changed, 326 insertions(+), 51 deletions(-) create mode 100644 components/spi_flash/partition.c diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index fd1223da5..6bdba149f 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -15,62 +15,73 @@ #ifndef __ESP_PARTITION_H__ #define __ESP_PARTITION_H__ +#include +#include #include "esp_err.h" #ifdef __cplusplus extern "C" { #endif -enum esp_partition_type_t { - PT_APP_MASK = 0x0000, - PT_APP_FACTORY = PT_APP_MASK | 0x00, - PT_APP_OTA_MIN = PT_APP_MASK | 0x10, - PT_APP_OTA_0 = PT_APP_OTA_MIN + 0, - PT_APP_OTA_1 = PT_APP_OTA_MIN + 1, - PT_APP_OTA_2 = PT_APP_OTA_MIN + 2, - PT_APP_OTA_3 = PT_APP_OTA_MIN + 3, - PT_APP_OTA_4 = PT_APP_OTA_MIN + 4, - PT_APP_OTA_5 = PT_APP_OTA_MIN + 5, - PT_APP_OTA_6 = PT_APP_OTA_MIN + 6, - PT_APP_OTA_7 = PT_APP_OTA_MIN + 7, - PT_APP_OTA_8 = PT_APP_OTA_MIN + 8, - PT_APP_OTA_9 = PT_APP_OTA_MIN + 9, - PT_APP_OTA_10 = PT_APP_OTA_MIN + 10, - PT_APP_OTA_11 = PT_APP_OTA_MIN + 11, - PT_APP_OTA_12 = PT_APP_OTA_MIN + 12, - PT_APP_OTA_13 = PT_APP_OTA_MIN + 13, - PT_APP_OTA_14 = PT_APP_OTA_MIN + 14, - PT_APP_OTA_15 = PT_APP_OTA_MIN + 15, - PT_APP_OTA_MAX = PT_APP_MASK | 0x1f, - PT_APP_TEST = PT_APP_MASK | 0x20, - PT_APP_ANY = PT_APP_MASK | 0xff, +typedef enum { + ESP_PARTITION_APP_MASK = 0x0000, + ESP_PARTITION_APP_FACTORY = ESP_PARTITION_APP_MASK | 0x00, + ESP_PARTITION_APP_OTA_MIN = ESP_PARTITION_APP_MASK | 0x10, + ESP_PARTITION_APP_OTA_0 = ESP_PARTITION_APP_OTA_MIN + 0, + ESP_PARTITION_APP_OTA_1 = ESP_PARTITION_APP_OTA_MIN + 1, + ESP_PARTITION_APP_OTA_2 = ESP_PARTITION_APP_OTA_MIN + 2, + ESP_PARTITION_APP_OTA_3 = ESP_PARTITION_APP_OTA_MIN + 3, + ESP_PARTITION_APP_OTA_4 = ESP_PARTITION_APP_OTA_MIN + 4, + ESP_PARTITION_APP_OTA_5 = ESP_PARTITION_APP_OTA_MIN + 5, + ESP_PARTITION_APP_OTA_6 = ESP_PARTITION_APP_OTA_MIN + 6, + ESP_PARTITION_APP_OTA_7 = ESP_PARTITION_APP_OTA_MIN + 7, + ESP_PARTITION_APP_OTA_8 = ESP_PARTITION_APP_OTA_MIN + 8, + ESP_PARTITION_APP_OTA_9 = ESP_PARTITION_APP_OTA_MIN + 9, + ESP_PARTITION_APP_OTA_10 = ESP_PARTITION_APP_OTA_MIN + 10, + ESP_PARTITION_APP_OTA_11 = ESP_PARTITION_APP_OTA_MIN + 11, + ESP_PARTITION_APP_OTA_12 = ESP_PARTITION_APP_OTA_MIN + 12, + ESP_PARTITION_APP_OTA_13 = ESP_PARTITION_APP_OTA_MIN + 13, + ESP_PARTITION_APP_OTA_14 = ESP_PARTITION_APP_OTA_MIN + 14, + ESP_PARTITION_APP_OTA_15 = ESP_PARTITION_APP_OTA_MIN + 15, + ESP_PARTITION_APP_OTA_MAX = ESP_PARTITION_APP_MASK | 0x1f, + ESP_PARTITION_APP_TEST = ESP_PARTITION_APP_MASK | 0x20, + ESP_PARTITION_APP_ANY = ESP_PARTITION_APP_MASK | 0xff, - PT_DATA_MASK = 0x0100, - PT_DATA_OTA = PT_DATA_MASK | 0x00, - PT_DATA_RF = PT_DATA_MASK | 0x01, - PT_DATA_WIFI = PT_DATA_MASK | 0x02, - PT_DATA_ANY = PT_DATA_MASK | 0xff, + ESP_PARTITION_DATA_MASK = 0x0100, + ESP_PARTITION_DATA_OTA = ESP_PARTITION_DATA_MASK | 0x00, + ESP_PARTITION_DATA_RF = ESP_PARTITION_DATA_MASK | 0x01, + ESP_PARTITION_DATA_WIFI = ESP_PARTITION_DATA_MASK | 0x02, + ESP_PARTITION_DATA_ANY = ESP_PARTITION_DATA_MASK | 0xff, - PT_FILESYSTEM_MASK = 0x0200, - PT_FILESYSTEM_ESPHTTPD = 0x0200, - PT_FILESYSTEM_FAT = 0x0201, - PT_FILESYSTEM_SPIFFS = 0x0202, - PT_FILESYSTEM_ANY = 0x20ff, + ESP_PARTITION_FILESYSTEM_MASK = 0x0200, + ESP_PARTITION_FILESYSTEM_ESPHTTPD = 0x0200, + ESP_PARTITION_FILESYSTEM_FAT = 0x0201, + ESP_PARTITION_FILESYSTEM_SPIFFS = 0x0202, + ESP_PARTITION_FILESYSTEM_ANY = 0x20ff, - PT_END = 0xffff -}; + ESP_PARTITION_END = 0xffff +} esp_partition_type_t; -#define PT_APP_OTA(i) ((esp_partition_type_t)(PT_APP_OTA_MIN + ((i) & 0xf))) +#define ESP_PARTITION_APP_OTA(i) ((esp_partition_type_t)(ESP_PARTITION_APP_OTA_MIN + ((i) & 0xf))) -typedef struct esp_partition_iterator_opaque_t* esp_partition_iterator_t; +typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t; + +typedef struct { + esp_partition_type_t type; + uint32_t address; + uint32_t size; + char label[17]; + bool encrypted; +} esp_partition_t; /** * @brief Find partition based on one or more parameters * * @param type Partition type, one of esp_partition_type_t values * To find all app partitions or all filesystem partitions, - * use PT_APP_ANY or PT_FILESYSTEM_ANY, respectively. + * use ESP_PARTITION_APP_ANY or ESP_PARTITION_FILESYSTEM_ANY, + * respectively. * @param label (optional) Partition label. Set this value if looking * for partition with a specific name. Pass NULL otherwise. * @@ -81,24 +92,47 @@ typedef struct esp_partition_iterator_opaque_t* esp_partition_iterator_t; */ esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, const char* label); +/** + * @brief Find first partition based on one or more parameters + * + * @param type Partition type, one of esp_partition_type_t values + * To find all app partitions or all filesystem partitions, + * use ESP_PARTITION_APP_ANY or ESP_PARTITION_FILESYSTEM_ANY, + * respectively. + * @param label (optional) Partition label. Set this value if looking + * for partition with a specific name. Pass NULL otherwise. + * + * @return pointer to esp_partition_t structure, or NULL if no parition is found. + * This pointer is valid for the lifetime of the application. + */ +const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, const char* label); + +/** + * @brief Get esp_partition_t structure for given partition + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return pointer to esp_partition_t structure. This pointer is valid for the lifetime + * of the application. + */ +const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator); /** * @brief Move partition iterator to the next partition found * - * Any pointers obtained using esp_partition_label function for this iterator - * will be invalid after this call. + * Any copies of the iterator will be invalid after this call. * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * - * @return iterator pointing to the next partition found, or NULL if no more - * partitions were found. - * + * @return NULL if no partition was found, valid esp_partition_iterator_t otherwise. */ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t iterator); /** * @brief Get partition type * + * @note This is a helper function built around esp_partition_get. + * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * * @return esp_partition_type_t value for partition pointed to by the iterator. @@ -108,6 +142,8 @@ esp_partition_type_t esp_partition_type(esp_partition_iterator_t iterator); /** * @brief Get partition size * + * @note This is a helper function built around esp_partition_get. + * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * * @return partition size, in bytes @@ -117,6 +153,8 @@ uint32_t esp_partition_size(esp_partition_iterator_t iterator); /** * @brief Get partition address * + * @note This is a helper function built around esp_partition_get. + * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * * @return flash address of partition start @@ -126,11 +164,12 @@ uint32_t esp_partition_address(esp_partition_iterator_t iterator); /** * @brief Get partition label * + * @note This is a helper function built around esp_partition_get. + * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * * @return pointer to a zero-terminated string with partition label. - * The pointer is valid until the call to esp_partition_next or - * esp_partition_iterator_release for the given iterator. + * The pointer is valid for the lifetime of the application. */ const char* esp_partition_label(esp_partition_iterator_t iterator); @@ -145,8 +184,8 @@ const char* esp_partition_label(esp_partition_iterator_t iterator); * @param size Size of data to be read, in bytes. * * @return ESP_OK, if data was read successfully; - * ESP_INVALID_ARG, if iterator or src are NULL; - * ESP_INVALID_SIZE, if read would go out of bounds of the partition; + * ESP_ERR_INVALID_ARG, if iterator or src are NULL; + * ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition; * or one of error codes from lower-level flash driver. */ esp_err_t esp_partition_read(esp_partition_iterator_t iterator, @@ -166,8 +205,8 @@ esp_err_t esp_partition_read(esp_partition_iterator_t iterator, * esp_partition_erase_range call. * * @return ESP_OK, if data was written successfully; - * ESP_INVALID_ARG, if iterator or dst are NULL; - * ESP_INVALID_SIZE, if write would go out of bounds of the partition; + * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; + * ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition; * or one of error codes from lower-level flash driver. */ esp_err_t esp_partition_write(esp_partition_iterator_t iterator, @@ -183,13 +222,28 @@ esp_err_t esp_partition_write(esp_partition_iterator_t iterator, * Must be divisible by 4 kilobytes. * * @return ESP_OK, if the range was erased successfully; - * ESP_INVALID_ARG, if iterator or dst are NULL; - * ESP_INVALID_SIZE, if erase would go out of bounds of the partition; + * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; + * ESP_ERR_INVALID_SIZE, if erase would go out of bounds of the partition; * or one of error codes from lower-level flash driver. */ esp_err_t esp_partition_erase_range(esp_partition_iterator_t iterator, uint32_t start_addr, uint32_t size); +/** + * @brief Configure MMU to map partition into data memory + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @param offset Offset from the beginning of partition where mapping should start. + * Must be aligned to 64k. + * + * @param size Size of the area to be mapped. + * + * @return pointer to mapped memory, if successful + * NULL, if memory can not be mapped for any reason + */ +void* esp_partition_mmap(esp_partition_iterator_t iterator, uint32_t offset, uint32_t size); + /** * @brief Release partition iterator diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c new file mode 100644 index 000000000..381a1624b --- /dev/null +++ b/components/spi_flash/partition.c @@ -0,0 +1,221 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "esp_attr.h" +#include "esp_flash_data_types.h" +#include "esp_spi_flash.h" +#include "esp_partition.h" +#include "esp_log.h" + + +#ifndef NDEBUG +// Enable built-in checks in queue.h in debug builds +#define INVARIANTS +#endif +#include "rom/queue.h" + + +typedef struct partition_list_item_ { + esp_partition_t info; + SLIST_ENTRY(partition_list_item_) next; +} partition_list_item_t; + +typedef struct esp_partition_iterator_opaque_ { + esp_partition_type_t type; // requested type + const char* label; // requested label (can be NULL) + partition_list_item_t* next_item; // next item to iterate to + esp_partition_t* info; // pointer to info (it is redundant, but makes code more readable) +} esp_partition_iterator_opaque_t; + + +static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, const char* label); +static esp_err_t load_partitions(); + + +static SLIST_HEAD(partition_list_head_, partition_list_item_) s_partition_list = + SLIST_HEAD_INITIALIZER(s_partition_list); +static _lock_t s_partition_list_lock; + + +static uint32_t get_major_type(esp_partition_type_t type) +{ + return (type >> 8) & 0xff; +} + +static uint32_t get_minor_type(esp_partition_type_t type) +{ + return type & 0xff; +} + +esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, + const char* label) +{ + if (SLIST_EMPTY(&s_partition_list)) { + // only lock if list is empty (and check again after acquiring lock) + _lock_acquire(&s_partition_list_lock); + esp_err_t err = ESP_OK; + if (SLIST_EMPTY(&s_partition_list)) { + err = load_partitions(); + } + _lock_release(&s_partition_list_lock); + if (err != ESP_OK) { + return NULL; + } + } + // create an iterator pointing to the start of the list + // (next item will be the first one) + esp_partition_iterator_t it = iterator_create(type, label); + // advance iterator to the next item which matches constraints + it = esp_partition_next(it); + // if nothing found, it == NULL and iterator has been released + return it; +} + +esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it) +{ + assert(it); + // iterator reached the end of linked list? + if (it->next_item == NULL) { + return NULL; + } + uint32_t requested_major_type = get_major_type(it->type); + uint32_t requested_minor_type = get_minor_type(it->type); + _lock_acquire(&s_partition_list_lock); + for (; it->next_item != NULL; it->next_item = SLIST_NEXT(it->next_item, next)) { + esp_partition_t* p = &it->next_item->info; + uint32_t it_major_type = get_major_type(p->type); + uint32_t it_minor_type = get_minor_type(p->type); + if (requested_major_type != it_major_type) { + continue; + } + if (requested_minor_type != 0xff && requested_minor_type != it_minor_type) { + continue; + } + if (it->label != NULL && strcmp(it->label, p->label) != 0) { + continue; + } + // all constraints match, bail out + break; + } + _lock_release(&s_partition_list_lock); + if (it->next_item == NULL) { + esp_partition_iterator_release(it); + return NULL; + } + it->info = &it->next_item->info; + it->next_item = SLIST_NEXT(it->next_item, next); + return it; +} + +const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, const char* label) +{ + esp_partition_iterator_t it = esp_partition_find(type, label); + if (it == NULL) { + return NULL; + } + const esp_partition_t* res = esp_partition_get(it); + esp_partition_iterator_release(it); + return res; +} + +void esp_partition_iterator_release(esp_partition_iterator_t iterator) +{ + // iterator == NULL is okay + free(iterator); +} + +const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator) +{ + assert(iterator != NULL); + return iterator->info; +} + +esp_partition_type_t esp_partition_type(esp_partition_iterator_t iterator) +{ + return esp_partition_get(iterator)->type; +} + +uint32_t esp_partition_size(esp_partition_iterator_t iterator) +{ + return esp_partition_get(iterator)->size; +} + +uint32_t esp_partition_address(esp_partition_iterator_t iterator) +{ + return esp_partition_get(iterator)->address; +} + +const char* esp_partition_label(esp_partition_iterator_t iterator) +{ + return esp_partition_get(iterator)->label; +} + +static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, const char* label) +{ + esp_partition_iterator_opaque_t* it = + (esp_partition_iterator_opaque_t*) malloc(sizeof(esp_partition_iterator_opaque_t)); + it->type = type; + it->label = label; + it->next_item = SLIST_FIRST(&s_partition_list); + it->info = NULL; + return it; +} + +// Create linked list of partition_list_item_t structures. +// This function is called only once, with s_partition_list_lock taken. +static esp_err_t load_partitions() +{ + const uint32_t* ptr; + spi_flash_mmap_handle_t handle; + // map 64kB block where partition table is located + esp_err_t err = spi_flash_mmap(ESP_PARTITION_TABLE_ADDR & 0xffff0000, + SPI_FLASH_SEC_SIZE, SPI_FLASH_MMAP_DATA, (const void**) &ptr, &handle); + if (err != ESP_OK) { + return err; + } + // calculate partition address within mmap-ed region + const esp_partition_info_t* it = (const esp_partition_info_t*) + (ptr + (ESP_PARTITION_TABLE_ADDR & 0xffff) / sizeof(*ptr)); + const esp_partition_info_t* end = it + SPI_FLASH_SEC_SIZE / sizeof(*it); + // tail of the linked list of partitions + partition_list_item_t* last = NULL; + for (; it != end; ++it) { + if (it->magic != ESP_PARTITION_MAGIC) { + break; + } + // allocate new linked list item and populate it with data from partition table + partition_list_item_t* item = (partition_list_item_t*) malloc(sizeof(partition_list_item_t)); + item->info.address = it->pos.offset; + item->info.size = it->pos.size; + item->info.type = (it->type << 8) | it->subtype; + item->info.encrypted = false; + // it->label may not be zero-terminated + strncpy(item->info.label, (const char*) it->label, sizeof(it->label)); + item->info.label[sizeof(it->label)] = 0; + // add it to the list + if (last == NULL) { + SLIST_INSERT_HEAD(&s_partition_list, item, next); + } else { + SLIST_INSERT_AFTER(last, item, next); + } + } + spi_flash_munmap(handle); + return ESP_OK; +} From 2c5340d47e2a482c2fc58ae02178332775c3a699 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 21 Oct 2016 17:28:50 +0800 Subject: [PATCH 089/110] spi_flash: change argument types spi_flash_read and spi_flash_write currently have a limitation that source and destination must be word-aligned. This can be fixed by adding code paths for various unaligned scenarios, but function signatures also need to be adjusted. As a first step (since we are pre-1.0 and can still change function signatures) alignment checks are added, and pointer types are relaxed to uint8_t. Later we will add handling of unaligned operations. This change also introduces spi_flash_erase_range and spi_flash_get_chip_size functions. We probably need something like spi_flash_chip_size_detect which will detect actual chip size. This is to allow single application binary to be used on a variety of boards and modules. --- components/nvs_flash/src/nvs_page.cpp | 27 ++++--- .../nvs_flash/test/spi_flash_emulation.cpp | 10 +-- .../nvs_flash/test/spi_flash_emulation.h | 6 +- .../test/test_spi_flash_emulation.cpp | 20 ++--- components/spi_flash/flash_ops.c | 81 +++++++++++++++++-- components/spi_flash/include/esp_spi_flash.h | 50 +++++++++--- 6 files changed, 146 insertions(+), 48 deletions(-) diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index f4fc5430c..9ba478e21 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -37,7 +37,7 @@ esp_err_t Page::load(uint32_t sectorNumber) mErasedEntryCount = 0; Header header; - auto rc = spi_flash_read(mBaseAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_read(mBaseAddress, reinterpret_cast(&header), sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -48,7 +48,7 @@ esp_err_t Page::load(uint32_t sectorNumber) // reading the whole page takes ~40 times less than erasing it uint32_t line[8]; for (uint32_t i = 0; i < SPI_FLASH_SEC_SIZE; i += sizeof(line)) { - rc = spi_flash_read(mBaseAddress + i, line, sizeof(line)); + rc = spi_flash_read(mBaseAddress + i, reinterpret_cast(line), sizeof(line)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -86,7 +86,7 @@ esp_err_t Page::load(uint32_t sectorNumber) esp_err_t Page::writeEntry(const Item& item) { - auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast(&item), sizeof(item)); + auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast(&item), sizeof(item)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -114,7 +114,7 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size) assert(mFirstUsedEntry != INVALID_ENTRY); const uint16_t count = size / ENTRY_SIZE; - auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast(data), static_cast(size)); + auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), data, size); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -396,8 +396,8 @@ esp_err_t Page::mLoadEntryTable() if (mState == PageState::ACTIVE || mState == PageState::FULL || mState == PageState::FREEING) { - auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(), - static_cast(mEntryTable.byteSize())); + auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, reinterpret_cast(mEntryTable.data()), + mEntryTable.byteSize()); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -435,7 +435,7 @@ esp_err_t Page::mLoadEntryTable() while (mNextFreeEntry < ENTRY_COUNT) { uint32_t entryAddress = getEntryAddress(mNextFreeEntry); uint32_t header; - auto rc = spi_flash_read(entryAddress, &header, sizeof(header)); + auto rc = spi_flash_read(entryAddress, reinterpret_cast(&header), sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -559,7 +559,7 @@ esp_err_t Page::initialize() header.mSeqNumber = mSeqNumber; header.mCrc32 = header.calculateCrc32(); - auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&header), sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -577,7 +577,8 @@ esp_err_t Page::alterEntryState(size_t index, EntryState state) mEntryTable.set(index, state); size_t wordToWrite = mEntryTable.getWordIndex(index); uint32_t word = mEntryTable.data()[wordToWrite]; - auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordToWrite) * 4, &word, 4); + auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordToWrite) * 4, + reinterpret_cast(&word), sizeof(word)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -600,7 +601,8 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state) } if (nextWordIndex != wordIndex) { uint32_t word = mEntryTable.data()[wordIndex]; - auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordIndex) * 4, &word, 4); + auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordIndex) * 4, + reinterpret_cast(&word), 4); if (rc != ESP_OK) { return rc; } @@ -612,7 +614,8 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state) esp_err_t Page::alterPageState(PageState state) { - auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&state), sizeof(state)); + uint32_t state_val = static_cast(state); + auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&state_val), sizeof(state)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -623,7 +626,7 @@ esp_err_t Page::alterPageState(PageState state) esp_err_t Page::readEntry(size_t index, Item& dst) const { - auto rc = spi_flash_read(getEntryAddress(index), reinterpret_cast(&dst), sizeof(dst)); + auto rc = spi_flash_read(getEntryAddress(index), reinterpret_cast(&dst), sizeof(dst)); if (rc != ESP_OK) { return rc; } diff --git a/components/nvs_flash/test/spi_flash_emulation.cpp b/components/nvs_flash/test/spi_flash_emulation.cpp index 5185bd34c..bd1482268 100644 --- a/components/nvs_flash/test/spi_flash_emulation.cpp +++ b/components/nvs_flash/test/spi_flash_emulation.cpp @@ -22,7 +22,7 @@ void spi_flash_emulator_set(SpiFlashEmulator* e) s_emulator = e; } -esp_err_t spi_flash_erase_sector(uint16_t sec) +esp_err_t spi_flash_erase_sector(size_t sec) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; @@ -35,26 +35,26 @@ esp_err_t spi_flash_erase_sector(uint16_t sec) return ESP_OK; } -esp_err_t spi_flash_write(uint32_t des_addr, const uint32_t *src_addr, uint32_t size) +esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; } - if (!s_emulator->write(des_addr, src_addr, size)) { + if (!s_emulator->write(des_addr, reinterpret_cast(src_addr), size)) { return ESP_ERR_FLASH_OP_FAIL; } return ESP_OK; } -esp_err_t spi_flash_read(uint32_t src_addr, uint32_t *des_addr, uint32_t size) +esp_err_t spi_flash_read(size_t src_addr, uint8_t *des_addr, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; } - if (!s_emulator->read(des_addr, src_addr, size)) { + if (!s_emulator->read(reinterpret_cast(des_addr), src_addr, size)) { return ESP_ERR_FLASH_OP_FAIL; } diff --git a/components/nvs_flash/test/spi_flash_emulation.h b/components/nvs_flash/test/spi_flash_emulation.h index d5a242b24..ba50c4f9e 100644 --- a/components/nvs_flash/test/spi_flash_emulation.h +++ b/components/nvs_flash/test/spi_flash_emulation.h @@ -44,7 +44,7 @@ public: spi_flash_emulator_set(nullptr); } - bool read(uint32_t* dest, uint32_t srcAddr, size_t size) const + bool read(uint32_t* dest, size_t srcAddr, size_t size) const { if (srcAddr % 4 != 0 || size % 4 != 0 || @@ -60,7 +60,7 @@ public: return true; } - bool write(uint32_t dstAddr, const uint32_t* src, size_t size) + bool write(size_t dstAddr, const uint32_t* src, size_t size) { uint32_t sectorNumber = dstAddr/SPI_FLASH_SEC_SIZE; if (sectorNumber < mLowerSectorBound || sectorNumber >= mUpperSectorBound) { @@ -96,7 +96,7 @@ public: return true; } - bool erase(uint32_t sectorNumber) + bool erase(size_t sectorNumber) { size_t offset = sectorNumber * SPI_FLASH_SEC_SIZE / 4; if (offset > mData.size()) { diff --git a/components/nvs_flash/test/test_spi_flash_emulation.cpp b/components/nvs_flash/test/test_spi_flash_emulation.cpp index ea233da61..329e721ce 100644 --- a/components/nvs_flash/test/test_spi_flash_emulation.cpp +++ b/components/nvs_flash/test/test_spi_flash_emulation.cpp @@ -30,7 +30,7 @@ TEST_CASE("flash starts with all bytes == 0xff", "[spi_flash_emu]") uint8_t sector[SPI_FLASH_SEC_SIZE]; for (int i = 0; i < 4; ++i) { - CHECK(spi_flash_read(0, reinterpret_cast(sector), sizeof(sector)) == ESP_OK); + CHECK(spi_flash_read(0, sector, sizeof(sector)) == ESP_OK); for (auto v: sector) { CHECK(v == 0xff); } @@ -42,9 +42,9 @@ TEST_CASE("invalid writes are checked", "[spi_flash_emu]") SpiFlashEmulator emu(1); uint32_t val = 0; - CHECK(spi_flash_write(0, &val, 4) == ESP_OK); + CHECK(spi_flash_write(0, reinterpret_cast(&val), 4) == ESP_OK); val = 1; - CHECK(spi_flash_write(0, &val, 4) == ESP_ERR_FLASH_OP_FAIL); + CHECK(spi_flash_write(0, reinterpret_cast(&val), 4) == ESP_ERR_FLASH_OP_FAIL); } @@ -53,11 +53,11 @@ TEST_CASE("out of bounds writes fail", "[spi_flash_emu]") SpiFlashEmulator emu(4); uint32_t vals[8]; std::fill_n(vals, 8, 0); - CHECK(spi_flash_write(0, vals, sizeof(vals)) == ESP_OK); + CHECK(spi_flash_write(0, reinterpret_cast(vals), sizeof(vals)) == ESP_OK); - CHECK(spi_flash_write(4*4096 - sizeof(vals), vals, sizeof(vals)) == ESP_OK); + CHECK(spi_flash_write(4*4096 - sizeof(vals), reinterpret_cast(vals), sizeof(vals)) == ESP_OK); - CHECK(spi_flash_write(4*4096 - sizeof(vals) + 4, vals, sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL); + CHECK(spi_flash_write(4*4096 - sizeof(vals) + 4, reinterpret_cast(vals), sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL); } @@ -65,9 +65,9 @@ TEST_CASE("after erase the sector is set to 0xff", "[spi_flash_emu]") { SpiFlashEmulator emu(4); uint32_t val1 = 0xab00cd12; - CHECK(spi_flash_write(0, &val1, sizeof(val1)) == ESP_OK); + CHECK(spi_flash_write(0, reinterpret_cast(&val1), sizeof(val1)) == ESP_OK); uint32_t val2 = 0x5678efab; - CHECK(spi_flash_write(4096 - 4, &val2, sizeof(val2)) == ESP_OK); + CHECK(spi_flash_write(4096 - 4, reinterpret_cast(&val2), sizeof(val2)) == ESP_OK); CHECK(emu.words()[0] == val1); CHECK(range_empty_n(emu.words() + 1, 4096 / 4 - 2)); @@ -83,7 +83,7 @@ TEST_CASE("after erase the sector is set to 0xff", "[spi_flash_emu]") TEST_CASE("read/write/erase operation times are calculated correctly", "[spi_flash_emu]") { SpiFlashEmulator emu(1); - uint32_t data[128]; + uint8_t data[512]; spi_flash_read(0, data, 4); CHECK(emu.getTotalTime() == 7); CHECK(emu.getReadOps() == 1); @@ -141,7 +141,7 @@ TEST_CASE("read/write/erase operation times are calculated correctly", "[spi_fla CHECK(emu.getTotalTime() == 37142); } -TEST_CASE("data is randomized predicatbly", "[spi_flash_emu]") +TEST_CASE("data is randomized predictably", "[spi_flash_emu]") { SpiFlashEmulator emu1(3); emu1.randomize(0x12345678); diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 99e8ef77f..512f6d20d 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -64,6 +64,11 @@ void spi_flash_init() #endif } +size_t spi_flash_get_chip_size() +{ + return g_rom_flashchip.chip_size; +} + SpiFlashOpResult IRAM_ATTR spi_flash_unlock() { static bool unlocked = false; @@ -77,28 +82,74 @@ SpiFlashOpResult IRAM_ATTR spi_flash_unlock() return SPI_FLASH_RESULT_OK; } -esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec) +esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) { + return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE); +} + +esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) +{ + if (start_addr % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + if (size % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_SIZE; + } + if (size + start_addr > spi_flash_get_chip_size()) { + return ESP_ERR_INVALID_SIZE; + } + size_t start = start_addr / SPI_FLASH_SEC_SIZE; + size_t end = start + size / SPI_FLASH_SEC_SIZE; + const size_t sectors_per_block = 16; COUNTER_START(); spi_flash_disable_interrupts_caches_and_other_cpu(); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { - rc = SPIEraseSector(sec); + for (size_t sector = start; sector != end && rc == SPI_FLASH_RESULT_OK; ) { + if (sector % sectors_per_block == 0 && end - sector > sectors_per_block) { + rc = SPIEraseBlock(sector / sectors_per_block); + sector += sectors_per_block; + COUNTER_ADD_BYTES(erase, sectors_per_block * SPI_FLASH_SEC_SIZE); + } + else { + rc = SPIEraseSector(sector); + ++sector; + COUNTER_ADD_BYTES(erase, SPI_FLASH_SEC_SIZE); + } + } } spi_flash_enable_interrupts_caches_and_other_cpu(); COUNTER_STOP(erase); return spi_flash_translate_rc(rc); } -esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size) +esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const uint8_t *src, size_t size) { + // TODO: replace this check with code which deals with unaligned sources + if (((ptrdiff_t) src) % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + // Destination alignment is also checked in ROM code, but we can give + // better error code here + // TODO: add handling of unaligned destinations + if (dest_addr % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + if (size % 4 != 0) { + return ESP_ERR_INVALID_SIZE; + } + // Out of bound writes are checked in ROM code, but we can give better + // error code here + if (dest_addr + size > g_rom_flashchip.chip_size) { + return ESP_ERR_INVALID_SIZE; + } COUNTER_START(); spi_flash_disable_interrupts_caches_and_other_cpu(); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { - rc = SPIWrite(dest_addr, src, (int32_t) size); + rc = SPIWrite((uint32_t) dest_addr, (const uint32_t*) src, (int32_t) size); COUNTER_ADD_BYTES(write, size); } spi_flash_enable_interrupts_caches_and_other_cpu(); @@ -106,11 +157,29 @@ esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uin return spi_flash_translate_rc(rc); } -esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size) +esp_err_t IRAM_ATTR spi_flash_read(size_t src_addr, uint8_t *dest, size_t size) { + // TODO: replace this check with code which deals with unaligned destinations + if (((ptrdiff_t) dest) % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + // Source alignment is also checked in ROM code, but we can give + // better error code here + // TODO: add handling of unaligned destinations + if (src_addr % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + if (size % 4 != 0) { + return ESP_ERR_INVALID_SIZE; + } + // Out of bound reads are checked in ROM code, but we can give better + // error code here + if (src_addr + size > g_rom_flashchip.chip_size) { + return ESP_ERR_INVALID_SIZE; + } COUNTER_START(); spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc = SPIRead(src_addr, dest, (int32_t) size); + SpiFlashOpResult rc = SPIRead((uint32_t) src_addr, (uint32_t*) dest, (int32_t) size); COUNTER_ADD_BYTES(read, size); spi_flash_enable_interrupts_caches_and_other_cpu(); COUNTER_STOP(read); diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 597e41857..ab66a4204 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -38,37 +38,63 @@ extern "C" { */ void spi_flash_init(); +/** + * @brief Get flash chip size, as set in binary image header + * + * @note This value does not necessarily match real flash size. + * + * @return size of flash chip, in bytes + */ +size_t spi_flash_get_chip_size(); + /** * @brief Erase the Flash sector. * - * @param uint16 sec : Sector number, the count starts at sector 0, 4KB per sector. + * @param sector Sector number, the count starts at sector 0, 4KB per sector. * * @return esp_err_t */ -esp_err_t spi_flash_erase_sector(uint16_t sec); +esp_err_t spi_flash_erase_sector(size_t sector); + +/** + * @brief Erase a range of flash sectors + * + * @param uint32_t start_address : Address where erase operation has to start. + * Must be 4kB-aligned + * @param uint32_t size : Size of erased range, in bytes. Must be divisible by 4kB. + * + * @return esp_err_t + */ +esp_err_t spi_flash_erase_range(size_t start_addr, size_t size); + /** * @brief Write data to Flash. * - * @param uint32 des_addr : destination address in Flash. - * @param uint32 *src_addr : source address of the data. - * @param uint32 size : length of data + * @note Both des_addr and src_addr have to be 4-byte aligned. + * This is a temporary limitation which will be removed. + * + * @param des_addr destination address in Flash + * @param src_addr source address of the data + * @param size length of data, in bytes * * @return esp_err_t */ -esp_err_t spi_flash_write(uint32_t des_addr, const uint32_t *src_addr, uint32_t size); +esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size); /** * @brief Read data from Flash. * - * @param uint32 src_addr : source address of the data in Flash. - * @param uint32 *des_addr : destination address. - * @param uint32 size : length of data + * @note Both des_addr and src_addr have to be 4-byte aligned. + * This is a temporary limitation which will be removed. + * + * @param src_addr source address of the data in Flash. + * @param des_addr destination address + * @param size length of data * * @return esp_err_t */ -esp_err_t spi_flash_read(uint32_t src_addr, uint32_t *des_addr, uint32_t size); - +esp_err_t spi_flash_read(size_t src_addr, uint8_t *des_addr, size_t size); /** * @brief Enumeration which specifies memory space requested in an mmap call @@ -135,7 +161,7 @@ void spi_flash_mmap_dump(); typedef struct { uint32_t count; // number of times operation was executed uint32_t time; // total time taken, in microseconds - uint32_t bytes; // total number of bytes, for read and write operations + uint32_t bytes; // total number of bytes } spi_flash_counter_t; typedef struct { From b6693225c188ab627a31bbe8e881bfa183f21c22 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 21 Oct 2016 18:44:57 +0800 Subject: [PATCH 090/110] spi_flash: implement partition API, drop trivial wrappers This implements esp_partition_read, esp_partition_write, esp_partition_erase_range, esp_partition_mmap. Also removed getters which didn't add much sugar after all. --- components/spi_flash/include/esp_partition.h | 213 +++++++++---------- components/spi_flash/include/esp_spi_flash.h | 1 + components/spi_flash/partition.c | 121 ++++++++--- 3 files changed, 186 insertions(+), 149 deletions(-) diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index 6bdba149f..89e523f9a 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -17,7 +17,9 @@ #include #include +#include #include "esp_err.h" +#include "esp_spi_flash.h" #ifdef __cplusplus extern "C" { @@ -128,123 +130,6 @@ const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator); */ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t iterator); -/** - * @brief Get partition type - * - * @note This is a helper function built around esp_partition_get. - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * - * @return esp_partition_type_t value for partition pointed to by the iterator. - */ -esp_partition_type_t esp_partition_type(esp_partition_iterator_t iterator); - -/** - * @brief Get partition size - * - * @note This is a helper function built around esp_partition_get. - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * - * @return partition size, in bytes - */ -uint32_t esp_partition_size(esp_partition_iterator_t iterator); - -/** - * @brief Get partition address - * - * @note This is a helper function built around esp_partition_get. - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * - * @return flash address of partition start - */ -uint32_t esp_partition_address(esp_partition_iterator_t iterator); - -/** - * @brief Get partition label - * - * @note This is a helper function built around esp_partition_get. - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * - * @return pointer to a zero-terminated string with partition label. - * The pointer is valid for the lifetime of the application. - */ -const char* esp_partition_label(esp_partition_iterator_t iterator); - -/** - * @brief Read data from the partition - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * @param src_offset Address of the data to be read, relative to the - * beginning of the partition. - * @param dst Pointer to the buffer where data should be stored. - * Must be non-NULL and at least 'size' bytes long. - * @param size Size of data to be read, in bytes. - * - * @return ESP_OK, if data was read successfully; - * ESP_ERR_INVALID_ARG, if iterator or src are NULL; - * ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition; - * or one of error codes from lower-level flash driver. - */ -esp_err_t esp_partition_read(esp_partition_iterator_t iterator, - uint32_t src_offset, uint8_t* dst, uint32_t size); - -/** - * @brief Write data to the partition - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * @param src Pointer to the source buffer. Must be non-NULL and - * at least 'size' bytes long. - * @param dst_offset Address where the data should be written, relative to the - * beginning of the partition. - * @param size Size of data to be written, in bytes. - * - * @note Prior to writing to flash memory, make sure it has been erased with - * esp_partition_erase_range call. - * - * @return ESP_OK, if data was written successfully; - * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; - * ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition; - * or one of error codes from lower-level flash driver. - */ -esp_err_t esp_partition_write(esp_partition_iterator_t iterator, - const uint8_t* src, uint32_t dst_offset, uint32_t size); - -/** - * @brief Erase part of the partition - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * @param start_addr Address where erase operation should start. Must be aligned - * to 4 kilobytes. - * @param size Size of the range which should be erased, in bytes. - * Must be divisible by 4 kilobytes. - * - * @return ESP_OK, if the range was erased successfully; - * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; - * ESP_ERR_INVALID_SIZE, if erase would go out of bounds of the partition; - * or one of error codes from lower-level flash driver. - */ -esp_err_t esp_partition_erase_range(esp_partition_iterator_t iterator, - uint32_t start_addr, uint32_t size); - -/** - * @brief Configure MMU to map partition into data memory - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * - * @param offset Offset from the beginning of partition where mapping should start. - * Must be aligned to 64k. - * - * @param size Size of the area to be mapped. - * - * @return pointer to mapped memory, if successful - * NULL, if memory can not be mapped for any reason - */ -void* esp_partition_mmap(esp_partition_iterator_t iterator, uint32_t offset, uint32_t size); - - /** * @brief Release partition iterator * @@ -256,6 +141,100 @@ void* esp_partition_mmap(esp_partition_iterator_t iterator, uint32_t offset, uin */ void esp_partition_iterator_release(esp_partition_iterator_t iterator); +/** + * @brief Read data from the partition + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param dst Pointer to the buffer where data should be stored. + * Pointer must be non-NULL and buffer must be at least 'size' bytes long. + * @param src_offset Address of the data to be read, relative to the + * beginning of the partition. + * @param size Size of data to be read, in bytes. + * + * @return ESP_OK, if data was read successfully; + * ESP_ERR_INVALID_ARG, if iterator or src are NULL; + * ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_read(const esp_partition_t* partition, + size_t src_offset, uint8_t* dst, size_t size); + +/** + * @brief Write data to the partition + * + * Before writing data to flash, corresponding region of flash needs to be erased. + * This can be done using esp_partition_erase_range function. + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param dst_offset Address where the data should be written, relative to the + * beginning of the partition. + * @param src Pointer to the source buffer. Pointer must be non-NULL and + * buffer must be at least 'size' bytes long. + * @param size Size of data to be written, in bytes. + * + * @note Prior to writing to flash memory, make sure it has been erased with + * esp_partition_erase_range call. + * + * @return ESP_OK, if data was written successfully; + * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; + * ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_write(const esp_partition_t* partition, + size_t dst_offset, const uint8_t* src, size_t size); + +/** + * @brief Erase part of the partition + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param start_addr Address where erase operation should start. Must be aligned + * to 4 kilobytes. + * @param size Size of the range which should be erased, in bytes. + * Must be divisible by 4 kilobytes. + * + * @return ESP_OK, if the range was erased successfully; + * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; + * ESP_ERR_INVALID_SIZE, if erase would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_erase_range(const esp_partition_t* partition, + uint32_t start_addr, uint32_t size); + +/** + * @brief Configure MMU to map partition into data memory + * + * Unlike spi_flash_mmap function, which requires a 64kB aligned base address, + * this function doesn't impose such a requirement. + * If offset results in a flash address which is not aligned to 64kB boundary, + * address will be rounded to the lower 64kB boundary, so that mapped region + * includes requested range. + * Pointer returned via out_ptr argument will be adjusted to point to the + * requested offset (not necessarily to the beginning of mmap-ed region). + * + * To release mapped memory, pass handle returned via out_handle argument to + * spi_flash_munmap function. + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param offset Offset from the beginning of partition where mapping should start. + * @param size Size of the area to be mapped. + * @param memory Memory space where the region should be mapped + * @param out_ptr Output, pointer to the mapped memory region + * @param out_handle Output, handle which should be used for spi_flash_munmap call + * + * @return ESP_OK, if successful + */ +esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset, uint32_t size, + spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle); + #ifdef __cplusplus } diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index ab66a4204..2aa92d9a5 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -16,6 +16,7 @@ #define ESP_SPI_FLASH_H #include +#include #include "esp_err.h" #include "sdkconfig.h" diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c index 381a1624b..d138f09b5 100644 --- a/components/spi_flash/partition.c +++ b/components/spi_flash/partition.c @@ -135,38 +135,6 @@ const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, const return res; } -void esp_partition_iterator_release(esp_partition_iterator_t iterator) -{ - // iterator == NULL is okay - free(iterator); -} - -const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator) -{ - assert(iterator != NULL); - return iterator->info; -} - -esp_partition_type_t esp_partition_type(esp_partition_iterator_t iterator) -{ - return esp_partition_get(iterator)->type; -} - -uint32_t esp_partition_size(esp_partition_iterator_t iterator) -{ - return esp_partition_get(iterator)->size; -} - -uint32_t esp_partition_address(esp_partition_iterator_t iterator) -{ - return esp_partition_get(iterator)->address; -} - -const char* esp_partition_label(esp_partition_iterator_t iterator) -{ - return esp_partition_get(iterator)->label; -} - static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, const char* label) { esp_partition_iterator_opaque_t* it = @@ -219,3 +187,92 @@ static esp_err_t load_partitions() spi_flash_munmap(handle); return ESP_OK; } + +void esp_partition_iterator_release(esp_partition_iterator_t iterator) +{ + // iterator == NULL is okay + free(iterator); +} + +const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator) +{ + assert(iterator != NULL); + return iterator->info; +} + +esp_err_t esp_partition_read(const esp_partition_t* partition, + size_t src_offset, uint8_t* dst, size_t size) +{ + assert(partition != NULL); + if (src_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (src_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + return spi_flash_read(partition->address + src_offset, dst, size); +} + +esp_err_t esp_partition_write(const esp_partition_t* partition, + size_t dst_offset, const uint8_t* src, size_t size) +{ + assert(partition != NULL); + if (dst_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (dst_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + return spi_flash_write(partition->address + dst_offset, src, size); +} + +esp_err_t esp_partition_erase_range(const esp_partition_t* partition, + size_t start_addr, size_t size) +{ + assert(partition != NULL); + if (start_addr > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (start_addr + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + if (size % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_SIZE; + } + if (start_addr % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + return spi_flash_erase_range(partition->address + start_addr, size); + +} + +/* + * Note: current implementation ignores the possibility of multiple regions in the same partition being + * mapped. Reference counting and address space re-use is delegated to spi_flash_mmap. + * + * If this becomes a performance issue (i.e. if we need to map multiple regions within the partition), + * we can add esp_partition_mmapv which will accept an array of offsets and sizes, and return array of + * mmaped pointers, and a single handle for all these regions. + */ +esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset, uint32_t size, + spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle) +{ + assert(partition != NULL); + if (offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + size_t phys_addr = partition->address + offset; + // offset within 64kB block + size_t region_offset = phys_addr & 0xffff; + size_t mmap_addr = phys_addr & 0xffff0000; + esp_err_t rc = spi_flash_mmap(mmap_addr, size, memory, out_ptr, out_handle); + // adjust returned pointer to point to the correct offset + if (rc == ESP_OK) { + *out_ptr = (void*) (((ptrdiff_t) *out_ptr) + region_offset); + } + return rc; +} From e229ec0340ba2a921223eec51169200cc10c8329 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 21 Oct 2016 19:33:42 +0800 Subject: [PATCH 091/110] spi_flash: check physical address in mmap against flash chip size --- components/spi_flash/flash_mmap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index 8636a2605..2165a784d 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -88,6 +88,9 @@ esp_err_t IRAM_ATTR spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mma if (src_addr & 0xffff) { return ESP_ERR_INVALID_ARG; } + if (src_addr + size > g_rom_flashchip.chip_size) { + return ESP_ERR_INVALID_ARG; + } spi_flash_disable_interrupts_caches_and_other_cpu(); if (s_mmap_page_refcnt[0] == 0) { spi_flash_mmap_init(); From e03b45eb0a9eb07eb1e78c7869af18a7faa386d5 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 25 Oct 2016 11:43:00 +0800 Subject: [PATCH 092/110] spi_flash: improve documentation --- .../esp32/include/esp_flash_data_types.h | 38 ++--- components/spi_flash/README.rst | 133 +++++++++++++++++- components/spi_flash/include/esp_spi_flash.h | 2 + 3 files changed, 152 insertions(+), 21 deletions(-) diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h index b16ee59f5..4bf886c84 100644 --- a/components/esp32/include/esp_flash_data_types.h +++ b/components/esp32/include/esp_flash_data_types.h @@ -24,7 +24,7 @@ extern "C" #define ESP_PARTITION_TABLE_ADDR 0x4000 #define ESP_PARTITION_MAGIC 0x50AA -/*spi mode,saved in third byte in flash */ +/* SPI flash mode, used in esp_image_header_t */ typedef enum { ESP_IMAGE_SPI_MODE_QIO, ESP_IMAGE_SPI_MODE_QOUT, @@ -34,7 +34,7 @@ typedef enum { ESP_IMAGE_SPI_MODE_SLOW_READ } esp_image_spi_mode_t; -/* spi speed*/ +/* SPI flash clock frequency */ enum { ESP_IMAGE_SPI_SPEED_40M, ESP_IMAGE_SPI_SPEED_26M, @@ -42,7 +42,7 @@ enum { ESP_IMAGE_SPI_SPEED_80M = 0xF } esp_image_spi_freq_t; -/*supported flash sizes*/ +/* Supported SPI flash sizes */ typedef enum { ESP_IMAGE_FLASH_SIZE_1MB = 0, ESP_IMAGE_FLASH_SIZE_2MB, @@ -52,22 +52,23 @@ typedef enum { ESP_IMAGE_FLASH_SIZE_MAX } esp_image_flash_size_t; +/* Main header of binary image */ typedef struct { - char magic; - char blocks; - char spi_mode; /* flag of flash read mode in unpackage and usage in future */ - char spi_speed: 4; /* low bit */ - char spi_size: 4; - unsigned int entry_addr; + uint8_t magic; + uint8_t blocks; + uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */ + uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */ + uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ + uint32_t entry_addr; uint8_t encrypt_flag; /* encrypt flag */ uint8_t secure_boot_flag; /* secure boot flag */ - char extra_header[14]; /* ESP32 additional header, unused by second bootloader */ + uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ } esp_image_header_t; -/* each header of flash bin block */ +/* Header of binary image segment */ typedef struct { - unsigned int load_addr; - unsigned int data_len; + uint32_t load_addr; + uint32_t data_len; } esp_image_section_header_t; @@ -85,13 +86,16 @@ typedef struct { uint32_t size; } esp_partition_pos_t; +/* Structure which describes the layout of partition table entry. + * See docs/partition_tables.rst for more information about individual fields. + */ typedef struct { uint16_t magic; - uint8_t type; /* partition Type */ - uint8_t subtype; /* part_subtype */ + uint8_t type; + uint8_t subtype; esp_partition_pos_t pos; - uint8_t label[16]; /* label for the partition */ - uint8_t reserved[4]; /* reserved */ + uint8_t label[16]; + uint8_t reserved[4]; } esp_partition_info_t; diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst index 22f98cf02..b479c3b0e 100644 --- a/components/spi_flash/README.rst +++ b/components/spi_flash/README.rst @@ -1,5 +1,128 @@ -Driver for SPI flash read/write/erase operations -================================================ +SPI flash related APIs +====================== + +Overview +-------- +Spi_flash component contains APIs related to reading, writing, erasing, +memory mapping data in the external SPI flash. It also has higher-level +APIs which work with partition table and partitions. + +Note that all the functionality is limited to the "main" flash chip, +i.e. the flash chip from which program runs. For ``spi_flash_*`` functions, +this is software limitation. Underlying ROM functions which work with SPI flash +do not have provisions for working with flash chips attached to SPI peripherals +other than SPI0. + +SPI flash access APIs +--------------------- + +This is the set of APIs for working with data in flash: + +- ``spi_flash_read`` used to read data from flash to RAM +- ``spi_flash_write`` used to write data from RAM to flash +- ``spi_flash_erase_sector`` used to erase individual sectors of flash +- ``spi_flash_erase_range`` used to erase range of addresses in flash +- ``spi_flash_get_chip_size`` returns flash chip size, in bytes, as configured in menuconfig + +There are some data alignment limitations which need to be considered when using +spi_flash_read/spi_flash_write functions: + +- buffer in RAM must be 4-byte aligned +- size must be 4-byte aligned +- address in flash must be 4-byte aligned + +These alignment limitations are purely software, and should be removed in future +versions. + +It is assumed that correct SPI flash chip size is set at compile time using +menuconfig. While run-time detection of SPI flash chip size is possible, it is +not implemented yet. Applications which need this (e.g. to provide one firmware +binary for different flash sizes) can do flash chip size detection and set +the correct flash chip size in ``chip_size`` member of ``g_rom_flashchip`` +structure. This size is used by ``spi_flash_*`` functions for bounds checking. + +SPI flash APIs disable instruction and data caches while reading/writing/erasing. +See implementation notes below on details how this happens. For application +this means that at some periods of time, code can not be run from flash, +and constant data can not be fetched from flash by the CPU. This is not an +issue for normal code which runs in a task, because SPI flash APIs prevent +other tasks from running while caches are disabled. This is an issue for +interrupt handlers, which can still be called while flash operation is in +progress. If the interrupt handler is not placed into IRAM, there is a +possibility that interrupt will happen at the time when caches are disabled, +which will cause an illegal instruction exception. + +To prevent this, make sure that all ISR code, and all functions called from ISR +code are placed into IRAM, or are located in ROM. Most useful C library +functions are located in ROM, so they can be called from ISR. + +To place a function into IRAM, use ``IRAM_ATTR`` attribute, e.g.:: + + #include "esp_attr.h" + + void IRAM_ATTR gpio_isr_handler(void* arg) + { + // ... + } + +When flash encryption is enabled, ``spi_flash_read`` will read data as it is +stored in flash (without decryption), and ``spi_flash_write`` will write data +in plain text. In other words, ``spi_flash_read/write`` APIs don't have +provisions to deal with encrypted data. + + +Partition table APIs +-------------------- + +ESP-IDF uses partition table to maintain information about various regions of +SPI flash memory (bootloader, various application binaries, data, filesystems). +More information about partition tables can be found in docs/partition_tables.rst. + +This component provides APIs to enumerate partitions found in the partition table +and perform operations on them. These functions are declared in ``esp_partition.h``: + +- ``esp_partition_find`` used to search partition table for entries with specific type, returns an opaque iterator +- ``esp_partition_get`` returns a structure describing the partition, for the given iterator +- ``esp_partition_next`` advances iterator to the next partition found +- ``esp_partition_iterator_release`` releases iterator returned by ``esp_partition_find`` +- ``esp_partition_find_first`` is a convenience function which returns structure describing the first partition found by esp_partition_find +- ``esp_partition_read``, ``esp_partition_write``, ``esp_partition_erase_range`` are equivalent to ``spi_flash_read``, ``spi_flash_write``, ``spi_flash_erase_range``, but operate within partition boundaries + +Most application code should use ``esp_partition_*`` APIs instead of lower level +``spi_flash_*`` APIs. Partition APIs do bounds checking and calculate correct +offsets in flash based on data stored in partition table. + +Memory mapping APIs +------------------- + +ESP32 features memory hardware which allows regions of flash memory to be mapped +into instruction and data address spaces. This mapping works only for read operations, +it is not possible to modify contents of flash memory by writing to mapped memory +region. Mapping happens in 64KB pages. Memory mapping hardware can map up to +4 megabytes of flash into data address space, and up to 16 megabytes of flash into +instruction address space. See the technical reference manual for more details +about memory mapping hardware. + +Note that some number of 64KB pages is used to map the application +itself into memory, so the actual number of available 64KB pages may be less. + +Reading data from flash using a memory mapped region is the only way to decrypt +contents of flash when flash encryption is enabled. Decryption is performed at +hardware level. + +Memory mapping APIs are declared in ``esp_spi_flash.h`` and ``esp_partition.h``: + +- ``spi_flash_mmap`` maps a region of physical flash addresses into instruction space or data space of the CPU +- ``spi_flash_munmap`` unmaps previously mapped region +- ``esp_partition_mmap`` maps part of a partition into the instruction space or data space of the CPU + +Differences between ``spi_flash_mmap`` and ``esp_partition_mmap`` are as follows: + +- ``spi_flash_mmap`` must be given a 64KB aligned physical address +- ``esp_partition_mmap`` may be given an arbitrary offset within the partition, it will adjust returned pointer to mapped memory as necessary + +Note that because memory mapping happens in 64KB blocks, it may be possible to +read data outside of the partition provided to ``esp_partition_mmap``. Implementation notes -------------------- @@ -19,8 +142,10 @@ signals that cache is disabled by setting s_flash_op_can_start flag. Then the task on CPU A disables cache as well, and proceeds to execute flash operation. -While flash operation is running, interrupts can still run on CPU B. -We assume that all interrupt code is placed into RAM. +While flash operation is running, interrupts can still run on CPUs A and B. +We assume that all interrupt code is placed into RAM. Once interrupt allocation +API is added, we should add a flag to request interrupt to be disabled for +the duration of flash operations. Once flash operation is complete, function on CPU A sets another flag, s_flash_op_complete, to let the task on CPU B know that it can re-enable diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 2aa92d9a5..16b50a718 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -35,6 +35,8 @@ extern "C" { * * This function must be called exactly once, before any other * spi_flash_* functions are called. + * Currently this function is called from startup code. There is + * no need to call it from application code. * */ void spi_flash_init(); From 9f0f05d5201aeed9390f94b50f5cf65403040729 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 27 Oct 2016 00:46:33 +0800 Subject: [PATCH 093/110] spi_flash: change pointer type to void* --- components/nvs_flash/src/nvs_page.cpp | 20 +++++++++---------- .../nvs_flash/test/spi_flash_emulation.cpp | 4 ++-- .../test/test_spi_flash_emulation.cpp | 14 ++++++------- components/spi_flash/flash_ops.c | 4 ++-- components/spi_flash/include/esp_partition.h | 4 ++-- components/spi_flash/include/esp_spi_flash.h | 4 ++-- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index 9ba478e21..a5e058758 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -37,7 +37,7 @@ esp_err_t Page::load(uint32_t sectorNumber) mErasedEntryCount = 0; Header header; - auto rc = spi_flash_read(mBaseAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_read(mBaseAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -48,7 +48,7 @@ esp_err_t Page::load(uint32_t sectorNumber) // reading the whole page takes ~40 times less than erasing it uint32_t line[8]; for (uint32_t i = 0; i < SPI_FLASH_SEC_SIZE; i += sizeof(line)) { - rc = spi_flash_read(mBaseAddress + i, reinterpret_cast(line), sizeof(line)); + rc = spi_flash_read(mBaseAddress + i, line, sizeof(line)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -86,7 +86,7 @@ esp_err_t Page::load(uint32_t sectorNumber) esp_err_t Page::writeEntry(const Item& item) { - auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast(&item), sizeof(item)); + auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), &item, sizeof(item)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -396,7 +396,7 @@ esp_err_t Page::mLoadEntryTable() if (mState == PageState::ACTIVE || mState == PageState::FULL || mState == PageState::FREEING) { - auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, reinterpret_cast(mEntryTable.data()), + auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(), mEntryTable.byteSize()); if (rc != ESP_OK) { mState = PageState::INVALID; @@ -435,7 +435,7 @@ esp_err_t Page::mLoadEntryTable() while (mNextFreeEntry < ENTRY_COUNT) { uint32_t entryAddress = getEntryAddress(mNextFreeEntry); uint32_t header; - auto rc = spi_flash_read(entryAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_read(entryAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -559,7 +559,7 @@ esp_err_t Page::initialize() header.mSeqNumber = mSeqNumber; header.mCrc32 = header.calculateCrc32(); - auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_write(mBaseAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -578,7 +578,7 @@ esp_err_t Page::alterEntryState(size_t index, EntryState state) size_t wordToWrite = mEntryTable.getWordIndex(index); uint32_t word = mEntryTable.data()[wordToWrite]; auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordToWrite) * 4, - reinterpret_cast(&word), sizeof(word)); + &word, sizeof(word)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -602,7 +602,7 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state) if (nextWordIndex != wordIndex) { uint32_t word = mEntryTable.data()[wordIndex]; auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordIndex) * 4, - reinterpret_cast(&word), 4); + &word, 4); if (rc != ESP_OK) { return rc; } @@ -615,7 +615,7 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state) esp_err_t Page::alterPageState(PageState state) { uint32_t state_val = static_cast(state); - auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&state_val), sizeof(state)); + auto rc = spi_flash_write(mBaseAddress, &state_val, sizeof(state)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -626,7 +626,7 @@ esp_err_t Page::alterPageState(PageState state) esp_err_t Page::readEntry(size_t index, Item& dst) const { - auto rc = spi_flash_read(getEntryAddress(index), reinterpret_cast(&dst), sizeof(dst)); + auto rc = spi_flash_read(getEntryAddress(index), &dst, sizeof(dst)); if (rc != ESP_OK) { return rc; } diff --git a/components/nvs_flash/test/spi_flash_emulation.cpp b/components/nvs_flash/test/spi_flash_emulation.cpp index bd1482268..914efc145 100644 --- a/components/nvs_flash/test/spi_flash_emulation.cpp +++ b/components/nvs_flash/test/spi_flash_emulation.cpp @@ -35,7 +35,7 @@ esp_err_t spi_flash_erase_sector(size_t sec) return ESP_OK; } -esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size) +esp_err_t spi_flash_write(size_t des_addr, const void *src_addr, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; @@ -48,7 +48,7 @@ esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size) return ESP_OK; } -esp_err_t spi_flash_read(size_t src_addr, uint8_t *des_addr, size_t size) +esp_err_t spi_flash_read(size_t src_addr, void *des_addr, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; diff --git a/components/nvs_flash/test/test_spi_flash_emulation.cpp b/components/nvs_flash/test/test_spi_flash_emulation.cpp index 329e721ce..0c77aa966 100644 --- a/components/nvs_flash/test/test_spi_flash_emulation.cpp +++ b/components/nvs_flash/test/test_spi_flash_emulation.cpp @@ -42,9 +42,9 @@ TEST_CASE("invalid writes are checked", "[spi_flash_emu]") SpiFlashEmulator emu(1); uint32_t val = 0; - CHECK(spi_flash_write(0, reinterpret_cast(&val), 4) == ESP_OK); + CHECK(spi_flash_write(0, &val, 4) == ESP_OK); val = 1; - CHECK(spi_flash_write(0, reinterpret_cast(&val), 4) == ESP_ERR_FLASH_OP_FAIL); + CHECK(spi_flash_write(0, &val, 4) == ESP_ERR_FLASH_OP_FAIL); } @@ -53,11 +53,11 @@ TEST_CASE("out of bounds writes fail", "[spi_flash_emu]") SpiFlashEmulator emu(4); uint32_t vals[8]; std::fill_n(vals, 8, 0); - CHECK(spi_flash_write(0, reinterpret_cast(vals), sizeof(vals)) == ESP_OK); + CHECK(spi_flash_write(0, vals, sizeof(vals)) == ESP_OK); - CHECK(spi_flash_write(4*4096 - sizeof(vals), reinterpret_cast(vals), sizeof(vals)) == ESP_OK); + CHECK(spi_flash_write(4*4096 - sizeof(vals), vals, sizeof(vals)) == ESP_OK); - CHECK(spi_flash_write(4*4096 - sizeof(vals) + 4, reinterpret_cast(vals), sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL); + CHECK(spi_flash_write(4*4096 - sizeof(vals) + 4, vals, sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL); } @@ -65,9 +65,9 @@ TEST_CASE("after erase the sector is set to 0xff", "[spi_flash_emu]") { SpiFlashEmulator emu(4); uint32_t val1 = 0xab00cd12; - CHECK(spi_flash_write(0, reinterpret_cast(&val1), sizeof(val1)) == ESP_OK); + CHECK(spi_flash_write(0, &val1, sizeof(val1)) == ESP_OK); uint32_t val2 = 0x5678efab; - CHECK(spi_flash_write(4096 - 4, reinterpret_cast(&val2), sizeof(val2)) == ESP_OK); + CHECK(spi_flash_write(4096 - 4, &val2, sizeof(val2)) == ESP_OK); CHECK(emu.words()[0] == val1); CHECK(range_empty_n(emu.words() + 1, 4096 / 4 - 2)); diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 512f6d20d..ae72568aa 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -124,7 +124,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) return spi_flash_translate_rc(rc); } -esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const uint8_t *src, size_t size) +esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const void *src, size_t size) { // TODO: replace this check with code which deals with unaligned sources if (((ptrdiff_t) src) % 4 != 0) { @@ -157,7 +157,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const uint8_t *src, size_t return spi_flash_translate_rc(rc); } -esp_err_t IRAM_ATTR spi_flash_read(size_t src_addr, uint8_t *dest, size_t size) +esp_err_t IRAM_ATTR spi_flash_read(size_t src_addr, void *dest, size_t size) { // TODO: replace this check with code which deals with unaligned destinations if (((ptrdiff_t) dest) % 4 != 0) { diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index 89e523f9a..432575f19 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -159,7 +159,7 @@ void esp_partition_iterator_release(esp_partition_iterator_t iterator); * or one of error codes from lower-level flash driver. */ esp_err_t esp_partition_read(const esp_partition_t* partition, - size_t src_offset, uint8_t* dst, size_t size); + size_t src_offset, void* dst, size_t size); /** * @brief Write data to the partition @@ -185,7 +185,7 @@ esp_err_t esp_partition_read(const esp_partition_t* partition, * or one of error codes from lower-level flash driver. */ esp_err_t esp_partition_write(const esp_partition_t* partition, - size_t dst_offset, const uint8_t* src, size_t size); + size_t dst_offset, const void* src, size_t size); /** * @brief Erase part of the partition diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 16b50a718..df0a8decc 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -83,7 +83,7 @@ esp_err_t spi_flash_erase_range(size_t start_addr, size_t size); * * @return esp_err_t */ -esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size); +esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); /** * @brief Read data from Flash. @@ -97,7 +97,7 @@ esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size) * * @return esp_err_t */ -esp_err_t spi_flash_read(size_t src_addr, uint8_t *des_addr, size_t size); +esp_err_t spi_flash_read(size_t src, void *dest, size_t size); /** * @brief Enumeration which specifies memory space requested in an mmap call From c581229e1d47da7b531433905896e4820ccdf54e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 27 Oct 2016 00:48:10 +0800 Subject: [PATCH 094/110] partition API: separate type and subtype into two enums --- components/spi_flash/include/esp_partition.h | 91 ++++++++++---------- components/spi_flash/partition.c | 41 ++++----- 2 files changed, 62 insertions(+), 70 deletions(-) diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index 432575f19..e2f289eb7 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -26,51 +26,52 @@ extern "C" { #endif typedef enum { - ESP_PARTITION_APP_MASK = 0x0000, - ESP_PARTITION_APP_FACTORY = ESP_PARTITION_APP_MASK | 0x00, - ESP_PARTITION_APP_OTA_MIN = ESP_PARTITION_APP_MASK | 0x10, - ESP_PARTITION_APP_OTA_0 = ESP_PARTITION_APP_OTA_MIN + 0, - ESP_PARTITION_APP_OTA_1 = ESP_PARTITION_APP_OTA_MIN + 1, - ESP_PARTITION_APP_OTA_2 = ESP_PARTITION_APP_OTA_MIN + 2, - ESP_PARTITION_APP_OTA_3 = ESP_PARTITION_APP_OTA_MIN + 3, - ESP_PARTITION_APP_OTA_4 = ESP_PARTITION_APP_OTA_MIN + 4, - ESP_PARTITION_APP_OTA_5 = ESP_PARTITION_APP_OTA_MIN + 5, - ESP_PARTITION_APP_OTA_6 = ESP_PARTITION_APP_OTA_MIN + 6, - ESP_PARTITION_APP_OTA_7 = ESP_PARTITION_APP_OTA_MIN + 7, - ESP_PARTITION_APP_OTA_8 = ESP_PARTITION_APP_OTA_MIN + 8, - ESP_PARTITION_APP_OTA_9 = ESP_PARTITION_APP_OTA_MIN + 9, - ESP_PARTITION_APP_OTA_10 = ESP_PARTITION_APP_OTA_MIN + 10, - ESP_PARTITION_APP_OTA_11 = ESP_PARTITION_APP_OTA_MIN + 11, - ESP_PARTITION_APP_OTA_12 = ESP_PARTITION_APP_OTA_MIN + 12, - ESP_PARTITION_APP_OTA_13 = ESP_PARTITION_APP_OTA_MIN + 13, - ESP_PARTITION_APP_OTA_14 = ESP_PARTITION_APP_OTA_MIN + 14, - ESP_PARTITION_APP_OTA_15 = ESP_PARTITION_APP_OTA_MIN + 15, - ESP_PARTITION_APP_OTA_MAX = ESP_PARTITION_APP_MASK | 0x1f, - ESP_PARTITION_APP_TEST = ESP_PARTITION_APP_MASK | 0x20, - ESP_PARTITION_APP_ANY = ESP_PARTITION_APP_MASK | 0xff, - - ESP_PARTITION_DATA_MASK = 0x0100, - ESP_PARTITION_DATA_OTA = ESP_PARTITION_DATA_MASK | 0x00, - ESP_PARTITION_DATA_RF = ESP_PARTITION_DATA_MASK | 0x01, - ESP_PARTITION_DATA_WIFI = ESP_PARTITION_DATA_MASK | 0x02, - ESP_PARTITION_DATA_ANY = ESP_PARTITION_DATA_MASK | 0xff, - - ESP_PARTITION_FILESYSTEM_MASK = 0x0200, - ESP_PARTITION_FILESYSTEM_ESPHTTPD = 0x0200, - ESP_PARTITION_FILESYSTEM_FAT = 0x0201, - ESP_PARTITION_FILESYSTEM_SPIFFS = 0x0202, - ESP_PARTITION_FILESYSTEM_ANY = 0x20ff, - - ESP_PARTITION_END = 0xffff + ESP_PARTITION_TYPE_APP = 0x00, + ESP_PARTITION_TYPE_DATA = 0x01, + ESP_PARTITION_TYPE_FILESYSTEM = 0x02, } esp_partition_type_t; -#define ESP_PARTITION_APP_OTA(i) ((esp_partition_type_t)(ESP_PARTITION_APP_OTA_MIN + ((i) & 0xf))) +typedef enum { + ESP_PARTITION_SUBTYPE_APP_FACTORY = 0x00, + ESP_PARTITION_SUBTYPE_APP_OTA_MIN = 0x10, + ESP_PARTITION_SUBTYPE_APP_OTA_0 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0, + ESP_PARTITION_SUBTYPE_APP_OTA_1 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1, + ESP_PARTITION_SUBTYPE_APP_OTA_2 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2, + ESP_PARTITION_SUBTYPE_APP_OTA_3 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3, + ESP_PARTITION_SUBTYPE_APP_OTA_4 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4, + ESP_PARTITION_SUBTYPE_APP_OTA_5 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5, + ESP_PARTITION_SUBTYPE_APP_OTA_6 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6, + ESP_PARTITION_SUBTYPE_APP_OTA_7 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7, + ESP_PARTITION_SUBTYPE_APP_OTA_8 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8, + ESP_PARTITION_SUBTYPE_APP_OTA_9 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9, + ESP_PARTITION_SUBTYPE_APP_OTA_10 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10, + ESP_PARTITION_SUBTYPE_APP_OTA_11 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11, + ESP_PARTITION_SUBTYPE_APP_OTA_12 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12, + ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13, + ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14, + ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15, + ESP_PARTITION_SUBTYPE_APP_OTA_MAX = 15, + ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, + + ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, + ESP_PARTITION_SUBTYPE_DATA_RF = 0x01, + ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, + + ESP_PARTITION_SUBTYPE_FILESYSTEM_ESPHTTPD = 0x00, + ESP_PARTITION_SUBTYPE_FILESYSTEM_FAT = 0x01, + ESP_PARTITION_SUBTYPE_FILESYSTEM_SPIFFS = 0x02, + + ESP_PARTITION_SUBTYPE_ANY = 0xff, +} esp_partition_subtype_t; + +#define ESP_PARTITION_SUBTYPE_OTA(i) ((esp_partition_subtype_t)(ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((i) & 0xf))) typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t; typedef struct { esp_partition_type_t type; + esp_partition_subtype_t subtype; uint32_t address; uint32_t size; char label[17]; @@ -81,9 +82,9 @@ typedef struct { * @brief Find partition based on one or more parameters * * @param type Partition type, one of esp_partition_type_t values - * To find all app partitions or all filesystem partitions, - * use ESP_PARTITION_APP_ANY or ESP_PARTITION_FILESYSTEM_ANY, - * respectively. + * @param subtype Partition subtype, of esp_partition_subtype_t values. + * To find all partitions of given type, use + * ESP_PARTITION_SUBTYPE_ANY. * @param label (optional) Partition label. Set this value if looking * for partition with a specific name. Pass NULL otherwise. * @@ -92,22 +93,22 @@ typedef struct { * Iterator obtained through this function has to be released * using esp_partition_iterator_release when not used any more. */ -esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, const char* label); +esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); /** * @brief Find first partition based on one or more parameters * * @param type Partition type, one of esp_partition_type_t values - * To find all app partitions or all filesystem partitions, - * use ESP_PARTITION_APP_ANY or ESP_PARTITION_FILESYSTEM_ANY, - * respectively. + * @param subtype Partition subtype, of esp_partition_subtype_t values. + * To find all partitions of given type, use + * ESP_PARTITION_SUBTYPE_ANY. * @param label (optional) Partition label. Set this value if looking * for partition with a specific name. Pass NULL otherwise. * * @return pointer to esp_partition_t structure, or NULL if no parition is found. * This pointer is valid for the lifetime of the application. */ -const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, const char* label); +const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); /** * @brief Get esp_partition_t structure for given partition diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c index d138f09b5..21013d96f 100644 --- a/components/spi_flash/partition.c +++ b/components/spi_flash/partition.c @@ -39,13 +39,14 @@ typedef struct partition_list_item_ { typedef struct esp_partition_iterator_opaque_ { esp_partition_type_t type; // requested type + esp_partition_subtype_t subtype; // requested subtype const char* label; // requested label (can be NULL) partition_list_item_t* next_item; // next item to iterate to esp_partition_t* info; // pointer to info (it is redundant, but makes code more readable) } esp_partition_iterator_opaque_t; -static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, const char* label); +static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); static esp_err_t load_partitions(); @@ -54,18 +55,8 @@ static SLIST_HEAD(partition_list_head_, partition_list_item_) s_partition_list = static _lock_t s_partition_list_lock; -static uint32_t get_major_type(esp_partition_type_t type) -{ - return (type >> 8) & 0xff; -} - -static uint32_t get_minor_type(esp_partition_type_t type) -{ - return type & 0xff; -} - esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, - const char* label) + esp_partition_subtype_t subtype, const char* label) { if (SLIST_EMPTY(&s_partition_list)) { // only lock if list is empty (and check again after acquiring lock) @@ -81,7 +72,7 @@ esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, } // create an iterator pointing to the start of the list // (next item will be the first one) - esp_partition_iterator_t it = iterator_create(type, label); + esp_partition_iterator_t it = iterator_create(type, subtype, label); // advance iterator to the next item which matches constraints it = esp_partition_next(it); // if nothing found, it == NULL and iterator has been released @@ -95,17 +86,13 @@ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it) if (it->next_item == NULL) { return NULL; } - uint32_t requested_major_type = get_major_type(it->type); - uint32_t requested_minor_type = get_minor_type(it->type); _lock_acquire(&s_partition_list_lock); for (; it->next_item != NULL; it->next_item = SLIST_NEXT(it->next_item, next)) { esp_partition_t* p = &it->next_item->info; - uint32_t it_major_type = get_major_type(p->type); - uint32_t it_minor_type = get_minor_type(p->type); - if (requested_major_type != it_major_type) { + if (it->type != p->type) { continue; } - if (requested_minor_type != 0xff && requested_minor_type != it_minor_type) { + if (it->subtype != 0xff && it->subtype != p->subtype) { continue; } if (it->label != NULL && strcmp(it->label, p->label) != 0) { @@ -124,9 +111,10 @@ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it) return it; } -const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, const char* label) +const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, + esp_partition_subtype_t subtype, const char* label) { - esp_partition_iterator_t it = esp_partition_find(type, label); + esp_partition_iterator_t it = esp_partition_find(type, subtype, label); if (it == NULL) { return NULL; } @@ -135,11 +123,13 @@ const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, const return res; } -static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, const char* label) +static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, + esp_partition_subtype_t subtype, const char* label) { esp_partition_iterator_opaque_t* it = (esp_partition_iterator_opaque_t*) malloc(sizeof(esp_partition_iterator_opaque_t)); it->type = type; + it->subtype = subtype; it->label = label; it->next_item = SLIST_FIRST(&s_partition_list); it->info = NULL; @@ -172,7 +162,8 @@ static esp_err_t load_partitions() partition_list_item_t* item = (partition_list_item_t*) malloc(sizeof(partition_list_item_t)); item->info.address = it->pos.offset; item->info.size = it->pos.size; - item->info.type = (it->type << 8) | it->subtype; + item->info.type = it->type; + item->info.subtype = it->subtype; item->info.encrypted = false; // it->label may not be zero-terminated strncpy(item->info.label, (const char*) it->label, sizeof(it->label)); @@ -201,7 +192,7 @@ const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator) } esp_err_t esp_partition_read(const esp_partition_t* partition, - size_t src_offset, uint8_t* dst, size_t size) + size_t src_offset, void* dst, size_t size) { assert(partition != NULL); if (src_offset > partition->size) { @@ -214,7 +205,7 @@ esp_err_t esp_partition_read(const esp_partition_t* partition, } esp_err_t esp_partition_write(const esp_partition_t* partition, - size_t dst_offset, const uint8_t* src, size_t size) + size_t dst_offset, const void* src, size_t size) { assert(partition != NULL); if (dst_offset > partition->size) { From dc2b5d0c96ff018ebd5b988939f44793dbed3f9d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 27 Oct 2016 10:16:42 +0800 Subject: [PATCH 095/110] spi_flash: update comment blocks --- components/spi_flash/include/esp_partition.h | 21 +++++++++----------- components/spi_flash/include/esp_spi_flash.h | 8 ++++---- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index e2f289eb7..ae0185dcd 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -82,9 +82,9 @@ typedef struct { * @brief Find partition based on one or more parameters * * @param type Partition type, one of esp_partition_type_t values - * @param subtype Partition subtype, of esp_partition_subtype_t values. - * To find all partitions of given type, use - * ESP_PARTITION_SUBTYPE_ANY. + * @param subtype Partition subtype, one of esp_partition_subtype_t values. + * To find all partitions of given type, use + * ESP_PARTITION_SUBTYPE_ANY. * @param label (optional) Partition label. Set this value if looking * for partition with a specific name. Pass NULL otherwise. * @@ -99,13 +99,13 @@ esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, esp_parti * @brief Find first partition based on one or more parameters * * @param type Partition type, one of esp_partition_type_t values - * @param subtype Partition subtype, of esp_partition_subtype_t values. - * To find all partitions of given type, use - * ESP_PARTITION_SUBTYPE_ANY. + * @param subtype Partition subtype, one of esp_partition_subtype_t values. + * To find all partitions of given type, use + * ESP_PARTITION_SUBTYPE_ANY. * @param label (optional) Partition label. Set this value if looking * for partition with a specific name. Pass NULL otherwise. * - * @return pointer to esp_partition_t structure, or NULL if no parition is found. + * @return pointer to esp_partition_t structure, or NULL if no partition is found. * This pointer is valid for the lifetime of the application. */ const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); @@ -134,9 +134,6 @@ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t iterator); /** * @brief Release partition iterator * - * Any pointers obtained using esp_partition_label function will be invalid - * after this call. - * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * */ @@ -155,7 +152,7 @@ void esp_partition_iterator_release(esp_partition_iterator_t iterator); * @param size Size of data to be read, in bytes. * * @return ESP_OK, if data was read successfully; - * ESP_ERR_INVALID_ARG, if iterator or src are NULL; + * ESP_ERR_INVALID_ARG, if src_offset exceeds partition size; * ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition; * or one of error codes from lower-level flash driver. */ @@ -181,7 +178,7 @@ esp_err_t esp_partition_read(const esp_partition_t* partition, * esp_partition_erase_range call. * * @return ESP_OK, if data was written successfully; - * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; + * ESP_ERR_INVALID_ARG, if dst_offset exceeds partition size; * ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition; * or one of error codes from lower-level flash driver. */ diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index df0a8decc..c65eaa583 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -77,8 +77,8 @@ esp_err_t spi_flash_erase_range(size_t start_addr, size_t size); * @note Both des_addr and src_addr have to be 4-byte aligned. * This is a temporary limitation which will be removed. * - * @param des_addr destination address in Flash - * @param src_addr source address of the data + * @param dest destination address in Flash + * @param src pointer to the source buffer * @param size length of data, in bytes * * @return esp_err_t @@ -91,8 +91,8 @@ esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); * @note Both des_addr and src_addr have to be 4-byte aligned. * This is a temporary limitation which will be removed. * - * @param src_addr source address of the data in Flash. - * @param des_addr destination address + * @param src source address of the data in Flash. + * @param dest pointer to the destination buffer * @param size length of data * * @return esp_err_t From 305b63209ecb3e7c3c6c0bf20f60d41135e7da8f Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Fri, 28 Oct 2016 12:03:51 +0800 Subject: [PATCH 096/110] lwip: support max 16 sockets Since the customers need more sockets in their application, support max 16 sockets, in other words, the total socket number of UDP/TCP/RAW sockets should not exceed 16. --- components/lwip/include/lwip/port/lwipopts.h | 32 +++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 75f349280..e72279067 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -99,13 +99,37 @@ extern unsigned long os_random(void); * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. * (requires the LWIP_TCP option) */ -#define MEMP_NUM_TCP_PCB 5 /** * MEMP_NUM_NETCONN: the number of struct netconns. * (only needed if you use the sequential API, like api_lib.c) */ -#define MEMP_NUM_NETCONN 10 +#define MEMP_NUM_NETCONN CONFIG_LWIP_MAX_SOCKETS + +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ +#define MEMP_NUM_RAW_PCB 16 + +/** + * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. + * (requires the LWIP_TCP option) + */ +#define MEMP_NUM_TCP_PCB 16 + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ +#define MEMP_NUM_TCP_PCB_LISTEN 16 + +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ +#define MEMP_NUM_UDP_PCB 16 /* -------------------------------- @@ -542,9 +566,9 @@ extern unsigned char misc_prof_get_tcp_snd_buf(void); * DHCP_DEBUG: Enable debugging in dhcp.c. */ #define DHCP_DEBUG LWIP_DBG_OFF -#define LWIP_DEBUG 0 +#define LWIP_DEBUG LWIP_DBG_OFF #define TCP_DEBUG LWIP_DBG_OFF -#define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF +#define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF #define CHECKSUM_CHECK_UDP 0 #define CHECKSUM_CHECK_IP 0 From 4d8ad3c87738eed231c2dec97b713c135b292524 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 28 Oct 2016 12:05:42 +0800 Subject: [PATCH 097/110] Fix int wdt iram, fix some fallout of moving panic stuff to esp32 --- components/esp32/Kconfig | 2 +- components/esp32/int_wdt.c | 5 +++-- components/freertos/FreeRTOS-openocd.c | 2 +- components/freertos/Kconfig | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 9e141529b..3b50dbd2c 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -157,7 +157,7 @@ config ULP_COPROC_RESERVE_MEM choice ESP32_PANIC prompt "Panic handler behaviour" - default FREERTOS_PANIC_PRINT_REBOOT + default ESP32_PANIC_PRINT_REBOOT help If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is invoked. Configure the panic handlers action here. diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c index 93a4d9fe6..11de8f20d 100644 --- a/components/esp32/int_wdt.c +++ b/components/esp32/int_wdt.c @@ -24,6 +24,7 @@ #include #include "esp_err.h" #include "esp_intr.h" +#include "esp_attr.h" #include "soc/timer_group_struct.h" #include "soc/timer_group_reg.h" @@ -66,7 +67,7 @@ void esp_int_wdt_init() { //Not static; the ISR assembly checks this. bool int_wdt_app_cpu_ticked=false; -void vApplicationTickHook(void) { +void IRAM_ATTR vApplicationTickHook(void) { if (xPortGetCoreID()!=0) { int_wdt_app_cpu_ticked=true; } else { @@ -82,7 +83,7 @@ void vApplicationTickHook(void) { } } #else -void vApplicationTickHook(void) { +void IRAM_ATTR vApplicationTickHook(void) { if (xPortGetCoreID()!=0) return; TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt diff --git a/components/freertos/FreeRTOS-openocd.c b/components/freertos/FreeRTOS-openocd.c index 6177f0205..d74564495 100644 --- a/components/freertos/FreeRTOS-openocd.c +++ b/components/freertos/FreeRTOS-openocd.c @@ -18,6 +18,6 @@ #define USED #endif -#ifdef CONFIG_FREERTOS_DEBUG_OCDAWARE +#ifdef CONFIG_ESP32_DEBUG_OCDAWARE const int USED uxTopUsedPriority = configMAX_PRIORITIES - 1; #endif \ No newline at end of file diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 413c710d2..25d5581e8 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -121,7 +121,7 @@ endchoice config FREERTOS_BREAK_ON_SCHEDULER_START_JTAG bool "Stop program on scheduler start when JTAG/OCD is detected" - depends on FREERTOS_DEBUG_OCDAWARE + depends on ESP32_DEBUG_OCDAWARE default y help If JTAG/OCD is connected, stop execution when the scheduler is started and the first From 38ff616e4a46650869fff52441c327dfa129932c Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Fri, 28 Oct 2016 12:29:26 +0800 Subject: [PATCH 098/110] lwip: add prompt when configure max sockets number in menuconfig --- components/lwip/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 715d7dd46..a957c1fdb 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -1,7 +1,7 @@ menu "LWIP" config LWIP_MAX_SOCKETS - int "Max number of open sockets" + int "Max number of open sockets, the valid value is from 1 to 16" range 1 16 default 4 help From 9e7bc900c51dd31545222dca993e18c2964cb060 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Fri, 28 Oct 2016 13:35:06 +0800 Subject: [PATCH 099/110] lwip: rework comments according to review --- components/lwip/Kconfig | 5 +++-- components/lwip/include/lwip/port/lwipopts.h | 4 ---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index a957c1fdb..15c94c66b 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -1,13 +1,14 @@ menu "LWIP" config LWIP_MAX_SOCKETS - int "Max number of open sockets, the valid value is from 1 to 16" + int "Max number of open sockets" range 1 16 default 4 help Sockets take up a certain amount of memory, and allowing fewer sockets to be open at the same time conserves memory. Specify - the maximum amount of sockets here. + the maximum amount of sockets here. The valid value is from 1 + to 16. config LWIP_THREAD_LOCAL_STORAGE_INDEX int "Index for thread-local-storage pointer for lwip" diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index e72279067..35c2800ed 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -95,10 +95,6 @@ extern unsigned long os_random(void); ---------- Internal Memory Pool Sizes ---------- ------------------------------------------------ */ -/** - * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. - * (requires the LWIP_TCP option) - */ /** * MEMP_NUM_NETCONN: the number of struct netconns. From a038a2b5335f2b330eb32748f1950add28b85947 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 28 Oct 2016 13:41:07 +0800 Subject: [PATCH 100/110] freertos: fix calling first task hook --- components/freertos/tasks.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 64cc3a65d..bd32f834f 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -1018,6 +1018,11 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) { +#if portFIRST_TASK_HOOK + if ( xPortGetCoreID() == 0 ) { + vPortFirstTaskHook(pxTaskCode); + } +#endif /* configFIRST_TASK_HOOK */ /* This is the first task to be created so do the preliminary initialisation required. We will not recover if this call fails, but we will report the failure. */ @@ -1044,12 +1049,6 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode /* Schedule if nothing is scheduled yet, or overwrite a task of lower prio. */ if ( pxCurrentTCB[i] == NULL || pxCurrentTCB[i]->uxPriority <= pxNewTCB->uxPriority ) { -#if portFIRST_TASK_HOOK - if ( i == 0) { - vPortFirstTaskHook(pxTaskCode); - } -#endif /* configFIRST_TASK_HOOK */ - pxCurrentTCB[i] = pxNewTCB; break; } From 309bd12855b15b55a3226733c9a25cd09033cb65 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 28 Oct 2016 14:32:11 +0800 Subject: [PATCH 101/110] Re-add panic.o to IRAM/DRAM. --- components/esp32/ld/esp32.common.ld | 3 ++- components/esp32/panic.c | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index 2226e9882..7b14b6da1 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -47,6 +47,7 @@ SECTIONS _iram_text_start = ABSOLUTE(.); *(.iram1 .iram1.*) *libfreertos.a:(.literal .text .literal.* .text.*) + *libesp32.a:panic.o(.literal .text .literal.* .text.*) *libphy.a:(.literal .text .literal.* .text.*) *librtc.a:(.literal .text .literal.* .text.*) *libpp.a:(.literal .text .literal.* .text.*) @@ -92,7 +93,7 @@ SECTIONS KEEP(*(.gnu.linkonce.s2.*)) KEEP(*(.jcr)) *(.dram1 .dram1.*) - *libfreertos.a:panic.o(.rodata .rodata.*) + *libesp32.a:panic.o(.rodata .rodata.*) _data_end = ABSOLUTE(.); . = ALIGN(4); _heap_start = ABSOLUTE(.); diff --git a/components/esp32/panic.c b/components/esp32/panic.c index c806dace2..c5d8aa669 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -30,7 +30,7 @@ #include "esp_gdbstub.h" #include "esp_panic.h" - +#include "esp_attr.h" /* Panic handlers; these get called when an unhandled exception occurs or the assembly-level @@ -38,6 +38,10 @@ task switching / interrupt code runs into an unrecoverable error. The default ta overflow handler also is in here. */ +/* +Note: The linker script will put everything in this file in IRAM/DRAM, so it also works with flash cache disabled. +*/ + #if !CONFIG_ESP32_PANIC_SILENT_REBOOT //printf may be broken, so we fix our own printing fns... inline static void panicPutchar(char c) { From 3cd86d6bce502b41248b6fb8bccaeb1a4ee47c7d Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 28 Oct 2016 14:37:36 +0800 Subject: [PATCH 102/110] Also call tick hook on app cpu when scheduler is suspended --- components/freertos/tasks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index bd32f834f..e4b887273 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -2308,7 +2308,7 @@ BaseType_t xSwitchRequired = pdFALSE; { /* Guard against the tick hook being called when the pended tick count is being unwound (when the scheduler is being unlocked). */ - if( uxPendedTicks == ( UBaseType_t ) 0U ) + if( ( uxSchedulerSuspended[ xPortGetCoreID() ] != ( UBaseType_t ) pdFALSE ) || uxPendedTicks == ( UBaseType_t ) 0U ) { vApplicationTickHook(); } From 0e90983c9f0aaf1caf003b92d2f0471c92a389f0 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 28 Oct 2016 16:16:12 +0800 Subject: [PATCH 103/110] vfs: fix adding CR --- components/vfs/vfs_uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c index bfccad896..d9d755f9b 100644 --- a/components/vfs/vfs_uart.c +++ b/components/vfs/vfs_uart.c @@ -18,6 +18,7 @@ #include "sys/errno.h" #include "sys/lock.h" #include "soc/uart_struct.h" +#include "sdkconfig.h" static uart_dev_t* s_uarts[3] = {&UART0, &UART1, &UART2}; static _lock_t s_uart_locks[3]; // per-UART locks, lazily initialized From 5ffd6155f28eb906212238868fe242e0aeaa6da4 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 28 Oct 2016 16:17:41 +0800 Subject: [PATCH 104/110] set default interrupt watchdog timeout to 300ms 10ms is too low for openocd/gdb to work, so it's not a very useful default value. --- components/esp32/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 3b50dbd2c..b5a8d2f2d 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -206,7 +206,7 @@ config INT_WDT config INT_WDT_TIMEOUT_MS int "Interrupt watchdog timeout (ms)" depends on INT_WDT - default 10 + default 300 range 10 10000 help The timeout of the watchdog, in miliseconds. Make this higher than the FreeRTOS tick rate. From 4f2719236fc49a7f15ef636da6369c4dd52ae7c7 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Fri, 28 Oct 2016 16:53:49 +0800 Subject: [PATCH 105/110] esp32/tcpip_adapter: softap supports max 10 stations The max number of stations softap supports is modified from 8 to 10 --- components/esp32/include/esp_wifi_types.h | 4 ++-- components/esp32/lib | 2 +- components/tcpip_adapter/include/tcpip_adapter.h | 4 ++-- components/tcpip_adapter/tcpip_adapter_lwip.c | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/components/esp32/include/esp_wifi_types.h b/components/esp32/include/esp_wifi_types.h index 0ea719a65..3304a8a3e 100644 --- a/components/esp32/include/esp_wifi_types.h +++ b/components/esp32/include/esp_wifi_types.h @@ -154,10 +154,10 @@ typedef struct { uint8_t mac[6]; /**< mac address of sta that associated with ESP32 soft-AP */ }wifi_sta_info_t; -#define ESP_WIFI_MAX_CONN_NUM (8+2) /**< max number of sta the eSP32 soft-AP can connect */ +#define ESP_WIFI_MAX_CONN_NUM (10) /**< max number of sta the ESP32 soft-AP can connect */ typedef struct { wifi_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM]; /**< station list */ - uint8_t num; /**< number of station that associated with ESP32 soft-AP */ + int num; /**< number of station that associated with ESP32 soft-AP */ }wifi_sta_list_t; typedef enum { diff --git a/components/esp32/lib b/components/esp32/lib index 12b3435fc..9d18fd1a8 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 12b3435fc0cd04efc249d52d71efb1cdecda50f8 +Subproject commit 9d18fd1a8f7610130e69f8be74ec68f6399221b1 diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index 218325320..e84701688 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -73,8 +73,8 @@ typedef struct { }tcpip_adapter_sta_info_t; typedef struct { - tcpip_adapter_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM+2]; - uint8_t num; + tcpip_adapter_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM]; + int num; }tcpip_adapter_sta_list_t; #endif diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 677368008..9b6e9d94f 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -598,6 +598,7 @@ esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapt return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; memset(tcpip_sta_list, 0, sizeof(tcpip_adapter_sta_list_t)); + tcpip_sta_list->num = wifi_sta_list->num; for (i=0; inum; i++){ memcpy(tcpip_sta_list->sta[i].mac, wifi_sta_list->sta[i].mac, 6); dhcp_search_ip_on_mac(tcpip_sta_list->sta[i].mac, &tcpip_sta_list->sta[i].ip); From 90b787636a49fd2f36c6c30e2a57c06303afb20e Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 31 Oct 2016 11:00:27 +0800 Subject: [PATCH 106/110] Remove redundant volatile keyword --- components/esp32/crosscore_int.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 78287d405..60a45da40 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -44,7 +44,7 @@ ToDo: There is a small chance the CPU already has yielded when this ISR is servi the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that. */ static void esp_crosscore_isr(void *arg) { - volatile uint32_t myReasonVal; + uint32_t myReasonVal; #if 0 //A pointer to the correct reason array item is passed to this ISR. volatile uint32_t *myReason=arg; From f30887bc700aff63f88362dca8e0fb3557ada7f9 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Mon, 31 Oct 2016 11:17:33 +0800 Subject: [PATCH 107/110] modify comments according to review --- components/esp32/include/esp_wifi_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/include/esp_wifi_types.h b/components/esp32/include/esp_wifi_types.h index 3304a8a3e..fb19aa8fc 100644 --- a/components/esp32/include/esp_wifi_types.h +++ b/components/esp32/include/esp_wifi_types.h @@ -154,7 +154,7 @@ typedef struct { uint8_t mac[6]; /**< mac address of sta that associated with ESP32 soft-AP */ }wifi_sta_info_t; -#define ESP_WIFI_MAX_CONN_NUM (10) /**< max number of sta the ESP32 soft-AP can connect */ +#define ESP_WIFI_MAX_CONN_NUM (10) /**< max number of stations which can connect to ESP32 soft-AP */ typedef struct { wifi_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM]; /**< station list */ int num; /**< number of station that associated with ESP32 soft-AP */ From a5552b1e21b725bc17882e2b44452d194b56f97a Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Mon, 31 Oct 2016 17:50:09 +0800 Subject: [PATCH 108/110] lwip: fix tcp rx abnormal issue(tw8242) In tcp_alloc(), initialize per_soc_tcp_wnd before initializing recv_wnd because recv_wnd depends on per_soc_tcp_wnd. --- components/lwip/core/tcp.c | 11 ++++++----- components/lwip/core/tcp_out.c | 4 ++-- components/lwip/include/lwip/port/lwipopts.h | 20 -------------------- 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/components/lwip/core/tcp.c b/components/lwip/core/tcp.c index 87ddf5f1a..627df6d29 100755 --- a/components/lwip/core/tcp.c +++ b/components/lwip/core/tcp.c @@ -1532,6 +1532,12 @@ tcp_alloc(u8_t prio) } if (pcb != NULL) { memset(pcb, 0, sizeof(struct tcp_pcb)); + +#if ESP_PER_SOC_TCP_WND + pcb->per_soc_tcp_wnd = TCP_WND_DEFAULT; + pcb->per_soc_tcp_snd_buf = TCP_SND_BUF_DEFAULT; +#endif + pcb->prio = prio; pcb->snd_buf = TCP_SND_BUF_DEFAULT; pcb->snd_queuelen = 0; @@ -1575,11 +1581,6 @@ tcp_alloc(u8_t prio) #endif /* LWIP_TCP_KEEPALIVE */ pcb->keep_cnt_sent = 0; - -#if ESP_PER_SOC_TCP_WND - pcb->per_soc_tcp_wnd = TCP_WND_DEFAULT; - pcb->per_soc_tcp_snd_buf = TCP_SND_BUF_DEFAULT; -#endif } return pcb; diff --git a/components/lwip/core/tcp_out.c b/components/lwip/core/tcp_out.c index f189623f5..35a8aa145 100755 --- a/components/lwip/core/tcp_out.c +++ b/components/lwip/core/tcp_out.c @@ -1320,9 +1320,9 @@ tcp_rst(u32_t seqno, u32_t ackno, #endif #else #if LWIP_WND_SCALE - tcphdr->wnd = PP_HTONS(((TCP_WND >> TCP_RCV_SCALE) & 0xFFFF)); + tcphdr->wnd = PP_HTONS(((TCP_WND_DEFAULT >> TCP_RCV_SCALE) & 0xFFFF)); #else - tcphdr->wnd = PP_HTONS(TCP_WND); + tcphdr->wnd = PP_HTONS(TCP_WND_DEFAULT); #endif #endif tcphdr->chksum = 0; diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 35c2800ed..b8811813d 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -240,26 +240,6 @@ extern unsigned long os_random(void); ---------- TCP options ---------- --------------------------------- */ -/** - * TCP_WND: The size of a TCP window. This must be at least - * (2 * TCP_MSS) for things to work well - */ - -#define ESP_PER_SOC_TCP_WND 1 -#if ESP_PER_SOC_TCP_WND -#define TCP_WND_DEFAULT (4*TCP_MSS) -#define TCP_SND_BUF_DEFAULT (2*TCP_MSS) - -#define TCP_WND(pcb) (pcb->per_soc_tcp_wnd) -#define TCP_SND_BUF(pcb) (pcb->per_soc_tcp_snd_buf) -#else -#ifdef PERF -extern unsigned char misc_prof_get_tcpw(void); -extern unsigned char misc_prof_get_tcp_snd_buf(void); -#define TCP_WND(pcb) (misc_prof_get_tcpw()*TCP_MSS) -#define TCP_SND_BUF(pcb) (misc_prof_get_tcp_snd_buf()*TCP_MSS) -#endif -#endif /** From 19f61332a9a9c5c0d585d88d2bef7475594724a2 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Mon, 31 Oct 2016 19:38:47 +0800 Subject: [PATCH 109/110] make build pass when disable per soc tcp window --- components/lwip/core/init.c | 22 ++++++++++---------- components/lwip/include/lwip/port/lwipopts.h | 5 +++-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/components/lwip/core/init.c b/components/lwip/core/init.c index 774e9a2be..8b2e92669 100755 --- a/components/lwip/core/init.c +++ b/components/lwip/core/init.c @@ -135,7 +135,7 @@ //#endif #else /* LWIP_WND_SCALE */ -#if (ESP_PER_SOC_TCP_WND == 0) +#if ! ESP_PER_SOC_TCP_WND #if (LWIP_TCP && (TCP_WND > 0xffff)) #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h (or enable window scaling)" #endif @@ -143,11 +143,11 @@ #endif /* LWIP_WND_SCALE */ -#if (ESP_PER_SOC_TCP_WND == 0) -#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) +#if ! ESP_PER_SOC_TCP_WND +#if (LWIP_TCP && (TCP_SND_QUEUELEN(0) > 0xffff)) #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" #endif -#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2)) +#if (LWIP_TCP && (TCP_SND_QUEUELEN(0) < 2)) #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" #endif @@ -286,25 +286,25 @@ /* TCP sanity checks */ #if !LWIP_DISABLE_TCP_SANITY_CHECKS +#if ! ESP_PER_SOC_TCP_WND #if LWIP_TCP -#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) +#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN(0)) #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif -#if (ESP_PER_SOC_TCP_WND == 0) -#if TCP_SND_BUF < (2 * TCP_MSS) +#if TCP_SND_BUF(0) < (2 * TCP_MSS) #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif -#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) - #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#if TCP_SND_QUEUELEN(0) < (2 * (TCP_SND_BUF(0) / TCP_MSS)) + #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF(0)/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif -#if TCP_SNDLOWAT >= TCP_SND_BUF +#if TCP_SNDLOWAT >= TCP_SND_BUF(0) #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif #if TCP_SNDLOWAT >= (0xFFFF - (4 * TCP_MSS)) #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must at least be 4*MSS below u16_t overflow!" #endif -#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN +#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN(0) #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif #endif diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index b8811813d..2e11bdd5c 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -512,7 +512,7 @@ extern unsigned long os_random(void); /* Enable all Espressif-only options */ #define ESP_LWIP 1 -#define ESP_PER_SOC_TCP_WND 1 +#define ESP_PER_SOC_TCP_WND 0 #define ESP_THREAD_SAFE 1 #define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF #define ESP_DHCP 1 @@ -524,9 +524,10 @@ extern unsigned long os_random(void); #define ESP_LIGHT_SLEEP 1 -#if ESP_PER_SOC_TCP_WND #define TCP_WND_DEFAULT (4*TCP_MSS) #define TCP_SND_BUF_DEFAULT (2*TCP_MSS) + +#if ESP_PER_SOC_TCP_WND #define TCP_WND(pcb) (pcb->per_soc_tcp_wnd) #define TCP_SND_BUF(pcb) (pcb->per_soc_tcp_snd_buf) #else From 2b722ea468ee69693d5e728101e7b4e36c66f231 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Mon, 31 Oct 2016 19:43:18 +0800 Subject: [PATCH 110/110] turn on per socket tcp window by default --- components/lwip/include/lwip/port/lwipopts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 2e11bdd5c..67a62b822 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -512,7 +512,7 @@ extern unsigned long os_random(void); /* Enable all Espressif-only options */ #define ESP_LWIP 1 -#define ESP_PER_SOC_TCP_WND 0 +#define ESP_PER_SOC_TCP_WND 1 #define ESP_THREAD_SAFE 1 #define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF #define ESP_DHCP 1