Simple ARM timer interrupt question

I'm trying to get a very simple timer interrupt working in C on Atmel's EB40A board. The processor is an Atmel AT91R40008. I'm using the gnu tools from codesourcery (arm-none-elf-gcc etc). I have some non-interrupt samples working. The interrupt is not working. LED1 (the main loop LED) blinks, but the timer LED doesn't blink. I found this sample on Atmel's web site but that loaded timer1_asm_irq_handler into AIC_SVR5. I'd LIKE to load it with "time1_c_irq_handler" instead. Could someone tell me if I can do that and how to get this simple example working? Here's a link to the files:

formatting link

Paul

Reply to
Paul Marcel
Loading thread data ...

Only thing I can think of right now is to look at the assembly language that the compiler generates for the interrupt routine. Make sure it conforms to an interrupt service routine. Most CPUs have a seperate return instruction for regular subroutines and for interrupt service routines.

You didn't mention any compile or link errors/warnings so I assume it all goes together ok.

Reply to
Gary Kato

You can't vector the interrupt direct to C. You need an asm reflector shim.

Reply to
Lewin A.R.W. Edwards

Oh. I didn't know that. Do you have an example of how the asm reflector should be?

Paul

Reply to
Paul Marcel

shim.

well I have an ARM timer interrupt written entirely in C that uses the GCC extension

void __attribute__((interrupt("IRQ"))) SYS_kbd_irq_handler(void) { ... }

I then stuff the function pointer into the VIC

works fine for me.

--
Peter.Dickerson (at)...
Reply to
Peter Dickerson

I added a "shim" in boot.asm and tried that by loading the AIC_SVR5 register with the assembly routine. I had to modify the assembly code to get it to assemble with arm-none-elf-as. But still no interrupts. It did not even break in the assembly routine.

I switcheck back to a strictly C interrupt changing the function declartion to the form of your Sys_kbd_irq_handler, but still no interrupts. The program is running because the main loop is blinking the mainline led.

It seems that I need to do something else to enable interrupts. Any ideas? The current state of the test program is at

formatting link

Paul

Reply to
Paul Marcel

What GCC version are you using?

Reply to
Lewin A.R.W. Edwards

Never heard of an asm reflector shim.

GCC allows the interrupt to be written in C by using the __attribute__ extension.

Search for __attribute__ on

formatting link
The FreeRTOS source code has examples for the serial port and a timer interrupt - but for a Philips not an Atmel.

Reply to
Ken

Would that be the same as a veneer?

[snip blah blah]

At some point in the output code is there something like MRS r0, CPSR BIC r0, r0, #I_BIT MSR CPSR_cxsf, r0 present? ARM's reset with global interrupts disabled, Sprow.

Reply to
Sprow

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. It was necessary to write a little snippet of ARM assembler like:

fiq_handler: stmdb r13!, {r0-r7, r12} ldr r12, irqvector bx r12 ldmia r13!, {r0-r7, r12} subs pc, r14, #4

irqvector: .word c_fiq_handler

where c_fiq_handler is the C routine containing the actual ISR code.

Reply to
Lewin A.R.W. Edwards

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.
Reply to
Hans-Bernhard Broeker

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.

Reply to
Lewin A.R.W. Edwards

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

Reply to
Paul Marcel

"Paul Marcel" escribió en el mensaje

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

Reply to
Josep Durán

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

Reply to
Paul Marcel

"Paul Marcel" escribió en el mensaje >

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

Reply to
Josep Durán

[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,

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.

Reply to
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.

formatting link

Reply to
Paul Marcel

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.