LPC2138/crt
After reset
The 12kB boot block is mapped into 0x7d000. It's also visible at 0x7fff d000.
Bootloader checks for valid checksum of vectors, then hands off control to flash at 0x0, or start serial port shit.
Some explanation of startup (relevant to other MCUs too): On startup CPU jumps to location 0, and just starts executing code. Since there's only one instruction space, the first instruction is a branch to reset handler.
Reset handler needs to set up stacks. In this example it only sets up the current (Supervisor) mode stack, but it's easy to set them for other modes... something along these lines: MODE_FIQ ldr sp, =_ram_end-0x100
IF you use User mode... don't forget to switch to that mode last, because you can't switch back.
A word on sections
There's basically three differently handled sections:
- .text/.rodata for code and readonly data
- .data for initialized data
- .bss for uninitialized data
.text and .rodata are obviously just saved into FLASH, and that's fine for them. .data is a bit more tricky, since addresses are from RAM, but data needs to be saved somewhere (well... FLASH, where else). So you need to have code that copies .data section from flash. And following that is the code to zero the .bss.
And that's it. C environment is ready!
arm exception vector locations
ok, so these fuckers can be remapped:
* boot loader mode (on start up), boot block vectors are mapped here * user flash mode (from boot code), activated by BL, needs valid program sig and non-forced BL. non re-mapped vectors (== stays in flash, bottom) * user ram mode (from program), user program can remap vectors to bottom of SRAM
The 64-byte block is remapped. So some handlers can fit there.
0x0 | Reset |
0x4 | Undefined Instruction |
0x8 | Software Interrupt |
0xc | Prefetch Abort |
0x10 | Data Abort |
0x14 | 2's complement of checksum of vectors |
0x18 | IRQ |
0x1c | FIQ |
Crt.s looks like this<enscript lang=asm>
.section .vectors .code 32 .align 0
.global _vectors
_vectors: .reset: b start .undefined: bl endless .swi: bl endless .prefetchabort: bl endless .dataabort: bl endless .checksum: .word 0x92ffffec /* hand calculated, grr */ .irq: bl endless .fiq: bl endless
endless:
b endless
start:
/* setup sp */ ldr sp, =_ram_end
bl init bl main
b endless
</enscript>
A mofo was also the linker script:<enscript lang=asm> MEMORY {
FLASH (rx) : ORIGIN = 0, LENGTH = 512K RAM (rw) : ORIGIN = 0x40000000, LENGTH = 32K
}
ENTRY(_vectors)
/* http://sourceware.org/binutils/docs/ld/Output-Section-LMA.html */
_ram_end = ORIGIN(RAM) + LENGTH(RAM) - 4;
SECTIONS {
.text : { *(.vectors) *(.text) *(.rodata) *(.rodata.*) } >FLASH
_fldata = . ; .data : ALIGN(4) { _data = . ; *(.data) _edata = . ; } >RAM AT>FLASH
.bss : ALIGN(4) { _bss = . ; *(.bss) *(COMMON) _ebss = . ; } >RAM
} </enscript>
But ok, it works now, and after crt, we can transfer more init to c code:<enscript lang=c>
- include <types.h>
- include <board.h>
extern u32 _fldata, _data, _edata, _bss, _ebss, _ram_end; void init() {
u32 *src = &_fldata; u32 *dest = &_data;
/* copy .data from flash */ while (dest < &_edata) *dest++ = *src++;
/* zero .bss */ dest = &_bss; while (dest < &_ebss) *dest++ = 0;
/* poison the rest of ram */ while (dest < (u32*)&src-16) *dest++ = 0xcbababe;
board_init();
} </enscript>
Hrm, code dumps are boring, but crt.s and linker script really took a while to make it work.