Interrupthandling without ASF

Go To Last Post
68 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi!

I'm looking for some example how to handle interrupts without the ASF. Is there a similar way to handle these like with the old MegaAVR oder XMegas, e.g. ISR(vect)?

Thanks in advance!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With the ISR(vect) style the compiler placed your code at the correct position in the address space. This won’t work for the AVR32. If you don’t want to use the interrupt handling from ASF, you can do it yourself by writing the routines in assembler and then filling the IPRx registers with the correct offsets. That’s what I did in a timing-critical application. I can’t recommend it if you don’t like writing assembler code because it introduces complexity that you’d better do without.

Another option would be to use linker scripts to place your ISRs written in C inside a special address range, compatible with the EVBA requirements, then set the IPRx registers accordingly. Unless you know how to handle linker scripts, I can’t recommend this either.

Better stick with the ASF and learn to do it AVR32-style. Registering the callbacks isn’t complicated. Just have a look at the example programs.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you.

Maybe you can help me a little bit to get a more clear view of the differences in the interrupt handling between the AVR and AVR32 cores?

As far as I think to know, the interrupt table is an address space in the memory with fixed addresses for each interrupt. At this location there's usually code which jumps to the interrupt handler location?
For AVR the compiler/linker fills this table with the corresponding address of the ISR(vect) handler, but for AVR32 devices this isn't implemented?

Or is the interrupt handling between AVR and AVR32 much more different on core layer?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This thread appeals to my interests.

I tried implementing an interrupt on an AVR32 without using ASF and I failed miserably. I changed the way my code works so as to not need an interrupt, hopefully.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fuseburner wrote:
As far as I think to know, the interrupt table is an address space in the memory with fixed addresses for each interrupt. At this location there's usually code which jumps to the interrupt handler location?
Correct, I think. There’s a special area where interrupt handlers must reside. This special area can be anywhere, though. Its begin is set in the EVBA register.
Fuseburner wrote:
For AVR the compiler/linker fills this table with the corresponding address of the ISR(vect) handler, but for AVR32 devices this isn't implemented?
Correct. The AVR32 compilers place a blob of assembler code at the correct address. This code looks for the correct handler in some tables and these tables are set up by registering your handler with the functions from the INTC module from the ASF. That way your handlers can be wherever you want.

Try some of the example projects that come with the ASF and AVR(32) Studio.

cookiegalore wrote:
I tried implementing an interrupt on an AVR32 without using ASF and I failed miserably.
Yes, it’s a bit tricky, but possible. Take a look at how interrupts are handled by the code in exception.x and intc.c. Assembler code knowledge is a must.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, tried it and it works. Looks really simple. Instead of writing an ISR(vect) I have to write the same function and register it with the INTC_register_interrupt function.

Is there a way to have to precompiler to do these steps with a #define ISR or something else define?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, because ISR() for the (X)megas starts a function declaration. You can’t execute code at that point.

Just accept that programming for the (X)megas and AVR32 is different and don’t try to carry old baggage around.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

catweax wrote:
No, because ISR() for the (X)megas starts a function declaration. You can’t execute code at that point.

Got this in mind after hitting the submit button. Maybe I should increase the PLL multiplier for my brain :lol:

I think you're right and I shouldn't bother with direct register banging any more. Maybe the ASF and I are becoming friends some day :D

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a static implementation you could peek at in the software package for Display Xplained, check http://www.atmel.com/dyn/product... and download the ZIP along with application note AVR1913: Display Xplained Firmware - Getting Started

Direct URL; http://www.atmel.com/dyn/resourc...

Hans-Christian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi

See the following for a version of interrupt handling that overcomes restrictions and complications implied by the the AVR32 interrupt controller:
http://www.utasker.com/forum/ind...

No assembler required and the ISR can be positioned anywhere in the memory map.

Regards

Mark

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One thing I'm not sure about is where you can put the interrupt code in AVR32 memory space. Is there a specific region for interrupts? Can it go anywhere? Are there regions that are off limits?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The interrupt table (evba) can go anywhere, but it must be aligned. It does rjmp's, so you handlers could be anywhere.

Hans-Christian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you use the INTC from the ASF or the alternate handling mentioned by mjbcswitzerland you can place them wherever you want: Flash, HRAM, external S(D)RAM or even the internal SRAM, if you want.

If you mean the interrupt service routine intry points, they must all be located in one block of 16kB and the start address of this block must be stored in the EVBA system register. This block can be wherever you want, though.

The two above mentioned methods basically put a bit of code, consisting mostly of some jumps, in that block and then jump to wherever you want.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi

The 16kB block for direct interrupt handling allows 2 instructions to be saved against the intermediate jump method.

In my opinion, the complications and restrictions to having to ensure that the interrupt code location respects this limitation will outweigh the fact that it takes these 2 instructions longer to get into the routine. The advantages are simpler project management and portability between development environments (how to ensure that the code location is correctly controlled in multiple environments is a major headache and takes some study in each new case to work out how it can actually be achieved).

Of course some people will say that they can't afford the two instruction execution times (either based on a real deterministic requirement or - more often - without any quantitative reasoning apart from the belief that their coding is generally so fantastically efficient that they can't afford to unnecessarily sacrifice these 50ns or so ;-)

My feeling is that if 50ns means make or break in a real project someone has chosen the wrong processor anyway and something else will more likely cause it to fail anyway...

Regards

Mark

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you use the INTC from the ASF, those two instructions aren’t your only slowdown: You get the lookups from the handler tables as well. Those tables can be quite helpful for the interrupt groups that have several lines, but for those groups, that have only one line, they’re not very helpful. In those cases you can save more than just 50ns by directly jumping to your handler.

Basically the ASF version uses the same handler for every interrupt, thereby discarding the information about the interrupt source provided by the hardware, just to find the source by software.

Placing the handlers in the EVBA-area yourself or using the version posted by mjbcswitzerland does the handling much faster. Depends on whether you need it for your project or not. ;)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi!

Fuseburner wrote:
I'm looking for some example how to handle interrupts without the ASF.

This is my coin for it. I do not use it yet. So, be careful.

Function set_evba() see in uTasker.

Ilya

ps. And sorry my english.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mjbcswitzerland,

I was able to get my interrupt working using some of your code and your explanation on how to set up the EVBA address. Thank you!! It was very helpful.

But now I have a problem when running my code: unless I have a breakpoint set at the beginning of my ISR, the processor always receives an exception and just loops that exception's address, as it should. Usually the exception it receives is 0x34 = Data Address (Read) but it has also received 0x00 = Unrecoverable Exception and one other I can't remember.

I also notice that even when the breakpoint is set, after the interrupt is taken care of, when it returns from the ISR, the code it goes back to is actually in the SRAM, not in the Flash, so something is not right here.

I don't understand why it is receiving an exception to begin with.

Here is my ISR code:

void _PWMAInterrupt(void)
{
	int	dummy;
	AVR32_GPIO.port[0].ovrt |= 1 << 22;	//Toggle the LED on PA22.
	AVR32_PWMA.scr = 0x5;	//Write to the SCR register to clear the interrupt.
	dummy = AVR32_PWMA.sr; //Dummy load from SR
	asm("rete");
}

Any thoughts? Thanks in advance.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you add the attribute interrupt to your function, you'll get a rete instead of a ret.

void _PWMAInterrupt(void) __attribute__ ((interrupt))
{
...
}

