Tuesday, April 28, 2009

Fighting the GNU as assembler (contd.)

Okay, yesterday I tested the RTOS prototype with the exception vectors put in an assembly language file instead. It did not work quite as expected...

To begin with, I should tell you that my code is linked to be run from the embedded SRAM in the STM32. This is because I think it is prettier to not have to erase and reprogram the flash all the time. On the other hand, the Cortex-M3 always has it's exception vectors placed at 0x0 out of reset, and that is within the flash area. That means that following a reset, the CPU will always start executing from the reset vector at 0x4. It is possible to relocate the exception vectors to 0x20000000 (SRAM) by modifying the vector table offset register once you're running and thereby pointing out your own vectors placed in SRAM.

To be able to practically run my code from SRAM I implemented a minimal bootstrap routine that I put into the flash area:

static void reset();

unsigned int *vectors[2] __attribute__ ((section(".vectors"))) = {
(unsigned int *) 0, /* No stack used. */
(unsigned int *) reset
};

__attribute__ ((naked)) static void reset()
{
/* We enter here, running as privileged in thread mode. TRM 2.2.
We use SP_main. TRM 2.2.1 and 5.4.
NVIC interrupts disabled. NMI and Hard Fault disabled. TRM 5.9. */

/* Remap vectors to 0x20000000.
Read stack top and entry point from
user's RAM-based vectors. Jump to
entry point. */
asm("mov r0, #0x20000000\n\t"
"ldr r1, =0xE000ED08\n\t"
"str r0, [r1]\n\t"
"ldr sp, [r0]\n\t"
"ldr pc, [r0, #4]");
}

TRM refers to the Cortex-M3 Technical Reference Manual. The reset routine reads the initial stack pointer and SRAM-based boot routine address from the user's exception vector at 0x20000000. It set the vector table offset register and jumps to the user's reset routine. This way, it appears like the board boots from SRAM.

So first, load your code including your exception vectors to SRAM, then issue a system reset to the board.

The problem I ran into yesterday was when performing an svc (system call). The CPU properly jumped to my exception handler:

  .global  CM3_handler_svc
.extern portable_syscall
CM3_handler_svc:
bl portable_syscall
bx lr

Problem was that when executing the first instruction (the bl), an exception occured. I noticed that the T-bit in the xPSR register was cleared when taking the svc exception. Executing instructions in the Cortex-M3 with the T-bit (thumb mode) cleared is not allowed. The fix was to add the pseudo-op .thumb_func before the exception handler code. That way, the exception table entry corresponding to svc sets the LSB of the jump address and the T-bit gets set when running the svc handler. Haven't tested this yet, but it should work.

Monday, April 27, 2009

Fighting the GNU as assembler.

For my RTOS experiments, I need vectors to be nicely linked into the binary, which I download to the Cortex M3 target, being an STM32 development board from Olimex.

To start with, I used to put exception vectors in the C source file, in the following way:


/* Vector Table */
unsigned int *vectors[12]
__attribute__ ((section(".vectors")))= {
(unsigned int *) START_STACK_TOP,
(unsigned int *) boot_rtos,
(unsigned int *) 0x00000000,
(unsigned int *) 0x00000000,
(unsigned int *) 0x00000000,
(unsigned int *) 0x00000000,
(unsigned int *) 0x00000000,
(unsigned int *) 0x00000000,
(unsigned int *) 0x00000000,
(unsigned int *) 0x00000000,
(unsigned int *) 0x00000000,
(unsigned int *) CM3_SVC
};


That way, I tell gcc to put the array of vectors in the .vectors section. As I use OpenOCD to communicate with the board, using the ARM-USB-OCD, I can download the executable directly from gdb, using the "load" command.

However, I would like to have the vectors defined in an assembly language source file, together with a few of my Cortex M3 specific routines (such as system call handling etc). Therefore I tried to put the vectors in the .s file:

    .section        .vectors,"aw"
.global CM3_vectors
.extern boot_rtos
CM3_vectors:
.word START_STACK_TOP
.word boot_rtos
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word CM3_handler_svc
.word 0
.word 0
.word CM3_handler_pendsv


However, I initially did not use the "aw" attribute to the .section operator. This resulted in the vectors area being considered a debug symbol when using nm. Therefore, gdb did not download it to the board.

Using the -S flag to gcc, I could examine the assembly output from gcc and that way find out that I should use the "aw" attribute. Apparently GNU as does not apply any default attributes on a section called .vectors.

First entry...

Hello!

I intend to write a few lines now and then about my electronics projects. Hopefully, it will be of interest to someone! :-)