Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Не могу переделать IRQ на FIQ (fast forcing) для FreeRTOS
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > FreeRTOS
OlegHmt
Помогите, пожалуйста, найти ошибку.

Собственно система работает под управлением FreeRTOS - это модифицированный вариант примера IwIp для Rowley только вместо стека lwIP я прикрутил uIP. В системе запущены три задачи: первая обслуживает сеть, вторая USB (виртуальный COM), третья мониторит и перерабатывает данные полученные через USB. Кроме этого в системе добавлено прерывание по таймеру (сравнение с регистром RC) по которому с заданым периодом из полученных даных фомируется короткая посылка, которая скармливается DMA для SPI. Приоритет этого прерывания я поставил наивысший (7), понизив приоритет таймера ОС до 6. Система работает, но (как я понимаю) в связи с наличием критических секций, период вывода в SPI плавает. Мне порекомендовали перейти на FIQ, что я и попробовал сделать. Но возникли проблемы - в таком варианте система отказывается работать. Если период вывода большой, то вывод на SPI работает, а всё остальное не откликается, если маленький, то даже вывод в SPI молчит. Стоит забрать опцию FastForcing, то-есть перевести тот-же код на IRQ - всё нормально работает. Само FIQ прерывание, похоже работает нормально, так-как если после инициализиции запустить систему по пустому циклу, то вывод на SPI работает:
Код
InitTimerInt();
  portENABLE_INTERRUPTS();
  for(;;)
  {
  }  
  /* Finally, start the scheduler.     
  vTaskStartScheduler();

А при старте в нормальном режиме с выполнением задач в какой-то момент система останавливается, что-ли.
Ума уже не приложу что ещё нужно сделать. Похоже на какой-то конфликт ядра FreeRTOS с FIQ, но где именно копать понять не могу. Часть кода относящаяся к этой проблеме ниже. По сравнению с вариантом реализованым на IRQ изменения следующие:
1. В flash_placement.xml изменил размер стека для FIQ с нуля до 0х190
2. В AT91SAM7_Startup.s изменил команду перехода на обработку прерывания FIQ
Код
ldr pc, [PC, #-0xF20]    /* irq */
  //ldr pc, [pc, #fiq_handler_address - . - 8]    /* fiq */
  ldr pc, [PC, #-0xF20]    /* fiq */


3. У функцию инициализации прерывания добавлено установку адреса обработчика для FIQ и включение FastForcing:
Код
void InitTimerInt(void)
{
//  AT91F_AIC_ConfigureIt( AT91C_ID_TC0, DACsTIMER_PRIORITY, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, ( void (*)( void ) ) vTimer_ISR );
  //Disable the interrupt on the interrupt controller
  AT91C_BASE_AIC->AIC_IDCR=(0x1<<AT91C_ID_TC0);
  AT91C_BASE_AIC->AIC_IDCR=(0x1<<AT91C_ID_FIQ); //Добавлено это
  //Save the interrupt handler routine pointer and the interrupt priority
  AT91C_BASE_AIC->AIC_SVR[AT91C_ID_FIQ] = (unsigned int) vTimer_ISR;  //Добавлено это
  AT91C_BASE_AIC->AIC_SVR[AT91C_ID_TC0] = (unsigned int) vTimer_ISR;
  //Store the Source Mode Register  AT91C_BASE_AIC->AIC_SMR[AT91C_ID_TC0]=AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL|DACsTIMER_PRIORITY;
  //Clear the interrupt on the interrupt controller
  AT91C_BASE_AIC->AIC_ICCR=(0x1<<AT91C_ID_TC0); //Добавлено это
  AT91C_BASE_AIC->AIC_ICCR=(0x1<<AT91C_ID_FIQ); //Добавлено это
  AT91C_BASE_AIC->AIC_FFER=(1<<AT91C_ID_TC0); //Добавлено это
AT91C_BASE_TC0->TC_CMR=AT91C_TC_CLKS_TIMER_DIV1_CLOCK|AT91C_TC_EEVT_XC0|AT91C_TC_WAVESEL_UP_
AUTO|AT91C_TC_WAVE|AT91C_TC_ACPA_SET|AT91C_TC_ACPC_CLEAR;
  AT91C_BASE_TC0->TC_IER=AT91C_TC_CPCS;
  AT91C_BASE_AIC->AIC_IECR = 0x1 << AT91C_ID_TC0;
  AT91C_BASE_TC0->TC_CCR=AT91C_TC_CLKEN | AT91C_TC_SWTRG;
}


4. Изменённый обработчик выглядит следующим образом:
Код
void vTimer_ISR( void ) __attribute__ ((section(".fast"))) __attribute__ ((naked));
void vTimer_ISR( void )
{
  portENTER_SWITCHING_ISR();
  AT91C_BASE_PIOB->PIO_OER = BIT27;
  AT91C_BASE_PIOB->PIO_PER = BIT27;
  AT91C_BASE_PIOB->PIO_CODR = BIT27;//Вывод на светодиод чтобы проверить что обработчик работает
  AT91C_BASE_TC0->TC_SR;
  AT91C_BASE_PIOA->PIO_CODR = BIT3;
  AT91C_BASE_PIOA->PIO_SODR = BIT3;
  if (OutFlags & NotFlag){
    tempBuff[0]=AnPDCS3;
    tempBuff[1]=XCS0;
    tempBuff[2]=BnPDCS3;
    tempBuff[3]=XCS0;
    tempBuff[4]=CnPDCS3;
    tempBuff[5]=YCS0;
    tempBuff[6]=DnPDnCCS3;
    Current++;
    if (Current>=Numb)
    {
      Current=0;
    }
  } else {
    tempBuff[0]=(A1[Current]<<4)|AnPDCS3;
    tempBuff[1]=XCS0;
    tempBuff[2]=(A2[Current]<<4)|BnPDCS3;
    tempBuff[3]=A3[Current]|XCS0;
    tempBuff[4]=(A4[Current]<<4)|CnPDCS3;
    tempBuff[5]=A5[Current]|YCS0;
    tempBuff[6]=(A6[Current]<<4)|DnPDnCCS3;
    Current++;
    if (Current>=Numb)
    {
      Current=0;
    }
  }
  AT91C_BASE_SPI1->SPI_TPR=(unsigned int)tempBuff;
  AT91C_BASE_SPI1->SPI_TCR=7;
  //AT91C_BASE_AIC->AIC_EOICR = 0; \\Это включается для IRQ
  AT91C_BASE_AIC->AIC_ICCR = (0x1 << AT91C_ID_TC0)|(0x1 << AT91C_ID_FIQ);
  if (Current==300) {
    AT91C_BASE_AIC->AIC_IDCR = 0x1 << AT91C_ID_TC0;
  } //Вот этой конструкцией я проверял начинает ли работать FIQ. Если где-то после 300 (или меньше) прерываний их запретить, то дальше вся система работает нормально (конечно кроме вывода в SPI)
  portEXIT_SWITCHING_ISR(pdFALSE);
}


На всякий случай ещё привожу расшифровку макросов обёртки прерывания, может там что нужно поменять:

Код
#define portRESTORE_CONTEXT()                                            \
{                                                                        \
extern volatile void * volatile pxCurrentTCB;                            \
extern volatile unsigned portLONG ulCriticalNesting;                    \
    /* Set the LR to the task stack. */                                    \
    asm volatile (                                                        \
    "LDR        R0, =pxCurrentTCB                                \n\t"    \
    "LDR        R0, [R0]                                        \n\t"    \
    "LDR        LR, [R0]                                        \n\t"    \
    /* The critical nesting depth is the first item on the stack. */    \
    /* Load it into the ulCriticalNesting variable. */                    \
    "LDR        R0, =ulCriticalNesting                            \n\t"    \
    "LDMFD    LR!, {R1}                                            \n\t"    \
    "STR        R1, [R0]                                        \n\t"    \
    /* Get the SPSR from the stack. */                                    \
    "LDMFD    LR!, {R0}                                            \n\t"    \
    "MSR        SPSR, R0                                        \n\t"    \
    /* Restore all system mode registers for the task. */                \
    "LDMFD    LR, {R0-R14}^                                        \n\t"    \
    "NOP                                                        \n\t"    \
    /* Restore the return address. */                                    \
    "LDR        LR, [LR, #+60]                                    \n\t"    \
    /* And return - correcting the offset in the LR to obtain the */    \
    /* correct address. */                                                \
    "SUBS    PC, LR, #4                                            \n\t"    \
    );                                                                    \
    ( void ) ulCriticalNesting;                                            \
    ( void ) pxCurrentTCB;                                                \
}
/*-----------------------------------------------------------*/

#define portSAVE_CONTEXT()                                                \
{                                                                        \
extern volatile void * volatile pxCurrentTCB;                            \
extern volatile unsigned portLONG ulCriticalNesting;                    \
    /* Push R0 as we are going to use the register. */                    \
    asm volatile (                                                        \
    "STMDB    SP!, {R0}                                            \n\t"    \
    /* Set R0 to point to the task stack pointer. */                    \
    "STMDB    SP,{SP}^                                            \n\t"    \
    "NOP                                                        \n\t"    \
    "SUB    SP, SP, #4                                            \n\t"    \
    "LDMIA    SP!,{R0}                                            \n\t"    \
    /* Push the return address onto the stack. */                        \
    "STMDB    R0!, {LR}                                            \n\t"    \
    /* Now we have saved LR we can use it instead of R0. */                \
    "MOV    LR, R0                                                \n\t"    \
    /* Pop R0 so we can save it onto the system mode stack. */            \
    "LDMIA    SP!, {R0}                                            \n\t"    \
    /* Push all the system mode registers onto the task stack. */        \
    "STMDB    LR,{R0-LR}^                                            \n\t"    \
    "NOP                                                        \n\t"    \
    "SUB    LR, LR, #60                                            \n\t"    \
    /* Push the SPSR onto the task stack. */                            \
    "MRS    R0, SPSR                                            \n\t"    \
    "STMDB    LR!, {R0}                                            \n\t"    \
    "LDR    R0, =ulCriticalNesting                                \n\t"    \
    "LDR    R0, [R0]                                            \n\t"    \
    "STMDB    LR!, {R0}                                            \n\t"    \
    /* Store the new top of stack for the task. */                        \
    "LDR    R0, =pxCurrentTCB                                    \n\t"    \
    "LDR    R0, [R0]                                            \n\t"    \
    "STR    LR, [R0]                                            \n\t"    \
    );                                                                    \
    ( void ) ulCriticalNesting;                                            \
    ( void ) pxCurrentTCB;                                                \
}


/*-----------------------------------------------------------
* ISR entry and exit macros.  These are only required if a task switch
* is required from the ISR.
*----------------------------------------------------------*/
#define portENTER_SWITCHING_ISR()                                        \
    /* Save the context of the interrupted task. */                        \
    portSAVE_CONTEXT();                                                    \
    /* We don't know the stack requirements for the ISR, so the frame */\
    /* pointer will be set to the top of the task stack, and the stack*/\
    /* pointer left where it is.  The IRQ stack will get used for any */\
    /* functions calls made by this ISR. */                                \
    asm volatile ( "SUB        R11, LR, #4" );                            \
    {

#define portEXIT_SWITCHING_ISR( SwitchRequired )                        \
        /* If a switch is required then we just need to call */            \
        /* vTaskSwitchContext() as the context has already been */        \
        /* saved. */                                                    \
        if( SwitchRequired )                                            \
        {                                                                \
            vTaskSwitchContext();                                        \
        }                                                                \
    }                                                                    \
    /* Restore the context of which ever task is now the highest */        \
    /* priority that is ready to run. */                                \
    portRESTORE_CONTEXT();
zltigo
Цитата(OlegHmt @ Jan 12 2007, 12:18) *
2. В AT91SAM7_Startup.s изменил команду перехода на обработку прерывания FIQ
Код
ldr pc, [PC, #-0xF20]    /* irq */
  //ldr pc, [pc, #fiq_handler_address - . - 8]    /* fiq */
  ldr pc, [PC, #-0xF20]    /* fiq */

Не заменить! а просто НАПИСАТЬ свой обработчик FIQ и ПРЯМО подставить ЕГО адрес.
Сам обработчик FIQ и естественно он НЕ сможет "работать обработчиком IRQ" все обертки для переключения контекста из IRQ УБРАТЬ они нужны только для случая если Вы собрались переключить задачу при выходе из IRQ и уж по всякому не годяться для FIQ - просто чистый как слеза обработчик FIQ "как в книжке" напишите и ВСЕ!
При инициализации прерывание классифицировать как FIQ и больше никаких телодвижений.
Все будет работать.
Потом придется sad.gif доработать FreeRTOS с целью не запрещения FIQ в критических секциях, ибо
в оригинале сделано огульное запрещение и IRQ и FIQ разом.
OlegHmt
Цитата(zltigo @ Jan 12 2007, 12:53) *
Не заменить! а просто НАПИСАТЬ свой обработчик FIQ и ПРЯМО подставить ЕГО адрес.
Сам обработчик FIQ и естественно он НЕ сможет "работать обработчиком IRQ" все обертки для переключения контекста из IRQ УБРАТЬ они нужны только для случая если Вы собрались переключить задачу при выходе из IRQ и уж по всякому не годяться для FIQ - просто чистый как слеза обработчик FIQ "как в книжке" напишите и ВСЕ!
При инициализации прерывание классифицировать как FIQ и больше никаких телодвижений.
Все будет работать.
Потом придется sad.gif доработать FreeRTOS с целью не запрещения FIQ в критических секциях, ибо
в оригинале сделано огульное запрещение и IRQ и FIQ разом.


Спасибо. Правда первый раз, когда я так попробовал сделать, у меня что-то на обработчик не переходило совсем, а через вектор заработало. Правда, тогда, я ещё очень слабо представлял что это такое, поэтому наверняка ошибся. Сегодня попробую.
А на счёт контекста - переключение убрать, а оставить сохранение?
portSAVE_CONTEXT()
portRESTORE_CONTEXT()

Немножко дурацкий вопрос :-), но всё же - у Вас FIQ вместе с FreeRTOS работал?
Так как вроде-бы то что у меня написано, оно тоже "по книжке". И само по себе работает, но не хочет уживаться с FreeRTOS. Может что-то забыл ещё записать?

А доработать отмену запрещения FIQ - это я уже нашёл, там в нескольких местах нужно запись в регистр поправить.
zltigo
Цитата
Правда первый раз, когда я так попробовал сделать, у меня что-то на обработчик не переходило совсем, а через вектор заработало.

Это не принципиально, это просто лишнее при одном единственом обработчике FIQ.
Цитата(OlegHmt @ Jan 12 2007, 14:45) *
А на счёт контекста - переключение убрать, а оставить сохранение?
portSAVE_CONTEXT()
portRESTORE_CONTEXT()

У Вас НЕТ переключения задач. Посему и save/restore от FreeRTOS как минимум НЕ нужны даже для IRQ. Нужен просто (с учетом предыдущих постов уже притомился писать одно и тоже) обработчик FIQ. Причем обработчик FIQ самый обычный а не какой-то особеный для FreeRTOS, ему глубоко все равно кого прерывать!
Цитата
Немножко дурацкий вопрос :-), но всё же - у Вас FIQ вместе с FreeRTOS работал?

Естественно работал, работает и будет работать и с FreeRTOS и без нее. Естественно без переключения контекстов - прервал всех и вся сделал дело и все.
Цитата
Так как вроде-бы то что у меня написано, оно тоже "по книжке".

За каким-то чертом было проведено бездумное сваливание в одну кучу кусков обработчика IRQ и не простого а того из которого можно производить переключение задач FreeRTOS, и банального обработчика FIQ, причем на специфика FIQ была проигнорирована.

Вместо того, что-бы просто по, например, первой попавшейся "книжке" сваять абстрактный обработчик FIQ и все!.
OlegHmt
Наконец нашёл я причину, почему у меня не работало FIQ прерывание. Проблема оказалась в том, что компилятор RowleyCrossWorks при некоторых значениях опции оптимизации кода формирует код, который потом нетак работает. Недавно я с таким уже сталкивался, когда пробовал заменить стек IwIP в примере на стек uIP. Пока не отключил оптимизацию, мои пакеты стек принимать не хотел.

Что же касается FIQ реализации то, абстрактный обработчик мне запустить не удалось - пока не смог до конца разобратся (а у меня из книжек сейчас только даташит на процесор и описание команд ARM ядра), да и перебрасывать код между файлами не особенно хотелось (а там кажется нужно было), то я реализовал обработчик на базе вектора прерываний (как даташит пишет), выкинул сохранение контекста, добавил команды сбрасывания в FIQ стек первых восьми регистров и всё заработало.

zltigo
Тем не менее, огромное спасибо за помощь smile.gif
zltigo
Цитата(OlegHmt @ Jan 13 2007, 13:04) *
то я реализовал обработчик на базе вектора прерываний (как даташит пишет),

Не принципиально, просто лишнее чтение из медленной перефирии.
А в чем может быть "проблема" с реализацией прямого вызова обработчика FIQ вообще не понятно!
Прямо в FreeRTOSовской болванке boot.s вместо вместо зацикливания пустого обработчика самого на себя имя внешнего обработчика ( по образу и подобию имеющегося vPortYieldProcessor висящего на SWI а не IRQ c его чтением адреса из контроллера) и все.
Цитата
выкинул сохранение контекста,

Всенепременнейше, по причине нахренненужности.
Цитата
добавил команды сбрасывания в FIQ стек первых восьми регистров и всё заработало.

1.Восьмой - лишний.
2.И вообще надо компилятору поручмть разбираться сохранять то, что нужно (Fast всетаки!) а не все подряд
__attribute__ ((interrupt ("FIQ"))) для GCC, если мне память не изменяет
OlegHmt
Заработало. Спасибо.

Проблема была в том, что я не знал и не нашёл как. Я пробовал просто подставить
Код
fiq_handler:

в функцию прерывания, а такую конструкцию компилятор не хотел воспринимать.
OlegHmt
И вот ещё какой глюк компилятора у меня проявился, если я объявляю процедуру __attribute__ ((interrupt ("FIQ"))). Компилятор, почему-то, два раза уменьшает регистр lr (при входе и выходе из обработчика), если включена какая-либо оптимизация. То-есть только без оптимизации такой код работает
А если я обработчик объявляю __attribute__ ((interrupt ("FIQ"), naked)) и сам добавляю сохранение и востановление регистров, тогда всё работает как без оптимизации, так и в одном из вариантов (Optimize for size).
Alex03
Цитата(OlegHmt @ Jan 13 2007, 20:04) *
И вот ещё какой глюк компилятора у меня проявился, если я объявляю процедуру __attribute__ ((interrupt ("FIQ"))). Компилятор, почему-то, два раза уменьшает регистр lr (при входе и выходе из обработчика), если включена какая-либо оптимизация. То-есть только без оптимизации такой код работает

Эт известный баг, обсуждалось например тут

Цитата
А если я обработчик объявляю __attribute__ ((interrupt ("FIQ"), naked)) и сам добавляю сохранение и востановление регистров, тогда всё работает как без оптимизации, так и в одном из вариантов (Optimize for size).

ИМХО interrupt ("FIQ") и naked взаимоисключающие вещи, т.е. достаточно только naked оставить, но лучше GCC пропатчить. Ссылки есть тут.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.