Hans-Christian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also, to elaborate on what hce said, the interrupt attribute will get the stack cleaned up properly so you don't wind up executing code from SRAM.

Letting the smoke out since 1978

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks HCE and digitalDan, I'll try the interrupt attribute.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Still no luck. When I set it up like this:

void _PWMAInterrupt(void) __attribute__ ((interrupt))

it doesn't compile.

I tried it like this:

void _PWMAInterrupt(void) __attribute__ ((interrupt));

still won't compile.

Then I tried the interrupt attribute before the sub-routine like so

__attribute__((interrupt))
void _PWMAInterrupt(void)

and it fails the same as if the attribute was not included.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try it like this:

__attribute__((__interrupt__)) void _PWMAInterrupt(void)

and be sure to remove the asm("rete"); you show above.

Letting the smoke out since 1978

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

catweax wrote:
Basically the ASF version uses the same handler for every interrupt, thereby discarding the information about the interrupt source provided by the hardware, just to find the source by software.

So, when using the ASF, the EVBA table is always jumping to the general interrupt handler code which decides, which specific interrupt handler has to be used?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi

The following can be used for interrupt routines:

static __interrupt void isr(void)
{
}

This allows for compatible code between IAR and GCC by using the define:
#define __interrupt __attribute__((__interrupt__))
when the project is build with GCC (IAR uses the __interrupt as its notification that the routine is an interrupt).

Note that the exception is a little different. An example of an exception rather than an interrupt is the external Non-maskable interupt, whose handler can be specified as:

static __exception void NMI(void)
{
}

with the definition for the __exception as
#define __exception __attribute__((interrupt("full")))

Quote:
So, when using the ASF, the EVBA table is always jumping to the general interrupt handler code which decides, which specific interrupt handler has to be used?

I believe that this is a valid explanation of the operation although I haven't checked (I just remember quickly looking at it when I started with the AVR32 about mid-2009 and deciding that it was a rather inefficient method requiring an alternative which removes the AVR32 IRQ restrictions of having to respect a small code space for the direct handlers but doesn't slow it down with unnecessary software decisions as to which IRQ will eventually be used to do the specific IRQ work).

At the bottom of this post is an update to the uTasker handling which supports also the UC3C and saves a few bytes in the group look-up table.

Regards

Mark

static const unsigned char ucGroupLocation[] = {18, 21, 25, 
}; // location of peripheral group handler jump in the exception table

#define EXCEPTION_VECTOR_BASE_ADDRESS   START_OF_SRAM
#define LOAD_PC_WITH_NEXT_VALUE          0x481fd703   // LDDPC relative plus NOP

