Hi Adam,
ok, basically we made some customized ld files.
To start we have a MQX_ROM.ld and an APP_ROM.ld.
These files just contain the objects we need in the ROM to boot, for simplicity only down to the object level for the most part, but we have a couple where just specific symbols need the performance boost of being in ROM.
I'm afraid finding which objects you need can be a bit of trial and error - if you set up the MPU so you can print an exception with the problematic PC if something tries to access the DDR before you've got it going, that really helps, you can just use arm-none-eabi-addr2line to find the culprit.
In main ld file we have
MEMORY
{
vectorrom (RX): ORIGIN = 0x00000000, LENGTH = 0x00000400 /* interrupt vectors */
fcfmprotrom (R): ORIGIN = 0x00000400, LENGTH = 0x00000010 /* Flash Configuration Field */
rom_header (R): ORIGIN = 0x00000410, LENGTH = 0x00000020 /* version, checksum */
romlow (RX): ORIGIN = 0x00000430, LENGTH = 0x0007DBD0 /* lower half of ROM (MQX) */
permdata (R): ORIGIN = 0x0007E000, LENGTH = 0x00001000 /* permanent data, e.g. serial number */
filow (R): ORIGIN = 0x0007F000, LENGTH = 0x00001000 /* Flash Indicator for ROM low (swap) */
ram (RW): ORIGIN = 0x1FFF0000, LENGTH = 0x00020000 /* SRAM - RW data */
end_of_kd (RW): ORIGIN = 0x2000FFF0, LENGTH = 0x00000000
bstack (RW): ORIGIN = 0x2000FA00, LENGTH = 0x00000200
end_bstack (RW): ORIGIN = 0x2000FC00, LENGTH = 0x00000000
exception (RW): ORIGIN = 0x2000FC04, LENGTH = 0x000003FC /* SRAM - RW data for storing CPU exception info */
ddr_header (RX): ORIGIN = 0x70000000, LENGTH = 0x00000020 /* magic, version, checksum */
ddr (RX): ORIGIN = 0x70000020, LENGTH = 0x03FFFFE0 /* LPDDR - 64MB */
}
This gives us various sections - the headers for the ROM and the DDR are used because we use the swap method for upgrading, which means that the actual upgrade is atomic, with very low risk of bricking a unit.
In the sections bit we added:
.rom_header :
{
__ROM_HEADER = .;
KEEP(*(.rom_header))
. = ALIGN (0x4);
} > rom_header
PROVIDE(__rom_header = __ROM_HEADER);
so we can access the header from code - and a similar section for the ddr:
.ddr_header :
{
__DDR_HEADER = .;
KEEP(*(.ddr_header))
. = ALIGN (0x10);
} > ddr_header
PROVIDE(__ddr_base = __EXTERNAL_DDR2_RAM_BASE);
PROVIDE(__ddr_size = __EXTERNAL_DDR2_RAM_SIZE);
PROVIDE(__ddr_header = __DDR_HEADER);
PROVIDE(__ddr_end = __EXTERNAL_DDR2_RAM_END);
Then we specify the parts which must be in ROM:
.rodata :
{
*(KERNEL)
*(S_BOOT)
*(IPSUM)
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(0x4);
*main.obj(.rodata*) /* add comment */
. = ALIGN(0x4);
*(.rdata*)
. = ALIGN(0x4);
*(.exception)
. = ALIGN(0x4);
__exception_table_start__ = .;
__exception_table_end__ = .;
__sinit__ = .;
} > romlow
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } > romlow
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > romlow
.ctors :
{
__CTOR_LIST__ = .;
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
/* We don't want to include the .ctor section from
from the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
__CTOR_END__ = .;
} > romlow
.dtors :
{
__DTOR_LIST__ = .;
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
__DTOR_END__ = .;
} > romlow
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} > romlow
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} > romlow
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
___ROM_AT = .;
} > romlow
.mqx_rom :
{
. = ALIGN(0x4);
/* ideally we could do something like libmfs.a(.text*) but
couldn't get it working, so including the list of .o files */
INCLUDE ../MQX_ROM.ld
} > romlow
.app_rom :
{
. = ALIGN(0x4);
INCLUDE ../APP_ROM.ld
} > romlow
Then we have the catchall, to put any new objects into DDR:
.catchall :
{
. = ALIGN(0x4);
*.obj(.text*)
*.obj(.rodata*)
. = ALIGN(0x20);
__DDR_CODE_END = .;
} > ddr
PROVIDE(__ddr_code_end = __DDR_CODE_END);
PROVIDE(__ddr_pool_start = (__DDR_CODE_END + 0x200) & 0xfffffe00);
The final 2 provides are useful, they tell you where the DDR code image ends and the DDR memory pool can start - you can either extend the existing memory pool or create another - I made a separate DDR memory pool because in my app I have to cope with external power going away without warning, and loosing the DDR, so I keep critical items in the internal memory.