From e014e4ec81a947ae8235a51dc2349af58bb5752d Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Tue, 9 Sep 2025 08:11:31 -0400 Subject: [PATCH 1/2] Test how the runtime computes ITCM start, size No changes here; we're testing an existing behavior. The next commit changes how this math works. --- src/host.rs | 70 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/src/host.rs b/src/host.rs index f301711..43472fe 100644 --- a/src/host.rs +++ b/src/host.rs @@ -696,19 +696,7 @@ fn write_flexram_memories( flexram_banks: &FlexRamBanks, ) -> io::Result<()> { if flexram_banks.itcm > 0 { - let itcm_size = flexram_banks.itcm * family.flexram_bank_size(); - let itcm_start = match family { - Family::Imxrt1010 - | Family::Imxrt1015 - | Family::Imxrt1020 - | Family::Imxrt1040 - | Family::Imxrt1050 - | Family::Imxrt1060 - | Family::Imxrt1064 - | Family::Imxrt1160 - | Family::Imxrt1170 => 0x00000000, - Family::Imxrt1180 => 0x10000000 - itcm_size, - }; + let (itcm_start, itcm_size) = family.itcm_start_size(flexram_banks.itcm); writeln!( output, "ITCM (RWX) : ORIGIN = {itcm_start:#X}, LENGTH = {itcm_size:#X}" @@ -798,6 +786,8 @@ pub enum Family { Imxrt1180, } +/// Adding a new MCU? You'll probably need to update +/// these methods. impl Family { /// Family identifier. /// @@ -954,6 +944,28 @@ impl Family { } } + /// Returns the start and size of the ITCM memory region. + const fn itcm_start_size(self, itcm_banks: u32) -> (u32, u32) { + let itcm_size = itcm_banks * self.flexram_bank_size(); + let itcm_start = match self { + Family::Imxrt1010 + | Family::Imxrt1015 + | Family::Imxrt1020 + | Family::Imxrt1040 + | Family::Imxrt1050 + | Family::Imxrt1060 + | Family::Imxrt1064 + | Family::Imxrt1160 + | Family::Imxrt1170 => 0x00000000, + Family::Imxrt1180 => 0x10000000 - itcm_size, + }; + (itcm_start, itcm_size) + } +} + +/// If you're adding a new MCU family, you probably +/// don't need to change these methods. +impl Family { /// Returns the starting address for the given `flexspi` instance. /// /// If a FlexSPI instance isn't available for this family, the return @@ -1071,7 +1083,7 @@ mod tests { use super::{Family, FlexRamBanks, RuntimeBuilder}; use std::{error, io}; - const ALL_FAMILIES: &[Family] = &[ + const MOST_FAMILIES: &[Family] = &[ Family::Imxrt1010, Family::Imxrt1015, Family::Imxrt1020, @@ -1080,6 +1092,7 @@ mod tests { Family::Imxrt1060, Family::Imxrt1064, Family::Imxrt1170, + // Imxrt1180 wasn't ever tested here. ]; type Error = Box; @@ -1171,7 +1184,7 @@ mod tests { ]; for (banks, expected) in TABLE { - let actual = banks.config(Family::Imxrt1010); + let actual = banks.config_gpr(); assert!( actual == *expected, "\nActual: {actual:#034b}\nExpected: {expected:#034b}\nBanks: {banks:?}" @@ -1181,7 +1194,7 @@ mod tests { #[test] fn runtime_builder_default_from_flexspi() -> Result<(), Error> { - for family in ALL_FAMILIES { + for family in MOST_FAMILIES { RuntimeBuilder::from_flexspi(*family, 16 * 1024 * 1024) .write_linker_script(&mut io::sink())?; } @@ -1201,7 +1214,7 @@ mod tests { dtcm: 32, ocram: 32, }; - for family in ALL_FAMILIES { + for family in MOST_FAMILIES { let res = RuntimeBuilder::from_flexspi(*family, 16 * 1024) .flexram_banks(banks) .write_linker_script(&mut io::sink()); @@ -1226,7 +1239,7 @@ mod tests { placement!(heap), ]; - for family in ALL_FAMILIES { + for family in MOST_FAMILIES { for placement in placements { let mut bldr = RuntimeBuilder::from_flexspi(*family, 16 * 1024); placement.0(&mut bldr); @@ -1235,4 +1248,25 @@ mod tests { } } } + + #[test] + fn itcm_start_size() { + // Most parts have an ITCM that could touch address 0. + for family in MOST_FAMILIES { + for itcm_banks in 0..=family.flexram_bank_count() { + let (start, size) = family.itcm_start_size(itcm_banks); + assert_eq!(start, 0); + assert_eq!(size, family.flexram_bank_size() * itcm_banks); + } + } + + // The 1180's ITCM never touches address 0 when the ITCM banks + // are properly configured. + let family = Family::Imxrt1180; + for itcm_banks in 0..=family.flexram_bank_count() { + let (start, size) = family.itcm_start_size(itcm_banks); + assert_ne!(start, 0); + assert_eq!(size, family.flexram_bank_size() * itcm_banks); + } + } } From ce97696bcd4515b5b2556163aa50c1b4723e7c0f Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Tue, 9 Sep 2025 08:29:25 -0400 Subject: [PATCH 2/2] Reserve address 0 by shifting ITCM start address The runtime previously allowed function placement at address 0 in ITCM. However, if you ever formed a pointer to the function placed there, it would look like a null pointer. And you would never be able to call that function if you relied on null pointer optimization. Also, most MCU reference manuals (RM) recommend against this placement. This commit reduces the total capacity of ITCM by 32 bytes, the smallest possible size of a MPU region. Note that this is greater than the RM's recommendation of a four byte reservation. It affects all supported MCUs, except the 1180. If you're so inclined, your MPU could disallow loads, stores, and execution from this reservation. Revised unit tests should cover this change. Additionally, you can manually verify that the ITCM region lengths are reduced by 32 bytes by opening the linker scripts generated by the ELF test suite. --- CHANGELOG.md | 4 ++++ src/host.rs | 19 +++++++++++++++---- tests/inspect_elf.rs | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a43b8e5..7e59f3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +Establish a 32 byte reservation at the start of ITCM where instructions are +never placed. This reduces the total capacity of ITCM by 32 bytes on nearly +all MCUs, except the 1180. + ## [0.1.7] 2025-06-14 Introduce `RuntimeBuilder::in_flash` for creating images that can be launched diff --git a/src/host.rs b/src/host.rs index 43472fe..ba299dd 100644 --- a/src/host.rs +++ b/src/host.rs @@ -946,7 +946,7 @@ impl Family { /// Returns the start and size of the ITCM memory region. const fn itcm_start_size(self, itcm_banks: u32) -> (u32, u32) { - let itcm_size = itcm_banks * self.flexram_bank_size(); + let mut itcm_size = itcm_banks * self.flexram_bank_size(); let itcm_start = match self { Family::Imxrt1010 | Family::Imxrt1015 @@ -956,7 +956,13 @@ impl Family { | Family::Imxrt1060 | Family::Imxrt1064 | Family::Imxrt1160 - | Family::Imxrt1170 => 0x00000000, + | Family::Imxrt1170 => { + // Establish a reservation for null pointers. + // Note that this reservation is the minimum + // size of an MPU region. + itcm_size = itcm_size.saturating_sub(32); + 32 + } Family::Imxrt1180 => 0x10000000 - itcm_size, }; (itcm_start, itcm_size) @@ -1252,11 +1258,16 @@ mod tests { #[test] fn itcm_start_size() { // Most parts have an ITCM that could touch address 0. + // However, the implementation reserves an MPU region + // at address 0. for family in MOST_FAMILIES { for itcm_banks in 0..=family.flexram_bank_count() { let (start, size) = family.itcm_start_size(itcm_banks); - assert_eq!(start, 0); - assert_eq!(size, family.flexram_bank_size() * itcm_banks); + assert_eq!(start, 32); + assert_eq!( + size, + (family.flexram_bank_size() * itcm_banks).saturating_sub(32) + ); } } diff --git a/tests/inspect_elf.rs b/tests/inspect_elf.rs index 8108f3f..e9a8f73 100644 --- a/tests/inspect_elf.rs +++ b/tests/inspect_elf.rs @@ -198,7 +198,7 @@ struct Section { } const DTCM: u64 = 0x2000_0000; -const ITCM: u64 = 0x0000_0000; +const ITCM: u64 = 0x0000_0020; const fn aligned(value: u64, alignment: u64) -> u64 { (value + (alignment - 1)) & !(alignment - 1)