ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Четверг
21 августа
1537263
Nikolay_Po (Вчера, 15:16 - 16:14, просмотров: 300)
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 надёжное или нужно ещё что-то специальное?