iOBC Bootloader stabilized
There was an issue that the ISIS bootloader was only able to load very simple projects are reduced versions of our OBSW. Our custom bootloader was not working either.
After various tests in the cleanroom, I finally appear to make our bootloader work and I stabilized the boot process.
I suspect that some changes in the norflash.lds GNU linker script and the board startup file were critical. Some critical differences
Prerelocate section
norflash.lds AT91 prerelocate:
.prerelocate : AT (_efixed)
{
. = ALIGN(4);
_sprerelocate = .;
*board_lowlevel.o(.text*)
*board_memories.o(.text*)
. = ALIGN(4);
_eprerelocate = .;
} >sram0
norflash.lds custom prerelocate:
.prerelocate : AT (_efixed)
{
. = ALIGN(4);
_sprerelocate = .;
KEEP(*(.vectors));
*board_lowlevel.o(.text .text*)
*board_memories.o(.text .text*)
. = ALIGN(4);
_eprerelocate = .;
} >sram0
This section is copied to SRAM0 before the LowLevelInit is called. For the NORFlash, it is critical LowLevelInit is called in the SRAM0. The AT91 bootstrap variation to put the object files into prerelocate did not work for us, but the version above does (confirmed by inspecting the map file generated by the linker). What is really odd in the AT91 bootstrap ld file is that vectors is not located in the start but in the postrelocate section because it is required that the ARM vectors are at the address 0x0 (and SRAM0 is the address 0x00 after the remap performed in LowLevelInit).
Postrelocate
AT91 bootsstrap:
.postrelocate : AT (_efixed + SIZEOF(.prerelocate))
{
. = ALIGN(4);
_spostrelocate = .;
*(.vectors)
*(.ramfunc)
*(.data)
. = ALIGN(4);
_epostrelocate = .;
} >sram0
Custom lds:
.postrelocate : AT (_efixed + SIZEOF(.prerelocate))
{
. = ALIGN(4);
_spostrelocate = .;
*(.sramfunc*)
/* *SPI.o(.text .text*); */
/* *spi_at91.o(.text .text*); */
/* *HAL.a:*(.text .text.*); */
/* contains all initialized global and static variables */
*(.sramdata*)
*(.data*)
. = ALIGN(4);
_epostrelocate = .;
} >sram0
Adapted for our software. There is specific sramdata and sram functions are generally put into the .sramfunc instead of a .ramfunc section in the code.
Rest of linker script
AT91 bootsstrap:
.fixed :
{
. = ALIGN(4);
_sfixed = .;
*(.text*)
*(.rodata*)
*(.glue_7)
*(.glue_7t)
. = ALIGN(4);
_efixed = .;
} >ebi_cs0
.postrelocate : AT (_efixed + SIZEOF(.prerelocate))
{
. = ALIGN(4);
_spostrelocate = .;
*(.vectors)
*(.ramfunc)
*(.data)
. = ALIGN(4);
_epostrelocate = .;
} >sram0
.bss (NOLOAD) : {
_szero = .;
*(.bss)
. = ALIGN(4);
_ezero = .;
} >sram1
_sstack = 0x301000;
}
end = .;
Custom lds:
.fixed :
{
. = ALIGN(4);
_sfixed = .;
KEEP(*(.startup));
*(.text .text.*)
*(.rodata*)
*(.glue_7)
*(.glue_7t)
*(.CP15_*)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data, the constructor addresses are placed in this sections */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data, destructor addresses are placed in this section */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
} >ebi_cs0
/* used for unwinding, can be useful for debugging */
.ARM.extab (NOLOAD) :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} >ebi_cs0
/* used for unwinding, can be useful for debugging */
__exidx_start = .;
.ARM.exidx (NOLOAD) :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} >ebi_cs0
. = ALIGN(4);
__exidx_end = .;
_efixed = .;
.srambss (NOLOAD) :
{
. = ALIGN(4);
_ssrambss = .;
*(.srambss*)
. = ALIGN(4);
_esrambss = .;
} >sram1
.bss (NOLOAD) :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
*(COMMON.*)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >sram1
/* Heap section */
_sheap_ = .;
end = .;
_eheap_ = 0x302000;
/* top of stack */
_sstack = 0x304000;
}
heap definitions as required for FreeRTOS allocation, some additional bss sections, special srambss taken over from sdram.lds provided by ISIS. Other new sections are require for more complex code for C++. Those sections were empty when inspected in the map file.
Startup file
Startup file adapted for FreeRTOS
The startup file is the first thing that runs after a processor reset. It sets up low level stuff, like copying functions to SRAM, setting up clocks etc. and then branches to main.
Summary of differences:
-
irqHandler
adapted for FreeRTOS - More stacks
- Assembler function to jump to application, the core is also put into the default power on state and all interupts are disabled
AT91 bootsstrap startup file
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "board.h"
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
#define IRQ_STACK_SIZE 8*3*4
#define ARM_MODE_ABT 0x17
#define ARM_MODE_FIQ 0x11
#define ARM_MODE_IRQ 0x12
#define ARM_MODE_SVC 0x13
#define I_BIT 0x80
#define F_BIT 0x40
//------------------------------------------------------------------------------
// Startup routine
//------------------------------------------------------------------------------
.align 4
.arm
/* Exception vectors
*******************/
.section .vectors, "a", %progbits
resetVector:
ldr pc, =resetHandler /* Reset */
undefVector:
b undefVector /* Undefined instruction */
swiVector:
b swiVector /* Software interrupt */
prefetchAbortVector:
b prefetchAbortVector /* Prefetch abort */
dataAbortVector:
b dataAbortVector /* Data abort */
reservedVector:
b reservedVector /* Reserved for future use */
irqVector:
b irqHandler /* Interrupt */
fiqVector:
/* Fast interrupt */
//------------------------------------------------------------------------------
/// Handles a fast interrupt request by branching to the address defined in the
/// AIC.
//------------------------------------------------------------------------------
fiqHandler:
b fiqHandler
//------------------------------------------------------------------------------
/// Handles incoming interrupt requests by branching to the corresponding
/// handler, as defined in the AIC. Supports interrupt nesting.
//------------------------------------------------------------------------------
irqHandler:
/* Save interrupt context on the stack to allow nesting */
sub lr, lr, #4
stmfd sp!, {lr}
mrs lr, SPSR
stmfd sp!, {r0, lr}
/* Write in the IVR to support Protect Mode */
ldr lr, =AT91C_BASE_AIC
ldr r0, [lr, #AIC_IVR]
str lr, [lr, #AIC_IVR]
/* Branch to interrupt handler in Supervisor mode */
msr CPSR_c, #ARM_MODE_SVC
stmfd sp!, {r1-r3, r4, r12, lr}
blx r0
/* Restore scratch/used registers and LR from User Stack */
/* Disable Interrupt and switch back in IRQ mode */
ldmia sp!, {r1-r3, r4, r12, lr}
msr CPSR_c, #ARM_MODE_IRQ | I_BIT
/* Acknowledge interrupt */
ldr lr, =AT91C_BASE_AIC
str lr, [lr, #AIC_EOICR]
/* Restore interrupt context and branch back to calling code */
ldmia sp!, {r0, lr}
msr SPSR_cxsf, lr
ldmia sp!, {pc}^
//------------------------------------------------------------------------------
/// Initializes the chip and branches to the main() function.
//------------------------------------------------------------------------------
.section .text
.global entry
entry:
resetHandler:
/* Useless instruction for referencing the .vectors section */
ldr r0, =resetVector
/* Set pc to actual code location (i.e. not in remap zone) */
ldr pc, =1f
/* Initialize the prerelocate segment */
1:
ldr r0, =_efixed
ldr r1, =_sprerelocate
ldr r2, =_eprerelocate
1:
cmp r1, r2
ldrcc r3, [r0], #4
strcc r3, [r1], #4
bcc 1b
/* Perform low-level initialization of the chip using LowLevelInit() */
ldr sp, =_sstack
stmfd sp!, {r0}
ldr r0, =LowLevelInit
blx r0
/* Initialize the postrelocate segment */
ldmfd sp!, {r0}
ldr r1, =_spostrelocate
ldr r2, =_epostrelocate
1:
cmp r1, r2
ldrcc r3, [r0], #4
strcc r3, [r1], #4
bcc 1b
/* Clear the zero segment */
ldr r0, =_szero
ldr r1, =_ezero
mov r2, #0
1:
cmp r0, r1
strcc r2, [r0], #4
bcc 1b
/* Setup stacks
**************/
/* IRQ mode */
msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT
ldr sp, =_sstack
sub r4, sp, #IRQ_STACK_SIZE
/* Supervisor mode (interrupts enabled) */
msr CPSR_c, #ARM_MODE_SVC | F_BIT
mov sp, r4
/* Branch to main()
******************/
ldr r0, =main
blx r0
/* Loop indefinitely when program is finished */
1:
b 1b