diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml index 377a8d5d..c83e66ce 100644 --- a/cortex-m-rt/Cargo.toml +++ b/cortex-m-rt/Cargo.toml @@ -25,6 +25,7 @@ cortex-m-rt-macros = { path = "macros", version = "=0.7.5" } cortex-m = { version = "0.7.4", path = "../cortex-m" } panic-halt = "0.2.0" cortex-m-semihosting = { path = "../cortex-m-semihosting" } +far = "0.2.1" [target.'cfg(not(target_os = "none"))'.dev-dependencies] compiletest_rs = "0.11" @@ -49,5 +50,8 @@ set-msplim = [] zero-init-ram = [] paint-stack = [] +[build-dependencies] +far = "0.2.1" + [package.metadata.docs.rs] features = ["device"] diff --git a/cortex-m-rt/build.rs b/cortex-m-rt/build.rs index 344ff872..c472da5b 100644 --- a/cortex-m-rt/build.rs +++ b/cortex-m-rt/build.rs @@ -1,8 +1,18 @@ +use far::{find, Render}; use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; use std::{env, ffi::OsStr}; +const FLASH_REGION_ENV: &str = "CORTEX_M_RT_FLASH_REGION"; +const RAM_REGION_ENV: &str = "CORTEX_M_RT_RAM_REGION"; + +#[derive(Render)] +struct LinkXReplacements { + flash_region: String, + ram_region: String, +} + fn main() { let mut target = env::var("TARGET").unwrap(); @@ -18,11 +28,29 @@ fn main() { // Put the linker script somewhere the linker can find it let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); - let link_x = include_bytes!("link.x.in"); + let link_x = include_str!("link.x.in"); + + // Replace regions in the linker script with the user's + // specified region names, or defaults if not specified + let tmpl = find::<_, LinkXReplacements>(link_x).unwrap(); + let mut replacements = LinkXReplacements { + flash_region: "FLASH".to_owned(), + ram_region: "RAM".to_owned(), + }; + if let Ok(region) = env::var(FLASH_REGION_ENV) { + println!("cargo:rerun-if-env-changed={}", FLASH_REGION_ENV); + replacements.flash_region = region; + }; + if let Ok(region) = env::var(RAM_REGION_ENV) { + println!("cargo:rerun-if-env-changed={}", RAM_REGION_ENV); + replacements.ram_region = region; + }; + let link_x = tmpl.replace(&replacements); + let mut f = if env::var_os("CARGO_FEATURE_DEVICE").is_some() { let mut f = File::create(out.join("link.x")).unwrap(); - f.write_all(link_x).unwrap(); + f.write_all(link_x.as_bytes()).unwrap(); // *IMPORTANT*: The weak aliases (i.e. `PROVIDED`) must come *after* `EXTERN(__INTERRUPTS)`. // Otherwise the linker will ignore user defined interrupts and always populate the table @@ -38,7 +66,7 @@ INCLUDE device.x"# f } else { let mut f = File::create(out.join("link.x")).unwrap(); - f.write_all(link_x).unwrap(); + f.write_all(link_x.as_bytes()).unwrap(); f }; diff --git a/cortex-m-rt/link.x.in b/cortex-m-rt/link.x.in index 39433827..ae39c1f8 100644 --- a/cortex-m-rt/link.x.in +++ b/cortex-m-rt/link.x.in @@ -60,13 +60,13 @@ PROVIDE(__pre_init = DefaultPreInit); /* # Sections */ SECTIONS { - PROVIDE(_ram_start = ORIGIN(RAM)); - PROVIDE(_ram_end = ORIGIN(RAM) + LENGTH(RAM)); + PROVIDE(_ram_start = ORIGIN({{ram_region}})); + PROVIDE(_ram_end = ORIGIN({{ram_region}}) + LENGTH({{ram_region}})); PROVIDE(_stack_start = _ram_end); - /* ## Sections in FLASH */ + /* ## Sections in {{flash_region}} */ /* ### Vector table */ - .vector_table ORIGIN(FLASH) : + .vector_table ORIGIN({{flash_region}}) : { __vector_table = .; @@ -87,7 +87,7 @@ SECTIONS /* Device specific interrupts */ KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */ - } > FLASH + } > {{flash_region}} PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table)); @@ -106,7 +106,7 @@ SECTIONS . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */ __etext = .; - } > FLASH + } > {{flash_region}} /* ### .rodata */ .rodata : ALIGN(4) @@ -120,9 +120,9 @@ SECTIONS section will have the correct alignment. */ . = ALIGN(4); __erodata = .; - } > FLASH + } > {{flash_region}} - /* ## Sections in RAM */ + /* ## Sections in {{ram_region}} */ /* ### .data */ .data : ALIGN(4) { @@ -130,7 +130,7 @@ SECTIONS __sdata = .; *(.data .data.*); . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ - } > RAM AT>FLASH + } > {{ram_region}} AT>{{flash_region}} /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to * use the .data loading mechanism by pushing __edata. Note: do not change * output region or load region in those user sections! */ @@ -143,14 +143,14 @@ SECTIONS /* ### .gnu.sgstubs This section contains the TrustZone-M veneers put there by the Arm GNU linker. */ /* Security Attribution Unit blocks must be 32 bytes aligned. */ - /* Note that this pads the FLASH usage to 32 byte alignment. */ + /* Note that this pads the {{flash_region}} usage to 32 byte alignment. */ .gnu.sgstubs : ALIGN(32) { . = ALIGN(32); __veneer_base = .; *(.gnu.sgstubs*) . = ALIGN(32); - } > FLASH + } > {{flash_region}} /* Place `__veneer_limit` outside the `.gnu.sgstubs` section because veneers are * always inserted last in the section, which would otherwise be _after_ the `__veneer_limit` symbol. */ @@ -165,7 +165,7 @@ SECTIONS *(.bss .bss.*); *(COMMON); /* Uninitialized C statics */ . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ - } > RAM + } > {{ram_region}} /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to * use the .bss zeroing mechanism by pushing __ebss. Note: do not change * output region or load region in those user sections! */ @@ -180,12 +180,12 @@ SECTIONS *(.uninit .uninit.*); . = ALIGN(4); __euninit = .; - } > RAM + } > {{ram_region}} /* Align `__sheap` and `_stack_end` pointers to 4 bytes */ . = ALIGN(4); - /* Place the heap start and stack end at the end of allocated RAM */ + /* Place the heap start and stack end at the end of allocated {{ram_region}} */ PROVIDE(__sheap = .); PROVIDE(_stack_end = .); @@ -209,11 +209,11 @@ SECTIONS /* Do not exceed this mark in the error messages below | */ /* # Alignment checks */ -ASSERT(ORIGIN(FLASH) % 4 == 0, " -ERROR(cortex-m-rt): the start of the FLASH region must be 4-byte aligned"); +ASSERT(ORIGIN({{flash_region}}) % 4 == 0, " +ERROR(cortex-m-rt): the start of the {{flash_region}} region must be 4-byte aligned"); -ASSERT(ORIGIN(RAM) % 4 == 0, " -ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned"); +ASSERT(ORIGIN({{ram_region}}) % 4 == 0, " +ERROR(cortex-m-rt): the start of the {{ram_region}} region must be 4-byte aligned"); ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " BUG(cortex-m-rt): .data is not 4-byte aligned"); @@ -230,7 +230,7 @@ BUG(cortex-m-rt): start of .heap is not 4-byte aligned"); ASSERT(_stack_start % 8 == 0, " ERROR(cortex-m-rt): stack start address is not 8-byte aligned. If you have set _stack_start, check it's set to an address which is a multiple of 8 bytes. -If you haven't, stack starts at the end of RAM by default. Check that both RAM +If you haven't, stack starts at the end of {{ram_region}} by default. Check that both {{ram_region}} origin and length are set to multiples of 8 in the `memory.x` file."); ASSERT(_stack_end % 4 == 0, " @@ -273,9 +273,9 @@ ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, " ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section Set _stext to an address greater than the end of .vector_table (See output of `nm`)"); -ASSERT(_stext >= ORIGIN(FLASH) && _stext < ORIGIN(FLASH) + LENGTH(FLASH), " -ERROR(cortex-m-rt): The .text section must be placed inside the FLASH memory. -Set _stext to an address within the FLASH region."); +ASSERT(_stext >= ORIGIN({{flash_region}}) && _stext < ORIGIN({{flash_region}}) + LENGTH({{flash_region}}), " +ERROR(cortex-m-rt): The .text section must be placed inside the {{flash_region}} memory. +Set _stext to an address within the {{flash_region}} region."); /* # Other checks */ ASSERT(SIZEOF(.got) == 0, " diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index 2aaeadde..f769aa5e 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -49,6 +49,11 @@ //! program will be placed in the `FLASH` region, whereas the `.bss` and `.data` sections, as well //! as the heap, will be placed in the `RAM` region. //! +//! The region names `FLASH` and `RAM` are defaults, but different names can be used if +//! desired. See the [Advanced Usage](#custom-memory-region-names) section for +//! details. Throughout the rest of this documentation, `FLASH` and `RAM` will be used to +//! indicate the purpose of these memory regions, but their names may be different. +//! //! ```text //! /* Linker script for the STM32F103C8T6 */ //! MEMORY @@ -308,6 +313,17 @@ //! The unmangled `main` symbol must have signature `extern "C" fn() -> !` or its invocation from //! `Reset` will result in undefined behavior. //! +//! ## Custom memory region names +//! +//! If an environment variable named `CORTEX_M_RT_FLASH_REGION` is defined, its value will be +//! used instead of `FLASH`. Similarly, if an environment variable named +//! `CORTEX_M_RT_RAM_REGION` is defined, its value will be used instead of `RAM`. +//! +//! This feature can be useful in projects where binary output from two crates (for example a +//! bootloader and an application) will both be loaded into Flash memory, but at different +//! addresses. Using these environment variables allows those crates to share a single +//! `memory.x` file but direct their output to their respective regions. +//! //! ## Incorporating device specific interrupts //! //! This section covers how an external crate can insert device specific interrupt handlers into the