- Sun Jan 13, 2013 11:30 pm
#154200
Hi, I've got a broken program which is really frustrating. I don't know if anyone here can help, it's probably a long shot.
It's a simple LED-flashing test program which I've adapted from a tutorial you can find here. I'm using YAGARTO and OpenOCD.
The program is for a SAM7S256, and the actual LED flashing code I've written seems fine in its entirety. The cause of the problem seems to lie in the initialisation C code, or it may be the fault of something in the startup assembly code (I've included the contents of these two files at the bottom of this post).
Here is where it gets really screwy. The code actually runs fine - as long as there's optimisation specified when compiling. I've narrowed it down, and the single optimisation setting which (when enabled) lets the program work is "-fomit-frame-pointer".
Without this setting, an abort exception occurs when running the program on the MCU. The exception occurs just before the end of the initialisation function.
What's more, this only happens if I don't have the memory remapped:
So if I power up the microcontroller, so that flash is mapped to address 0x0, the program aborts after attempting to execute the instruction at 0x340. This is just a few instructions before the point where the initialisation function is supposed to return to the startup assembly code.
If I then use OpenOCD's "soft_reset_halt" command to get back to address 0x0, then use the "mww" command to write a register and ensure that RAM is remapped to 0x0 instead of flash, the program works and the LED flashing loop is reached.
When the program starts from RAM, it jumps to the reset code in flash all the same, but the code in flash is run from its actual addresses in this case (starting at 0x00100000). So when it reaches 0x00100340, the same place where it aborted when running from the remap region, the program continues without a problem and returns to the startup code after 0x00100348. From there the startup code finishes, the main function is branched to, and the LEDs start flashing.
So I have no idea how the little bit of extra code to do with frame pointers is breaking the program in these circumstances. It's obviously got something to do with the address differences between remapping to ROM/RAM, but I just don't understand how.
I'm thinking that maybe the code (which is optimised out when I use the "-fomit-frame-pointer" option) is doing some relative addressing stuff at the end of the sam7s256_init function. The compiler must be putting something in after the function body. But because the memory remap occurs at the end of the function, any relative addressing stuff appended after that would all of a sudden be pointing to RAM instead of flash. This is the only explanation I can think of, but I have no idea if it's right, and how would I fix it? Is the best solution just to ensure I use "-fomit-frame-pointer" when compiling the initialisation code?
One more thing, I'm compiling with these options:
arm-none-eabi-gcc -marm -g -c -mcpu=arm7tdmi -mthumb-interwork -ffunction-sections -Wall -I . -o sam7s256_init.o sam7s256_init.c
I'm hoping someone more experienced will just see the problem in my code straight away. Thanks for reading.
It's a simple LED-flashing test program which I've adapted from a tutorial you can find here. I'm using YAGARTO and OpenOCD.
The program is for a SAM7S256, and the actual LED flashing code I've written seems fine in its entirety. The cause of the problem seems to lie in the initialisation C code, or it may be the fault of something in the startup assembly code (I've included the contents of these two files at the bottom of this post).
Here is where it gets really screwy. The code actually runs fine - as long as there's optimisation specified when compiling. I've narrowed it down, and the single optimisation setting which (when enabled) lets the program work is "-fomit-frame-pointer".
Without this setting, an abort exception occurs when running the program on the MCU. The exception occurs just before the end of the initialisation function.
What's more, this only happens if I don't have the memory remapped:
So if I power up the microcontroller, so that flash is mapped to address 0x0, the program aborts after attempting to execute the instruction at 0x340. This is just a few instructions before the point where the initialisation function is supposed to return to the startup assembly code.
If I then use OpenOCD's "soft_reset_halt" command to get back to address 0x0, then use the "mww" command to write a register and ensure that RAM is remapped to 0x0 instead of flash, the program works and the LED flashing loop is reached.
When the program starts from RAM, it jumps to the reset code in flash all the same, but the code in flash is run from its actual addresses in this case (starting at 0x00100000). So when it reaches 0x00100340, the same place where it aborted when running from the remap region, the program continues without a problem and returns to the startup code after 0x00100348. From there the startup code finishes, the main function is branched to, and the LEDs start flashing.
So I have no idea how the little bit of extra code to do with frame pointers is breaking the program in these circumstances. It's obviously got something to do with the address differences between remapping to ROM/RAM, but I just don't understand how.
I'm thinking that maybe the code (which is optimised out when I use the "-fomit-frame-pointer" option) is doing some relative addressing stuff at the end of the sam7s256_init function. The compiler must be putting something in after the function body. But because the memory remap occurs at the end of the function, any relative addressing stuff appended after that would all of a sudden be pointing to RAM instead of flash. This is the only explanation I can think of, but I have no idea if it's right, and how would I fix it? Is the best solution just to ensure I use "-fomit-frame-pointer" when compiling the initialisation code?
One more thing, I'm compiling with these options:
arm-none-eabi-gcc -marm -g -c -mcpu=arm7tdmi -mthumb-interwork -ffunction-sections -Wall -I . -o sam7s256_init.o sam7s256_init.c
I'm hoping someone more experienced will just see the problem in my code straight away. Thanks for reading.
Code: Select all
/* File: sam7s_startup.s
*/
/* Program Status Register bit definitions: */
.equ I_BIT, 0x80
.equ F_BIT, 0x40
.equ USR_MODE, 0x10
.equ FIQ_MODE, 0x11
.equ IRQ_MODE, 0x12
.equ SVC_MODE, 0x13
.equ ABT_MODE, 0x17
.equ UND_MODE, 0x1B
.equ SYS_MODE, 0x1F
.equ STACK_FILL, 0xAAAAAAAA
.text
.arm
/* The following code, being the exception vectors, needs to be linked at the
* start of ROM.
*/
.global _vectors
.func _vectors
_vectors:
/* Vector table
* This is used only until RAM is remapped to address zero instead of flash.
*/
B _reset /* Reset (Remapped or not, this branches correctly.) */
B . /* Undefined Instruction */
B . /* Software Interrupt */
B . /* Prefetch Abort */
B . /* Data Abort */
B . /* Reserved */
B . /* IRQ */
B . /* FIQ */
.size _vectors, . - _vectors
.endfunc
.string "Test LED Flashing Program"
.align 2
.func _reset
_reset:
/* NOTE: This startup file is suitable for any SAM7S chip, as long as the
* following branch instruction calls an initialisation function appropriate
* for the chip being used, in this case the SAM7S256.
*/
LDR r0, =_reset /* Pass the reset address as the 1st arg */
LDR r1, =_cstartup /* Pass the return address as the 2nd arg */
MOV lr, r1 /* Store return address in the link register */
LDR sp, =__stack_end__ /* Set the temporary stack pointer */
B sam7s256_init /* Branch to the initialisation function */
_cstartup:
/* NOTE: The following executes upon return from the chip-specific
* initialisation code. At this time, the memory remap will have occurred,
* and so from this point on the code will execute at its linked address
* (i.e. from 0x00100000 onwards).
*/
/* Copy the .fastcode section from ROM to RAM. */
LDR r0, =__fastcode_load
LDR r1, =__fastcode_start
LDR r2, =__fastcode_end
1:
CMP r1, r2
LDMLTIA r0!, {r3}
STMLTIA r1!, {r3}
BLT 1b
/* Copy the .data section from ROM to RAM. (Initialised variables) */
LDR r0, =__data_load
LDR r1, =__data_start
LDR r2, =_edata
1:
CMP r1, r2
LDMLTIA r0!, {r3}
STMLTIA r1!, {r3}
BLT 1b
/* Clear the .bss section to zero. (Clearing uninitialised variables.) */
LDR r1, =__bss_start__
LDR r2, =__bss_end__
MOV r3, #0
1:
CMP r1, r2
STMLTIA r1!, {r3}
BLT 1b
/* Fill in the .stack section. */
LDR r1, =__stack_start__
LDR r2, =__stack_end__
LDR r3, =STACK_FILL
1:
CMP r1, r2
STMLTIA r1!, {r3}
BLT 1b
/* Initialise the stack pointers for each of the exception modes. */
MSR CPSR_c, #(IRQ_MODE | I_BIT | F_BIT) /* Switch to IRQ mode */
LDR sp, =__irq_stack_top__ /* Set the IRQ stack pointer */
MSR CPSR_c, #(FIQ_MODE | I_BIT | F_BIT) /* Switch to FIQ mode */
LDR sp, =__fiq_stack_top__ /* Set the FIQ stack pointer */
MSR CPSR_c, #(SVC_MODE | I_BIT | F_BIT) /* Switch to SVC mode */
LDR sp, =__svc_stack_top__ /* Set the SVC stack pointer */
MSR CPSR_c, #(ABT_MODE | I_BIT | F_BIT) /* Switch to ABT mode */
LDR sp, =__abt_stack_top__ /* Set the ABT stack pointer */
MSR CPSR_c, #(UND_MODE | I_BIT | F_BIT) /* Switch to UND mode */
LDR sp, =__und_stack_top__ /* Set the UND stack pointer */
/* This is for setting the system and user mode stack pointer. This is the
* normal mode of operation.
*/
MSR CPSR_c, #(SYS_MODE | I_BIT | F_BIT) /* Switch to SYS mode */
LDR sp, =__c_stack_top__ /* Set the C stack pointer */
/* Enter the C code. */
LDR r12, =main
MOV lr, pc /* Set the return address */
BX r12 /* The target code can be ARM or THUMB */
/* Cause an exception if main() ever returns. */
SWI 0xFFFFFF
.size _reset, . - _reset
.endfunc
.end
Code: Select all
// File: sam7s256_init.c
#include <stdint.h>
#include "AT91SAM7S256.h"
#include "dev_board.h"
void sam7s256_init(void (*reset_addr)(), void (*return_addr)())
{
// This is the address where RAM starts, as defined by the linker script,
// i.e. 0x00200000.
extern uint8_t __ram_start;
// LDR instruction for setting PC to PC + 0x18.
static uint32_t const LDR_PC_PC_0x18 = 0xE59FF000U | 0x18;
static uint32_t const MAGIC = 0xDEADBEEFU;
// Enable the User Reset.
AT91C_BASE_RSTC->RSTC_RMR |= AT91C_RSTC_URSTEN | (0xA5 << 24);
// Set up the Embedded Flash Controller.
AT91C_BASE_MC->MC_FMR = ((AT91C_MC_FMCN) & ((MCK + 500000)/1000000 << 16))
| AT91C_MC_FWS_1FWS;
// Disable the Watchdog Timer.
AT91C_BASE_WDTC->WDTC_WDMR = AT91C_WDTC_WDDIS;
// Enable the Main Oscillator.
AT91C_BASE_PMC->PMC_MOR = ((6 << 8) & AT91C_CKGR_OSCOUNT) |
AT91C_CKGR_MOSCEN;
// Wait for the startup time to elapse and the Main Oscillator to stabilise.
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS));
// Configure the PLL.
AT91C_BASE_PMC->PMC_PLLR = (AT91C_CKGR_DIV & 0x05) |
(AT91C_CKGR_PLLCOUNT & (29 << 8)) |
(AT91C_CKGR_MUL & (25 << 16));
// Wait for the startup time to elapse.
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK));
// Wait for the Master Clock to become ready.
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
// Configure the Master Clock and CPU clock.
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
// Wait for the Master Clock to become ready.
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
// Set the Master Clock to the PLL Clock now that the prescaler has been set
// to 2, meaning that the clock speed won't go over the 55MHz limit.
AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK;
// Wait for the Master Clock to become ready.
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
// Setup the exception vectors in RAM.
*(uint32_t volatile *)(&__ram_start + 0x00) = LDR_PC_PC_0x18;
*(uint32_t volatile *)(&__ram_start + 0x04) = LDR_PC_PC_0x18;
*(uint32_t volatile *)(&__ram_start + 0x08) = LDR_PC_PC_0x18;
*(uint32_t volatile *)(&__ram_start + 0x0C) = LDR_PC_PC_0x18;
*(uint32_t volatile *)(&__ram_start + 0x10) = LDR_PC_PC_0x18;
*(uint32_t volatile *)(&__ram_start + 0x14) = MAGIC;
*(uint32_t volatile *)(&__ram_start + 0x18) = LDR_PC_PC_0x18;
*(uint32_t volatile *)(&__ram_start + 0x1C) = LDR_PC_PC_0x18;
// Secondary vector table.
*(uint32_t volatile *)(&__ram_start + 0x20) = (uint32_t)reset_addr;
*(uint32_t volatile *)(&__ram_start + 0x24) = 0x04U;
*(uint32_t volatile *)(&__ram_start + 0x28) = 0x08U;
*(uint32_t volatile *)(&__ram_start + 0x2C) = 0x0CU;
*(uint32_t volatile *)(&__ram_start + 0x30) = 0x10U;
*(uint32_t volatile *)(&__ram_start + 0x34) = 0x14U;
*(uint32_t volatile *)(&__ram_start + 0x38) = 0x18U;
*(uint32_t volatile *)(&__ram_start + 0x3C) = 0x1CU;
// Remap the memory.
if (MAGIC != (*(uint32_t volatile *)0x14))
{
AT91C_BASE_MC->MC_RCR = 1;
}
}