EmbeddedRelated.com
Forums

Simple ARM timer interrupt question

Started by Paul Marcel November 16, 2004
Lewin A.R.W. Edwards <larwe@larwe.com> wrote:

> At the time I last wrote an ISR on an ARM platform - which was > probably at least 18 months ago - the GCC documentation said > explicitly that interrupts are not part of C, and are therefore not > directly supported.
That was true back then, and is still true now. The difference is one of interpretation (i.e. one of nitpicking, if you want), and to some extent, one of what GCC version you're looking at. For *some* CPUs, GCC goes out of its way to help you make the task of writing the necessary glue code between C and the interrupt mechanisms of a CPU a bit easier. That's what __attribute__((interrupt ("IRQ NAME"))) is all about --- it essentially is a way of telling GCC to generate the glue code (or "shim", "wrapper", "veneer", "redirector" --- pick your name) for you, instead of having to do it manually. As of GCC-3.4.1, this extension exists for ARM, too. Since __attribute__(()) is a GNU extension to C, it's technically not part of C, because "C" devoid of any qualifiers by convention always means "standard C". So the above statement from the GCC docs is still correct. But __attribute__(()), if supported, looks and feels sufficiently similar to ordinary C that it would seem a bit misguided to insist on that technicality. I.e. people working on CPUs that have GCC supporting the __attribute__((interrupt)) extension aren't quite completely wrong when they state they're writing interrupt handlers in C (e.g, they're definitely not coding any assembler themselves) --- they're just not 100% accurately right either. -- Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de) Even if all the snow were burnt, ashes would remain.
> That was true back then, and is still true now. The difference is one > of interpretation (i.e. one of nitpicking, if you want), and to some > extent, one of what GCC version you're looking at.
I think the bottom line here is that at that time I was using 2.95.2 (yes, there were more recent versions around, but 2.95.2 was the "most tested" version with all the other code I was using). The interrupt attribute didn't cause an error on this compiler, but it didn't create a usable ISR either.
On 19 Nov 2004 12:18:55 -0800, larwe@larwe.com (Lewin A.R.W. Edwards)
wrote:

