ImaginationOverflow

The knowings and experiences of a group of developers.

AVR32 First Contact, First Code

So this is the third of the AVR32 4 dummies post series, you can check the other blog posts on:

I finally started coding some stuff, namely the board startup, the context switch, the interrupt controller driver and the system timer driver. Along the way I discovered more things about the architecture, namely where the AVR32 call convention is documented. The Atmel support, when I contacted them to know about the call convention, forwarded me to the IAR compiler Reference Guide. I loled and checked if my reverse engineering discovers were right (they were).

Before any more comments and discoveries, here it is the overall description on my development board and CPU:

Board: EVK1104:

  • CPU : AT32UC3A3256, AVR32.
    • RAM: 128Kb.
    • ROM: 256Kb.
    • SPI: 6.
    • UART: 4.
    • I2C: 2.
    • ADC: 10bits (max resolution), with 8 channels, up to 384 ksps.
    • Speed: up to 66MHz.
  • 16 bit DAC.
  • SD card.
  • 256 Mbyte external RAM.
  • 256 Mbyte external ROM.
  • Touch Wheel for QMatrix.
  • 240×320 RBG LCD

Both the board and CPU websites are linked in the previous list, for more information about the EVK1104, download the Schematics and BOM since there is no user manual. The BOM (Bill of materials) have all the hardware used to create a dev board, so it’s the first place where you should go to find out what kind of hardware is “under the hood” or if you want a specific datasheet.

AVR32 Bootloader

A bootloader is a simple program or piece of code that initializes the hardware so that an operating system or application can run. The bootloader can be “called” by the reset event handler or, as usual, be part of the reset event handler. On AVR32 architecture the reset event is the only event that is statically mapped on the memory layout, at the address 0x80000000.

The bootloader present in the Atmel software libraries for my dev board follows the same principles on the bootloaders that I’ve done before for the ARM architecture:

  1. Set stack pointer.
  2. Set other system required configurations.
  3. Load the .data section (initialized variables).
  4. Set the .bss section to 0(uninitialized variables).
  5. Load .text to RAM (optional, used for better performance)
  6. Run the the global instances constructors (c++ only).
  7. Call main (or a operating system boot)

The loads are simple memcpy, from one place to another, in most of the cases from Flash to RAM.

I didn’t do my own bootloader for the board, I used one of the Atmel startups and added what I needed, namely the constructors call (MOS is in C++). I did the same for the linker script, I grabbed the Atmel ldscript and corrected some errors on it, again because of my C++ code (on the default linker scripts, although the C++ init arrays are present, the global variables are misplaced and without any change the init and fini addresses are always the same, for more information about C++ initialization check out the previous link of IAR compiler reference).

You can check out the startup and the ldscript via github.

Context Switch

The context switch is the operating system routine responsible for changing threads, this is done by saving the context of the current thread and loading the context of the next thread. A thread’s context is the minimal information needed to represent a thread’s execution state at the time of the switch. The context should be sufficient so that when a thread is switched its previous execution state can be restored and the thread execution continues like nothing happened.
In CPUs without MMU, FPU, etc, the thread context is reduced to the CPU registers, in AVR32 from R0-R15.

MOS has two ways to switch threads:

  • By interrupt: when an interrupt occurs the system checks if it is time to switch threads (MOS have a time-slice scheduler), if so the system changes the current thread context pointer (this context is stored on the epilogue of the interrupt and passed to system interrupt service routine) to the next thread context. With this way the context switch only happens when the interrupt returns.
  • By thread choice: when a thread doesn’t have nothing to do or is waiting to be signalized it calls kernel primitives like Yield, Sleep or Wait (in synchronizers), this functions can trigger a context switch.

Because of this feature, the two context switch implementations have to be linked to each other. Since the AVR32 interrupt handling automatically stores the r8-r12 registers, the “normal” context switch must save this registers first by the same order that is done on the interrupt. After that it’s needed to save the rest of the registers r0-r7, lr and pc. You’re probably asking why the sp (stack pointer) isn’t saved, the stack pointer is the only pointer that MOS has to know where the thread context is, so the stack pointer is saved on the thread instance, so that further switches can restore the thread context.

I’ve already pushed the AVR32 thread context and the context switch to github, go check it out.

If you check the context switch code, you will see that I don’t store all registers. Actually what I’m doing in some cases is to move stack pointer further down. For instance when the “normal” context switch is called, the caller must preserve all the scratch registers and since the context switch doesn’t use any local variable, I don’t need to store them (they are already stored in the thread stack by the caller of the context switch) so I move the stack  pointer down, saving precious memory accesses.

But when I restore the next thread I restore all the registers, the main reason is because it’s “impossible” to know how the next thread switched (by interrupt or by the normal way) so I simply restore them all.

On another note, the status register is also saved on the context switch, again this isn’t needed when thread is switched using the “normal” way, but when switched via interrupt is mandatory.

Final Comments

When doing the context switch I felt a bit forced to do things the AVR32 way, since the interrupt handling system stores automatically R8-R12 registers into the system stack. So MOS port will not take advantage of the Application mode, and all the code is going to run on System mode. After browsing the Atmel ldscript I felt a little disappointing on the development leftovers that were present and the fact that they don’t even bother to test the CPU that I’m using with C++, I can say this because the global constructors pointers are not correctly configured on the ldscript.
Also, In a previous post I hysterically talked about the status register being mapped on memory, I was wrong, you need to use special assembly instructions to read and write things to the SR.

On the next post I’ll talk about the interrupt handling system, the UC3 peripherals and the device drivers construction.

Feel free to ask any question or comment, see you next time.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: