imxrt-rt 0.1.8

Startup and runtime support for i.MX RT processors.
Documentation
//! i.MX RT target support.
//!
//! Defines a `cortex-m-rt` pre-init function that disables watchdogs and initializes TCM.
//! It then copies instructions, read-only data, and the vector table to their intended location.
//! This only happens if LMAs and VMAs differ.
//!
//! There's a few behaviors worth mentioning:
//!
//! - The implementation never clears the INIT_xTCM_EN bits in GPR16 if the xTCM is unused.
//!   The first reason is because we can't do this on the 1170 chips; the bits are reserved and
//!   should always be set (guessing its for the CM4, which always uses TCM). The second reason
//!   is that it doesn't seem necessary; we'll let xTCM initialize out of non-POR reset. From what
//!   I could gather, this would be the case if we set fuse values to specify an all-OCRAM config,
//!   and nothing says we need to flip these bits if the _fuses_ don't allocate xTCM. (Maybe this
//!   automagically happens? Not sure.)
//! - We're not changing CM7_xTCMSZ to reflect the xTCM sizes. Again, the setting isn't available
//!   on the 1170 chips. It's also OK to keep the POR value, since it represents the maximum-possible
//!   TCM size. This means that users have finer control over xTCM memory sizes, but invalid xTCM accesses
//!   won't cause a bus fault. See 3.1.3.2. in AN12077 for more discussion.
//!
//! Other notes:
//!
//! It's important that something sets the stack pointer. On the 10xx, the boot ROM sets the stack
//! pointer. But on the 11xx, the boot ROM doesn't set the stack pointer. See the link below for
//! more information. This implementation relies on the cortex-m-rt 0.7.2 "set-sp" feature to always
//! set the stack pointer, no matter the target chip.
//!
//! <https://community.nxp.com/t5/i-MX-RT/RT1176-ROM-code-does-not-set-stack-pointer-correctly/td-p/1388830>

use core::{arch::global_asm, ffi::c_void};

pub use cortex_m_rt::*;

global_asm! {r#"
.cfi_sections .debug_frame
.section .__pre_init,"ax"
.global __pre_init
.type __pre_init,%function
.thumb_func
.cfi_startproc

__pre_init:
    ldr r0, =__imxrt_family         @ Need to know which chip family we're initializing.
    ldr r1, =1180
    cmp r0, r1                      @ Is this an 1180?
    beq flexram_1180
    ldr r1, =1100
    cmp r0, r1                      @ Is this an 1160 or 1170?

    # Disable RTWODOG3.
    ite gt
    ldrgt r2, =0x40038000           @ RTWDOG base address for 11xx chips...
    ldrle r2, =0x400BC000           @ RTWDOG base address for 10xx chips...
    ldr r3, =0xD928C520             @ RTWDOG magic number
    str r3, [r2, #4]                @ RTWDOG[CNT] = 0xD928C520.
    ldr r3, [r2]                    @ r3 = RTWDOG[CS]
    bic r3, r3, #1<<7               @ r3 = r3 & !(1 << 7), clears enable.
    str r3, [r2]                    @ RTWDOG[CS] = r3

    # Prepare FlexRAM regions.
    ldr r0, =0x400AC000             @ IMXRT_IOMUXC_GPR base address for 10xx chips, overwritten if actually 11xx...
    ldr r1, =__flexram_config       @ Value for GPR17 (and GPR18 for 11xx)
    itttt gt                        @ Need a few extra operations to handle 11xx split banks.
    ldrgt r0, =0x400E4000           @ IMXRT_IOMUXC_GPR base address for 11xx chips, overwrite 10xx address...
    lsrgt r2, r1, #16               @ r2 = ((unsigned)r1 >> 16)
    strgt r2, [r0, #72]             @ *(IMXRT_IOMUXC_GPR + 18) = r2
    ubfxgt r1, r1, #0, #16          @ r1 = ((unsigned)r1 >> 0) & 0xFFFF, overwrite r1 with lower halfword.
    str r1, [r0, #68]               @ *(IMXRT_IOMUXC_GPR + 17) = r1
    ldr r1, [r0, #64]               @ r1 = *(IMXRT_IOMUXC_GPR + 16)
    orr r1, r1, #1<<2               @ r1 |= 1 << 2
    str r1, [r0, #64]               @ *(IMXRT_IOMUXC_GPR + 16) = r1
    b copy_from_flash

    flexram_1180:
    ldr r0, =0x444F0060             @ M33_CONFIG
    ldr r1, =__flexram_config
    str r1, [r0, #0]

    copy_from_flash:
    # Conditionally copy text.
    ldr r0, =__stext
    ldr r2, =__sitext
    cmp r2, r0
    beq 42f

    ldr r1, =__etext
    43:
    cmp r1, r0
    beq 42f
    ldm r2!, {{r3}}
    stm r0!, {{r3}}
    b 43b
    42:

    # Conditionally copy the vector table.
    ldr r0, =__svector_table
    ldr r2, =__sivector_table
    cmp r2, r0
    beq 52f

    ldr r1, =__evector_table
    53:
    cmp r1, r0
    beq 52f
    ldm r2!, {{r3}}
    stm r0!, {{r3}}
    b 53b
    52:

    # Conditionally copy read-only data.
    ldr r0, =__srodata
    ldr r2, =__sirodata
    cmp r2, r0
    beq 62f

    ldr r1, =__erodata
    63:
    cmp r1, r0
    beq 62f
    ldm r2!, {{r3}}
    stm r0!, {{r3}}
    b 63b
    62:

    # All done; back to the reset handler.
    bx lr

.cfi_endproc
.size __pre_init, . - __pre_init
"#
}

/// Returns a pointer to the end of the heap.
///
/// The returned pointer is guaranteed to be 4-byte aligned.
#[inline]
pub fn heap_end() -> *mut u32 {
    extern "C" {
        static mut __eheap: c_void;
    }

    // It used to be unsafe. Keeping it unsafe is backwards
    // compatible.
    #[allow(unused_unsafe)]
    unsafe {
        core::ptr::addr_of_mut!(__eheap) as _
    }
}