|
|
  |
Не могу переделать IRQ на FIQ (fast forcing) для FreeRTOS, AT91SAM7X256 |
|
|
|
Jan 12 2007, 13:18
|
Участник

Группа: Участник
Сообщений: 70
Регистрация: 5-12-06
Пользователь №: 23 146

|
Помогите, пожалуйста, найти ошибку. Собственно система работает под управлением 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();
Сообщение отредактировал OlegHmt - Jan 12 2007, 13:22
|
|
|
|
|
Jan 12 2007, 13:53
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(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 и больше никаких телодвижений. Все будет работать. Потом придется  доработать FreeRTOS с целью не запрещения FIQ в критических секциях, ибо в оригинале сделано огульное запрещение и IRQ и FIQ разом.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jan 12 2007, 15:45
|
Участник

Группа: Участник
Сообщений: 70
Регистрация: 5-12-06
Пользователь №: 23 146

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

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата Правда первый раз, когда я так попробовал сделать, у меня что-то на обработчик не переходило совсем, а через вектор заработало. Это не принципиально, это просто лишнее при одном единственом обработчике 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 и все!.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jan 13 2007, 14:04
|
Участник

Группа: Участник
Сообщений: 70
Регистрация: 5-12-06
Пользователь №: 23 146

|
Наконец нашёл я причину, почему у меня не работало FIQ прерывание. Проблема оказалась в том, что компилятор RowleyCrossWorks при некоторых значениях опции оптимизации кода формирует код, который потом нетак работает. Недавно я с таким уже сталкивался, когда пробовал заменить стек IwIP в примере на стек uIP. Пока не отключил оптимизацию, мои пакеты стек принимать не хотел. Что же касается FIQ реализации то, абстрактный обработчик мне запустить не удалось - пока не смог до конца разобратся (а у меня из книжек сейчас только даташит на процесор и описание команд ARM ядра), да и перебрасывать код между файлами не особенно хотелось (а там кажется нужно было), то я реализовал обработчик на базе вектора прерываний (как даташит пишет), выкинул сохранение контекста, добавил команды сбрасывания в FIQ стек первых восьми регистров и всё заработало. zltigoТем не менее, огромное спасибо за помощь
|
|
|
|
|
Jan 13 2007, 15:05
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(OlegHmt @ Jan 13 2007, 13:04)  то я реализовал обработчик на базе вектора прерываний (как даташит пишет), Не принципиально, просто лишнее чтение из медленной перефирии. А в чем может быть "проблема" с реализацией прямого вызова обработчика FIQ вообще не понятно! Прямо в FreeRTOSовской болванке boot.s вместо вместо зацикливания пустого обработчика самого на себя имя внешнего обработчика ( по образу и подобию имеющегося vPortYieldProcessor висящего на SWI а не IRQ c его чтением адреса из контроллера) и все. Цитата выкинул сохранение контекста, Всенепременнейше, по причине нахренненужности. Цитата добавил команды сбрасывания в FIQ стек первых восьми регистров и всё заработало. 1.Восьмой - лишний. 2.И вообще надо компилятору поручмть разбираться сохранять то, что нужно (Fast всетаки!) а не все подряд __attribute__ ((interrupt ("FIQ"))) для GCC, если мне память не изменяет
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jan 13 2007, 16:53
|
Участник

Группа: Участник
Сообщений: 70
Регистрация: 5-12-06
Пользователь №: 23 146

|
Заработало. Спасибо. Проблема была в том, что я не знал и не нашёл как. Я пробовал просто подставить Код fiq_handler: в функцию прерывания, а такую конструкцию компилятор не хотел воспринимать.
|
|
|
|
|
Jan 13 2007, 18:04
|
Участник

Группа: Участник
Сообщений: 70
Регистрация: 5-12-06
Пользователь №: 23 146

|
И вот ещё какой глюк компилятора у меня проявился, если я объявляю процедуру __attribute__ ((interrupt ("FIQ"))). Компилятор, почему-то, два раза уменьшает регистр lr (при входе и выходе из обработчика), если включена какая-либо оптимизация. То-есть только без оптимизации такой код работает А если я обработчик объявляю __attribute__ ((interrupt ("FIQ"), naked)) и сам добавляю сохранение и востановление регистров, тогда всё работает как без оптимизации, так и в одном из вариантов (Optimize for size).
|
|
|
|
|
Jan 14 2007, 14:13
|
Местный
  
Группа: Свой
Сообщений: 359
Регистрация: 9-12-05
Пользователь №: 12 034

|
Цитата(OlegHmt @ Jan 13 2007, 20:04)  И вот ещё какой глюк компилятора у меня проявился, если я объявляю процедуру __attribute__ ((interrupt ("FIQ"))). Компилятор, почему-то, два раза уменьшает регистр lr (при входе и выходе из обработчика), если включена какая-либо оптимизация. То-есть только без оптимизации такой код работает Эт известный баг, обсуждалось например тутЦитата А если я обработчик объявляю __attribute__ ((interrupt ("FIQ"), naked)) и сам добавляю сохранение и востановление регистров, тогда всё работает как без оптимизации, так и в одном из вариантов (Optimize for size). ИМХО interrupt ("FIQ") и naked взаимоисключающие вещи, т.е. достаточно только naked оставить, но лучше GCC пропатчить. Ссылки есть тут.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|