Merge branch 'cumulative_backport_into_v4.4' into 'release/v4.4'

Cumulative backport MR (v4.4)

See merge request espressif/esp-idf!17194
This commit is contained in:
Zim Kalinowski
2022-02-18 07:16:52 +00:00
20 changed files with 617 additions and 488 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
import argparse
import datetime import datetime
import hashlib import hashlib
import hmac import hmac
@ -15,6 +16,15 @@ from cryptography.hazmat.primitives.asymmetric.rsa import _modinv as modinv # t
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.utils import int_to_bytes from cryptography.utils import int_to_bytes
supported_targets = {'esp32s2', 'esp32c3', 'esp32s3'}
supported_key_size = {'esp32s2':[4096, 3072, 2048, 1024],
'esp32c3':[3072, 2048, 1024],
'esp32s3':[4096, 3072, 2048, 1024]}
NUM_HMAC_KEYS = 3
NUM_MESSAGES = 10
NUM_CASES = 6
def number_as_bignum_words(number): # type: (int) -> str def number_as_bignum_words(number): # type: (int) -> str
""" """
@ -45,117 +55,133 @@ def bytes_as_char_array(b): # type: (bytes) -> str
return '{ ' + ', '.join('0x%02x' % x for x in b) + ' }' return '{ ' + ', '.join('0x%02x' % x for x in b) + ' }'
NUM_HMAC_KEYS = 3 def generate_tests_cases(target): # type: (str) -> None
NUM_MESSAGES = 10
NUM_CASES = 6
max_key_size = max(supported_key_size[target])
print('Generating tests cases for {} (max key size = {})'.format(target, max_key_size))
hmac_keys = [os.urandom(32) for x in range(NUM_HMAC_KEYS)] hmac_keys = [os.urandom(32) for x in range(NUM_HMAC_KEYS)]
messages = [random.randrange(0, 1 << 4096) for x in range(NUM_MESSAGES)] messages = [random.randrange(0, 1 << max_key_size) for x in range(NUM_MESSAGES)]
with open('digital_signature_test_cases.h', 'w') as f: with open('digital_signature_test_cases.h', 'w') as f:
f.write('/*\n') f.write('/*\n')
year = datetime.datetime.now().year year = datetime.datetime.now().year
f.write(' * SPDX-FileCopyrightText: {year} Espressif Systems (Shanghai) CO LTD\n'.format(year=year)) f.write(' * SPDX-FileCopyrightText: {year} Espressif Systems (Shanghai) CO LTD\n'.format(year=year))
f.write(' *\n') f.write(' *\n')
f.write(' * SPDX-License-Identifier: Apache-2.0\n') f.write(' * SPDX-License-Identifier: Apache-2.0\n')
f.write(' *\n') f.write(' *\n')
f.write(' * File generated by gen_digital_signature_tests.py\n') f.write(' * File generated by gen_digital_signature_tests.py\n')
f.write(' */\n') f.write(' */\n')
# Write out HMAC keys # Write out HMAC keys
f.write('#define NUM_HMAC_KEYS %d\n\n' % NUM_HMAC_KEYS) f.write('#define NUM_HMAC_KEYS %d\n\n' % NUM_HMAC_KEYS)
f.write('static const uint8_t test_hmac_keys[NUM_HMAC_KEYS][32] = {\n') f.write('static const uint8_t test_hmac_keys[NUM_HMAC_KEYS][32] = {\n')
for h in hmac_keys: for h in hmac_keys:
f.write(' %s,\n' % bytes_as_char_array(h)) f.write(' %s,\n' % bytes_as_char_array(h))
f.write('};\n\n') f.write('};\n\n')
# Write out messages # Write out messages
f.write('#define NUM_MESSAGES %d\n\n' % NUM_MESSAGES) f.write('#define NUM_MESSAGES %d\n\n' % NUM_MESSAGES)
f.write('static const uint32_t test_messages[NUM_MESSAGES][4096/32] = {\n') f.write('static const uint32_t test_messages[NUM_MESSAGES][%d/32] = {\n' % max_key_size)
for m in messages:
f.write(' // Message %d\n' % messages.index(m))
f.write(' %s,\n' % number_as_bignum_words(m))
f.write(' };\n')
f.write('\n\n\n')
f.write('#define NUM_CASES %d\n\n' % NUM_CASES)
f.write('static const encrypt_testcase_t test_cases[NUM_CASES] = {\n')
for case in range(NUM_CASES):
f.write(' { /* Case %d */\n' % case)
iv = os.urandom(16)
f.write(' .iv = %s,\n' % (bytes_as_char_array(iv)))
hmac_key_idx = random.randrange(0, NUM_HMAC_KEYS)
aes_key = hmac.HMAC(hmac_keys[hmac_key_idx], b'\xFF' * 32, hashlib.sha256).digest()
sizes = [4096, 3072, 2048, 1024, 512]
key_size = sizes[case % len(sizes)]
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=key_size,
backend=default_backend())
priv_numbers = private_key.private_numbers()
pub_numbers = private_key.public_key().public_numbers()
Y = priv_numbers.d
M = pub_numbers.n
rr = 1 << (key_size * 2)
rinv = rr % pub_numbers.n
mprime = - modinv(M, 1 << 32)
mprime &= 0xFFFFFFFF
length = key_size // 32 - 1
f.write(' .p_data = {\n')
f.write(' .Y = %s,\n' % number_as_bignum_words(Y))
f.write(' .M = %s,\n' % number_as_bignum_words(M))
f.write(' .Rb = %s,\n' % number_as_bignum_words(rinv))
f.write(' .M_prime = 0x%08x,\n' % mprime)
f.write(' .length = %d, // %d bit\n' % (length, key_size))
f.write(' },\n')
# calculate MD from preceding values and IV
# Y4096 || M4096 || Rb4096 || M_prime32 || LENGTH32 || IV128
md_in = number_as_bytes(Y, 4096) + \
number_as_bytes(M, 4096) + \
number_as_bytes(rinv, 4096) + \
struct.pack('<II', mprime, length) + \
iv
assert len(md_in) == 12480 / 8
md = hashlib.sha256(md_in).digest()
# generate expected C value from P bitstring
#
# Y4096 || M4096 || Rb4096 || M_prime32 || LENGTH32 || MD256 || 0x08*8
p = number_as_bytes(Y, 4096) + \
number_as_bytes(M, 4096) + \
number_as_bytes(rinv, 4096) + \
md + \
struct.pack('<II', mprime, length) + \
b'\x08' * 8
assert len(p) == 12672 / 8
cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
c = encryptor.update(p) + encryptor.finalize()
f.write(' .expected_c = %s,\n' % bytes_as_char_array(c))
f.write(' .hmac_key_idx = %d,\n' % (hmac_key_idx))
f.write(' // results of message array encrypted with these keys\n')
f.write(' .expected_results = {\n')
mask = (1 << key_size) - 1 # truncate messages if needed
for m in messages: for m in messages:
f.write(' // Message %d\n' % messages.index(m)) f.write(' // Message %d\n' % messages.index(m))
f.write(' %s,' % (number_as_bignum_words(pow(m & mask, Y, M)))) f.write(' %s,\n' % number_as_bignum_words(m))
f.write(' },\n') f.write(' };\n')
f.write(' },\n') f.write('\n\n\n')
f.write('};\n') f.write('#define NUM_CASES %d\n\n' % NUM_CASES)
f.write('static const encrypt_testcase_t test_cases[NUM_CASES] = {\n')
for case in range(NUM_CASES):
f.write(' { /* Case %d */\n' % case)
iv = os.urandom(16)
f.write(' .iv = %s,\n' % (bytes_as_char_array(iv)))
hmac_key_idx = random.randrange(0, NUM_HMAC_KEYS)
aes_key = hmac.HMAC(hmac_keys[hmac_key_idx], b'\xFF' * 32, hashlib.sha256).digest()
sizes = supported_key_size[target]
key_size = sizes[case % len(sizes)]
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=key_size,
backend=default_backend())
priv_numbers = private_key.private_numbers()
pub_numbers = private_key.public_key().public_numbers()
Y = priv_numbers.d
M = pub_numbers.n
rr = 1 << (key_size * 2)
rinv = rr % pub_numbers.n
mprime = - modinv(M, 1 << 32)
mprime &= 0xFFFFFFFF
length = key_size // 32 - 1
f.write(' .p_data = {\n')
f.write(' .Y = %s,\n' % number_as_bignum_words(Y))
f.write(' .M = %s,\n' % number_as_bignum_words(M))
f.write(' .Rb = %s,\n' % number_as_bignum_words(rinv))
f.write(' .M_prime = 0x%08x,\n' % mprime)
f.write(' .length = %d, // %d bit\n' % (length, key_size))
f.write(' },\n')
# calculate MD from preceding values and IV
# Y_max_key_size || M_max_key_size || Rb_max_key_size || M_prime32 || LENGTH32 || IV128
md_in = number_as_bytes(Y, max_key_size) + \
number_as_bytes(M, max_key_size) + \
number_as_bytes(rinv, max_key_size) + \
struct.pack('<II', mprime, length) + \
iv
md = hashlib.sha256(md_in).digest()
# generate expected C value from P bitstring
#
# Y_max_key_size || M_max_key_size || Rb_max_key_size || M_prime32 || LENGTH32 || 0x08*8
# E.g. for C3: Y3072 || M3072 || Rb3072 || M_prime32 || LENGTH32 || MD256 || 0x08*8
p = number_as_bytes(Y, max_key_size) + \
number_as_bytes(M, max_key_size) + \
number_as_bytes(rinv, max_key_size) + \
md + \
struct.pack('<II', mprime, length) + \
b'\x08' * 8
# expected_len = max_len_Y + max_len_M + max_len_rinv + md (32 bytes) + (mprime + length packed (8bytes)) + padding (8 bytes)
expected_len = (max_key_size / 8) * 3 + 32 + 8 + 8
assert len(p) == expected_len
cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
c = encryptor.update(p) + encryptor.finalize()
f.write(' .expected_c = %s,\n' % bytes_as_char_array(c))
f.write(' .hmac_key_idx = %d,\n' % (hmac_key_idx))
f.write(' // results of message array encrypted with these keys\n')
f.write(' .expected_results = {\n')
mask = (1 << key_size) - 1 # truncate messages if needed
for m in messages:
f.write(' // Message %d\n' % messages.index(m))
f.write(' %s,' % (number_as_bignum_words(pow(m & mask, Y, M))))
f.write(' },\n')
f.write(' },\n')
f.write('};\n')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='''Generates Digital Signature Test Cases''')
parser.add_argument(
'--target',
required=True,
choices=supported_targets,
help='Target to generate test cases for, different targets support different max key length')
args = parser.parse_args()
generate_tests_cases(args.target)

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -22,20 +22,6 @@ extern "C" {
*/ */
typedef void (*esp_ipc_isr_func_t)(void* arg); typedef void (*esp_ipc_isr_func_t)(void* arg);
/**
* @brief Initialize the IPC ISR feature
*
* This function initializes the IPC ISR feature and must be called before any other esp_ipc_isr...() functions.
* The IPC ISR feature allows for callbacks (written in assembly) to be run on a particular CPU in the context of a
* High Priority Interrupt.
*
* - This function will register a High Priority Interrupt on each CPU. The priority of the interrupts is dependent on
* the CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL option.
* - Callbacks written in assembly can then run in context of the registered High Priority Interrupts
* - Callbacks can be executed by calling esp_ipc_isr_asm_call() or esp_ipc_isr_asm_call_blocking()
*/
void esp_ipc_isr_init(void);
/** /**
* @brief Execute an assembly callback on the other CPU * @brief Execute an assembly callback on the other CPU
* *

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -10,7 +10,7 @@
#include <assert.h> #include <assert.h>
#include "esp_err.h" #include "esp_err.h"
#include "esp_ipc.h" #include "esp_ipc.h"
#include "esp_ipc_isr.h" #include "esp_private/esp_ipc_isr.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
@ -44,6 +44,9 @@ static void IRAM_ATTR ipc_task(void* arg)
{ {
const int cpuid = (int) arg; const int cpuid = (int) arg;
assert(cpuid == xPortGetCoreID()); assert(cpuid == xPortGetCoreID());
#ifdef CONFIG_ESP_IPC_ISR_ENABLE
esp_ipc_isr_init();
#endif
while (true) { while (true) {
// Wait for IPC to be initiated. // Wait for IPC to be initiated.
// This will be indicated by giving the semaphore corresponding to // This will be indicated by giving the semaphore corresponding to
@ -97,9 +100,6 @@ static void esp_ipc_init(void) __attribute__((constructor));
static void esp_ipc_init(void) static void esp_ipc_init(void)
{ {
#ifdef CONFIG_ESP_IPC_ISR_ENABLE
esp_ipc_isr_init();
#endif
char task_name[15]; char task_name[15];
for (int i = 0; i < portNUM_PROCESSORS; ++i) { for (int i = 0; i < portNUM_PROCESSORS; ++i) {

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -23,6 +23,7 @@
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/portmacro.h" #include "freertos/portmacro.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"
#include "esp_private/esp_ipc_isr.h"
#include "esp_ipc_isr.h" #include "esp_ipc_isr.h"
#include "xtensa/core-macros.h" #include "xtensa/core-macros.h"
#include "sdkconfig.h" #include "sdkconfig.h"
@ -60,31 +61,17 @@ static void esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* arg, esp_ip
/* Initializing IPC_ISR */ /* Initializing IPC_ISR */
static void esp_ipc_isr_init_cpu(void* arg) void esp_ipc_isr_init(void)
{ {
(void) arg;
const uint32_t cpuid = xPortGetCoreID(); const uint32_t cpuid = xPortGetCoreID();
uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE + cpuid; // ETS_FROM_CPU_INTR2_SOURCE and ETS_FROM_CPU_INTR3_SOURCE uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE + cpuid; // ETS_FROM_CPU_INTR2_SOURCE and ETS_FROM_CPU_INTR3_SOURCE
ESP_INTR_DISABLE(ETS_IPC_ISR_INUM); ESP_INTR_DISABLE(ETS_IPC_ISR_INUM);
intr_matrix_set(cpuid, intr_source, ETS_IPC_ISR_INUM); intr_matrix_set(cpuid, intr_source, ETS_IPC_ISR_INUM);
ESP_INTR_ENABLE(ETS_IPC_ISR_INUM); ESP_INTR_ENABLE(ETS_IPC_ISR_INUM);
/* If this fails then the minimum stack size for this config is too close to running out */
assert(uxTaskGetStackHighWaterMark(NULL) > 128);
if (cpuid != 0) { if (cpuid != 0) {
s_stall_state = STALL_STATE_RUNNING; s_stall_state = STALL_STATE_RUNNING;
} }
vTaskDelete(NULL);
}
void esp_ipc_isr_init(void)
{
for (unsigned i = 0; i < portNUM_PROCESSORS; ++i) {
portBASE_TYPE res = xTaskCreatePinnedToCore(esp_ipc_isr_init_cpu, "ipc_isr_init", configMINIMAL_STACK_SIZE, NULL, 5, NULL, i);
assert(res == pdTRUE);
(void)res;
}
} }
/* End initializing IPC_ISR */ /* End initializing IPC_ISR */

View File

@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef CONFIG_ESP_IPC_ISR_ENABLE
/**
* @brief Initialize the IPC ISR feature, must be called for each CPU
*
* @note This function is called from ipc_task().
*
* This function initializes the IPC ISR feature and must be called before any other esp_ipc_isr...() functions.
* The IPC ISR feature allows for callbacks (written in assembly) to be run on a particular CPU in the context of a
* High Priority Interrupt.
*
* - This function will register a High Priority Interrupt for a CPU where it is called. The priority of the interrupts is dependent on
* the CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL option.
* - Callbacks written in assembly can then run in context of the registered High Priority Interrupts
* - Callbacks can be executed by calling esp_ipc_isr_asm_call() or esp_ipc_isr_asm_call_blocking()
*/
void esp_ipc_isr_init(void);
#endif // CONFIG_ESP_IPC_ISR_ENABLE
#ifdef __cplusplus
}
#endif