>> That was true back then, and is still true now. The difference is one >> of interpretation (i.e. one of nitpicking, if you want), and to some >> extent, one of what GCC version you're looking at. > >I think the bottom line here is that at that time I was using 2.95.2 >(yes, there were more recent versions around, but 2.95.2 was the "most >tested" version with all the other code I was using). The interrupt >attribute didn't cause an error on this compiler, but it didn't create >a usable ISR either.
OK. It seems that I CAN have an interrupt routine in C with this version of the gnu arm compiler. But I am still trying to get interrupts to work with this code. The main loop runs (LED1 flashes), but the C interrupt function never gets called. I'm using a nohau jtag interface with Seehau to load the code. I have set breakpoints inside the C interrupt and also at the interrupt vector location, but neither one happens. This is for the eb40a board. Here's the compiler info: C:\arm\bin>arm-none-elf-gcc -v Reading specs from c:/arm/bin/../lib/gcc/arm-none-elf/3.4.2/specs Configured with: /scratch/paul/release/gcc/configure --prefix=/scratch/paul/rele ase/install/i686-mingw32/arm-none-elf --target=arm-none-elf --with-newlib --disa ble-libmudflap --enable-languages=c,c++ --build=i686-pc-linux-gnu --host=i686-mi ngw32 Thread model: single gcc version 3.4.2 (release) (CodeSourcery ARM Q3D 2004) Could someone who knows please tell me why this doesn't work? thanks, Paul BOOT.ASM: ====================== .section .text .code 32 .global vectors vectors: b reset @ Reset b exception @ Undefined instruction b exception @ SWI b exception @ Prefetch abort b exception @ Data abort b exception @ reserved vector ldr pc, [pc, # -0xF20] @ irqs b exception @ fast irqs exception: b exception reset: @ Begin by clearing out the .bss section ldr r1, bss_start ldr r2, bss_end ldr r3, =0 clear_bss: cmp r1,r2 strne r3,[r1],#+4 bne clear_bss ldr r13,stack_pointer @ Initialize the stack pointer bl main b vectors stack_pointer: .word _stack_top bss_start: .word __bss_start__ bss_end: .word __bss_end__ .end MAIN.C: ================= #include "parts/r40008/reg_r40008.h" #include "periph/usart/usart.h" #include "periph/power_saving/ps40008.h" #include "targets/eb40a/eb40a.h" void init(void); void timer1_c_irq_handler( void ) __attribute__((interrupt("IRQ"))); #define WRITEREGW(addr,value) *((volatile unsigned int *) (addr)) = (value) unsigned long leds[8] = { 0x00010000, // led1 0x00020000, // led2 0x00040000, // led3 0x00080000, // led4 0x00000008, // led5 0x00000010, // led6 0x00000020, // led7 0x00000040 // led8 }; /*********************************************************************** ***********************************************************************/ int main ( void ) { unsigned int i; unsigned int dummy; init(); PIO_PER = LED8 | LED1; // Enable the PIO/LED8 pin PIO_OER = LED8 | LED1; // Enable the PIO/LED8 pin as Output PIO_CODR = LED8 | LED1; // Set LED8 // Timer1 Init TC1_CCR = TC_CLKDIS; // Channel Control Register: disabled TC1_IDR = 0xFFFFFFFF; // Interrupt Disable Register: disable all interrupts dummy = TC1_SR; // Status Register: dummy read TC1_CMR = TC_CLKS_MCK1024 | TC_CPCTRG; // mclk/1024, compare resets.... TC1_CCR = TC_CLKEN; // Enable the Clock counter TC1_IER = TC_CPCS; // enable the RC compare interrupt AIC_IDCR = (1<<TC1_ID); // Disable timer 1 interrupt at AIC level AIC_SVR5 = (unsigned int) timer1_c_irq_handler; // Set the TC1 IRQ handler address AIC_SMR5 = ( AIC_SRCTYPE_INT_LEVEL_SENSITIVE | 0x4 ); // Set the trigg and priority for TC1 interrupt AIC_ICCR = (1<<TC1_ID); // Clear the TC1 interrupt AIC_IECR = (1<<TC1_ID); // Enable the TC1 interrupt TC1_RC = 0xFBC5; TC1_CCR |= TC_SWTRG; WRITEREGW(0xffff4000, 0); WRITEREGW(0xffff4004, 0x00000020); WRITEREGW(0xffff4008, ~0x00000020); // Enable global interrupts asm( "mrs r0, cpsr;" // comment "ldr r1, =0xFFFFFF7F;" "and r0, r0, r1;" "msr cpsr, r0;" : // input : // output :"r0", "r1" // tell the compiler that these are clobbered ); while (1) { PIO_CODR = LED1; for (i=0; i<1000000; i++) { } PIO_SODR = LED1; for (i=0; i<1000000; i++) { } } return 0; } /*********************************************************************** Function Name : timer1_c_irq_handler Object : Timer 1 interrupt Handler ***********************************************************************/ void __attribute__((interrupt("IRQ"))) timer1_c_irq_handler(void) { unsigned int dummy; dummy = TC1_SR; // Read TC1 Status Register to clear it if ( (PIO_PDSR & LED8) == LED8 ) PIO_CODR = LED8; else PIO_SODR = LED8; return; } /************************************************************************************ ************************************************************************************/ void init(void) { int i; //------------------------- // turn off the leds: //------------------------- for(i=0;i<8;i++) { PIO_PER = leds[i]; // enable the PIO pin PIO_OER = leds[i]; // enable the PIO pin as output PIO_SODR = leds[i]; // turns OFF LED } return; } EB40A-RAM.LD: ================= ENTRY(vectors) SEARCH_DIR(.) /* There is a single memory segment, representing the 256K on-board SRAM. */ MEMORY { sram : org = 0x00000000, len = 256K } SECTIONS { .text : { *(.text); . = ALIGN(4); *(.glue_7t); . = ALIGN(4); *(.glue_7); . = ALIGN(4); etext = .; } > sram .data ADDR(.text) + SIZEOF(.text) : { datastart = .; __data_start__ = . ; *(.data) . = ALIGN(4); __data_end__ = . ; edata = .; _edata = .; } > sram .bss ADDR(.data) + SIZEOF(.data) : { __bss_start__ = . ; *(.bss); *(COMMON) __bss_end__ = . ; _stack_bottom = . ; . += 0x800 ; _stack_top = . ; } > sram /* .rodata ADDR(.bss) + SIZEOF(.bss) : { } > sram */ .rodata ADDR(.bss) + SIZEOF(.bss) : { *(.rodata*) } > sram end = .; _end = .; __end__ = .; /* Symbols */ .stab 0 (NOLOAD) : { [ .stab ] } .stabstr 0 (NOLOAD) : { [ .stabstr ] } } MAKEFILE: ======================= LD = eb40A-ram.ld CFLAGS = -g -I. -IC:\flo4\logger\gnu -mcpu=arm7tdmi #CFLAGS = -g -I. -mcpu=arm7tdmi ASFLAGS = -mcpu=arm7tdmi -gstabs LDFLAGS = -Teb40a-ram.ld -nostartfiles -Lgcc -L. # OBJS = boot.o main.o # OBJS = boot.o main.o ext_irq.o interrupt_Usart.o OBJS = boot.o main.o EXE = test.elf BIN = test.bin DIS = test.lst HEX = test.hex S = test.s $(BIN): $(EXE) arm-none-elf-objcopy -O binary $(EXE) $(BIN) arm-none-elf-objcopy -O ihex $(EXE) $(HEX) arm-none-elf-objcopy -O srec $(EXE) $(S) arm-none-elf-objdump -D $(EXE) >$(DIS) ls -l test.* # size $(EXE) $(EXE): $(OBJS) arm-none-elf-gcc $(LDFLAGS) -o $(EXE) $(OBJS) $(OBJS): $(LD) $(OBJS): Makefile %.o:%.c arm-none-elf-gcc -c $(CFLAGS) $< -o $@ %.o:%.asm arm-none-elf-as $(ASFLAGS) $< -o $@ clean: rm -f $(OBJS) rm -f $(EXE) rm -f $(BIN) rm -f $(DIS) rm -f $(HEX) rm -f $(S)
"Paul Marcel" <pama19800522@hotmail.com> escribi&#4294967295; en el mensaje
> > OK. It seems that I CAN have an interrupt routine in C with this > version of the gnu arm compiler. But I am still trying to get > interrupts to work with this code. The main loop runs (LED1 flashes), > but the C interrupt function never gets called. I'm using a nohau jtag > interface with Seehau to load the code. I have set breakpoints > inside the C interrupt and also at the interrupt vector location, but > neither one happens. This is for the eb40a board. >
Some time ago I had trouble with the interrupts and the eb55. I used GCC 3.3.1 and GDB, and I never got the ' __attribute__ interrupt' thing to work. Eventually I got it working using an assembly entry routine which in turn called the C function 'void timer1_interrupt_handler (void)'. I am including the assembler routine just in case you feel like giving it a try. It is ATMEL sample code, modified to get it assembled with GNU. HTH Josep Duran /*;------------------------------------------------------------------------- ---------------------------------- ; The software is delivered "AS IS" without warranty or condition of any kind, either express, implied or statutory. ; This includes without limitation any warranty or condition with respect to merchantability or fitness for any particular purpose, or ; against the ;infringements of intellectual property rights of others. ;--------------------------------------------------------------------------- -------------------------------- ;- File source : irq_timer.s ;- Object : Assembler timer Interrupt Handler ;- Author : AT91 Application Group ;--------------------------------------------------------------------------- -------------------------------- ;--------------------------------------------------------------------------- -------------------------------- ;- Area Definition ;--------------------------------------------------------------------------- -------------------------------- */ .section code /* ,"r" segurament no cal */ /*.interwork ************* */ .equ ARM_MODE_USER,0x10 .equ AIC_BASE, 0xFFFFF000 .equ AIC_IVR, 0x100 .equ AIC_EOICR,0x130 /* ARM Core Mode and Status Bits */ .equ ARM_MODE_IRQ, 0x12 .equ ARM_MODE_SYS, 0x1F .equ I_BIT, 0x80 .MACRO IRQ_ENTRY reg /* Adjust and save LR_irq in IRQ stack */ sub r14, r14, #4 stmfd sp!, {r14} /* Write in the IVR to support Protect Mode */ /* No effect in Normal Mode */ /* De-assert the NIRQ and clear the source in Protect Mode */ ldr r14, =AIC_BASE str r14, [r14, #AIC_IVR] /* Save SPSR and r0 in IRQ stack */ mrs r14, SPSR stmfd sp!, {r0, r14} /* Enable Interrupt and Switch in SYS Mode */ mrs r0, CPSR bic r0, r0, #I_BIT orr r0, r0, #ARM_MODE_SYS msr CPSR_c, r0 /* Save scratch/used registers and LR in User Stack */ .ifndef reg stmfd sp!, { r1-r3, r12, r14} .ELSE stmfd sp!, { r1-r3, \reg, r12, r14} .ENDIF .ENDM .MACRO IRQ_EXIT reg /* Restore scratch/used registers and LR from User Stack */ .ifndef reg ldmia sp!, { r1-r3, r12, r14} .ELSE ldmia sp!, { r1-r3, \reg, r12, r14} .ENDIF /* Disable Interrupt and switch back in IRQ mode */ mrs r0, CPSR bic r0, r0, #ARM_MODE_SYS orr r0, r0, #I_BIT | ARM_MODE_IRQ msr CPSR_c, r0 /* Mark the End of Interrupt on the AIC */ ldr r0, =AIC_BASE str r0, [r0, #AIC_EOICR] /* Restore SPSR_irq and r0 from IRQ stack */ ldmia sp!, {r0, r14} msr SPSR_cxsf, r14 /* Restore adjusted LR_irq from IRQ stack directly in the PC */ ldmia sp!, {pc}^ .ENDM /* ;--------------------------------------------------------------------------- -------------------------------- ;- Function : timer1_asm_irq_handler ;- Treatments : Timer 1 interrupt handler. ;- Called Functions : timer1_c_irq_handler ;- Called Macros : IRQ_ENTRY, IRQ_EXIT ;--------------------------------------------------------------------------- -------------------------------- */ .global timer1_asm_irq_handler /* .IMPORT */ .extern timer1_c_irq_handler /* .EXPORT */ timer1_asm_irq_handler: /* Manage Exception Entry */ IRQ_ENTRY /* Call the timer Interrupt C handler */ ldr r1, =timer1_c_irq_handler mov r14, pc bx r1 /* Manage Exception Exit */ IRQ_EXIT /* **;------------------------------------------------------------------------- ----- */ .GLOBAL habili habili: mov ip, sp stmfd sp!, {fp, ip, lr, pc} sub fp, ip, #4 mrs R0, CPSR bic r0,r0,#0x80 msr CPSR, R0 ldmfd sp, {fp, sp, pc} .END
On Thu, 25 Nov 2004 16:34:23 GMT, "Josep Dur&#4294967295;n" <j.duran@teleline.es>
wrote:

> >"Paul Marcel" <pama19800522@hotmail.com> escribi&#4294967295; en el mensaje >> >> OK. It seems that I CAN have an interrupt routine in C with this >> version of the gnu arm compiler. But I am still trying to get >> interrupts to work with this code. The main loop runs (LED1 flashes), >> but the C interrupt function never gets called. I'm using a nohau jtag >> interface with Seehau to load the code. I have set breakpoints >> inside the C interrupt and also at the interrupt vector location, but >> neither one happens. This is for the eb40a board. >> > >Some time ago I had trouble with the interrupts and the eb55. >I used GCC 3.3.1 and GDB, and I never got the ' __attribute__ >interrupt' thing to work. > >Eventually I got it working using an assembly entry routine which in turn >called the C function 'void timer1_interrupt_handler (void)'. > >I am including the assembler routine just in case you feel like giving it >a try. It is ATMEL sample code, modified to get it assembled with GNU. > >HTH > >Josep Duran > >
I also had gotten the Atmel sample to assemble in gnu. I tried that but still didn't get any interrupt even at the interrupt vector. What code did you have at the interrupt vector address? I haven't exactly tried yours yet since I'm not breaking at 0x18. I think that my first problem is that interrupts are not being generated at all. Could I see your initialization of the timer1 interrupt? thank you, Paul
"Paul Marcel" <pama19800522@hotmail.com> escribi&#4294967295; en el mensaje >

