aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--board/Cargo.toml1
-rw-r--r--board/build.rs25
-rw-r--r--src/host.rs103
-rw-r--r--src/host/imxrt-boot-header-1180.x3
-rw-r--r--src/host/imxrt-boot-header.x3
-rw-r--r--src/host/imxrt-link.x2
-rw-r--r--tests/inspect_elf.rs161
8 files changed, 273 insertions, 28 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2920663..810cb36 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
## [Unreleased]
+Introduce `RuntimeBuilder::in_flash` for creating images that can be launched
+by your own software, instead of NXP's boot ROM.
+
Place sections starting with `.xip` into the same load region of `.text`.
Unlike `.text`, the contents in `.xip` will not be relocated.
diff --git a/board/Cargo.toml b/board/Cargo.toml
index 9072274..9afdfb0 100644
--- a/board/Cargo.toml
+++ b/board/Cargo.toml
@@ -26,6 +26,7 @@ rtt-target = { version = "0.3", optional = true, features = ["cortex-m"] }
panic-rtt-target = { version = "0.1", optional = true, features = ["cortex-m"] }
[features]
+nonboot = []
rtic = []
# Begin board features.
teensy4 = [
diff --git a/board/build.rs b/board/build.rs
index 8474e85..4531667 100644
--- a/board/build.rs
+++ b/board/build.rs
@@ -7,6 +7,16 @@ fn extract_features() -> HashSet<String> {
.collect()
}
+/// Creates a runtime for a particular family, adjusting whether the image is expected to be boot
+/// based on provided features.
+fn create_runtime(family: imxrt_rt::Family, flash_size: usize) -> imxrt_rt::RuntimeBuilder {
+ if cfg!(feature = "nonboot") {
+ imxrt_rt::RuntimeBuilder::in_flash(family, flash_size, 16 * 1024)
+ } else {
+ imxrt_rt::RuntimeBuilder::from_flexspi(family, flash_size)
+ }
+}
+
/// Configures the runtime for a variety of boards.
///
/// Note that some automated tests may check these runtimes. Feel free to change
@@ -44,15 +54,12 @@ fn main() {
.heap_size_env_override("BOARD_HEAP")
.build()
.unwrap(),
- "imxrt1170evk_cm7" => imxrt_rt::RuntimeBuilder::from_flexspi(
- imxrt_rt::Family::Imxrt1170,
- 16 * 1024 * 1024,
- )
- .rodata(imxrt_rt::Memory::Dtcm)
- .stack_size_env_override("BOARD_STACK")
- .heap_size_env_override("BOARD_HEAP")
- .build()
- .unwrap(),
+ "imxrt1170evk_cm7" => create_runtime(imxrt_rt::Family::Imxrt1170, 8 * 1024 * 1024)
+ .rodata(imxrt_rt::Memory::Dtcm)
+ .stack_size_env_override("BOARD_STACK")
+ .heap_size_env_override("BOARD_HEAP")
+ .build()
+ .unwrap(),
_ => continue,
}
break;
diff --git a/src/host.rs b/src/host.rs
index eb10b22..e8325a7 100644
--- a/src/host.rs
+++ b/src/host.rs
@@ -127,9 +127,26 @@ fn region_alias(output: &mut dyn Write, name: &str, placement: Memory) -> io::Re
#[derive(Debug, Clone, PartialEq, Eq)]
struct FlashOpts {
size: usize,
+ offset: u32,
flexspi: FlexSpi,
}
+impl FlashOpts {
+ /// Produce the flash address of the image within
+ /// FlexSPI memory.
+ fn flash_origin(&self, family: Family) -> Option<u32> {
+ self.flexspi
+ .start_address(family)
+ .map(|start_address| start_address + self.offset)
+ }
+
+ /// A bootable image (with the boot header) isn't offset
+ /// in flash.
+ fn is_boot_image(&self) -> bool {
+ self.offset == 0
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq)]
struct EnvOverride {
default: usize,
@@ -342,6 +359,43 @@ impl RuntimeBuilder {
heap_size: EnvOverride::new(0),
flash_opts: Some(FlashOpts {
size: flash_size,
+ offset: 0,
+ flexspi: FlexSpi::family_default(family),
+ }),
+ linker_script_name: DEFAULT_LINKER_SCRIPT_NAME.into(),
+ }
+ }
+
+ /// Allocate a flash partition for this program to be booted by your software.
+ ///
+ /// `partition_size` is the size of the flash allocation, in bytes, for this
+ /// program. `partition_offset` describes the byte offset where the partition
+ /// starts. The offset is from the start of the FlexSPI memory region.
+ ///
+ /// The program constructed at this flash location cannot be booted by NXP's boot
+ /// ROM. You should bring your own software to execute this program. Note that
+ /// [the runtime behaviors](RuntimeBuilder) ensure that the vector table is placed
+ /// in flash at the given `partition_offset`.
+ ///
+ /// To compute a partition offset from two absolute flash addresses, use
+ /// [`Family::flexspi_start_addr`] to learn the FlexSPI starting address.
+ pub fn in_flash(family: Family, partition_size: usize, partition_offset: u32) -> Self {
+ Self {
+ family,
+ flexram_banks: family.default_flexram_banks(),
+ text: Memory::Itcm,
+ rodata: Memory::Ocram,
+ data: Memory::Ocram,
+ vectors: Memory::Dtcm,
+ bss: Memory::Ocram,
+ uninit: Memory::Ocram,
+ stack: Memory::Dtcm,
+ stack_size: EnvOverride::new(8 * 1024),
+ heap: Memory::Dtcm,
+ heap_size: EnvOverride::new(0),
+ flash_opts: Some(FlashOpts {
+ size: partition_size,
+ offset: partition_offset,
flexspi: FlexSpi::family_default(family),
}),
linker_script_name: DEFAULT_LINKER_SCRIPT_NAME.into(),
@@ -508,19 +562,21 @@ impl RuntimeBuilder {
if let Some(flash_opts) = &self.flash_opts {
write_flash_memory_map(writer, self.family, flash_opts, &self.flexram_banks)?;
- let boot_header_x = match self.family {
- Family::Imxrt1010
- | Family::Imxrt1015
- | Family::Imxrt1020
- | Family::Imxrt1040
- | Family::Imxrt1050
- | Family::Imxrt1060
- | Family::Imxrt1064
- | Family::Imxrt1160
- | Family::Imxrt1170 => include_bytes!("host/imxrt-boot-header.x").as_slice(),
- Family::Imxrt1180 => include_bytes!("host/imxrt-boot-header-1180.x").as_slice(),
- };
- writer.write_all(boot_header_x)?;
+ if flash_opts.is_boot_image() {
+ let boot_header_x = match self.family {
+ Family::Imxrt1010
+ | Family::Imxrt1015
+ | Family::Imxrt1020
+ | Family::Imxrt1040
+ | Family::Imxrt1050
+ | Family::Imxrt1060
+ | Family::Imxrt1064
+ | Family::Imxrt1160
+ | Family::Imxrt1170 => include_bytes!("host/imxrt-boot-header.x").as_slice(),
+ Family::Imxrt1180 => include_bytes!("host/imxrt-boot-header-1180.x").as_slice(),
+ };
+ writer.write_all(boot_header_x)?;
+ }
} else {
write_ram_memory_map(writer, self.family, &self.flexram_banks)?;
}
@@ -695,10 +751,7 @@ fn write_flash_memory_map(
writeln!(
output,
"FLASH (RX) : ORIGIN = {:#X}, LENGTH = {:#X}",
- flash_opts
- .flexspi
- .start_address(family)
- .expect("Already checked"),
+ flash_opts.flash_origin(family).expect("Already checked"),
flash_opts.size
)?;
write_flexram_memories(output, family, flexram_banks)?;
@@ -901,6 +954,22 @@ impl Family {
},
}
}
+
+ /// Returns the starting address for the given `flexspi` instance.
+ ///
+ /// If a FlexSPI instance isn't available for this family, the return
+ /// is `None`. Otherwise, the return is the starting address in the
+ /// MCU's memory map.
+ ///
+ /// ```
+ /// use imxrt_rt::{Family::*, FlexSpi::*};
+ ///
+ /// assert_eq!(Imxrt1060.flexspi_start_addr(FlexSpi1), Some(0x6000_0000));
+ /// assert!(Imxrt1010.flexspi_start_addr(FlexSpi2).is_none());
+ /// ```
+ pub fn flexspi_start_addr(self, flexspi: FlexSpi) -> Option<u32> {
+ flexspi.start_address(self)
+ }
}
/// FlexRAM bank allocations.
diff --git a/src/host/imxrt-boot-header-1180.x b/src/host/imxrt-boot-header-1180.x
index 101725a..cf93e7c 100644
--- a/src/host/imxrt-boot-header-1180.x
+++ b/src/host/imxrt-boot-header-1180.x
@@ -101,4 +101,7 @@ SECTIONS
} > FLASH
}
+ASSERT((__dcd_end - __dcd_start) % 4 == 0, "
+ERROR(imxrt-rt): .dcd (Device Configuration Data) size must be a multiple of 4 bytes.");
+
/* ===--- End imxrt-boot-header-1180.x ---=== */
diff --git a/src/host/imxrt-boot-header.x b/src/host/imxrt-boot-header.x
index 3f33603..165e858 100644
--- a/src/host/imxrt-boot-header.x
+++ b/src/host/imxrt-boot-header.x
@@ -78,4 +78,7 @@ SECTIONS
} > FLASH
}
+ASSERT((__dcd_end - __dcd_start) % 4 == 0, "
+ERROR(imxrt-rt): .dcd (Device Configuration Data) size must be a multiple of 4 bytes.");
+
/* ===--- End imxrt-boot-header.x ---=== */
diff --git a/src/host/imxrt-link.x b/src/host/imxrt-link.x
index dfe7355..7fcd76c 100644
--- a/src/host/imxrt-link.x
+++ b/src/host/imxrt-link.x
@@ -203,8 +203,6 @@ Dynamic relocations are not supported. If you are linking to C code compiled usi
the 'cc' crate then modify your build script to compile the C code _without_
the -fPIC flag. See the documentation of the `cc::Build.pic` method for details.");
-ASSERT((__dcd_end - __dcd_start) % 4 == 0, "
-ERROR(imxrt-rt): .dcd (Device Configuration Data) size must be a multiple of 4 bytes.");
/* Do not exceed this mark in the error messages above | */
/* ===--- End imxrt-link.x ---=== */
diff --git a/tests/inspect_elf.rs b/tests/inspect_elf.rs
index e9211eb..3576385 100644
--- a/tests/inspect_elf.rs
+++ b/tests/inspect_elf.rs
@@ -39,6 +39,36 @@ fn cargo_build_with_envs(board: &str, envs: &[(&str, &str)]) -> Result<PathBuf>
Ok(path)
}
+fn cargo_build_nonboot(board: &str) -> Result<PathBuf> {
+ let status = Command::new("cargo")
+ .arg("build")
+ .arg("--example=blink-rtic")
+ .arg(format!(
+ "--features=board/{},board/rtic,board/nonboot",
+ board
+ ))
+ .arg("--target=thumbv7em-none-eabihf")
+ .arg(format!("--target-dir=target/{}-nonboot", board))
+ .arg("--quiet")
+ .spawn()?
+ .wait()?;
+
+ // TODO(summivox): `ExitStatus::exit_ok()` stabilization (can be chained after the `.wait()?)
+ if !status.success() {
+ return Err(format!(
+ "Building board '{}' failed: process returned {:?}",
+ board, status,
+ )
+ .into());
+ }
+
+ let path = PathBuf::from(format!(
+ "target/{}-nonboot/thumbv7em-none-eabihf/debug/examples/blink-rtic",
+ board
+ ));
+ Ok(path)
+}
+
/// Build an example, returning a path to the ELF.
fn cargo_build(board: &str) -> Result<PathBuf> {
cargo_build_with_envs(board, &[])
@@ -644,3 +674,134 @@ fn imxrt1170evk_cm7() {
"increment_data is not XiP"
);
}
+
+#[test]
+#[ignore = "building an example can take time"]
+fn imxrt1170evk_cm7_nonboot() {
+ const IMAGE_OFFSET: u64 = 16 * 1024;
+ let path = cargo_build_nonboot("imxrt1170evk-cm7").expect("Unable to build example");
+ let contents = fs::read(path).expect("Could not read ELF file");
+ let elf = Elf::parse(&contents).expect("Could not parse ELF");
+
+ let binary = ImxrtBinary::new(&elf, &contents);
+ assert_eq!(binary.symbol_value("__dcd_start"), None);
+ assert_eq!(binary.symbol_value("__dcd_end"), None);
+ assert_eq!(binary.symbol_value("__dcd"), None);
+ assert!(binary.fcb().is_err());
+ assert_eq!(
+ binary.flexram_config().unwrap(),
+ 0b1111111111111111_1010101010101010
+ );
+
+ assert!(
+ binary.ivt().is_err(),
+ "Non boot image still contains boot IVT"
+ );
+ assert!(
+ binary.section(".boot").is_err(),
+ "Boot section is included in a non boot image"
+ );
+
+ let stack = binary.section(".stack").unwrap();
+ assert_eq!(
+ Section {
+ address: DTCM,
+ size: 8 * 1024
+ },
+ stack,
+ "stack not at ORIGIN(DTCM), or not 8 KiB large"
+ );
+ assert_eq!(binary.section_lma(".stack"), stack.address);
+
+ let vector_table = binary.section(".vector_table").unwrap();
+ assert_eq!(
+ Section {
+ address: stack.address + stack.size,
+ size: 16 * 4 + 240 * 4
+ },
+ vector_table,
+ "vector table not at expected VMA behind the stack"
+ );
+ assert!(
+ vector_table.address % 1024 == 0,
+ "vector table is not 1024-byte aligned"
+ );
+ assert_eq!(
+ binary.section_lma(".vector_table"),
+ 0x3000_0000 + IMAGE_OFFSET
+ );
+
+ let xip = binary.section(".xip").unwrap();
+ // xip's lma==vma
+ assert_eq!(
+ xip.address,
+ 0x3000_0000 + IMAGE_OFFSET + vector_table.size,
+ "xip"
+ );
+ assert_eq!(
+ binary.section_lma(".xip"),
+ 0x3000_0000 + IMAGE_OFFSET + vector_table.size,
+ "text VMA expected behind vector table"
+ );
+
+ let text = binary.section(".text").unwrap();
+ assert_eq!(text.address, ITCM, "text");
+ assert_eq!(
+ binary.section_lma(".text"),
+ 0x3000_0000 + IMAGE_OFFSET + aligned(xip.size, 4) + vector_table.size,
+ "text VMA expected behind vector table"
+ );
+
+ let rodata = binary.section(".rodata").unwrap();
+ assert_eq!(
+ rodata.address,
+ vector_table.address + vector_table.size,
+ "rodata moved to DTCM behind vector table"
+ );
+ assert!(
+ binary.section_lma(".rodata") >= 0x3000_2000 + vector_table.size + aligned(text.size, 4),
+ );
+
+ let data = binary.section(".data").unwrap();
+ assert_eq!(data.address, 0x2024_0000, "data VMA in OCRAM");
+ assert_eq!(
+ data.size, 4,
+ "blink-rtic expected to have a single static mut u32"
+ );
+ assert_eq!(
+ binary.section_lma(".data"),
+ binary.section_lma(".rodata") + aligned(rodata.size, 4),
+ "data LMA starts behind rodata"
+ );
+
+ let bss = binary.section(".bss").unwrap();
+ assert_eq!(
+ bss.address,
+ data.address + aligned(data.size, 4),
+ "bss in OCRAM behind data"
+ );
+ assert_eq!(binary.section_lma(".bss"), bss.address, "bss is NOLOAD");
+
+ let uninit = binary.section(".uninit").unwrap();
+ assert_eq!(
+ uninit.address,
+ bss.address + aligned(bss.size, 4),
+ "uninit in OCRAM behind bss"
+ );
+ assert_eq!(
+ binary.section_lma(".uninit"),
+ uninit.address,
+ "uninit is NOLOAD"
+ );
+
+ let heap = binary.section(".heap").unwrap();
+ assert_eq!(
+ Section {
+ address: rodata.address + aligned(rodata.size, 4),
+ size: 0,
+ },
+ heap,
+ "0 byte heap in DTCM behind rodata table"
+ );
+ assert_eq!(binary.section_lma(".heap"), heap.address, "Heap is NOLOAD");
+}