View File

@ -20,6 +20,10 @@
#include "hal/systimer_ll.h" #include "hal/systimer_ll.h"
#endif #endif
#ifdef CONFIG_PM_TRACE
#include "esp_private/pm_trace.h"
#endif //CONFIG_PM_TRACE
BaseType_t xPortSysTickHandler(void); BaseType_t xPortSysTickHandler(void);
#ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT

View File

@ -85,7 +85,6 @@
#include "riscv/riscv_interrupts.h" #include "riscv/riscv_interrupts.h"
#include "riscv/interrupt.h" #include "riscv/interrupt.h"
#include "esp_private/crosscore_int.h" #include "esp_private/crosscore_int.h"
#include "esp_private/pm_trace.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_system.h" #include "esp_system.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"

View File

@ -45,10 +45,12 @@
#undef taskEXIT_CRITICAL #undef taskEXIT_CRITICAL
#undef taskENTER_CRITICAL_ISR #undef taskENTER_CRITICAL_ISR
#undef taskEXIT_CRITICAL_ISR #undef taskEXIT_CRITICAL_ISR
#define taskENTER_CRITICAL( ) portENTER_CRITICAL( taskCRITICAL_MUX ) #define taskENTER_CRITICAL( ) portENTER_CRITICAL( taskCRITICAL_MUX )
#define taskEXIT_CRITICAL( ) portEXIT_CRITICAL( taskCRITICAL_MUX ) #define taskEXIT_CRITICAL( ) portEXIT_CRITICAL( taskCRITICAL_MUX )
#define taskENTER_CRITICAL_ISR( ) portENTER_CRITICAL_ISR( taskCRITICAL_MUX ) #define taskENTER_CRITICAL_ISR( ) portENTER_CRITICAL_ISR( taskCRITICAL_MUX )
#define taskEXIT_CRITICAL_ISR( ) portEXIT_CRITICAL_ISR( taskCRITICAL_MUX ) #define taskEXIT_CRITICAL_ISR( ) portEXIT_CRITICAL_ISR( taskCRITICAL_MUX )
#undef _REENT_INIT_PTR
#define _REENT_INIT_PTR esp_reent_init
#endif #endif
/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified /* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified
@ -1102,11 +1104,8 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
#if ( configUSE_NEWLIB_REENTRANT == 1 ) #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. */ /* Initialise this task's Newlib reent structure. */
esp_reent_init(&pxNewTCB->xNewLib_reent); _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
} }
#endif #endif