> I also had gotten the Atmel sample to assemble in gnu. I tried that > but still didn't get any interrupt even at the interrupt vector. What > code did you have at the interrupt vector address? I haven't exactly > tried yours yet since I'm not breaking at 0x18. > > I think that my first problem is that interrupts are not being > generated at all. Could I see your initialization of the timer1 > interrupt? > >
Only minor diferences to what you posted before. Anyway, here it goes : (remember I've got a EB55) //*------------------------------------------------------------------------- -------------------------------- //* File Name: Timer_interrupt.c //* Object : AT91EB40A - Timer Counter - Interrupt //* Author: AT91 Application Group //*------------------------------------------------------------------------- -------------------------------- #define TCB1_BASE ((StructTCBlock *)0xFFFD4000) /* Channels 3, 4, 5 */ #define TCB0_BASE ((StructTCBlock *)0xFFFD0000) /* Channels 0, 1, 2 */ #define TC1_CCR ((volatile unsigned int *) 0xFFFD0040) #define TC1_CMR ((volatile unsigned int *) 0xFFFD0044) #define TC1_CV ((volatile unsigned int *) 0xFFFD0050) #define TC1_RC ((volatile unsigned int *) 0xFFFD005C) #define TC1_SR ((volatile unsigned int *) 0xFFFD0060) #define TC1_IER ((volatile unsigned int *) 0xFFFD0064) #define TC1_IDR ((volatile unsigned int *) 0xFFFD0068) #define PIO_PER ((volatile unsigned int *) 0xFFFF0000) #define PIO_OER ((volatile unsigned int *) 0xFFFF0010) #define PIO_SODR ((volatile unsigned int *) 0xFFFF0030) #define PIO_CODR ((volatile unsigned int *) 0xFFFF0034) #define PIO_PDSR ((volatile unsigned int *) 0xFFFF003C) #define AIC_SMR5 ((volatile unsigned int *) 0xFFFFF01c) #define AIC_SVR5 ((volatile unsigned int *) 0xFFFFF09c) #define AIC_IECR ((volatile unsigned int *) 0xFFFFF120) #define AIC_IDCR ((volatile unsigned int *) 0xFFFFF124) #define AIC_ICCR ((volatile unsigned int *) 0xFFFFF128) #define TC1_ID 7 /* Timer Channel 1 interrupt */ //* TC_CMR: Timer Counter Channel Mode Register Bits Definition #define TC_CLKS_MCK1024 0x4 #define TC_CPCTRG 0x4000 //* TC_CCR: Timer Counter Control Register Bits Definition #define TC_CLKEN 0x1 #define TC_CLKDIS 0x2 #define TC_SWTRG 0x4 //* TC_SR: Timer Counter Status Register Bits Definition #define TC_CPCS 0x10 /* RC Compare Status */ //* AIC_SMR: Interrupt Source Mode Registers #define AIC_SRCTYPE_INT_LEVEL_SENSITIVE 0x00 /* Level Sensitive */ //* Leds Definition #define LED1 (1<<15) #define LED8 (1<<8) extern void timer1_asm_irq_handler(void); //*------------------------------------------------------------------------- ---- //* Function Name : timer1_c_irq_handler //* Object : Timer 1 interrupt Handler //*------------------------------------------------------------------------- ---- void timer1_c_irq_handler (void) { unsigned int dummy ; dummy = *TC1_SR; /* Read TC1 Status Register to clear it */ if ( (*PIO_PDSR & LED8) == LED8 ) *PIO_CODR = LED8 ; else *PIO_SODR = LED8 ; } void delay (void) { unsigned int i; for (i=0; i<1000000 ; i++); } //*------------------------------------------------------------------------- ---- //* Function Name : main //* Object : AT91 - Timer Counter- PWM generation //* Input Parameters : none //* Output Parameters : none //* Functions called : none //*------------------------------------------------------------------------- ---- int main ( void ) { unsigned int dummy ; int suma_ct0 ; *PIO_PER = LED8 | LED1 ; /* Enable the PIO/LED8 pin */ *PIO_OER = LED8 | LED1; /* Enable the PIO/LED8 pin as Output */ *PIO_CODR = LED8 | LED1 ; /* Set LED8 */ // Habilitar interrupcions a lo bestia : *(unsigned int *) 0x018 = 0xfffff100 ; extern int habili () ; habili () ; // Timer1 Init *TC1_CCR = TC_CLKDIS ; /* Disable the Clock Counter */ *TC1_IDR = 0xFFFFFFFF ; dummy = *TC1_SR ; *TC1_CMR = TC_CLKS_MCK1024 | TC_CPCTRG ; *TC1_CCR = TC_CLKEN ; /* Enable the Clock counter */ *TC1_IER = TC_CPCS ; /* Validate the RC compare interrupt */ // Deshabilitar la interrupci&#4294967295; del timer 1 *AIC_IDCR = (1<<TC1_ID) ; // Handler de la interrupcio : *AIC_SVR5 = (unsigned int) timer1_asm_irq_handler ; // Modo de trigger i prioritat : *AIC_SMR5 = ( AIC_SRCTYPE_INT_LEVEL_SENSITIVE | 0x4 ) ; // Interrupt Clear i Enable *AIC_ICCR = (1<<TC1_ID) ; *AIC_IECR = (1<<TC1_ID) ; // Cargar el registre C del Contador : *TC1_RC = 0xFBC5; // Software trigger : counter reset, clock started *TC1_CCR = TC_SWTRG ; while (1) { *PIO_CODR = LED1 ; delay(); *PIO_SODR = LED1 ; delay(); { int h1 = *AIC_SMR5 ; // Modo i prioritat int h2 = *AIC_SVR5 ; // tindria que ser la direccio del handler. int h3 = *((volatile unsigned int *) 0xfffff110) ; int h4 = *((volatile unsigned int *) 0xfffff10c) ; int h5 = *TC1_CV ; suma_ct0 = 0 ; } } return(0) ; }
Paul Marcel <pama19800522@hotmail.com> wrote in message news:<fncbq0h1n9nqt10gts0doftijrv70rurus@4ax.com>...
> > OK. It seems that I CAN have an interrupt routine in C with this > version of the gnu arm compiler. But I am still trying to get > interrupts to work with this code. The main loop runs (LED1 flashes), > but the C interrupt function never gets called. I'm using a nohau jtag > interface with Seehau to load the code. I have set breakpoints > inside the C interrupt and also at the interrupt vector location, but > neither one happens. This is for the eb40a board. > > > Here's the compiler info:
[snip] It's generally not good form to paste in hundreds of lines of source code because those people not interested in the thread don't want to spend precious seconds of their valuable lives downloading it. Anyway,
> asm( > "mrs r0, cpsr;" // comment > "ldr r1, =0xFFFFFF7F;" > "and r0, r0, r1;" > "msr cpsr, r0;"
Sheesh, please at least use BIC instead of corrupting r1. Also, I can't spot anywhere where the peripheral clock for the timer is turned on! Sprow.
I got it working in windows. No assembly "shim", no cygwin. This
example is self-contained, except for the make.exe that I used which
was the one from the MKS Toolkit. It uses codesourcery's free gnu
compiler. It's not perfect, but it works. I still have to figure out
how to use the ram.ld linker file better and I think that I should not
use the .space directive in boot.s.

http://www.toadhaul.org/arm.php