Startup and linker
Nothing in a generated app hides the startup path. The board fragment vendored into
boards/<board>/ names the startup source and the linker script explicitly, and both are
files you can open.
The board decides
Section titled “The board decides”boards/<board>/memory.cmake is the fragment that does it. For apollo510_evb it reads:
nsx_module_dir_for_name(_nsx_core_module_dir "nsx-core")set(NSX_CORE_DIR "${NSX_ROOT}/${_nsx_core_module_dir}")
if(NSX_TOOLCHAIN_FAMILY STREQUAL "armclang") set(NSX_STARTUP_SOURCE "${NSX_CORE_DIR}/src/apollo510/armclang/startup_keil6.c") set(NSX_SYSTEM_SOURCE "${NSX_AMBIQSUITE_ROOT}/CMSIS/AmbiqMicro/Source/system_apollo510.c") set(_nsx_linker_script_default "${NSX_CORE_DIR}/src/apollo510/armclang/linker_script_sbl.sct") set(_nsx_linker_script_itcm "${NSX_CORE_DIR}/src/apollo510/armclang/linker_script_itcm_sbl.sct")else() set(NSX_STARTUP_SOURCE "${NSX_CORE_DIR}/src/apollo510/gcc/startup_gcc.c") set(NSX_SYSTEM_SOURCE "${NSX_AMBIQSUITE_ROOT}/CMSIS/AmbiqMicro/Source/system_apollo510.c") set(_nsx_linker_script_default "${NSX_CORE_DIR}/src/apollo510/gcc/linker_script_sbl.ld") set(_nsx_linker_script_itcm "${NSX_CORE_DIR}/src/apollo510/gcc/linker_script_itcm_sbl.ld")endif()Three things follow from that.
The startup file comes from the nsx-core module, per SoC and per toolchain family.
Arm Compiler for Embedded gets startup_keil6.c; GCC and ATfE get startup_gcc.c. Both
live under modules/.../nsx-core/src/<soc>/ in your app.
The CMSIS system file comes from the SDK, under NSX_AMBIQSUITE_ROOT, not from NSX.
Which SDK tree that is depends on the provider resolution described in
SDK foundation and overrides.
Linker scripts are per SoC, per toolchain and per profile. Arm Compiler for Embedded
uses scatter files (.sct); GCC and ATfE use linker scripts (.ld).
Bootloader and no-bootloader variants
Section titled “Bootloader and no-bootloader variants”Scripts come in two families, named for whether the image sits behind Ambiq’s secure bootloader:
linker_script_sbl.*, the secure-bootloader layout, used by the Apollo boards.linker_script_nbl.*, the no-bootloader layout, used byatomiq110_fpga_turbo.
The difference is where the image starts in non-volatile memory, which is why an image built for one will not run under the other. Your board’s fragment picks the right one; you do not choose between them.
The ITCM profile
Section titled “The ITCM profile”Each board names two scripts, a default and an ITCM variant, and hands both to a selector:
if(COMMAND nsx_select_linker_script) nsx_select_linker_script( DEFAULT "${_nsx_linker_script_default}" ITCM "${_nsx_linker_script_itcm}" )else() # SDK predates named linker profiles — fall back to the default script. set(NSX_LINKER_SCRIPT "${_nsx_linker_script_default}")endif()Setting NSX_LINKER_PROFILE to itcm selects the ITCM variant, which lays the image out
to execute from tightly coupled instruction memory rather than from NVM. That is the
layout the coremark example uses, and it is the one to reach for when instruction fetch
is what you are measuring.
nsx_select_linker_script is provided by the nsx-core module rather than by NSX itself,
which is why the fragment guards on if(COMMAND ...). Against an SDK revision that
predates the feature, the default script is used and the profile is ignored.
Overriding the script
Section titled “Overriding the script”The whole selection is skipped if NSX_LINKER_SCRIPT is already set:
if(NOT DEFINED NSX_LINKER_SCRIPT)So pointing the build at a script of your own is one CMake variable:
nsx configure -- -DNSX_LINKER_SCRIPT=/path/to/my_layout.ldStart from the script your board would have selected, since it already has the correct
regions and section names for the part. The power_benchmark example ships its own
linker_script_itcm.ld for exactly this reason, and it is a readable starting point.
Stack and heap
Section titled “Stack and heap”The board’s board.cmake passes the startup code its stack and heap sizes as compile
definitions, for example:
# STACK_SIZE: startup C-runtime stack size in bytes (used by SDK startup).STACK_SIZE=4096The startup source in nsx-core is what consumes them, and it is the file to read if you
need to know exactly how a given SDK revision interprets the value. Both come out of the
same region as your data, so raising either one takes space from everything else placed
there. See Memory placement.
What you get after linking
Section titled “What you get after linking”build/<board>/ holds the linked ELF, a .bin and a .map. The map file is the record
of what actually ended up where: if you placed something with an NSX_MEM_ macro and want
to confirm it landed in the region you meant, the map is the evidence, not the source.