// Function used to enter interrupts
//
static void fnEnterAVRInterrupt(int iIntGroup, unsigned char ucIntLevel, void (*InterruptFunc)(void))
{
    unsigned long *ptrEventTable = (unsigned long *)EXCEPTION_VECTOR_BASE_ADDRESS;
    unsigned long *ptrIntPriority = (unsigned long *)INTC_BLOCK;
    unsigned long ulGroupLocation;
    ptrIntPriority += iIntGroup;
    if (iIntGroup >= 3) {
        ulGroupLocation = (29 + ((iIntGroup - 3) * 2));
    #if defined _AT32UC3C
        if (ulGroupLocation > 61) {
            ulGroupLocation += 2;
        }
    #endif
    }
    else {
        ulGroupLocation = ucGroupLocation[iIntGroup];
    }
    ptrEventTable += ulGroupLocation;
    *ptrEventTable++ = LOAD_PC_WITH_NEXT_VALUE;
    *ptrEventTable = (unsigned long)InterruptFunc;
    *ptrIntPriority = (((unsigned long)ucIntLevel << 24) | ((ulGroupLocation) << 2));
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fuseburner wrote:
So, when using the ASF, the EVBA table is always jumping to the general interrupt handler code which decides, which specific interrupt handler has to be used?
Yes, exactly.

the UC3 interrupt module knows which (prioritised) group has an interrupt pending, discards that information and then goes to fetch the correct group and correct line by software.

It’s probably a workaround for dynamically installing handlers for each interrupt line, but it sounds quite inefficient, especially for interrupt groups that have only one line.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

digitalDan wrote:

and be sure to remove the asm("rete"); you show above.

Removing the rete command did the trick. Interrupt is working perfectly now.

Thanks!!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If I understand correctly, every interrupt group is assigned to an interrupt level (0-3) and has an autovector offset to EVBA base. So, I can only write interrupt handlers for a group which has, in the case that one group servers more than one interrupt line, to distinguish which line triggered the interrupt.

But, what I don't understand is, that there's an IRR for each group where I can see, which line triggered within this group and there's an ICR for each level where I can see, which group has a pending interrupt in this level. Also, in the datasheet for the UC3B there are only the 4 levels covered by the autovector?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fuseburner wrote:
If I understand correctly, every interrupt group is assigned to an interrupt level (0-3) and has an autovector offset to EVBA base. So, I can only write interrupt handlers for a group which has, in the case that one group servers more than one interrupt line, to distinguish which line triggered the interrupt.
That is correct by my understanding.
Fuseburner wrote:
But, what I don't understand is, that there's an IRR for each group where I can see, which line triggered within this group and there's an ICR for each level where I can see, which group has a pending interrupt in this level.
The handler that you registered for one group can look at the IRR to figure out which line caused the interrupt for this group. If a group has only one line, this step is unnecessary and the ISR can immediately look at what status inside the corresponding module caused the interrupt.
You don’t need the ICR if you place your own handlers at near the EVBA and tell the MCU to jump there. It picks the highest-priority pending interrupt by itself. You only need the ICR if you have handlers like the ones from the ASF that discard the interrupt group information.
Fuseburner wrote:
Also, in the datasheet for the UC3B there are only the 4 levels covered by the autovector?
The interrupt handling of the UC3B is quite different from the UC3A because it’s geared at low-latency applications. Can’t help you there.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

catweax wrote:
The interrupt handling of the UC3B is quite different from the UC3A because it’s geared at low-latency applications. Can’t help you there.

Really? In which way? I cannot find differences, except the UC3A has more interrupt groups for more devices e.g. the FREQM

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fuseburner wrote:
Really? In which way? I cannot find differences, except the UC3A has more interrupt groups for more devices e.g. the FREQM
Haven’t looked at it in detail, but the UC3B has a separate stack just for interrupts/exceptions. On the UC3A interrupts and exceptions are executed on the main stack.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

catweax wrote:
Haven’t looked at it in detail, but the UC3B has a separate stack just for interrupts/exceptions. On the UC3A interrupts and exceptions are executed on the main stack.

Mhm, I think for a dumb C programming hobbyist like me not really important. At least I hope so :lol:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

catweax wrote:
\The interrupt handling of the UC3B is quite different from the UC3A because it’s geared at low-latency applications. Can’t help you there.
This is surprising for me because I'm using a UC3B and I've found that it takes over 130 clock cycles to execute _get_interrupt_handler(). This is using the approach to interrupt handling given by the ASF example projects.

130 clock cycles is insane. That's not even an interrupt. Is that at all normal for the UC3 devices?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That’s normal for the interrupt handling as done by the ASF. It’s kinda slow. See discussion above.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow, well I need that number much lower.... like preferable below 30.

I'm looking at the code mjbcswitzerland directed to, it looks solid but I can't get it to compile correctly. Here's what it looks like:

// Event table which is put to SRAM to handle all exceptions and interrupts
//
typedef struct stEXCEPTION_TABLE
{
    unsigned long evUnrecoverableException;
    unsigned long evTLBmultipleHit;
    unsigned long evBusErrorDataFetch;
    unsigned long evBusErrorInstructionFetch;
    unsigned long evNonMaskableInterrupt;
    unsigned long evMissingAddress;
    unsigned long evITLBProtection;
    unsigned long evBreakPoint;
    unsigned long evIllegalOpcode;
    unsigned long evUnimplementedInstruction;
    unsigned long evPrivilegeViolation;
    unsigned long evFloatingPoint;
    unsigned long evCoprocessorAbsent;
    unsigned long evDataAddressRead;
    unsigned long evDataAddressWrite;
    unsigned long evDTLBProtectionRead;
    unsigned long evDTLBProtectionWrite;
    unsigned long evDTLBModified;
    unsigned long evGroup0;                                              // 0x48
    unsigned long evAdd0;                                                // 0x4c
    unsigned long evITLBMiss;
    unsigned long evGroup1;                                              // 0x54
    unsigned long evAdd1;                                                // 0x58
    unsigned long evRes1;                                                // 0x5c
    unsigned long evITLBMissRead;
    unsigned long evGroup2;                                              // 0x64
    unsigned long evAdd2;                                                // 0x68
    unsigned long evRes2;                                                // 0x6c
    unsigned long evITLBMissWrite;
    unsigned long evGroup3;                                              // 0x74
    unsigned long evAdd3;                                                // 0x78
    unsigned long evGroup4;                                              // 0x7c
    unsigned long evAdd4;                                                // 0x80
    unsigned long evGroup5;                                              // 0x84
    unsigned long evAdd5;                                                // 0x88
    unsigned long evGroup6;                                              // 0x8c
    unsigned long evAdd6;                                                // 0x90
    unsigned long evGroup7;                                              // 0x94
    unsigned long evAdd7;                                                // 0x98
    unsigned long evGroup8;                                              // 0x9c
    unsigned long evAdd8;                                                // 0xa0
    unsigned long evGroup9;                                              // 0xa4
    unsigned long evAdd9;                                                // 0xa8
    unsigned long evGroup10;                                             // 0xac
    unsigned long evAdd10;                                               // 0xb0
    unsigned long evGroup11;                                             // 0xb4
    unsigned long evAdd11;                                               // 0xb8
    unsigned long evGroup12;                                             // 0xbc
    unsigned long evAdd12;                                               // 0xc0
    unsigned long evGroup13;                                             // 0xc4
    unsigned long evAdd13;                                               // 0xc8
    unsigned long evGroup14;                                             // 0xcc
    unsigned long evAdd14;                                               // 0xd0
    unsigned long evGroup15;                                             // 0xd4
    unsigned long evAdd15;                                               // 0xd8
    unsigned long evGroup16;                                             // 0xdc
    unsigned long evAdd16;                                               // 0xe0
    unsigned long evGroup17;                                             // 0xe4
    unsigned long evAdd17;                                               // 0xe8
    unsigned long evGroup18;                                             // 0xec
    unsigned long evAdd18;                                               // 0xf0
    unsigned long evGroup19;                                             // 0xf4
    unsigned long evAdd19;                                               // 0xf8
    unsigned long evRes3;                                                // 0xfc
    unsigned long evSupervisorCall;                                      // 0x100
} EXCEPTION_TABLE;


#define BRANCH_TO_SELF                   0xe08f0000                      // AVR32 machine code to create a forever loop
#define LOAD_PC_WITH_NEXT_VALUE          0x481fd703                      // LDDPC relative plus NOP

#define EXCEPTION_VECTOR_BASE_ADDRESS 0x00000000


// Perform very low level AVR32 initialisation - called by the start up code
//
static void AVR32_LowLevelInit(void)
{
    EXCEPTION_TABLE *ptrEventTable = (EXCEPTION_TABLE *)EXCEPTION_VECTOR_BASE_ADDRESS; // place an event table at the start of RAM
    unsigned long *ulPtrEntries = &ptrEventTable->evUnrecoverableException;
    int i = 0;
    while (i++ < (sizeof(EXCEPTION_TABLE)/sizeof(unsigned long))) {
        *ulPtrEntries++ = BRANCH_TO_SELF;                                // fill the event table with forever loops to catch unexpected exceptions
    }
    __set_EVBA(EXCEPTION_VECTOR_BASE_ADDRESS);                           // set EVBA to the start of SRAM
}


static const unsigned char ucGroupLocation[] = {18, 21, 25, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61};

// Function used to enter interrupts
//
static void fnEnterAVRInterrupt(int iIntGroup, unsigned long ulIntLevel, void (__interrupt *InterruptFunc)(void))
{
    unsigned long *ptrEventTable  = (unsigned long *)EXCEPTION_VECTOR_BASE_ADDRESS;
    unsigned long *ptrIntPriority = (unsigned long *)INTC_BLOCK;
    ptrIntPriority += iIntGroup;
    ptrEventTable += ucGroupLocation[iIntGroup];
    *ptrEventTable++ = LOAD_PC_WITH_NEXT_VALUE;
    *ptrEventTable = (unsigned long)InterruptFunc;
    *ptrIntPriority = (ulIntLevel | (((unsigned long)ucGroupLocation[iIntGroup]) << 2));
}

AVR studio 5 is complaining that INT_BLOCK and InterruptFunc are undeclared, and that __set_EVBA is an implicit declaration of a function... also __interrupt is undefined which causes a couple other errors.

Is there a header file somewhere with these definitions? Has anyone else gotten this method working?

edit: oh and is this code even meant to be usable on UC3B devices, or just on UC3A?

edit2: and where in the interrupt registering routine is the IPR register written? Unless INTC_BLOCK is a pointer to the INTC registers? Is that it??

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi

INTC_BLOCK is the address of the interrupt controller interface in the particular processor used. For the UC3A and UC3B it is

  #define INTC_BLOCK                     0xffff0800                      // Interrupt controller interface

When using GCC
__interrupt is defined as
#define __interrupt __attribute__((__interrupt__))
but (unfortunately) GCC doesn't accept this (although IAR does) and so it has been removed

static void fnEnterAVRInterrupt(int iIntGroup, unsigned char ucIntLevel, void (*InterruptFunc)(void)) {}

(the forum reference is quite old and shows the code used by only IAR at the time; the project now supports IAR and GCC - including AVR32 Studio 5). The down-side is that the caller of function fnEnterAVRInterrupt() needs to cast the pointer to the interrupt handler as shown in the following example (which it didn't have to do with IAR, whereby __interrupt is a keyword in the IAR environment):

fnEnterAVRInterrupt(IR_GROUP_SYSBLOCK, PRIORITY_TICK_TIMER, (void (*)(void))_RealTimeInterrupt);

__set_EVBA() is a routine usually supplied in an assembler file in the project

__set_evba:                      ; write the passed value to EVBA register
        mtsr    4,r12
        ret 0

and so needs a prototype to stop there being warnings when it is called:

extern void __set_evba(unsigned long ulEVBA);

The code snippet is from the uTasker project which includes all project code for IAR and GCC (Studio 5) as well as an AVR32 simulator. It works on UC3A, UC3B and UC3C devices (UC3C has a slight extension since it has more interrupts needing to be supported). UC3A and UC3B are compatible in this area.

The IPR entry is written with
*ptrIntPriority = (ulIntLevel | (((unsigned long)ucGroupLocation[iIntGroup]) << 2));

The offset of the particular priority entry is calculated from the start of the block using ptrIntPriority += iIntGroup;

Regards

Mark

P.S. The latest project versions can be obtained here:
http://www.utasker.com/forum/ind...

Registration at http://www.utasker.com/Licensing...
The project and support is free for non-commerical users and the project is also available on ATMEL SAM7 and SAM3 processors (SAM3 version will be released shortly)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow, thanks for the comments. I have a feeling this is exactly what I need.

A few questions/comments:

mjbcswitzerland wrote:
Hi

INTC_BLOCK is the address of the interrupt controller interface in the particular processor used. For the UC3A and UC3B it is

  #define INTC_BLOCK                     0xffff0800                      // Interrupt controller interface

Yeah, that's what I figured, makes sense.
Quote:

When using GCC
__interrupt is defined as
#define __interrupt __attribute__((__interrupt__))
but (unfortunately) GCC doesn't accept this (although IAR does) and so it has been removed

static void fnEnterAVRInterrupt(int iIntGroup, unsigned char ucIntLevel, void (*InterruptFunc)(void)) {}

Okay my C is still pretty weak, especially when it comes to things like casting pointers, so I want to clarify things.

Ultimately when registering/entering an interrupt into the table, you just want to pass the address of your interrupt service routine into the exception table (like for interrupt group 0 you would set evAdd0 to the address of your routine), right? But this code:

void (*InterruptFunc)(void)

Seems to be casting some (void) into a pointer to type InterruptFunc. Does there have to be a declaration somewhere else for the type interruptfunc?

Quote:

(the forum reference is quite old and shows the code used by only IAR at the time; the project now supports IAR and GCC - including AVR32 Studio 5). The down-side is that the caller of function fnEnterAVRInterrupt() needs to cast the pointer to the interrupt handler as shown in the following example (which it didn't have to do with IAR, whereby __interrupt is a keyword in the IAR environment):

fnEnterAVRInterrupt(IR_GROUP_SYSBLOCK, PRIORITY_TICK_TIMER, (void (*)(void))_RealTimeInterrupt);

Again, this is throwing me off. Wouldn't it be sufficient to just pass fnEnterAVRInterrupt the address of the ISR like so?:

fnEnterAVRInterrupt(IR_GROUP_SYSBLOCK, PRIORITY_TICK_TIMER, (long)&_RealTimeInterrupt);

And then have the entry function be like this?:

static void fnEnterAVRInterrupt(int iIntGroup, unsigned long ulIntLevel, long ISR_address)
{
    unsigned long *ptrEventTable  = (unsigned long *)EXCEPTION_VECTOR_BASE_ADDRESS;
    unsigned long *ptrIntPriority = (unsigned long *)INTC_BLOCK;
    ptrIntPriority += iIntGroup;
    ptrEventTable += ucGroupLocation[iIntGroup];
    *ptrEventTable++ = LOAD_PC_WITH_NEXT_VALUE;
    *ptrEventTable = ISR_address;
    *ptrIntPriority = (ulIntLevel | (((unsigned long)ucGroupLocation[iIntGroup]) << 2));
}  

Seems simple and equivalent to me, but I'm probably wrong.

Quote:

__set_EVBA() is a routine usually supplied in an assembler file in the project

__set_evba:                      ; write the passed value to EVBA register
        mtsr    4,r12
        ret 0

and so needs a prototype to stop there being warnings when it is called:

extern void __set_evba(unsigned long ulEVBA);

I couldn't find this in my project. In fact I searched through everything, including exception.S, trying to find where the value of EVBA is actually determined, and couldn't find it. The only way ASF seemed to provide to write EVBA was some init_EVBA routine which isn't passed a value, but sets EVBA to some value I can't determine...
Quote:

The IPR entry is written with
*ptrIntPriority = (ulIntLevel | (((unsigned long)ucGroupLocation[iIntGroup]) << 2));

The offset of the particular priority entry is calculated from the start of the block using ptrIntPriority += iIntGroup;

Okay the addressing of IPR is clear now. But the way the actual data is written seems wrong, at least for UC3 devices. The int level field is 31:30 and the autovector field is 13:0, so shouldn't it be:

*ptrIntPriority = (ulIntLevel << 30 | ((unsigned long)ucGroupLocation[iIntGroup]));

Quote:

P.S. The latest project versions can be obtained here:
http://www.utasker.com/forum/ind...

Registration at http://www.utasker.com/Licensing...
The project and support is free for non-commerical users and the project is also available on ATMEL SAM7 and SAM3 processors (SAM3 version will be released shortly)

I'm really tempted, especially if you say there's a working simulator for UC3B devices, and maybe even a good USB stack... I'll give it a close look though.

Many thanks,
-Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Mike

There are several ways to solve the casting complication. The register simply needs a 32 bit address value which is physically an unsigned long but could also be a pointer in C, and is in fact a pointer to an interrupt handler in the actual use case. Interrupt handlers are always "void isr(void)" and so this is the way that prototype is set up. Somewhere on the way it is then casted to a simple unsigned long type for the actual register write.

The original method (developed using IAR) was the best since it allowed the compiler to generate warnings if anything was being attempted that was not right. Due to the fact that GCC doesn't allow it the final solution is not optimal (in terms of compiler checking) but, as you have already pointed out, and seeing as though the checking is being overridden by castings anyway, there are various possible method of 'doing it not quite right'. However the one chosen is probably as good as any others and results in the correct operation.

Probably the framework has as __set_EVB() call but it may not be identical. The one in the uTasker project allows the address to be passed - maybe the framework one sets a fixed value?

There are 4 interrupt levels supported by the interrupt controller. When calling the ISR entry function the user uses one of the defines:

  #define INT_LEVEL_0                    0x00
  #define INT_LEVEL_1                    0x40
  #define INT_LEVEL_2                    0x80
  #define INT_LEVEL_3                    0xc0

which explains why the shift doesn't match with what you may be expecting...

The (ucGroupLocation[iIntGroup] << 2) is to convert offsets to address offsets (x4).

Regards

Mark

PS. The utasker project for AVR32 doesn't support USB at the moment but this will be added shortly. In fact I just invested in a new Beagle USB 480 (to go with my the Ellisys USB Explorer) to accelarate this (the SAM7 project - and some others - do already support USB CDC and USB MSD - there is a demo of a USB MSD boot loader here http://www.youtube.com/watch?v=H...
(and http://www.youtube.com/watch?v=e...). However there is nothing stopping the framework driver being used together with the project in the meantime.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mjbcswitzerland wrote:

There are several ways to solve the casting complication. The register simply needs a 32 bit address value which is physically an unsigned long but could also be a pointer in C, and is in fact a pointer to an interrupt handler in the actual use case. Interrupt handlers are always "void isr(void)" and so this is the way that prototype is set up. Somewhere on the way it is then casted to a simple unsigned long type for the actual register write.

The original method (developed using IAR) was the best since it allowed the compiler to generate warnings if anything was being attempted that was not right. Due to the fact that GCC doesn't allow it the final solution is not optimal (in terms of compiler checking) but, as you have already pointed out, and seeing as though the checking is being overridden by castings anyway, there are various possible method of 'doing it not quite right'. However the one chosen is probably as good as any others and results in the correct operation.

I didn't really have compiler checking in mind when I brought up the casting... for me compiler warnings would be icing on the cake at the moment... but would the method I described work? I just want to make sure I understand what's going on correctly.
Quote:

Probably the framework has as __set_EVB() call but it may not be identical. The one in the uTasker project allows the address to be passed - maybe the framework one sets a fixed value?
While toying with the intc.c stuff from ASF, I noticed that changing the code would often change what EVBA was set to (I just checked it with the debugger) slightly. I am unable to find how this is determined... but hopefully I won't need to worry about since I'll be ditching intc.c.
Quote:

There are 4 interrupt levels supported by the interrupt controller. When calling the ISR entry function the user uses one of the defines:

  #define INT_LEVEL_0                    0x00
  #define INT_LEVEL_1                    0x40
  #define INT_LEVEL_2                    0x80
  #define INT_LEVEL_3                    0xc0

Wait, you mean this, right?:

  #define INT_LEVEL_0                    0x00000000
  #define INT_LEVEL_1                    0x40000000
  #define INT_LEVEL_2                    0x80000000
  #define INT_LEVEL_3                    0xc0000000

That would align the INTLEVEL field correctly so they can be simply OR'd into IPR.

edit: oh okay your definitions would work in the second version of the code you posted above, which shifts them left by 24 bits before ORing them into IPR. Got it.

Quote:

The (ucGroupLocation[iIntGroup] << 2) is to convert offsets to address offsets (x4).
You'll have to clarify this one for me. Why do we need to shift address offsets left by two bits? Wouldn't that mess up the autovector values and cause them not to point to the correct exception table addresses?

Thanks,
-Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Mike

You could also pass the interrupt routine as
fnEnterAVRInterrupt(IR_GROUP_SYSBLOCK, PRIORITY_TICK_TIMER, (long)&_RealTimeInterrupt);

but then you would need to cast the parameter equally at all of the calls to the routine. It wouldn't make any real difference they will both do the same.
I prefer using void (*InterruptFunc)(void) since that is in fact what is 'really' being passed.

Also I would avoid using long in preference to unsigned long since long is usually a signed long and, since the AVR32 has its FLASH at 0x80000000 it would mean that all interrupt in Flash would be understood as negative values. It probably won't make any difference in the routine being discussed since it is forced to cast to (unsigned long) anyway but unsigned long is certainly more logical and gerenally safer.

The reason why the interrupt levels were defined as they are is so that they can be saved as bytes rather than as long words and then passed like that

static void fnEnterAVRInterrupt(int iIntGroup, unsigned char ucIntLevel, void (*InterruptFunc)(void))

Otherwise they would have had to be stored as 32 bit works and also passed like that. It is questionable whether there is an advantage at the routine itself but the interrupt priority levels are sometimes store in parameter flash and so it may be slightly more efficient in the long run.

The interrupts are divided into groups. Each group has an interrupt vector entry. Each interrupt vector entry is aligned on a long work boundary (eg. 0x00000000, 0x00000004, 0x00000008 etc.). Therefore the offset of group 0 is 0, the offset of group 1 is 4, the offset of group 2 is 8 etc. That is, (group * 4).
In the code the (ucGroupLocation[iIntGroup] << 2) is simply doing this.

It would also have been possible to save the ucGroupLocation[] directly with the x4 values but then the array would have needed to be of 16 bit elements rather than 8 bit.

Since interrupts tend to be entered only once during initialisation the method is geared for space optimisation rather than speed optimisation.

Regards

Mark

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One question:

Since the Exception table is being initialized by hand using a pointer, how do you prevent the linker from placing other variables somewhere in the 260 bytes space at the start of the SRAM?

Daniel Campora http://www.wipy.io

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK no need to answer... I just checked the uTasker files, and the linker script is correctly configured so the SRAM space starts after those 260 bytes. Thanks for e-mailing the password Mark.

Daniel Campora http://www.wipy.io

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Daniel

One can declare some variable space like
volatile unsigned char vectors[260]={0};
and then force this to be linked at address 0x00000000 so that space is reserved for the exception table at the start of SRAM.

However I tend to simply daclare the RAM to start at 0x104 (assuming no more interrupt exception space is needed - eg. the UC3C needs 476 bytes if all of its interrupst are used)

This is simpy done in the linker script:

RAM_START   = 0x00000104; /* reserve space for interrupt vectors */
RAM_SIZE    = 64*1024 - 0x104 - 4;
FLASH_START = 0x80000000;
FLASH_SIZE  = 512*1024;

MEMORY
{
    SRAM (wx)  : ORIGIN = 0x00000104, LENGTH = 0x00010000 - 0x104 - 4
    FLASH (rx) : ORIGIN = 0x80000000, LENGTH = 0x00080000
}


SECTIONS
{
    __SRAM_segment_start__  = RAM_START;
    __SRAM_segment_end__    = RAM_START + RAM_SIZE;
...

SRAM (wx) is in fact not important. The linker will start locating variables after 0x00000104 and so leave the exception area free. As far as the linker is concerned there is no memory between 0x00000000 and 0x00000104 but that doesn't stop program code from using it.

Regards

Mark

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay, I think I've got it working! Here's the gist of my code:

//#define AVR32_EVBA                                     0x00000004
typedef struct stEXCEPTION_TABLE
{
    unsigned long evUnrecoverableException;
    unsigned long evTLBmultipleHit;
    unsigned long evBusErrorDataFetch;
    unsigned long evBusErrorInstructionFetch;
    unsigned long evNonMaskableInterrupt;
    unsigned long evMissingAddress;
    unsigned long evITLBProtection;
    unsigned long evBreakPoint;
    unsigned long evIllegalOpcode;
    unsigned long evUnimplementedInstruction;
    unsigned long evPrivilegeViolation;
    unsigned long evFloatingPoint;
    unsigned long evCoprocessorAbsent;
    unsigned long evDataAddressRead;
    unsigned long evDataAddressWrite;
    unsigned long evDTLBProtectionRead;
    unsigned long evDTLBProtectionWrite;
    unsigned long evDTLBModified;
    unsigned long evGroup0;                                              // 0x48
    unsigned long evAdd0;                                                // 0x4c
    unsigned long evITLBMiss;
    unsigned long evGroup1;                                              // 0x54
    unsigned long evAdd1;                                                // 0x58
    unsigned long evRes1;                                                // 0x5c
    unsigned long evITLBMissRead;
    unsigned long evGroup2;                                              // 0x64
    unsigned long evAdd2;                                                // 0x68
    unsigned long evRes2;                                                // 0x6c
    unsigned long evITLBMissWrite;
    unsigned long evGroup3;                                              // 0x74
    unsigned long evAdd3;                                                // 0x78
    unsigned long evGroup4;                                              // 0x7c
    unsigned long evAdd4;                                                // 0x80
    unsigned long evGroup5;                                              // 0x84
    unsigned long evAdd5;                                                // 0x88
    unsigned long evGroup6;                                              // 0x8c
    unsigned long evAdd6;                                                // 0x90
    unsigned long evGroup7;                                              // 0x94
    unsigned long evAdd7;                                                // 0x98
    unsigned long evGroup8;                                              // 0x9c
    unsigned long evAdd8;                                                // 0xa0
    unsigned long evGroup9;                                              // 0xa4
    unsigned long evAdd9;                                                // 0xa8
    unsigned long evGroup10;                                             // 0xac
    unsigned long evAdd10;                                               // 0xb0
    unsigned long evGroup11;                                             // 0xb4
    unsigned long evAdd11;                                               // 0xb8
    unsigned long evGroup12;                                             // 0xbc
    unsigned long evAdd12;                                               // 0xc0
    unsigned long evGroup13;                                             // 0xc4
    unsigned long evAdd13;                                               // 0xc8
    unsigned long evGroup14;                                             // 0xcc
    unsigned long evAdd14;                                               // 0xd0
    unsigned long evGroup15;                                             // 0xd4
    unsigned long evAdd15;                                               // 0xd8
    unsigned long evGroup16;                                             // 0xdc
    unsigned long evAdd16;                                               // 0xe0
    unsigned long evGroup17;                                             // 0xe4
    unsigned long evAdd17;                                               // 0xe8
    unsigned long evGroup18;                                             // 0xec
    unsigned long evAdd18;                                               // 0xf0
    unsigned long evGroup19;                                             // 0xf4
    unsigned long evAdd19;                                               // 0xf8
    unsigned long evRes3;                                                // 0xfc
    unsigned long evSupervisorCall;                                      // 0x100
} EXCEPTION_TABLE;


#define BRANCH_TO_SELF                   0xe08f0000                      // AVR32 machine code to create a forever loop
#define LOAD_PC_WITH_NEXT_VALUE          0x481fd703                      // LDDPC relative plus NOP

#define EXCEPTION_VECTOR_BASE_ADDRESS 0x00000000

#define INT_LEVEL_0                    0x00
#define INT_LEVEL_1                    0x40
#define INT_LEVEL_2                    0x80
#define INT_LEVEL_3                    0xc0

extern void _evba;

// Perform very low level AVR32 initialisation - called by the start up code
//
static void AVR32_LowLevelInit(void)
{
    EXCEPTION_TABLE *ptrEventTable = (EXCEPTION_TABLE *)EXCEPTION_VECTOR_BASE_ADDRESS; // place an event table at the start of RAM
    unsigned long *ulPtrEntries = &ptrEventTable->evUnrecoverableException;
    int i = 0;
    while (i++ < (sizeof(EXCEPTION_TABLE)/sizeof(unsigned long))) {
        *ulPtrEntries++ = BRANCH_TO_SELF;                                // fill the event table with forever loops to catch unexpected exceptions
    }
    //__set_EVBA(EXCEPTION_VECTOR_BASE_ADDRESS);                           // set EVBA to the start of SRAM
	Set_system_register(AVR32_EVBA, EXCEPTION_VECTOR_BASE_ADDRESS);
}


static const unsigned char ucGroupLocation[] = {18, 21, 25, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61};

// Function used to enter interrupts
//
static void fnEnterAVRInterrupt(int iIntGroup, unsigned char ucIntLevel, void (*InterruptFunc)(void))
{
    unsigned long *ptrEventTable = (unsigned long *)EXCEPTION_VECTOR_BASE_ADDRESS;
    unsigned long *ptrIntPriority = (unsigned long *)AVR32_INTC_ADDRESS;
    unsigned long ulGroupLocation;
    ptrIntPriority += iIntGroup;
    if (iIntGroup >= 3) {
        ulGroupLocation = (29 + ((iIntGroup - 3) * 2));
    }
    else {
        ulGroupLocation = ucGroupLocation[iIntGroup];
    }
    ptrEventTable += ulGroupLocation;
    *ptrEventTable++ = LOAD_PC_WITH_NEXT_VALUE;
    *ptrEventTable = (unsigned long)InterruptFunc;
    *ptrIntPriority = (((unsigned long)ucIntLevel << 24) | ((ulGroupLocation) << 2));
} 

__attribute__((__interrupt__)) void tc_irq(void)
{
	tc_read_sr(MY_TC, MY_TC_CHANNEL);
	update_timer = true;
	gpio_local_tgl_gpio_pin(ADC_CNVST_pin);
}

int main (void)
{
	board_init();
	AVR32_LowLevelInit();
   fnEnterAVRInterrupt((int)(AVR32_TC_IRQ0 / AVR32_INTC_MAX_NUM_IRQS_PER_GRP), INT_LEVEL_0,(void (*)(void))tc_irq);
   tc_init(MY_TC);
   cpu_irq_enable();
	usart_write_line(MY_USART, "Global interrupt enabled \r\n");
	tc_start(MY_TC, MY_TC_CHANNEL);
while(1);
}

Obviously I've mashed the two versions of the code from mark together, and added a couple things of my own... but it seems to work!

My timer is set to be clocked at fcpu/2. If I step through the assembly in the debugger, then I see that when code points to 0x000000CC (the entry in the exception table for my TC interrupt group), the timer value is about 11. Then from there to the "pushm LR" instruction right before my ISR, it reaches 22. Actual execution of the ISR begins around 25. So all in all I have about 50 clock cycles of latency, which is already a huge improvement over ASF.

Still I wonder, why so many clock cycles for those two jumps? Could it be that the debugger operation adds a few clock cycles onto each, making it seem like it will take longer than it would if it were running with no debugger?

danicampora wrote:
One question:

Since the Exception table is being initialized by hand using a pointer, how do you prevent the linker from placing other variables somewhere in the 260 bytes space at the start of the SRAM?

I can confirm that when using that code, there does seem to be a hazard with memory overwrite. When the program starts, before the exception table initialization, there is some data in that space, which is overwritten. No idea what it is though. Keep in mind I'm not actually using uTasker, so this is only the case with me.

What is a simple way to prevent the compiler from trying to store other data there? Should I declare a static vector in that space first, and then do the initialization?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

What is a simple way to prevent the compiler from trying to store other data there? Should I declare a static vector in that space first, and then do the initialization?

Just change the SRAM definition in the linker script. Check the uTasker linker scripts for hints... that way the first 260 bytes of RAM will be invisible to the Linker.

Also, I think the exception vector initialization should be done inside _init_startup().

Daniel Campora http://www.wipy.io

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have my interrupt working using most of mjbcswitzerland's code.

I'm running into a couple problems with regard to the interrupt. There is only one interrupt enabled.

1) Occasionally one specific function returns the wrong data. I found that if I disable the interrupt before entering this function the data error goes away. When the interrupt is always enabled, this problem occurs about 0.5% of the time.

2) I'm using ELM FatFS code to communicate with an SD card. If I have the interrupt enabled, the data block reads to the SD card are unreliable. If I disable the interrupt before doing the data block read, there are no problems with the card reads. The thing is that it takes quite a while to perform the card read and I need to service the interrupt while it is going on.

I don't know if this is a stack issue, a problem of SRAM data being overwritten by some other instruction or a different problem. Any help is greatly appreciated!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi

Interrupt releated problems can be quite difficult to pin-point so I don't think that there is any standard recipe to solve it without working it through with the debugger (somehow).

However note that the uTasker project also includes FAT (FAT16 and FAT32 with LFN support) for SD cards and the AVR32 devices. I believe that it has more features than the FatFS so you may like to compare that to see whether it gives you the same problems. I haven't heard of such difficulties and it allows you (assuming you are using an AVR32 with Ethernet) to run large web server content from an SD card and exchange data via FTP (plus a DOS like command line interface for the file system via UART or TELNET). Its user's manual is here: http://www.utasker.com/docs/uTas...

Regards

Mark

P.S. It also supports NAND Flash based systems (like the ATMEL SAM7SE with NAND controller) including the layer for block and level wear management - http://www.utasker.com/docs/uTas...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

danicampora wrote:

Just change the SRAM definition in the linker script. Check the uTasker linker scripts for hints... that way the first 260 bytes of RAM will be invisible to the Linker.

Also, I think the exception vector initialization should be done inside _init_startup().

Could you give me some guidance with modifying the linker scripts? Am I supposed to manually edit the linker file (in my case I think it's link_uc3b0256.lds)?

In that file I see the following:

MEMORY
{
  FLASH (rxai!w) : ORIGIN = 0x80000000, LENGTH = 0x00040000
  INTRAM (wxa!ri) : ORIGIN = 0x00000004, LENGTH = 0x00007FFC
  USERPAGE : ORIGIN = 0x80800000, LENGTH = 0x00000200
}

So is it just a matter of changing the ORIGIN address of INTRAM? Or is there something else to it?

And to add to the discussion, I have also experienced some strange issues since I've started using this method... mainly that sometimes my debugger will start losing its mind at times. Like I will be stepping through disassembly, then I will get a message saying 'cannot show disassembly in run mode,' even though I never hit the run button. And from there I cannot recover; I have to stop the debugger completely. When doing this I will usually see the debugger complain of all sorts of issues with exiting debug mode, such as an inability to read the OCD register, inability to determine if the CPU is in debug mode....

I don't know if this is directly related to the new interrupt code (it's happened before), but this has begun happening much more often since making the change.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just change the memory definition to this:

MEMORY
{
FLASH (rxai!w) : ORIGIN = 0x80000000, LENGTH = 0x00040000
INTRAM (wxa!ri) : ORIGIN = 0x00000104, LENGTH = 0x7EFC
USERPAGE : ORIGIN = 0x80800000, LENGTH = 0x00000200
}

Maybe after fixing the linker script your problems will dissaper...

Daniel Campora http://www.wipy.io

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So... are the strange issues still present after modifying the linker script?

Daniel Campora http://www.wipy.io

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

danicampora wrote:
So... are the strange issues still present after modifying the linker script?
I haven't had the opportunity to try it yet, and might not for a week or two... holiday break and all that. I will follow up on this though, thanks.

In related news, I submitted a support request to atmel about the interrupt latency, and they suggested trying IAR as well, but didn't really mention what kind of latency I could expect from that. Does anyone know how easy it would be to integrate an ISR driver into a project already made with ASF?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is the extension on the linker script file? Where is the GCC linker script located?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

cookiegalore wrote:
What is the extension on the linker script file? Where is the GCC linker script located?
src/SOFTWARE_FRAMEWORK/UTILS/LINKER_SCRIPTS/[your MCU series]/[Flash size]/GCC/link_[something].lds

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay so I tried to edit the linker script file for my project, but it doesn't even seem to exist. The utils directory lacks the linker_scripts folder (as well as the debug folder which should be there). I haven no idea what my project is using as a linker script...

This is a project I created from the "user board" option given by AVR studio 5. I tried creating another project, and it also does so without including the linker_scripts or debug folders. However, if I create a project using the "atmel board" option, the folders will appear.

What the hell atmel? How do I configure, or even access, linker scripts for user board projects?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi

I suppose that there must be a linker script hidden somewhere (or else it is cryptically embedded in the project configuration somewhere).

I would take a look in the project properties -> Toolchain -> AVR32/GNU C Linker -> you may find the path to and name of the linker script being used is there somewhere.

Otherwise you can also use an external make file (under the build options) which declares its own linker script for use.

The uTasker project uses the external make file since it ensures that there is full control over what is going on and avoids any quirkyness of the environment which otherwise thinks that it has "everything under control for you and you shouldn't be poking your nose around anyway...".

The IDE manufacturers tend to be a bit like modern automobile manufacturers. Everything is being more and more fully automated and the user is expected to understand less and less of the internal workings. This is great while everything works as expected but can be a disaster as soon as something minor develops - you can hardly see the engine and you need a 100k comuterized analysis system connected to the board computer to be able to find out any details of the problem. But great for garagists who can charge a days work for the replacement of an expensive computer board when there was only really a screw loose somewhere, but the user can't really knew any better.

Using an external make file gets back to basics and allows full control without the down-side of 'user friendly' black-boxs which work most of the time but when they don't you are rather stuck...

Regards

Mark

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah I looked in the toolchain linker menu. For the atmel board project, I see this under "all options":

-nostartfiles -Wl,--gc-sections -T../src/asf/avr32/utils/linker_scripts/at32uc3b/0256/gcc/link_uc3b0256.lds -Wl,--relax -Wl,-e,_trampoline  -mpart=uc3b0256 

And under the "miscellaneous" category I see this in "linker flags":

-T../src/asf/avr32/utils/linker_scripts/at32uc3b/0256/gcc/link_uc3b0256.lds -Wl,--relax -Wl,-e,_trampoline

.

The user board project, on the other hand, has this as "all options":

-nostartfiles -Wl,--gc-sections -Wl,--relax -Wl,-e,_trampoline  -mpart=uc3b0256 

and this for linker flags:

-Wl,--relax -Wl,-e,_trampoline

That doesn't explain where the linker script for the user board projects are, but I guess I can just copy those linker flags, along with the linker_scripts folder, from the atmel board project and it should work...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay I've copied the linker stuff as I described above, and when I start debugging the project it looks like the SRAM start address has changed as expected: at startup, no data exists before address 0x0104. Whether this actually solves the issue with the debugger screwing up, I don't know... it usually takes some fooling around for the errors to start showing up, but haven't seen any yet.

I had another question about the IAR interrupt handling. I had assumed that IAR had its own interrupt drivers which could be copied over to be used in AVR studio 5 and compiled by GCC. But some talk with an atmel rep suggests that it's the opposite... that the superior performance of IAR interrupt handling comes from the compiler and the existing ASF drivers. Is this true? I don't think I can use IAR for my project, but I just want to see how it achieves the results it does.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi

The IAR compiler will generally produce better (smaller/faster) code from C files than GCC. The IAR compiler doesn't do any interrupt handling but only compilers/assembles the code that the user uses to do it.

Generally the IAR programs are delivered with reference projects so that there are some examples that work 'out-of-the-box'. In the case of AVR32 these examples are supplied by ATMEL and are presumably the same as included in ASF (with a few adaptations so that they suit the IAR compiler/assembler). I think that the part in question is written in assembler and so IAR will not have any advantage compared to GCC since assembler is a direct representation of the machine code that will be used and will not be optimised.

Therefore, the interrupt processing will probably not be compiler dependent.

Regards

Mark

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include  ;Register names, pin names, peripherals etc.
        NAME    inth
        PUBLIC  evba
        PUBLIC  int0
        PUBLIC  int1
        PUBLIC  int2
        PUBLIC  int3
        RSEG    EVSEG:CODE:NOROOT(2)    ; Exceptions and interrupt handlers.

evba:                                   
e_uex:  rjmp    e_uex   ; Unrecoverable exception
        ORG     0x4     
e_tlb:  rjmp    e_tlb   ; TLB mult. hit
        ORG     0x8
e_bud:  rjmp    e_bud   ; Bus data
        ORG     0xC
e_bui:  rjmp    e_bui   ; Bus instruction
        ORG     0x10
e_nmi:  rjmp    e_nmi   ; NMI
        ORG     0x14
e_iad:  rjmp    e_iad   ; Instruction address
        ORG     0x18
e_itb:  rjmp    e_itb   ; ITLB protection
        ORG     0x1C
e_brp:  rjmp    e_brp   ; Breakpoint
        ORG     0x20
e_iop:  rjmp    e_iop   ; Illegal opcode
        ORG     0x24
e_uin:  rjmp    e_uin   ; Unimplemented instruction
        ORG     0x28
e_pvi:  rjmp    e_pvi   ; Privilege violation
        ORG     0x2C
e_fpo:  rjmp    e_fpo   ; Floating point (unused)
        ORG     0x30
e_coa:  rjmp    e_coa   ; Coprocessor abcent (unused)
        ORG     0x34
e_dar:  rjmp    e_dar   ; Data address (read)
        ORG     0x38
e_daw:  rjmp    e_daw   ; Data address (write)
        ORG     0x3C
e_dpr:  rjmp    e_dpr   ; DTLB protection (read)
        ORG     0x40
e_dpw:  rjmp    e_dpw   ; DTLB protection (write) 
        ORG     0x44
e_dtm:  rjmp    e_dtm   ; DTLB modified
        ORG     0x50
e_itm:  rjmp    e_itm   ; ITML miss
        ORG     0x60
e_dmr:  rjmp    e_dmr   ; DTLB miss (read)
        ORG     0x70
e_dmw:  rjmp    e_dmw   ; DTLB miss (write)
        ORG     0x100
e_sup:  rjmp    e_sup   ; Supervisor call
;Remember: only registers r8..r12 is backed up,
; so r0..r7 must be pushed if used here
 
        ALIGN   2                                  
int0:   mov     r12, LWRD(AVR32_INTC_ADDRESS)   ; INTC address.
        orh     r12, HWRD(AVR32_INTC_ADDRESS)
        ld.w    r11, r12[3*4+512]               ; Get int cause register 0 (ICR0)
        lsl     r11, 2                          ; Calculate IRR address..
        add     r12, r11
        ld.w    r12, r12[256]                   ; Get interrupt request line(s) (IRR)
		;If more than one interrupt source is registered to INT0
		;the group ID and IRR must be checked,
		; then jump to corresponding handler code.
		; If only one source is registered to each (this)
		; level, this code frame can be removed.
		; Handler here...
        rete
        
        ALIGN   2        
int1:   mov     r12, LWRD(AVR32_INTC_ADDRESS)   ; INTC address
        orh     r12, HWRD(AVR32_INTC_ADDRESS)
        ld.w    r11, r12[2*4+512]               ; Get int cause register 1 (ICR1)
        lsl     r11, 2                          ; Calculate IRR address..
        add     r12, r11
        ld.w    r12, r12[256]                   ; Get interrupt request line(s) (IRR)       
        ; Handler here...
        rete
        
        ALIGN   2
int2:   ; Handler here...
		rete
        
        ALIGN   2
int3:   ; Handler here...
		rete


        END

This is simple skeleton code I use for interrupt handlers in programs written in assembler with IAR Workbench. The "PUBLIC" keywords refer to other
source files exporting them, ie "evba" address
is exported from the "main" program.

If you have 4 or less interrupts in your system, the interrupt source check above can be removed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One question regarding the need for changing the linker scripts: Wouldn't it be possible to normally create the EVBA struct anywhere in RAM and pass its address to the EVBA base instead of creating them at a given, fixed address? Or do I miss something important?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fuseburner wrote:
One question regarding the need for changing the linker scripts: Wouldn't it be possible to normally create the EVBA struct anywhere in RAM and pass its address to the EVBA base instead of creating them at a given, fixed address?
Anywhere? No.
Fuseburner wrote:
Or do I miss something important?
Yes. :P
The lowest 14 bits of the EVBA start address must be 0 because the relative ISR addresses are ORed with that address. If you can guarantee that the EVBA struct is created at an appropriate address, you can create it “anywhere”.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

catweax wrote:

The lowest 14 bits of the EVBA start address must be 0 because the relative ISR addresses are ORed with that address. If you can guarantee that the EVBA struct is created at an appropriate address, you can create it “anywhere”.

That's true, but in my simple point of understanding, only the lower bits for storing the 104 bytes of the struct must be 0 (I think I saw something mentioned in the exception.s).

There's no compiler contruct to align such a struct to a given boundary? I'm not that C-crack but I'm looking for a solution which doesn't need the linker hinting.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
struct evba __attribute__((aligned(0x100))) {
...
};

Hans-Christian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the EVBA is defined in File ../src/asf/avr32/drivers/intc/exeption.S and looks like this:

.section  .exception, "ax", @progbits	
// Start of Exception Vector Table.
/*
 * EVBA must be aligned with a power of two strictly greater
 * than the EVBA-relative offset of the last vector.
 */
.balign 0x200

// Export symbol.
.global _evba
.type _evba, @function

_evba:
	.org  0x000
	// Unrecoverable Exception.
_handle_Unrecoverable_Exception:
	rjmp $
.
.
.
	.org  0x100
	// Supervisor Call.
_handle_Supervisor_Call:
	rjmp $

using ASF Version 2.9.0 with AT32UC3A0512

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

catweax wrote:
If you mean the interrupt service routine intry points, they must all be located in one block of 16kB and the start address of this block must be stored in the EVBA system register. This block can be wherever you want, though.
Sorry for bumping up an old post, but this "must be located in one block of 16 kB" was confusing me.

The datasheet says:

Quote:
All interrupt requests have entry points located at an offset relative to EVBA. Bla bla bla... The autovector offset has 14 address bits, giving an offset of maximum 16384 bytes.

So if I have understood correctly, the 16 kB is maximum but not the required size. It can be smaller.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kblomqvist wrote:
Sorry for bumping up an old post, but this "must be located in one block of 16 kB" was confusing me.
[...]
So if I have understood correctly, the 16 kB is maximum but not the required size. It can be smaller.
Yes, it can be smaller. If your ISRs use only 4 KiB, they’re still within a 16 KiB block, though. :wink:

kblomqvist wrote:
The datasheet says:
Quote:
All interrupt requests have entry points located at an offset relative to EVBA. Bla bla bla... The autovector offset has 14 address bits, giving an offset of maximum 16384 bytes.
To be very correct, the ISRs aren’t limited to 16 KiB either. Only the entry points must be within the 16 KiB block, so the last ISR could actually exceed that block.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

catweax wrote:
Yes, it can be smaller. If your ISRs use only 4 KiB, they’re still within a 16 KiB block, though. :wink:
Yeah but I mean that I shouldn't be forced to allocate 16 kB block for ISRs which, for example, only have one jump instruction to the main handler?

Taking your example. If I had ISRs that use 4 kB of memory, would I waste that 12 kB of memory?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kblomqvist wrote:
Yeah but I mean that I shouldn't be forced to allocate 16 kB block for ISRs which, for example, only have one jump instruction to the main handler?

Taking your example. If I had ISRs that use 4 kB of memory, would I waste that 12 kB of memory?

You don’t have to allocate the full 16 KiB, so you don’t need to waste 12 KiB if you only use 4 KiB.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A better way of saying it would be that it has to be 16kB aligned.

Hans-Christian