View File

@ -23,6 +23,10 @@
#include "heap_private.h" #include "heap_private.h"
#include "esp_system.h" #include "esp_system.h"
// forward declaration
IRAM_ATTR static void *heap_caps_realloc_base( void *ptr, size_t size, uint32_t caps);
/* /*
This file, combined with a region allocator that supports multiple heaps, solves the problem that the ESP32 has RAM This file, combined with a region allocator that supports multiple heaps, solves the problem that the ESP32 has RAM
that's slightly heterogeneous. Some RAM can be byte-accessed, some allows only 32-bit accesses, some can execute memory, that's slightly heterogeneous. Some RAM can be byte-accessed, some allows only 32-bit accesses, some can execute memory,
@ -84,18 +88,18 @@ bool heap_caps_match(const heap_t *heap, uint32_t caps)
return heap->heap != NULL && ((get_all_caps(heap) & caps) == caps); return heap->heap != NULL && ((get_all_caps(heap) & caps) == caps);
} }
/* /*
Routine to allocate a bit of memory with certain capabilities. caps is a bitfield of MALLOC_CAP_* bits. This function should not be called directly as it does not
check for failure / call heap_caps_alloc_failed()
*/ */
IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps ) IRAM_ATTR static void *heap_caps_malloc_base( size_t size, uint32_t caps)
{ {
void *ret = NULL; void *ret = NULL;
if (size > HEAP_SIZE_MAX) { if (size > HEAP_SIZE_MAX) {
// Avoids int overflow when adding small numbers to size, or // Avoids int overflow when adding small numbers to size, or
// calculating 'end' from start+size, by limiting 'size' to the possible range // calculating 'end' from start+size, by limiting 'size' to the possible range
heap_caps_alloc_failed(size, caps, __func__);
return NULL; return NULL;
} }
@ -105,8 +109,6 @@ IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps )
//NULL directly, even although our heap capabilities (based on soc_memory_tags & soc_memory_regions) would //NULL directly, even although our heap capabilities (based on soc_memory_tags & soc_memory_regions) would
//indicate there is a tag for this. //indicate there is a tag for this.
if ((caps & MALLOC_CAP_8BIT) || (caps & MALLOC_CAP_DMA)) { if ((caps & MALLOC_CAP_8BIT) || (caps & MALLOC_CAP_DMA)) {
heap_caps_alloc_failed(size, caps, __func__);
return NULL; return NULL;
} }
caps |= MALLOC_CAP_32BIT; // IRAM is 32-bit accessible RAM caps |= MALLOC_CAP_32BIT; // IRAM is 32-bit accessible RAM
@ -152,13 +154,26 @@ IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps )
} }
} }
heap_caps_alloc_failed(size, caps, __func__);
//Nothing usable found. //Nothing usable found.
return NULL; return NULL;
} }
/*
Routine to allocate a bit of memory with certain capabilities. caps is a bitfield of MALLOC_CAP_* bits.
*/
IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps){
void* ptr = heap_caps_malloc_base(size, caps);
if (!ptr){
heap_caps_alloc_failed(size, caps, __func__);
}
return ptr;
}
#define MALLOC_DISABLE_EXTERNAL_ALLOCS -1 #define MALLOC_DISABLE_EXTERNAL_ALLOCS -1
//Dual-use: -1 (=MALLOC_DISABLE_EXTERNAL_ALLOCS) disables allocations in external memory, >=0 sets the limit for allocations preferring internal memory. //Dual-use: -1 (=MALLOC_DISABLE_EXTERNAL_ALLOCS) disables allocations in external memory, >=0 sets the limit for allocations preferring internal memory.
static int malloc_alwaysinternal_limit=MALLOC_DISABLE_EXTERNAL_ALLOCS; static int malloc_alwaysinternal_limit=MALLOC_DISABLE_EXTERNAL_ALLOCS;
@ -176,16 +191,26 @@ IRAM_ATTR void *heap_caps_malloc_default( size_t size )
if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) { if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) {
return heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL); return heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
} else { } else {
// use heap_caps_malloc_base() since we'll
// check for allocation failure ourselves
void *r; void *r;
if (size <= (size_t)malloc_alwaysinternal_limit) { if (size <= (size_t)malloc_alwaysinternal_limit) {
r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL ); r=heap_caps_malloc_base( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
} else { } else {
r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM ); r=heap_caps_malloc_base( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM );
} }
if (r==NULL) { if (r==NULL) {
//try again while being less picky //try again while being less picky
r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT ); r=heap_caps_malloc_base( size, MALLOC_CAP_DEFAULT );
} }
// allocation failure?
if (r==NULL){
heap_caps_alloc_failed(size, MALLOC_CAP_DEFAULT, __func__);
}
return r; return r;
} }
} }
@ -199,15 +224,25 @@ IRAM_ATTR void *heap_caps_realloc_default( void *ptr, size_t size )
if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) { if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) {
return heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL ); return heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
} else { } else {
// We use heap_caps_realloc_base() since we'll
// handle allocation failure ourselves
void *r; void *r;
if (size <= (size_t)malloc_alwaysinternal_limit) { if (size <= (size_t)malloc_alwaysinternal_limit) {
r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL ); r=heap_caps_realloc_base( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
} else { } else {
r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM ); r=heap_caps_realloc_base( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM);
} }
if (r==NULL && size>0) { if (r==NULL && size>0) {
//We needed to allocate memory, but we didn't. Try again while being less picky. //We needed to allocate memory, but we didn't. Try again while being less picky.
r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT ); r=heap_caps_realloc_base( ptr, size, MALLOC_CAP_DEFAULT);
}
// allocation failure?
if (r==NULL && size>0){
heap_caps_alloc_failed(size, MALLOC_CAP_DEFAULT, __func__);
} }
return r; return r;
} }
@ -305,14 +340,18 @@ IRAM_ATTR void heap_caps_free( void *ptr)
multi_heap_free(heap->heap, ptr); multi_heap_free(heap->heap, ptr);
} }
IRAM_ATTR void *heap_caps_realloc( void *ptr, size_t size, uint32_t caps) /*
This function should not be called directly as it does not
check for failure / call heap_caps_alloc_failed()
*/
IRAM_ATTR static void *heap_caps_realloc_base( void *ptr, size_t size, uint32_t caps)
{ {
bool ptr_in_diram_case = false; bool ptr_in_diram_case = false;
heap_t *heap = NULL; heap_t *heap = NULL;
void *dram_ptr = NULL; void *dram_ptr = NULL;
if (ptr == NULL) { if (ptr == NULL) {
return heap_caps_malloc(size, caps); return heap_caps_malloc_base(size, caps);
} }
if (size == 0) { if (size == 0) {
@ -321,8 +360,6 @@ IRAM_ATTR void *heap_caps_realloc( void *ptr, size_t size, uint32_t caps)
} }
if (size > HEAP_SIZE_MAX) { if (size > HEAP_SIZE_MAX) {
heap_caps_alloc_failed(size, caps, __func__);
return NULL; return NULL;
} }
@ -360,7 +397,7 @@ IRAM_ATTR void *heap_caps_realloc( void *ptr, size_t size, uint32_t caps)
// if we couldn't do that, try to see if we can reallocate // if we couldn't do that, try to see if we can reallocate
// in a different heap with requested capabilities. // in a different heap with requested capabilities.
void *new_p = heap_caps_malloc(size, caps); void *new_p = heap_caps_malloc_base(size, caps);
if (new_p != NULL) { if (new_p != NULL) {
size_t old_size = 0; size_t old_size = 0;
@ -378,11 +415,20 @@ IRAM_ATTR void *heap_caps_realloc( void *ptr, size_t size, uint32_t caps)
return new_p; return new_p;
} }
heap_caps_alloc_failed(size, caps, __func__);
return NULL; return NULL;
} }
IRAM_ATTR void *heap_caps_realloc( void *ptr, size_t size, uint32_t caps)
{
ptr = heap_caps_realloc_base(ptr, size, caps);
if (ptr == NULL && size > 0){
heap_caps_alloc_failed(size, caps, __func__);
}
return ptr;
}
IRAM_ATTR void *heap_caps_calloc( size_t n, size_t size, uint32_t caps) IRAM_ATTR void *heap_caps_calloc( size_t n, size_t size, uint32_t caps)
{ {
void *result; void *result;

View File

@ -1,16 +1,8 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
// Licensed under the Apache License, Version 2.0 (the "License"); *
// you may not use this file except in compliance with the License. * SPDX-License-Identifier: Apache-2.0
// 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_NVS_H #ifndef ESP_NVS_H
#define ESP_NVS_H #define ESP_NVS_H
@ -105,7 +97,7 @@ typedef enum {
*/ */
typedef struct { typedef struct {
char namespace_name[16]; /*!< Namespace to which key-value belong */ char namespace_name[16]; /*!< Namespace to which key-value belong */
char key[16]; /*!< Key of stored key-value pair */ char key[NVS_KEY_NAME_MAX_SIZE]; /*!< Key of stored key-value pair */
nvs_type_t type; /*!< Type of stored key-value pair */ nvs_type_t type; /*!< Type of stored key-value pair */
} nvs_entry_info_t; } nvs_entry_info_t;

View File

@ -1,16 +1,8 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
// Licensed under the Apache License, Version 2.0 (the "License"); *
// you may not use this file except in compliance with the License. * SPDX-License-Identifier: Apache-2.0
// 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 "nvs_page.hpp" #include "nvs_page.hpp"
#include <esp_rom_crc.h> #include <esp_rom_crc.h>
#include <cstdio> #include <cstdio>
@ -200,6 +192,10 @@ esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, c
return ESP_ERR_NVS_VALUE_TOO_LONG; return ESP_ERR_NVS_VALUE_TOO_LONG;
} }
if ((!isVariableLengthType(datatype)) && dataSize > 8) {
return ESP_ERR_INVALID_ARG;
}
size_t totalSize = ENTRY_SIZE; size_t totalSize = ENTRY_SIZE;
size_t entriesCount = 1; size_t entriesCount = 1;
if (isVariableLengthType(datatype)) { if (isVariableLengthType(datatype)) {
@ -244,7 +240,8 @@ esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, c
return err; return err;
} }
size_t left = dataSize / ENTRY_SIZE * ENTRY_SIZE; size_t rest = dataSize % ENTRY_SIZE;
size_t left = dataSize - rest;
if (left > 0) { if (left > 0) {
err = writeEntryData(static_cast<const uint8_t*>(data), left); err = writeEntryData(static_cast<const uint8_t*>(data), left);
if (err != ESP_OK) { if (err != ESP_OK) {
@ -252,7 +249,7 @@ esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, c
} }
} }
size_t tail = dataSize - left; size_t tail = rest;
if (tail > 0) { if (tail > 0) {
std::fill_n(item.rawData, ENTRY_SIZE, 0xff); std::fill_n(item.rawData, ENTRY_SIZE, 0xff);
memcpy(item.rawData, static_cast<const uint8_t*>(data) + left, tail); memcpy(item.rawData, static_cast<const uint8_t*>(data) + left, tail);

View File

@ -1,16 +1,8 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
// Licensed under the Apache License, Version 2.0 (the "License"); *
// you may not use this file except in compliance with the License. * SPDX-License-Identifier: Apache-2.0
// 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 "nvs_storage.hpp" #include "nvs_storage.hpp"
#ifndef ESP_PLATFORM #ifndef ESP_PLATFORM
@ -419,11 +411,6 @@ esp_err_t Storage::createOrOpenNamespace(const char* nsName, bool canCreate, uin
return ESP_ERR_NVS_NOT_ENOUGH_SPACE; return ESP_ERR_NVS_NOT_ENOUGH_SPACE;
} }
NamespaceEntry* entry = new (std::nothrow) NamespaceEntry;
if (!entry) {
return ESP_ERR_NO_MEM;
}
auto err = writeItem(Page::NS_INDEX, ItemType::U8, nsName, &ns, sizeof(ns)); auto err = writeItem(Page::NS_INDEX, ItemType::U8, nsName, &ns, sizeof(ns));
if (err != ESP_OK) { if (err != ESP_OK) {
return err; return err;
@ -431,6 +418,11 @@ esp_err_t Storage::createOrOpenNamespace(const char* nsName, bool canCreate, uin
mNamespaceUsage.set(ns, true); mNamespaceUsage.set(ns, true);
nsIndex = ns; nsIndex = ns;
NamespaceEntry* entry = new (std::nothrow) NamespaceEntry;
if (!entry) {
return ESP_ERR_NO_MEM;
}
entry->mIndex = ns; entry->mIndex = ns;
strncpy(entry->mName, nsName, sizeof(entry->mName) - 1); strncpy(entry->mName, nsName, sizeof(entry->mName) - 1);
entry->mName[sizeof(entry->mName) - 1] = 0; entry->mName[sizeof(entry->mName) - 1] = 0;
@ -734,11 +726,13 @@ esp_err_t Storage::calcEntriesInNamespace(uint8_t nsIndex, size_t& usedEntries)
void Storage::fillEntryInfo(Item &item, nvs_entry_info_t &info) void Storage::fillEntryInfo(Item &item, nvs_entry_info_t &info)
{ {
info.type = static_cast<nvs_type_t>(item.datatype); info.type = static_cast<nvs_type_t>(item.datatype);
strncpy(info.key, item.key, sizeof(info.key)); strncpy(info.key, item.key, sizeof(info.key) - 1);
info.key[sizeof(info.key) - 1] = '\0';
for (auto &name : mNamespaces) { for (auto &name : mNamespaces) {
if(item.nsIndex == name.mIndex) { if(item.nsIndex == name.mIndex) {
strncpy(info.namespace_name, name.mName, sizeof(info.namespace_name)); strncpy(info.namespace_name, name.mName, sizeof(info.namespace_name) - 1);
info.namespace_name[sizeof(info.namespace_name) -1] = '\0';
break; break;
} }
} }

View File

@ -4,7 +4,7 @@ Copyrights and Licenses
Software Copyrights Software Copyrights
=================== ===================
All original source code in this repository is Copyright (C) 2015-2021 Espressif Systems. This source code is licensed under the Apache License 2.0 as described in the file LICENSE. All original source code in this repository is Copyright (C) 2015-2022 Espressif Systems. This source code is licensed under the Apache License 2.0 as described in the file LICENSE.
Additional third party copyrighted code is included under the following licenses. Additional third party copyrighted code is included under the following licenses.

View File

@ -4,7 +4,7 @@ High-Level Interrupts
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
The Xtensa architecture has support for 32 interrupts, divided over 8 levels, plus an assortment of exceptions. On the {IDF_TARGET_NAME}, the interrupt mux allows most interrupt sources to be routed to these interrupts using the :doc:`interrupt allocator <../api-reference/system/intr_alloc>`. Normally, interrupts will be written in C, but ESP-IDF allows high-level interrupts to be written in assembly as well, allowing for very low interrupt latencies. The Xtensa architecture has support for 32 interrupts, divided over 7 levels (levels 1 to 7, with 7 being an NMI), plus an assortment of exceptions. On the {IDF_TARGET_NAME}, the interrupt mux allows most interrupt sources to be routed to these interrupts using the :doc:`interrupt allocator <../api-reference/system/intr_alloc>`. Normally, interrupts will be written in C, but ESP-IDF allows high-level interrupts to be written in assembly as well, resulting in very low interrupt latencies.
Interrupt Levels Interrupt Levels
---------------- ----------------
@ -60,37 +60,35 @@ For a real-life example, see the :component_file:`esp_system/port/soc/{IDF_TARGE
Notes Notes
----- -----
- Do not call C code from a high-level interrupt; because these interrupts still run in critical sections, this can cause crashes. - Do not call C code from a high-level interrupt; as these interrupts are run from a critical section, this can cause the target to crash.
(The panic handler interrupt does call normal C code, but this is OK because there is no intention of returning to the normal code Note that although the panic handler interrupt does call normal C code, this exception is allowed due to the fact that this handler never returns (i.e., the application will not continue to run after the panic handler).
flow afterwards.) so breaking C code execution flow is not a problem.
.. only:: esp32 .. only:: esp32
And if :ref:`CONFIG_BTDM_CTRL_HLI` is enabled, it does call normal C code in high-level interrupt, but this is OK becase we add some protection for it. When :ref:`CONFIG_BTDM_CTRL_HLI` is enabled, C code is also called from a high-level interrupt, this is possible thanks to some additional protection added to it.
- Make sure your assembly code gets linked in. If the interrupt handler symbol is the only symbol the rest of the code uses from this - Make sure your assembly code gets linked in. Indeed, as the free-to-use symbols are declared as weak, the linker may discard the file containing the symbol. This will
file, the linker will take the default ISR instead and not link the assembly file into the final project. To get around this, in the happen if the only symbol defined, or used, from the user file is the ``xt_*`` free-to-use symbol. To avoid this, in the assembly file containing the ``xt_*`` symbol,
assembly file, define a symbol, like this:: define another symbol, like::
.global ld_include_my_isr_file .global ld_include_my_isr_file
ld_include_my_isr_file: ld_include_my_isr_file:
Here it is called ``ld_include_my_isr_file`` but can have any name, as long as it is not defined anywhere else in the project.
The symbol is called ``ld_include_my_isr_file`` here but can have any arbitrary name not defined anywhere else. Then, in the component ``CMakeLists.txt``, add this name as an unresolved symbol to the ld command line arguments::
Then, in the component CMakeLists.txt, add this file as an unresolved symbol to the ld command line arguments:: target_link_libraries(${COMPONENT_TARGET} "-u ld_include_my_isr_file")
target_link_libraries(${COMPONENT_TARGET} "-u ld_include_my_isr_file") This should cause the linker to always include the file defining ``ld_include_my_isr_file``, causing the ISR to always be linked in.
This should cause the linker to always include a file defining ``ld_include_my_isr_file``, causing the ISR to always be linked in.
- High-level interrupts can be routed and handled using :cpp:func:`esp_intr_alloc` and associated functions. The handler and handler arguments
to :cpp:func:`esp_intr_alloc` must be NULL, however.
- In theory, medium priority interrupts could also be handled in this way. ESP-IDF does not support this yet.
If using the legacy Make build system, add the following to component.mk, instead:: If using the legacy Make build system, add the following to component.mk, instead::
COMPONENT_ADD_LDFLAGS := -u ld_include_my_isr_file COMPONENT_ADD_LDFLAGS := -u ld_include_my_isr_file
This should cause the linker to always include a file defining ``ld_include_my_isr_file``, causing the ISR to always be linked in.
- High-level interrupts can be routed and handled using esp_intr_alloc and associated functions. The handler and handler arguments
to esp_intr_alloc must be NULL, however.
- In theory, medium priority interrupts could also be handled in this way. For now, ESP-IDF does not support this.

View File

@ -12,6 +12,10 @@ Overview
The {IDF_TARGET_NAME} has one core, with 32 interrupts. Each interrupt has a certain priority level, most (but not all) interrupts are connected to the interrupt mux. The {IDF_TARGET_NAME} has one core, with 32 interrupts. Each interrupt has a certain priority level, most (but not all) interrupts are connected to the interrupt mux.
.. only:: esp32s3
The {IDF_TARGET_NAME} has two cores, with 32 interrupts. Each interrupt has a certain priority level, most (but not all) interrupts are connected to the interrupt mux.
.. only:: esp32c3 .. only:: esp32c3
The {IDF_TARGET_NAME} has one core, with 31 interrupts. Each interrupt has a programmable priority level. The {IDF_TARGET_NAME} has one core, with 31 interrupts. Each interrupt has a programmable priority level.
@ -19,27 +23,28 @@ Overview
Because there are more interrupt sources than interrupts, sometimes it makes sense to share an interrupt in multiple drivers. The :cpp:func:`esp_intr_alloc` abstraction exists to hide all these implementation details. Because there are more interrupt sources than interrupts, sometimes it makes sense to share an interrupt in multiple drivers. The :cpp:func:`esp_intr_alloc` abstraction exists to hide all these implementation details.
A driver can allocate an interrupt for a certain peripheral by calling :cpp:func:`esp_intr_alloc` (or :cpp:func:`esp_intr_alloc_intrstatus`). It can use A driver can allocate an interrupt for a certain peripheral by calling :cpp:func:`esp_intr_alloc` (or :cpp:func:`esp_intr_alloc_intrstatus`). It can use
the flags passed to this function to set the type of interrupt allocated, specifying a specific level or trigger method. The the flags passed to this function to set the type of interrupt allocated, specifying a particular level or trigger method. The
interrupt allocation code will then find an applicable interrupt, use the interrupt mux to hook it up to the peripheral, and interrupt allocation code will then find an applicable interrupt, use the interrupt mux to hook it up to the peripheral, and
install the given interrupt handler and ISR to it. install the given interrupt handler and ISR to it.
This code has two different types of interrupts it handles differently: Shared interrupts and non-shared interrupts. The simplest This code presents two different types of interrupts, handled differently: shared interrupts and non-shared interrupts. The simplest
of the two are non-shared interrupts: a separate interrupt is allocated per esp_intr_alloc call and this interrupt is solely used for ones are non-shared interrupts: a separate interrupt is allocated per :cpp:func:`esp_intr_alloc` call and this interrupt is solely used for
the peripheral attached to it, with only one ISR that will get called. Shared interrupts can have multiple peripherals triggering the peripheral attached to it, with only one ISR that will get called. On the other hand, shared interrupts can have multiple peripherals triggering
it, with multiple ISRs being called when one of the peripherals attached signals an interrupt. Thus, ISRs that are intended for shared them, with multiple ISRs being called when one of the peripherals attached signals an interrupt. Thus, ISRs that are intended for shared
interrupts should check the interrupt status of the peripheral they service in order to see if any action is required. interrupts should check the interrupt status of the peripheral they service in order to check if any action is required.
Non-shared interrupts can be either level- or edge-triggered. Shared interrupts can Non-shared interrupts can be either level- or edge-triggered. Shared interrupts can
only be level interrupts (because of the chance of missed interrupts when edge interrupts are only be level interrupts due to the chance of missed interrupts when edge interrupts are
used.) used.
(The logic behind this: DevA and DevB share an int. DevB signals an int. Int line goes high. ISR handler
calls code for DevA -> does nothing. ISR handler calls code for DevB, but while doing that, For example, let's say DevA and DevB share an interrupt. DevB signals an interrupt, so INT line goes high. The ISR handler
DevA signals an int. ISR DevB is done, clears int for DevB, exits interrupt code. Now an calls code for DevA but does nothing. Then, ISR handler calls code for DevB, but while doing that,
interrupt for DevA is still pending, but because the int line never went low (DevA kept it high DevA signals an interrupt. DevB's ISR is done, it clears interrupt status for DevB and exits interrupt code. Now, an
even when the int for DevB was cleared) the interrupt is never serviced.) interrupt for DevA is still pending, but because the INT line never went low, as DevA kept it high
even when the interrupt for DevB was cleared, the interrupt is never serviced.
.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA .. only:: esp32 or esp32s3
Multicore issues Multicore issues
---------------- ----------------
@ -63,9 +68,9 @@ even when the int for DevB was cleared) the interrupt is never serviced.)
Internal interrupt sources are defined in esp_intr_alloc.h as ``ETS_INTERNAL_*_INTR_SOURCE``. Internal interrupt sources are defined in esp_intr_alloc.h as ``ETS_INTERNAL_*_INTR_SOURCE``.
These peripherals can only be configured from the core they are associated with. When generating an interrupt, These peripherals can only be configured from the core they are associated with. When generating an interrupt,
the interrupt they generate is hard-wired to their associated core; it's not possible to have e.g. an internal the interrupt they generate is hard-wired to their associated core; it's not possible to have, for example, an internal
timer comparator of one core generate an interrupt on another core. That is why these sources can only be managed timer comparator of one core generate an interrupt on another core. That is why these sources can only be managed
using a task running on that specific core. Internal interrupt sources are still allocatable using esp_intr_alloc using a task running on that specific core. Internal interrupt sources are still allocatable using :cpp:func:`esp_intr_alloc`
as normal, but they cannot be shared and will always have a fixed interrupt level (namely, the one associated in as normal, but they cannot be shared and will always have a fixed interrupt level (namely, the one associated in
hardware with the peripheral). hardware with the peripheral).
@ -80,9 +85,10 @@ even when the int for DevB was cleared) the interrupt is never serviced.)
- Allocating an external interrupt will always allocate it on the core that does the allocation. - Allocating an external interrupt will always allocate it on the core that does the allocation.
- Freeing an external interrupt must always happen on the same core it was allocated on. - Freeing an external interrupt must always happen on the same core it was allocated on.
- Disabling and enabling external interrupts from another core is allowed. - Disabling and enabling external interrupts from another core is allowed.
- Multiple external interrupt sources can share an interrupt slot by passing ``ESP_INTR_FLAG_SHARED`` as a flag to esp_intr_alloc(). - Multiple external interrupt sources can share an interrupt slot by passing ``ESP_INTR_FLAG_SHARED`` as a flag to :cpp:func:`esp_intr_alloc`.
Care should be taken when calling esp_intr_alloc() from a task which is not pinned to a core. During task switching, these tasks can migrate between cores. Therefore it is impossible to tell which CPU the interrupt is allocated on, which makes it difficult to free the interrupt handle and may also cause debugging difficulties. It is advised to use xTaskCreatePinnedToCore() with a specific CoreID argument to create tasks that will allocate interrupts. In the case of internal interrupt sources, this is required. Care should be taken when calling :cpp:func:`esp_intr_alloc` from a task which is not pinned to a core. During task switching, these tasks can migrate between cores. Therefore it is impossible to tell which CPU the interrupt is allocated on, which makes it difficult to free the interrupt handle and may also
cause debugging difficulties. It is advised to use :cpp:func:`xTaskCreatePinnedToCore` with a specific CoreID argument to create tasks that will allocate interrupts. In the case of internal interrupt sources, this is required.
IRAM-Safe Interrupt Handlers IRAM-Safe Interrupt Handlers
---------------------------- ----------------------------
@ -99,15 +105,16 @@ Multiple Handlers Sharing A Source
---------------------------------- ----------------------------------
Several handlers can be assigned to a same source, given that all handlers are allocated using the ``ESP_INTR_FLAG_SHARED`` flag. Several handlers can be assigned to a same source, given that all handlers are allocated using the ``ESP_INTR_FLAG_SHARED`` flag.
They'll be all allocated to the interrupt, which the source is attached to, and called sequentially when the source is active. They will all be allocated to the interrupt, which the source is attached to, and called sequentially when the source is active.
The handlers can be disabled and freed individually. The source is attached to the interrupt (enabled), if one or more handlers are enabled, otherwise detached. The handlers can be disabled and freed individually. The source is attached to the interrupt (enabled), if one or more handlers are enabled, otherwise detached.
A handler will never be called when disabled, while **its source may still be triggered** if any one of its handler enabled. A handler will never be called when disabled, while **its source may still be triggered** if any one of its handler enabled.
Sources attached to non-shared interrupt do not support this feature. Sources attached to non-shared interrupt do not support this feature.
Though the framework support this feature, you have to use it *very carefully*. There usually exist 2 ways to stop a interrupt from being triggered: *disable the source* or *mask peripheral interrupt status*. Though the framework support this feature, you have to use it *very carefully*. There usually exist two ways to stop an interrupt from being triggered: *disable the source* or *mask peripheral interrupt status*.
IDF only handles the enabling and disabling of the source itself, leaving status and mask bits to be handled by users. **Status bits should always be masked before the handler responsible for it is disabled, IDF only handles enabling and disabling of the source itself, leaving status and mask bits to be handled by users.
or the status should be handled in other enabled interrupt properly**. You may leave some status bits unhandled if you just disable one of all the handlers without masking the status bits, which causes the interrupt to trigger infinitely resulting in a system crash. **Status bits shall either be masked before the handler responsible for it is disabled, either be masked and then properly handled in another enabled interrupt**.
Please note that leaving some status bits unhandled without masking them, while disabling the handlers for them, will cause the interrupt(s) to be triggered indefinitely, resulting therefore in a system crash.
API Reference API Reference
------------- -------------

View File

@ -14,9 +14,13 @@ except ImportError:
sys.path.insert(0, os.path.abspath('../')) sys.path.insert(0, os.path.abspath('../'))
from conf_common import * # noqa: F403,F401 from conf_common import * # noqa: F403,F401
import datetime
current_year = datetime.datetime.now().year
# General information about the project. # General information about the project.
project = u'ESP-IDF Programming Guide' project = u'ESP-IDF Programming Guide'
copyright = u'2016 - 2021, Espressif Systems (Shanghai) Co., Ltd' copyright = u'2016 - {}, Espressif Systems (Shanghai) Co., Ltd'.format(current_year)
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -14,9 +14,13 @@ except ImportError:
sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('..'))
from conf_common import * # noqa: F403,F401 from conf_common import * # noqa: F403,F401
import datetime
current_year = datetime.datetime.now().year
# General information about the project. # General information about the project.
project = u'ESP-IDF 编程指南' project = u'ESP-IDF 编程指南'
copyright = u'2016 - 2021 乐鑫信息科技(上海)股份有限公司' copyright = u'2016 - {} 乐鑫信息科技(上海)股份有限公司'.format(current_year)
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -48,7 +48,6 @@ docs/sanitize_version.py
docs/test/en/conf.py docs/test/en/conf.py
docs/test/test_docs.py docs/test/test_docs.py
docs/test/test_sphinx_idf_extensions.py docs/test/test_sphinx_idf_extensions.py
docs/zh_CN/conf.py
examples/bluetooth/nimble/blecent/blecent_test.py examples/bluetooth/nimble/blecent/blecent_test.py
examples/bluetooth/nimble/blehr/blehr_test.py examples/bluetooth/nimble/blehr/blehr_test.py
examples/bluetooth/nimble/bleprph/bleprph_test.py examples/bluetooth/nimble/bleprph/bleprph_test.py