WCH CH32V317, ядро QingKeV4F. После взведения бита запроса
прерывания, требуется пауза, иначе следующая команда запрета
перываний, не даёт шанса сработать запрошенному. Ну, спецы по ядрам и контроллерам, подключайтесь!
Обнаружил, что не работает:
xAlreadyYielded = xTaskResumeAll(); /*Тут проверяется, вызван ли уже планировщик для переключения задач?*/
/* Force a reschedule if xTaskResumeAll has not already done so. */
if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) ) /*Тут проверяется, блокирована ли текущая задача или пусть работает до вызова планировщика по таймеру?*/
{
taskYIELD_WITHIN_API(); /*Ясно, что текущая задача блокирована, а планировщик ещё не вызван, поэтому вызываем принудительно, взводя бит программного прерывания планировщика*/
/*А вот хер там! Прерывание планировщика, несмотря на установленный бит "pending" не успевает сработать. Код ниже успевает заблокировать прерывания раньше входа в прерывание!*/
}
taskENTER_CRITICAL(); /*Тут сразу входим в критическую секцию для обработки пробуждения текущей задачи, которая... А вот хер! Которая должна была заснуть но продолжает выполняться!*/
А работает так:
xAlreadyYielded = xTaskResumeAll(); /*Тут проверяется, вызван ли уже планировщик для переключения задач?*/
/* Force a reschedule if xTaskResumeAll has not already done so. */
if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) ) /*Тут проверяется, блокирована ли текущая задача или пусть работает до вызова планировщика по таймеру?*/
{
taskYIELD_WITHIN_API(); /*Ясно, что текущая задача блокирована, а планировщик ещё не вызван, поэтому вызываем принудительно, взводя бит программного прерывания*/
asm volatile ("NOP"); /*Необходимо, чтобы дать шанс прерыванию планировщика сработать до входа в критическую секцию, блокирующую прерывания*/
}
taskENTER_CRITICAL(); /*Тут входим в критическую секцию для обработки пробуждения текущей задачи, которая уже стала eRunning*/
На ассемблере выглядит так. Вызов прерывания планировщика:
229 NVIC->IPSR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F));
00002486: lui a5,0xe000e
0000248a: lui a4,0x4
0000248c: sw a4,512(a5) # 0xe000e200
Критическая секция:
00002490: lui a5,0x8
00002492: addi a5,a5,-2048 # 0x7800
00002496: csrw mstatus,a5
0000249a: fence.i
Получается, если между 0000248c и 00002490 не вставить NOP (или, может, солиднее, fence.i), то прерывание блокируется раньше, чем случается вход в критическую секцию.
Как вижу решение. Вместо:
#define portYIELD() NVIC_SetPendingIRQ(Software_IRQn)
Определить:
#define portYIELD() do{NVIC_SetPendingIRQ(Software_IRQn);__asm volatile("fence.i");}while(0)
Полагаю, что несработка прерывания за время выполнения трёх инструкций после взведения бита запроса - это нормальное поведение. В конце-концов, латентность прерываний никто не отменял. И у меня не настроен безвекторный вход в прерывания. Проверил - такое исправление определения portYIELD() работает.
Как думаете? Решение выше с YEILD надёжное или нужно ещё что-то специальное?