|
Вложенные прерывания, GCC и ARM7 |
|
|
|
Mar 1 2010, 05:08
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Доброго времени суток! Работаю с платой LPC2478, требуется реализовать вложенные (nested) прерывания. Нарыл несколько статей на эту тему, но пока ни один метод не запахал. Тестирую прерывания таким образом: Код void TestInterrupts::TestTimers() { myPrintf("\nSTART TestTimers()\n");
Timer0VICInit(Timer0ISR); Timer1VICInit(Timer1ISR); Timer1StartWithInt(1000);
myPrintf("\nEND TestTimers()\n"); }
void TestInterrupts::Timer0ISR() { T0IR = TIR_MR0_INT; // reset interrrupt flags myPrintf("\nTimer0ISR()\n"); // Dummy write to VIC to signal end of interrupt VICVectAddr = 0x00000000; }
void TestInterrupts::Timer1ISR() { IENABLE;
T1IR = TIR_MR0_INT; // reset interrrupt flags myPrintf("\nSTART Timer1ISR()\n"); Timer0StartWithInt(5000); volatile tU8 dummy = 0; while(1) { dummy = 0; } myPrintf("\nEND Timer1ISR()\n"); IDISABLE; // Dummy write to VIC to signal end of interrupt VICVectAddr = 0x00000000; } Где в качестве IENABLE пробовал разные последовательности ассемблерных команд, вычитанных с разных статей: Код // FROM Nesting of interrupts in the LPC2000 (AN10381) #define IENABLE /* Nested Interrupts Entry */ \ __asm__ ("MRS LR, SPSR"); /* Copy SPSR_irq to LR */ \ __asm__ ("STMFD SP!, {LR}"); /* Save SPSR_irq */ \ __asm__ ("MSR CPSR_c, #0x1F"); /* Enable IRQ (Sys Mode) */ \ __asm__ ("STMFD SP!, {LR}"); /* Save LR */ \ Код // FROM Real-time processing with the Philips LPC ARM microcontroller; using GCC and the MicroC/OS-II RTOS. #define IENABLE /* Nested Interrupts Entry */ \ __asm__ ("sub lr, lr, #4"); /* (1) Save IRQ context, including the APCS registers, and r4-6 */ \ __asm__ ("stmfd sp!, {r0-r6, ip, lr}"); \ __asm__ ("mrs r4, spsr"); /* (2) Save the SPSR_irq register */ \ __asm__ ("ldr r5, =0xFFFFFF00"); /* (3) Read the VICVectAddr */ \ __asm__ ("ldr r6, [r5]"); \ __asm__ ("msr cpsr_c, 0x1F"); /* (4) Change to SYS mode and enable IRQ */ \ __asm__ ("stmfd sp!, {lr}"); /* (5) Save the banked SYS mode link register */ \ То есть, на консоли должно появиться Цитата START TestTimers() END TestTimers() START Timer1ISR() START Timer0ISR() А на самом деле последняя строка не выводиться, что говорит о невхождении в обработчик вложенного прерывания Timer0. Так вот, первый мой вопрос, какая же все таки последовательной команд должна быть в прологе и эпилоге обработчика для LPC2478? И еще, во втором случае IENABLE полное описание вхождения и вывода их вложенных прерываний выглядит так: Цитата nested_irq_isr: /* (1) Save IRQ context, including the APCS registers, and r4-6 */ sub lr, lr, #4 stmfd sp!, {r0-r6, ip, lr} /* (2) Save the SPSR_irq register */ mrs r4, spsr /* (3) Read the VICVectAddr */ ldr r5, VICVECTADDR ldr r6, [r5] /* (4) Change to SYS mode and enable IRQ */ msr cpsr_c, #SYS_MODE /* (5) Save the banked SYS mode link register */ stmfd sp!, {lr} /* (6) Call the C-coded handler */ mov lr, pc ldr pc, r6 /* (7) Restore SYS mode link register */ ldmfd sp!, {lr} 36 AR1803 May 10, 2006 /* (8) Change to IRQ mode and disable IRQ */ msr cpsr_c, #IRQ_MODE|IRQ_DISABLE /* (9) Restore the SPSR */ msr spsr, r4 /* (10) Acknowledge the VIC */ mov r0, #0 str r0, [r5] /* (11) Restore IRQ context and return from interrupt */ ldmfd sp!, {r0-r6, ip, pc}^ И это описание натолкнуло меня на дополнительный вопрос, а можно ли в GCC переопределять последовательность операций для вызова функций, например путем создания собственных атрибутов для функций. И дальнейшим их использованием в примерно таком виде: Код static void Timer0ISR() __attribute__ ((my_nested_interrupt))
Сообщение отредактировал Novichok1 - Mar 1 2010, 05:14
|
|
|
|
|
Mar 1 2010, 08:07
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Цитата 1. Заставить VIC вызывать по прерыванию С++ функцию класса можно, но тогда ее надо делать статической со всеми вытекающими. С этим все в порядке. Каждое из прерываний по отдельности работает нормально, и вызываются соответствующие обработчики. Дело в другом, мне нужны именно вложенные прерывания. то есть во время обработки одного прерывания(Timer1ISR) может произойти второе прерывание (Timer0), и это прерывание должно корректно обработаться. Цитата 2. Вы приводите только функции класса, да и те не полностью. Остается гадать, что написано в остальных и как выглядит таблица перываний. Виноват, но не хотелось перегружать топик. Код static void Timer::Timer0VICInit(_void_pointer handler) { VICIntSelect &= ~VIC_TIMER0; // Timer1 selected as IRQ VICVectCntl4 = VIC_TIMER0; VICVectAddr4 = (tU32)handler; // Address of the ISR VICIntEnable |= VIC_TIMER0; // Timer interrupt enabled }
static void Delay::Timer0StartWithInt(tU32 ms) { T0TCR = TCR_RESET; // stop and reset timer T0PR = 0x00; // set prescaler to zero T0IR = TIR_RESET_INTERRUPTS; // reset all interrrupt flags T0MR0 = TIMER_CONVERT_FROM_MS(ms); T0MCR = MCR_MR0_INT; // Interrupt on MR0: an interrupt is generated when MR0 matches the value in the TC. T0TCR = TCR_ENABLE; // start timer } Аналогично для Timer1. Не знаю, почему может не работать, вроде все делаю, как описано здесь, в случае дополнения тела обработчиками макросами IENABLE и IDISABLE.
|
|
|
|
|
Mar 1 2010, 09:12
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Может и правда, дело в стартапе, выложу его на всякий случай, может кто и увидит несостыковку. CODE # # *** Startup Code (executed after Reset) *** #
#include "config.h"
# Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs
.equ Mode_USR, 0x10 .equ Mode_FIQ, 0x11 .equ Mode_IRQ, 0x12 .equ Mode_SVC, 0x13 .equ Mode_ABT, 0x17 .equ Mode_UND, 0x1B .equ Mode_SYS, 0x1F
.equ I_Bit, 0x80 /* when I bit is set, IRQ is disabled */ .equ F_Bit, 0x40 /* when F bit is set, FIQ is disabled */
.equ sram_bottom, SRAM_SADDR .equ sram_top, SRAM_TOP .equ stackTop, SRAM_TOP
#define VAL_PLLCFG_MSEL ((PLL_MUL - 1) << 0) #if (PLL_DIV == 1) #define PLL_DIV_VALUE 0x00 #elif (PLL_DIV == 2) #define PLL_DIV_VALUE 0x01 #elif (PLL_DIV == 4) #define PLL_DIV_VALUE 0x10 #elif (PLL_DIV == 8) #define PLL_DIV_VALUE 0x11 #endif #define VAL_PLLCFG_PSEL (PLL_DIV_VALUE << 5) #define VAL_PLLCFG (VAL_PLLCFG_MSEL | VAL_PLLCFG_PSEL)
# Phase Locked Loop (PLL) definitions .equ PLL_BASE, 0xE01FC080 /* PLL Base Address */ .equ PLLCON_OFS, 0x00 /* PLL Control Offset*/ .equ PLLCFG_OFS, 0x04 /* PLL Configuration Offset */ .equ PLLSTAT_OFS, 0x08 /* PLL Status Offset */ .equ PLLFEED_OFS, 0x0C /* PLL Feed Offset */ .equ PLLCON_PLLE, (1<<0) /* PLL Enable */ .equ PLLCON_PLLC, (1<<1) /* PLL Connect */ .equ PLLSTAT_PLOCK, (1<<10) /* PLL Lock Status */
#define HANDLER(HandlerLabel,HandleLabel) \ HandlerLabel: ;\ sub sp, sp, #4 ;\ stmfd sp!, {r0};\ ldr r0, =HandleLabel;\ ldr r0, [r0] ;\ str r0, [sp,#4] ;\ ldmfd sp!, {r0,pc}
# Starupt Code must be linked first at Address at which it expects to run.
.text .arm # ****************************************************************************** # Declare external function # ****************************************************************************** .extern lowLevelInit .extern exceptionHandlerInit
.global _startup .func _startup _startup:
# Exception Vectors # Mapped to Address 0.
Vectors: _vectors: # If vectors are in FLASH, starting at 0x00000000 #if (MAM_MAP == 1) B handleReset /* jump to reset code */ B HandlerUndef /* handlerUndef */ B HandlerSWI /* SWI interrupt handler */ B HandlerPabort /* handlerPAbort */ B HandlerDabort /* handlerDAbort */ NOP /* Reserved Vector */ #if (IRQ_HANDLER == 0) B HandlerIRQ /* handlerIRQ */ #else LDR PC,[PC,#-0x120] /* jump to address supplied by VIC */ #endif B HandlerFIQ /* handlerFIQ */
# Create handlers HANDLER(HandlerUndef, HandleUndef) HANDLER(HandlerSWI, HandleSWI) HANDLER(HandlerPabort, HandlePabort) HANDLER(HandlerDabort, HandleDabort) #if (IRQ_HANDLER == 0) HANDLER(HandlerIRQ, HandleIRQ) #endif HANDLER(HandlerFIQ, HandleFIQ)
# If vectors are in RAM, starting at 0x40000000 #else LDR PC,[PC,#resetHandlerAddress - . - 8] /* handle reset */ LDR PC,[PC,#undefHandlerAddress - . - 8] /* handlerUndef */ LDR PC,[PC,#swiHandlerAddress - . - 8] /* SWI interrupt handler */ LDR PC,[PC,#pabortHandlerAddress - . - 8] /* handlerPAbort */ LDR PC,[PC,#dabortHandlerAddress - . - 8] /* handlerDAbort */ NOP /* Reserved Vector */ #if (IRQ_HANDLER == 0) LDR PC,[PC,#irqHandlerAddress - . - 8] /* jump to common irq handler */ #else LDR PC,[PC,#-0x120] /* jump to address supplied from VIC */ #endif LDR PC,[PC,#fiqHandlerAddress - . - 8] /* handlerFIQ */
resetHandlerAddress: .word handleReset
undefHandlerAddress: .word 0
swiHandlerAddress: .word 0
pabortHandlerAddress: .word 0
dabortHandlerAddress: .word 0
irqHandlerAddress: .word 0
fiqHandlerAddress: .word 0
#endif
# Reset Handler handleReset:
# Call pll initialization # BL pllInit
# Setup Stack for each mode LDR R0, =stackTop
# Enter Undefined Instruction Mode and set its Stack Pointer MSR CPSR_c, #Mode_UND|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #stackSize_UND
# Enter Abort Mode and set its Stack Pointer MSR CPSR_c, #Mode_ABT|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #stackSize_ABT
# Enter FIQ Mode and set its Stack Pointer MSR CPSR_c, #Mode_FIQ|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #stackSize_FIQ
# Enter IRQ Mode and set its Stack Pointer MSR CPSR_c, #Mode_IRQ|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #stackSize_IRQ
# Enter Supervisor Mode and set its Stack Pointer MSR CPSR_c, #Mode_SVC|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #stackSize_SVC
# Enter System Mode and set its Stack Pointer MSR CPSR_c, #Mode_SYS|I_Bit|F_Bit MOV SP, R0
# Setup a default Stack Limit (when compiled with "-mapcs-stack-check") SUB SL, SP, #stackSize_SYS
#if (MAM_MAP == 2) # Copy exception vectors into SRAM MOV R8, #SRAM_SADDR LDR R9, =Vectors # Move exception vectors (7 vectors + 1 reserved) LDMIA R9!, {R0-R7} STMIA R8!, {R0-R7} # Move addresses belonging to exception vectors (7 addresses) LDMIA R9!, {R0-R6} STMIA R8!, {R0-R6} #endif
# Call low-level initialization # BL lowLevelInit
# Relocate .data section (Copy from ROM to RAM) LDR R1, =_etext LDR R2, =_data LDR R3, =_edata LoopRel: CMP R2, R3 LDRLO R0, [R1], #4 STRLO R0, [R2], #4 BLO LoopRel
# Clear .bss section (Zero init) MOV R0, #0 LDR R1, =__bss_start__ LDR R2, =__bss_end__ LoopZI: CMP R1, R2 STRLO R0, [R1], #4 BLO LoopZI
# Initialize exception vectors # BL exceptionHandlerInit
# Enter the C code Jump_To_Main: ADR LR, __Return_from_Main MOV R0, #0 MOV R1, #0 LDR R2, =main BX R2
__Return_from_Main: B __Return_from_Main
.size _startup, . - _startup .endfunc
# # Reserve memory for exception handlers # .struct SRAM_SADDR + 0x20 HandleReset:
.struct SRAM_SADDR + 0x24 HandleUndef:
.struct SRAM_SADDR + 0x28 HandleSWI:
.struct SRAM_SADDR + 0x2c HandlePabort:
.struct SRAM_SADDR + 0x30 HandleDabort:
.struct SRAM_SADDR + 0x34 HandleIRQ:
.struct SRAM_SADDR + 0x38 HandleFIQ:
# # Reserve memory for stack areas # .struct STK_SADDR UserStack: /* User stack not used (size = 0) */
.struct UserStack + stackSize_SYS SYSStack:
.struct SYSStack + stackSize_SVC SVCStack:
.struct SVCStack + stackSize_UND UndefStack:
.struct UndefStack + stackSize_ABT AbortStack:
.struct AbortStack + stackSize_IRQ IRQStack:
.struct IRQStack + stackSize_FIQ FIQStack: /* this position equals top of SRAM */
.end Кстати, если следовать советам "Real-time processing with the Philips LPC ARM microcontroller; using GCC and the MicroC/OS-II RTOS." то я подозреваю этот код загрузки обработчика Код nested_irq_isr: /* (1) Save IRQ context, including the APCS registers, and r4-6 */ sub lr, lr, #4 stmfd sp!, {r0-r6, ip, lr} /* (2) Save the SPSR_irq register */ mrs r4, spsr /* (3) Read the VICVectAddr */ ldr r5, VICVECTADDR ldr r6, [r5] /* (4) Change to SYS mode and enable IRQ */ msr cpsr_c, #SYS_MODE /* (5) Save the banked SYS mode link register */ stmfd sp!, {lr} /* (6) Call the C-coded handler */ mov lr, pc ldr pc, r6 /* (7) Restore SYS mode link register */ ldmfd sp!, {lr} /* (8) Change to IRQ mode and disable IRQ */ msr cpsr_c, #IRQ_MODE|IRQ_DISABLE /* (9) Restore the SPSR */ msr spsr, r4 /* (10) Acknowledge the VIC */ mov r0, #0 str r0, [r5] /* (11) Restore IRQ context and return from interrupt */ ldmfd sp!, {r0-r6, ip, pc}^ нужно поместить в макрос HANDLER(HandlerLabel,HandleLabel)? Но тогда возникает вопрос, что такое VICVECTADDR, просто адрес 0xFFFFFF00, или нечто иное?
Сообщение отредактировал rezident - Mar 4 2010, 13:33
Причина редактирования: Редактирование оформления цитаты исходника.
|
|
|
|
|
Mar 4 2010, 05:44
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Хорошо, частично разобрался, для вызовов всех прерываний использую Код HandlerIRQ_ISR_NESTED: /* (1) Save IRQ context, including the APCS registers, and r4-6 */ sub lr, lr, #4 stmfd sp!, {r0-r6, ip, lr} /* (2) Save the SPSR_irq register */ mrs r4, spsr /* (3) Read the VICVectAddr */ ldr r5, =0xFFFFFF00 ldr r6, [r5] /* (4) Change to SYS mode and enable IRQ */ msr cpsr_c, #0x1F /* (5) Save the banked SYS mode link register */ stmfd sp!, {lr} /* (6) Call the C-coded handler */ mov lr, pc ldr pc, [r5] /* (7) Restore SYS mode link register */ ldmfd sp!, {lr} /* (8) Change to IRQ mode and disable IRQ */ msr cpsr_c, #Mode_IRQ|I_Bit /* (9) Restore the SPSR */ msr spsr, r4 /* (10) Acknowledge the VIC */ mov r0, #0 str r0, [r5] /* (11) Restore IRQ context and return from interrupt */ ldmfd sp!, {r0-r6, ip, pc}^ И что происходит, в обработчике внешнего прерывания в цикле просматриваю флаги прерываний, и вижу, что да, второе прерывание срабатывает, но его обработчик не запуститься до тех пор, пока мы не запишем что-либо в VicVectAddr, что сделает возможными и прерывания меньшего приоритета Цитата Writing to the VICVECTADDR Register indicates to the interrupt priority hardware that the current interrupt is serviced, enabling lower priority or the same priority interrupts to be removed, and for the interrupts to become active to go active И тем более запись в VicVectAddr советуют делать в самом конце обработчика прерывания, а без этой записи ядру не идет сообщение о прерывании. Может есть другой способ разрешить VIC инициализировать прерывания, без записи в VicVectAddr? И еще, если использовать директивы компилятора, указывающими на то что данная функция является обработчиком прерывания, то компилятор сам сохраняет изменяемые регистры, а тут мы это делаем ручками и для всех обработчиков, поэтому возникает вопрос, а достаточно ли сохранение первых семи регистров(stmfd sp!, {r0-r6, ip, lr})? И еще, небольшое наблюдение- приведенный выше подход проявляет более лучшую устойчивость, чем этот: Цитата #define IENABLE /* Nested Interrupts Entry */ \ __asm__ ("MRS LR, SPSR"); /* Copy SPSR_irq to LR */ \ __asm__ ("STMFD SP!, {LR}"); /* Save SPSR_irq */ \ __asm__ ("MSR CPSR_c, #0x1F"); /* Enable IRQ (Sys Mode) */ \ __asm__ ("STMFD SP!, {LR}"); /* Save LR */ \
// Macro for disabling interrupts, switching back to IRQ and relevant stack operations #define IDISABLE /* Nested Interrupts Exit */ \ __asm__ ("LDMFD SP!, {LR}"); /* Restore LR */ \ __asm__ ("MSR CPSR_c, #0x92"); /* Disable IRQ (IRQ Mode) */ \ __asm__ ("LDMFD SP!, {LR}"); /* Restore SPSR_irq to LR */ \ __asm__ ("MSR SPSR_cxsf, LR"); /* Copy LR to SPSR_irq */ \ Так, если в обработчике первого прерывания происходит второе прерывания, а в вызванном обработчике второго прерывания происходит первое, то после выхода из обработчика программа глючит.
Сообщение отредактировал Novichok1 - Mar 4 2010, 06:25
|
|
|
|
|
Mar 4 2010, 07:06
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
И благодаря такому подходу исчезли глюки, которые были связаны с прерываниями, и проявлялись в виде лишних вызовов функций с параметрами, которые, походу, оставались в регистрах во время исполнения обработчика прерывания. От этих причуд складывалось такое впечатление, что компилятор при входе или выходе сохранял(восстанавливал) не все требуемые регистры.
Сообщение отредактировал Novichok1 - Mar 4 2010, 07:06
|
|
|
|
|
Mar 4 2010, 23:57
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(GetSmart @ Mar 5 2010, 02:26)  театр одного актёра  Надо помочь, наверное, да пугают длинные копипасты мутного кода. Цитата(Novichok1 @ Mar 4 2010, 08:44)  И еще, если использовать директивы компилятора, указывающими на то что данная функция является обработчиком прерывания, то компилятор сам сохраняет изменяемые регистры, а тут мы это делаем ручками и для всех обработчиков, поэтому возникает вопрос, а достаточно ли сохранение первых семи регистров(stmfd sp!, {r0-r6, ip, lr})? Перед вызовом достаточно сохранить R0-R3, R12, R14, остальное вызываемая подпрограмма трогать не имеет права. R4-R6 портит сама обертка. Кроме того, следует обратить внимание на некорректное выравнивание стека - должно быть четное число слов. Использование одного обработчика для прерываний всех приоритетов - тоже, мягко говоря, не самая светлая идея. Цитата(Novichok1 @ Mar 4 2010, 08:44)  Так, если в обработчике первого прерывания происходит второе прерывания, а в вызванном обработчике второго прерывания происходит первое, то после выхода из обработчика программа глючит. Не происходит такого в нормальных условиях. Пока выполняется один обработчик прерывания с определенным приоритетом, другие прерывания с тем же или меньшим приоритетом маскируются. Иначе какой вообще смысл во вложенности?
|
|
|
|
|
Mar 5 2010, 04:59
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Цитата(aaarrr @ Mar 5 2010, 02:57)  Перед вызовом достаточно сохранить R0-R3, R12, R14, остальное вызываемая подпрограмма трогать не имеет права. R4-R6 портит сама обертка. Кроме того, следует обратить внимание на некорректное выравнивание стека - должно быть четное число слов. Большое спасибо за разъяснение! А насчет некорректного выравнивания стека - после stmfd sp!, {r0-r6, ip, lr} через некоторое число команд вызывается еще и stmfd sp!, {lr} (на шаге 5), так что в сумме получается четное число сохраненных регистров, так можно рассуждать, или при выполнении ОДНОЙ команды записи в стек должно сохраняться четное число слов? Цитата(aaarrr @ Mar 5 2010, 02:57)  Использование одного обработчика для прерываний всех приоритетов - тоже, мягко говоря, не самая светлая идея. Я наверно не совсем точно выразился, под " ...для вызовов всех прерываний использую..." не имелось ввиду использование одного сишного обработчика для всех прерываний, а подразумевалась последовательность ассемблерных команд для подготовки ко входу в обработчик прерывания, вызова сишной функции обработчика, адрес которой лежит в регистре ViсVectAddr, и восстановления регистров и состояния проца после завершения обработчика. Так что для каждого прерывания у меня свой обработчик, как и положено. Цитата(aaarrr @ Mar 5 2010, 02:57)  Не происходит такого в нормальных условиях. Пока выполняется один обработчик прерывания с определенным приоритетом, другие прерывания с тем же или меньшим приоритетом маскируются. Иначе какой вообще смысл во вложенности? Вот в этом-то и дело! К примеру, если в процессе работы обработчика прерывания с приоритетом 5 (Timer1), происходит прерывание с приоритетом 4 (Timer0), то выставляются флаги прерывания в T0IR, VICIRQStatus, записывается нужный адрес в VicVecAddress, НО перехода в обработчик прерывания Timer0 не происходит до тех пор, пока мы не запишем что-либо в регистр VicVecAddress, что само собой разрешит прерывания всех приоритетов. Хотя ни в "AN10381(Nesting of interrupts in the LPC2000)", ни в "Real-time processing with the Philips LPC ARM microcontroller; using GCC and the MicroC/OS-II RTOS." про необходимость записи в регистр VicVecAddress для разрешения вложенных прерываний ничего не сказано. Там говорится о достаточности применения вышеописанных макросов IENABLE, IDISABLE, либо ассемблерной процедурине для входа и выхода из обработчика. Вот и интересно, что же я упустил? А вообще, рассмотрение сего вопроса началось из-за следующего глюка: вызывается функция, которая допускает появления прерываний, далее походу при появлении этих прерываний, ИНОГДА появляются лишние вызовы функций со странными параметрами. При рассмотрении это было похоже на следующее: 1. Вызвали функцию Func1(param1), 2. В этой функции произошло прерывание, в котором использовалось значение по адресу param_ISR 3. Вышли из прерывания 4. ЛИШНИЙ повторный вызов функции Func1(param_ISR) (прошу обратить внимание на изменившийся параметр!) 5. Дальнейший ход выполнения программы. Обработчик прерывания был отмечен атрибутом __attribute__ ((interrupt)), никаких макросов типа IENABLE не было, вход в обработчик был стандартным. Вот и складывается впечатление, что при входе-выходе из прерывания сохраняются-ресторятся не все регистры. Цитата театр одного актёра Благодарю за участие)
Сообщение отредактировал Novichok1 - Mar 5 2010, 05:17
|
|
|
|
|
Mar 5 2010, 07:22
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Ну точнее, там используется два источника прерываний, вот что относится к первому: пролог: Код sub lr, lr, #4 stmfd sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip, lr} эпилог: Код ldr r3, [r4, #8] and r3, r3, #255 strb r3, [sp, #11] ldrb r3, [sp, #11] @ zero_extendqisi2 tst r3, #1 beq .L92 mov r2, #0 mvn r3, #0 str r2, [r3, #-255] add sp, sp, #12 ldmfd sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip, lr} subs pc, lr, #4 и ко второму: пролог: Код stmfd sp!, {r4, r5, r6, r7, r8, lr} ldr r4, .L23 mov r3, #1 sub sp, sp, #8 эпилог: Код add sp, sp, #8 ldmfd sp!, {r4, r5, r6, r7, r8, lr} Я не силен в ассемблерных кодах, поэтому могу напутать с границами эпилога-пролога. Что странно, во втором случае регистр r3 не сохраняется в стек, хотя в теле функции он проскакивает.
|
|
|
|
|
Mar 5 2010, 07:43
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Если после команды ldmfd в эпилоге второго ничего не стоит, то неудивительно, что есть глюки. Это может быть известная бага GCC с атрибутом interrupt. В эпилоге первого прерывания стоит subs pc, lr, #4, что является нормальным выходом из прерывания. В эпилоге второго нет этой команды. Есть ещё второй вариант правильного ухода из прерывания, но и его там тоже не видно. Почитайте http://electronix.ru/forum/index.php?showt...mp;#entry650548Вот ещё. http://electronix.ru/forum/index.php?showt...=69514&st=0Там есть мои короткие обёртки для вложенных прерываний. Они под ФриРТОС. В них можно чуть переделать режимы проца, чтобы nested IRQ/FIQ работало в System Mode без какой-либо оси.
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Mar 5 2010, 07:47
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Цитата(GetSmart @ Mar 5 2010, 10:43)  Если после команды ldmfd в эпилоге второго ничего не стоит, то неудивительно, что есть глюки. Это может быть известная бага GCC с атрибутом interrupt. Да, во втором случае еще стоит команда: bx lr Извините, ее пропустил. А что на счет r3, это нормально, что она не сохраняется в стек? За ссылки спасибо, буду изучать.
Сообщение отредактировал Novichok1 - Mar 5 2010, 08:05
|
|
|
|
|
Mar 5 2010, 10:04
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Novichok1 @ Mar 5 2010, 07:59)  Большое спасибо за разъяснение! А насчет некорректного выравнивания стека - после stmfd sp!, {r0-r6, ip, lr} через некоторое число команд вызывается еще и stmfd sp!, {lr} (на шаге 5), так что в сумме получается четное число сохраненных регистров, так можно рассуждать, или при выполнении ОДНОЙ команды записи в стек должно сохраняться четное число слов? На шаге 5 пишется уже другой стек (SYS), суммировать эти записи нельзя. Цитата(Novichok1 @ Mar 5 2010, 07:59)  Я наверно не совсем точно выразился, под " ...для вызовов всех прерываний использую..." не имелось ввиду использование одного сишного обработчика для всех прерываний, а подразумевалась последовательность ассемблерных команд для подготовки ко входу в обработчик прерывания Я ее и имел в виду. Для прерывания с высшим приоритетом эта ассемблерная последовательность - пустая трата времени. Цитата(Novichok1 @ Mar 5 2010, 07:59)  Вот и интересно, что же я упустил? Вложенные прерывания предполагают, что выполнение процедуры обработки прерывания с приоритетом 5 может быть прервано прерыванием с приоритетом 6 или выше. Прерывание с меньшим приоритетом (4) может отработать только после записи VicVectAddr.
|
|
|
|
|
Mar 5 2010, 12:14
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(aaarrr @ Mar 5 2010, 15:04)  Вложенные прерывания предполагают, что выполнение процедуры обработки прерывания с приоритетом 5 может быть прервано прерыванием с приоритетом 6 или выше. Прерывание с меньшим приоритетом (4) может отработать только после записи VicVectAddr. Приоритеты нумеруются в обратном порядке. №0 самое важное/приоритетное. №15 самое низкоприоритетное. Цитата(Novichok1 @ Mar 5 2010, 12:47)  Да, во втором случае еще стоит команда: bx lr Это точно пролог/эпилог функции с атрибутом interrupt ?
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Mar 5 2010, 12:23
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Цитата(aaarrr @ Mar 5 2010, 13:04)  Вложенные прерывания предполагают, что выполнение процедуры обработки прерывания с приоритетом 5 может быть прервано прерыванием с приоритетом 6 или выше. Прерывание с меньшим приоритетом (4) может отработать только после записи VicVectAddr. А не наоборот? Из мануала по LPC24xx Цитата There are 16 priority levels, corresponding to the values 0 through 15 decimal, of which 15 is the lowest priority. То есть четвертый приоритет выше пятого. Цитата Кроме того, следует обратить внимание на некорректное выравнивание стека - должно быть четное число слов Все-таки кажется странным, что некорректный код публиковали бы под столь серьезным названием.
Сообщение отредактировал Novichok1 - Mar 5 2010, 12:25
|
|
|
|
|
Mar 5 2010, 12:35
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата Все-таки кажется странным, что некорректный код публиковали бы под столь серьезным названием. Не парьтесь, выравнивание на ARM7 не влияет ни на что. На LPC2xxx даже можно SP по модулю 4 не выравнивать  (Хотя не проверял, но по всем признакам это так) Цитата(Novichok1) А что на счет r3, это нормально, что она не сохраняется в стек? Вероятнее всего функция объявлена не как прерывание, а как просто функция. Там в атрибутах ещё должно указываться тип IRQ или FIQ, но точно не проверял как это влияет на прологи/эпилоги. Просто ставил всегда, когда не ставил свои обёртки. Со своими обёртками использовал атрибут naked.
Сообщение отредактировал GetSmart - Mar 5 2010, 12:38
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Mar 5 2010, 12:41
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Novichok1 @ Mar 5 2010, 15:23)  А не наоборот? Из мануала по LPC24xx
То есть четвертый приоритет выше пятого. Наоборот, не важно, уберите цифры и читайте больший-меньший. Цитата(Novichok1 @ Mar 5 2010, 15:23)  Все-таки кажется странным, что некорректный код публиковали бы под столь серьезным названием. Легко. Вы думаете, этот код великие корифеи пишут? Да и на старуху бывает проруха. Цитата(GetSmart @ Mar 5 2010, 15:35)  Не парьтесь, выравнивание на ARM7 не влияет ни на что. На LPC2xxx даже можно SP по модулю 4 не выравнивать  (Хотя не проверял, но по всем признакам это так) Ну, когда наткнетесь на некорректную работу плавучки, вспомните про выравнивание стека
|
|
|
|
|
Mar 5 2010, 12:43
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Цитата(GetSmart @ Mar 5 2010, 15:14)  Это точно пролог/эпилог функции с атрибутом interrupt ? Ох, виновен, виновен. Вот пролог: Код @ Interrupt Service Routine. @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 sub lr, lr, #4 stmfd sp!, {r0, r1, r2, r3, ip, lr} и эпилог: Код ldmfd sp!, {r0, r1, r2, r3, ip, lr} subs pc, lr, #4 То есть вроде все нормально. Но глюк то есть. Но опять таки, если применять свои ассемблерные вставки входа и выхода из обработчиков, то все в порядке. Но чтобы в одном обработчике прерывания можно было перейти в другое с более высоким приоритетом, нужно совершить запись в VicVectAddr, что не есть правильно. Посмотрел ассемблерные вставки, предложенные Вами, GetSmart, но они вроде ничем принципиальным не отличаются от тех, которыми пользуюсь я, то есть, тоже сохранение контекста, переход в другой режим работы процессора с разрешенными прерываниями, и вызов функции. Так чего же не хватает?
Сообщение отредактировал Novichok1 - Mar 5 2010, 12:48
|
|
|
|
|
Mar 5 2010, 12:54
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(Novichok1 @ Mar 5 2010, 17:43)  То есть вроде все нормально. Но глюк то есть. Нихрена не нормально. Это бага GCC. Оно два раза вычетает из LR (адрес возврата в основную прогу) число 4. Кстати, я сразу не заметил, но и в первом прерывании та же самая беда. Цитата Но чтобы в одном обработчике прерывания можно было перейти в другое с более высоким приоритетом, нужно совершить запись в VicVectAddr, что не есть правильно. Такого не должно быть. Эта вещь реализована аппаратно в контроллере прерываний. Единственная причина по которой проц не может улететь на более высокоприоритетное прерывание - запрещённые глобально прерывания (все). То бишь установленный в "1" флаг I в регистре флагов.
Сообщение отредактировал GetSmart - Mar 5 2010, 12:57
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Mar 5 2010, 12:55
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Цитата(GetSmart @ Mar 5 2010, 15:35)  Со своими обёртками использовал атрибут naked. О! А этот вопрос был у меня в начале этой темы, не подскажете, как в GCC создавать свои атрибуты? Очень интересно и думаю будет полезно.
|
|
|
|
|
Mar 5 2010, 13:12
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Цитата(GetSmart @ Mar 5 2010, 15:54)  То бишь установленный в "1" флаг I в регистре флагов. Странно, я же сбрасываю его в ноль: /* (4) Change to SYS mode and enable IRQ */ msr cpsr_c, #0x1F Цитата Это не свой атрибут, а стандартный. Используется так: void AdcIntr_Wrapper() __attribute__((naked)); Спасибо, а не в курсе, в GCC все-таки можно создавать свои атрибуты?
|
|
|
|
|
Mar 5 2010, 14:03
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(Novichok1) Спасибо, а не в курсе, Не, не в курсе. Я с GCC недавно пересёкся и не знаю что там к чему. Есть здесь такой чел klen он должен быть в курсах. Кстати, пустышку-обработчик не стоит обзывать static иначе она оптимизируется и выкинется. Если глобальные прерывания разрешены, значит что-то другое делаете не так. Может приоритеты неправильно задаёте. Вы тут периодически ошибаетесь, а мне гадать не интересно.
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Mar 11 2010, 15:07
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
А разве к статическим членам нет доступа? Ведь обработчик-это просто функция (в моем случае-статический метод класса), у которой есть определенная отличная последовательность загрузки выгрузки. По сути, это низкоуровневый эквивалент событий(точнее, события-высокоуровневый эквивалент прерываний), по крайней мере по моим представлениям. Ну а к экземпляру класса, да, прямого доступа не будет. А сделал так, чтобы не нарушать стройность классовой модели, не хочу смешивать чисто сишные функции с классами. Это дает дополнительные преимущества по областям видимости данных и методов, а минусов я пока не нашел, если Вы их видите, то прошу ими поделиться.
|
|
|
|
|
Mar 11 2010, 18:22
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Цитата(GetSmart @ Mar 11 2010, 20:35)  Знаю только что обычным функциям класса передаётся "невидимый" параметр с указателем на текущий экземпляр класса. Это верно, но только для нестатических методов, то есть которые привязаны к конкретному экземпляру класса, на статические методы, которые принадлежат типу, а не конкретному экземпляру данного типа, это не распространяется, никаких невидимых параметров, типа параметра "this" туда не передается. Цитата(GetSmart @ Mar 11 2010, 20:35)  А уже из прерывания можно вызывать функцию(и) любого статического экземпляра, либо из статического указателя. О, а это очень даже полезная идея! Насколько я Вас понял, Вы предлагаете примерно следующее: этот главный обработчик обернуть в нечто подобное делегату из C#. В итоге это даст возможность легко подписываться на это прерывание из разных мест разным классам. Правда неизбежны некоторые накладные расходы на вызовы функций и дополнительные проверки.
|
|
|
|
|
Mar 11 2010, 19:02
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Вообще, излишняя универсальность, которая не понадобится в проге мне не нравится. Но как вариант, имеется класс низкого уровня с виртуальной функцией обработчика прерывания. Обработчик прерывания использует статическую переменную с указателем на этот класс. При входе в обработчик он вызывает эту функцию. Указатель можно инициализировать и менять в процессе любым совместимым экземпляром класса. А минимально, по простому: Код void MyIrq() { MyClassPtr->IrqHandler(); // функция класса, не статическая, статический указатель на экземпляр класса }
void MyIrq_Wrapper() // адрес этой функции будет записан в обработчик прерывания, должен быть атрибут (naked) { asm("пролог"); MyIrq(); asm("эпилог"); } Накладных расходов здесь почти нет. Главный накладной расход - компилятор GCC, так как у него не самый оптимальный код. В принципе, можно даже MyIrq() сократить, только предворительно проверить что за код создаёт компилер во всех режимах оптимизации. Лишь бы фрейм стека не выделялся. ----------- А чем вообще отличается статическая функция класса от обычной функции вне классовой дискриминации?  Область видимости чего-то и всё?
Сообщение отредактировал GetSmart - Mar 11 2010, 19:08
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Mar 12 2010, 04:49
|
Участник

Группа: Участник
Сообщений: 73
Регистрация: 8-07-09
Пользователь №: 51 032

|
Цитата(GetSmart @ Mar 11 2010, 22:02)  А чем вообще отличается статическая функция класса от обычной функции вне классовой дискриминации?  Область видимости чего-то и всё? На сколько я знаю, да. Но согласитесь, это удобно, если функция логически связана с классом, и ей нужен доступ к приватным и защищенным методам и полям, тогда проще ее объявить статическим методом, чем делать дополнительные телодвижения с установкой дружественной функцией. Да и более логичен вызов этой функции извне через название класса, как мне кажется. То есть не будет путаницы, если у вас несколько функций с одинаковыми именами, но относящихся к разным классам. А код я бы по другому сделал, например что-то типа: Код void OnIRQ // адрес этой функции будет записан в обработчик прерывания, должен быть атрибут (naked) { asm("пролог");
for(int i = 0; i < MAX_ISR_COUNT; i++) { if(interrupt_handlers[i] != NULL) { interrupt_handlers[i](); } }
asm("эпилог"); } Ну правда возникает вопрос в надобности использования нескольких обработчиков на одно прерывание) Ну в принципе можно и без массива обработчиков, тогда будет вызываться просто указатель на один обработчик. Но в любом случае, мне не совсем понятна нужность вот этого кода: Код void MyIrq() { MyClassPtr->IrqHandler(); // функция класса, не статическая, статический указатель на экземпляр класса } То есть, зачем нужен указатель на класс, ведь можно сразу в целевом классе назначить обработчик через указатель на функцию, без использования указателя на класс в главном обработчике прерывания.
Сообщение отредактировал Novichok1 - Mar 12 2010, 05:05
|
|
|
|
|
Mar 12 2010, 05:50
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(Novichok1) Но в любом случае, мне не совсем понятна нужность вот этого кода: Если статическая функция класса при вызове не имеет параметра экземпляра класса и отличается только областью видимости полей класса, то единственное отличие - вызов функции класса с конкретным экземпляром, то есть не статической. Это же самое можно делать из статической функции, то есть вызывать другие функции для конкретных экземпляров. Но лично у меня возникает вопрос - с чего бы это только для отработки прерывания я должен создавать статическую функцию? При отработке прерываний как в чистом Си, так и в С++ у меня не возникает вопроса при создании обработчика в виде обычной голой функции. Причём обычно двух, чтобы не вставать на грабли компилятора. В самом минимальном варианте, одна глобальная функция, вызывающая функцию конкретного экземпляра. Никакого оверхеда, кроме минимального для обхода граблей. Цитата А код я бы по другому сделал, например что-то типа: Так делать не рекомендовал бы, без обёртки из глобальной функции, на худой конец пустой (чисто с прологом и эпилогом, но без выдления локальных переменных) стической функции класса.
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Mar 13 2010, 18:30
|
Ортодокс
  
Группа: Свой
Сообщений: 219
Регистрация: 26-10-07
Из: Смела, Украина
Пользователь №: 31 775

|
Цитата(GetSmart @ Mar 12 2010, 07:50)  Так делать не рекомендовал бы, без обёртки из глобальной функции, на худой конец пустой (чисто с прологом и эпилогом, но без выдления локальных переменных) стической функции класса. +1 IMHO действительно оптимальный вариант для обработчика - глобальная функция, из которой вызывается НЕСТАТИЧЕСКАЯ функция-элемент для глобального объекта. Еще доводы в пользу такого решения: 1) Методологический. Все-таки и источник прерываний и периферийный узел процессора - вещи по сути своей глобальные. 2) Практический. Можно легко "наворачивать" обработчики сразу для нескольких периферийных узлов, каждый их которых будет задаваться глобальным экземпляром своего класса.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|