aboutsummaryrefslogtreecommitdiff
path: root/src/host.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/host.rs')
-rw-r--r--src/host.rs170
1 files changed, 128 insertions, 42 deletions
diff --git a/src/host.rs b/src/host.rs
index 2e47e50..131457a 100644
--- a/src/host.rs
+++ b/src/host.rs
@@ -11,7 +11,7 @@
use std::{
env,
fmt::Display,
- fs::{self, File},
+ fs,
io::{self, Write},
path::PathBuf,
};
@@ -301,6 +301,9 @@ impl RuntimeBuilder {
/// Commit the runtime configuration.
///
+ /// `build()` ensures that the generated linker script is available to the
+ /// linker.
+ ///
/// # Errors
///
/// The implementation ensures that your chip can support the FlexRAM bank
@@ -321,8 +324,6 @@ impl RuntimeBuilder {
/// the linker. No matter the error path, the implementation ensures that there
/// will be an error.
pub fn build(&self) -> Result<(), Box<dyn std::error::Error>> {
- self.check_configurations()?;
-
// Since `build` is called from a build script, the output directory
// represents the path to the _user's_ crate.
let out_dir = PathBuf::from(env::var("OUT_DIR")?);
@@ -331,69 +332,87 @@ impl RuntimeBuilder {
// The main linker script expects to INCLUDE this file. This file
// uses region aliases to associate region names to actual memory
// regions (see the Memory enum).
- let mut memory_x = File::create(out_dir.join("imxrt-memory.x"))?;
+ let mut in_memory = Vec::new();
+ self.write_linker_script(&mut in_memory)?;
+ fs::write(out_dir.join(&self.linker_script_name), &in_memory)?;
+ Ok(())
+ }
+
+ /// Write the generated linker script into the provided writer.
+ ///
+ /// Use this if you want more control over where the generated linker script
+ /// ends up. Otherwise, you should prefer [`build()`](Self::build) for an
+ /// easier experience.
+ ///
+ /// Unlike `build()`, this method does not ensure that the linker script is
+ /// available to the linker. Additionally, this method does not consider
+ /// the value set by [`linker_script_name`](Self::linker_script_name).
+ ///
+ /// # Errors
+ ///
+ /// See [`build()`](Self::build) to understand the possible errors.
+ fn write_linker_script(
+ &self,
+ writer: &mut dyn Write,
+ ) -> Result<(), Box<dyn std::error::Error>> {
+ self.check_configurations()?;
if let Some(flash_opts) = &self.flash_opts {
- write_flash_memory_map(&mut memory_x, self.family, flash_opts, &self.flexram_banks)?;
+ write_flash_memory_map(writer, self.family, flash_opts, &self.flexram_banks)?;
+
+ let boot_header_x = include_bytes!("host/imxrt-boot-header.x");
+ writer.write_all(boot_header_x)?;
} else {
- write_ram_memory_map(&mut memory_x, self.family, &self.flexram_banks)?;
+ write_ram_memory_map(writer, self.family, &self.flexram_banks)?;
}
#[cfg(feature = "device")]
- writeln!(&mut memory_x, "INCLUDE device.x")?;
+ writeln!(writer, "INCLUDE device.x")?;
// Keep these alias names in sync with the primary linker script.
// The main linker script uses these region aliases for placing
// sections. Then, the user specifies the actual placement through
// the builder. This saves us the step of actually generating SECTION
// commands.
- region_alias(&mut memory_x, "TEXT", self.text)?;
- region_alias(&mut memory_x, "VTABLE", self.vectors)?;
- region_alias(&mut memory_x, "RODATA", self.rodata)?;
- region_alias(&mut memory_x, "DATA", self.data)?;
- region_alias(&mut memory_x, "BSS", self.bss)?;
- region_alias(&mut memory_x, "UNINIT", self.uninit)?;
-
- region_alias(&mut memory_x, "STACK", self.stack)?;
- region_alias(&mut memory_x, "HEAP", self.heap)?;
+ region_alias(writer, "TEXT", self.text)?;
+ region_alias(writer, "VTABLE", self.vectors)?;
+ region_alias(writer, "RODATA", self.rodata)?;
+ region_alias(writer, "DATA", self.data)?;
+ region_alias(writer, "BSS", self.bss)?;
+ region_alias(writer, "UNINIT", self.uninit)?;
+
+ region_alias(writer, "STACK", self.stack)?;
+ region_alias(writer, "HEAP", self.heap)?;
// Used in the linker script and / or target code.
- writeln!(&mut memory_x, "__stack_size = {:#010X};", self.stack_size)?;
- writeln!(&mut memory_x, "__heap_size = {:#010X};", self.heap_size)?;
+ writeln!(writer, "__stack_size = {:#010X};", self.stack_size)?;
+ writeln!(writer, "__heap_size = {:#010X};", self.heap_size)?;
if self.flash_opts.is_some() {
// Runtime will see different VMA and LMA, and copy the sections.
- region_alias(&mut memory_x, "LOAD_VTABLE", Memory::Flash)?;
- region_alias(&mut memory_x, "LOAD_TEXT", Memory::Flash)?;
- region_alias(&mut memory_x, "LOAD_RODATA", Memory::Flash)?;
- region_alias(&mut memory_x, "LOAD_DATA", Memory::Flash)?;
+ region_alias(writer, "LOAD_VTABLE", Memory::Flash)?;
+ region_alias(writer, "LOAD_TEXT", Memory::Flash)?;
+ region_alias(writer, "LOAD_RODATA", Memory::Flash)?;
+ region_alias(writer, "LOAD_DATA", Memory::Flash)?;
} else {
// When the VMA and LMA are equal, the runtime performs no copies.
- region_alias(&mut memory_x, "LOAD_VTABLE", self.vectors)?;
- region_alias(&mut memory_x, "LOAD_TEXT", self.text)?;
- region_alias(&mut memory_x, "LOAD_RODATA", self.rodata)?;
- region_alias(&mut memory_x, "LOAD_DATA", self.data)?;
+ region_alias(writer, "LOAD_VTABLE", self.vectors)?;
+ region_alias(writer, "LOAD_TEXT", self.text)?;
+ region_alias(writer, "LOAD_RODATA", self.rodata)?;
+ region_alias(writer, "LOAD_DATA", self.data)?;
}
// Referenced in target code.
writeln!(
- &mut memory_x,
+ writer,
"__flexram_config = {:#010X};",
self.flexram_banks.config()
)?;
// The target runtime looks at this value to predicate some pre-init instructions.
// Could be helpful for binary identification, but it's an undocumented feature.
- writeln!(&mut memory_x, "__imxrt_family = {};", self.family.id(),)?;
+ writeln!(writer, "__imxrt_family = {};", self.family.id(),)?;
- // Place the primary linker script in the user's output directory. Name may be decided
- // by the user.
let link_x = include_bytes!("host/imxrt-link.x");
- fs::write(out_dir.join(&self.linker_script_name), link_x)?;
-
- // Also place the boot header in the search path. Do this unconditionally (even if
- // the user is booting from RAM). Name matters, since it's INCLUDEd in our linker
- // scripts.
- let boot_header_x = include_bytes!("host/imxrt-boot-header.x");
- fs::write(out_dir.join("imxrt-boot-header.x"), boot_header_x)?;
+ writer.write_all(link_x)?;
Ok(())
}
@@ -490,9 +509,6 @@ fn write_flexram_memories(
}
/// Generate a linker script MEMORY command that includes a FLASH block.
-///
-/// If called, the linker script includes the boot header, which is also
-/// expressed as a linker script.
fn write_flash_memory_map(
output: &mut dyn Write,
family: Family,
@@ -517,7 +533,6 @@ fn write_flash_memory_map(
write_flexram_memories(output, family, flexram_banks)?;
writeln!(output, "}}")?;
writeln!(output, "__fcb_offset = {:#X};", family.fcb_offset())?;
- writeln!(output, "INCLUDE imxrt-boot-header.x")?;
Ok(())
}
@@ -739,7 +754,21 @@ impl FlexRamBanks {
#[cfg(test)]
mod tests {
- use super::FlexRamBanks;
+ use crate::Memory;
+
+ use super::{Family, FlexRamBanks, RuntimeBuilder};
+ use std::{error, io};
+
+ const ALL_FAMILIES: &[Family] = &[
+ Family::Imxrt1010,
+ Family::Imxrt1015,
+ Family::Imxrt1020,
+ Family::Imxrt1050,
+ Family::Imxrt1060,
+ Family::Imxrt1064,
+ Family::Imxrt1170,
+ ];
+ type Error = Box<dyn error::Error>;
#[test]
fn flexram_config() {
@@ -836,4 +865,61 @@ mod tests {
);
}
}
+
+ #[test]
+ fn runtime_builder_default_from_flexspi() -> Result<(), Error> {
+ for family in ALL_FAMILIES {
+ RuntimeBuilder::from_flexspi(*family, 16 * 1024 * 1024)
+ .write_linker_script(&mut io::sink())?;
+ }
+ Ok(())
+ }
+
+ /// Strange but currently allowed.
+ #[test]
+ fn runtime_builder_from_flexspi_no_flash() -> Result<(), Error> {
+ RuntimeBuilder::from_flexspi(Family::Imxrt1060, 0).write_linker_script(&mut io::sink())
+ }
+
+ #[test]
+ fn runtime_builder_too_many_flexram_banks() {
+ let banks = FlexRamBanks {
+ itcm: 32,
+ dtcm: 32,
+ ocram: 32,
+ };
+ for family in ALL_FAMILIES {
+ let res = RuntimeBuilder::from_flexspi(*family, 16 * 1024)
+ .flexram_banks(banks)
+ .write_linker_script(&mut io::sink());
+ assert!(res.is_err(), "{family:?}");
+ }
+ }
+
+ #[test]
+ fn runtime_builder_invalid_flash_section() {
+ type Placer = fn(&mut RuntimeBuilder) -> &mut RuntimeBuilder;
+ macro_rules! placement {
+ ($section:ident) => {
+ (|bldr| bldr.$section(Memory::Flash), stringify!($section))
+ };
+ }
+ let placements: &[(Placer, &'static str)] = &[
+ placement!(data),
+ placement!(vectors),
+ placement!(bss),
+ placement!(uninit),
+ placement!(stack),
+ placement!(heap),
+ ];
+
+ for family in ALL_FAMILIES {
+ for placement in placements {
+ let mut bldr = RuntimeBuilder::from_flexspi(*family, 16 * 1024);
+ placement.0(&mut bldr);
+ let res = bldr.write_linker_script(&mut io::sink());
+ assert!(res.is_err(), "{:?}, section: {}", family, placement.1);
+ }
+ }
+ }
}