Skip to main content

xtensa_lx_rt/
lib.rs

1//! Minimal startup/runtime for Xtensa LX CPUs.
2//!
3//! ## Feature Flags
4#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
5#![doc(html_logo_url = "https://docs.espressif.com/projects/rust/esp-rs-grey-bg.svg")]
6#![allow(asm_sub_register, named_asm_labels)]
7#![feature(asm_experimental_arch)]
8#![no_std]
9
10use core::arch::global_asm;
11
12pub use macros::{entry, exception, interrupt, pre_init};
13pub use xtensa_lx;
14
15pub mod exception;
16pub mod interrupt;
17
18#[doc(hidden)]
19#[unsafe(no_mangle)]
20pub unsafe extern "C" fn no_init_hook() {}
21
22unsafe extern "C" {
23    fn __pre_init();
24    fn __post_init();
25
26    fn __zero_bss() -> bool;
27    fn __init_data() -> bool;
28
29    fn main() -> !;
30
31    static _bss_start: u32;
32    static _bss_end: u32;
33
34    static _data_start: u32;
35    static _data_end: u32;
36    static _sidata: u32;
37
38    static _init_start: u32;
39
40    static _stack_start_cpu0: u32;
41}
42
43global_asm!(
44    "
45    .section .rwtext,\"ax\",@progbits
46    .literal sym__pre_init, {__pre_init}
47    .literal sym__post_init, {__post_init}
48    .literal sym__zero_bss, {__zero_bss}
49    .literal sym_main, {main}
50
51    .literal sym_stack_start_cpu0, {_stack_start_cpu0}
52
53    .literal sym_init_start, {_init_start}
54    .literal sym_bss_end, {_bss_end}
55    .literal sym_bss_start, {_bss_start}
56    .literal sym__init_data, {__init_data}
57    .literal sym_data_start, {_data_start}
58    .literal sym_data_end, {_data_end}
59    .literal sym_sidata, {_sidata}
60",
61    __pre_init = sym __pre_init,
62    __post_init = sym __post_init,
63    __zero_bss = sym __zero_bss,
64
65    _stack_start_cpu0 = sym _stack_start_cpu0,
66
67    _bss_end =  sym _bss_end,
68    _bss_start =  sym _bss_start,
69    __init_data = sym __init_data,
70    _data_start = sym _data_start,
71    _data_end = sym _data_end,
72    _sidata = sym _sidata,
73
74    _init_start = sym _init_start,
75    main = sym main,
76);
77
78// `Reset` must be a single item: rustc does not emit separate `global_asm!` items in source
79// order.
80cfg_global_asm!(
81    "
82    // _xtensa_lx_rt_zero_fill
83    //
84    // Input arguments:
85    // a2: start address (used as a cursor)
86    // a3: end address
87
88    .section .rwtext,\"ax\",@progbits
89    .global _xtensa_lx_rt_zero_fill
90    .p2align 2
91    .type _xtensa_lx_rt_zero_fill,@function
92_xtensa_lx_rt_zero_fill:
93    entry  a1, 0                  // Leaf function, no need to reserve stack
94    bgeu   a2, a3, .Lfill_done    // If start >= end, skip zeroing
95    movi.n a5, 0
96
97.Lfill_loop:
98    s32i.n a5, a2, 0              // Store the zero at the current cursor
99    addi.n a2, a2, 4              // Increment the cursor by 4 bytes
100    bltu   a2, a3, .Lfill_loop    // If cursor < end, repeat
101.Lfill_done:
102    retw.n
103
104    // _xtensa_lx_rt_copy
105    //
106    // Input arguments:
107    // a2: source address
108    // a3: destination start address (used as a cursor)
109    // a4: destination end address
110
111    .section .rwtext,\"ax\",@progbits
112    .global _xtensa_lx_rt_copy
113    .p2align 2
114    .type _xtensa_lx_rt_copy,@function
115_xtensa_lx_rt_copy:
116    entry  a1, 0                 // Leaf function, no need to reserve stack
117    bgeu   a3, a4, .Lcopy_done   // If start >= end, skip copying
118.Lcopy_loop:
119    l32i.n a5, a2, 0             // Load word from source pointer
120    s32i.n a5, a3, 0             // Store word at destination pointer
121    addi.n a3, a3, 4             // Increment destination pointer by 4 bytes
122    addi.n a2, a2, 4             // Increment source pointer by 4 bytes
123    bltu   a3, a4, .Lcopy_loop   // If cursor < end, repeat
124.Lcopy_done:
125    retw.n
126
127    .section .rwtext,\"ax\",@progbits
128    .global Reset
129    .p2align 2
130    .type Reset,@function
131Reset:
132    entry  a1, 0x10                 // We're using call4/callx4, reserve 16 bytes for register spill area
133    movi   a0, 0                    // Trash the return address. Debuggers may use this to stop unwinding.
134
135    wsr.intenable a0                // Disable interrupts
136
137    l32r   a2, sym_stack_start_cpu0 // a2 is our temporary value register
138    mov    sp, a2                   // Set the stack pointer.
139
140    l32r   a2, sym__pre_init
141    callx4 a2                       // Call the pre-initialization function.
142
143.Linit_bss:
144    l32r   a2, sym__zero_bss        // Do we need to zero-initialize memory?
145    callx4 a2
146    beqz   a6, .Linit_data          // No -> skip to copying initialized data
147
148    l32r   a6, sym_bss_start        // Set input range to .bss
149    l32r   a7, sym_bss_end          //
150    call4  _xtensa_lx_rt_zero_fill  // Zero-fill
151
152.Linit_data:
153    l32r   a2, sym__init_data       // Do we need to initialize data sections?
154    callx4 a2
155    beqz   a6, .Linit_data_done     // If not, skip initialization
156
157    l32r   a6, sym_sidata           // Arguments - source data pointer
158    l32r   a7, sym_data_start       //           - destination pointer
159    l32r   a8, sym_data_end         //           - destination end pointer
160    call4  _xtensa_lx_rt_copy       // Copy .data section
161
162.Linit_data_done:
163    memw    // Make sure all writes are completed before proceeding. At this point, all static variables have been initialized.
164",
165    // According to 4.4.7.2 of the xtensa isa, ccount and compare are undefined on
166    // reset, set all values to zero to disable. ("timer interrupts are cleared by writing CCOMPARE[i]")
167    #[cfg(XCHAL_HAVE_TIMER0)]
168    "wsr.ccompare0 a0",
169    #[cfg(XCHAL_HAVE_TIMER1)]
170    "wsr.ccompare1 a0",
171    #[cfg(XCHAL_HAVE_TIMER2)]
172    "wsr.ccompare2 a0",
173    #[cfg(XCHAL_HAVE_TIMER3)]
174    "wsr.ccompare3 a0",
175    #[cfg(any(
176        XCHAL_HAVE_TIMER0,
177        XCHAL_HAVE_TIMER1,
178        XCHAL_HAVE_TIMER2,
179        XCHAL_HAVE_TIMER3
180    ))]
181    "isync",
182    "
183    l32r   a2, sym_init_start // vector table address
184    wsr.vecbase a2
185
186    l32r   a2, sym__post_init
187    callx4 a2
188
189    l32r   a2, sym_main       // program entry point
190    callx4 a2
191    ",
192);
193
194// We redefine these functions to avoid pulling in `xtensa-lx` as a dependency:
195
196// CPU Interrupts
197unsafe extern "C" {
198    #[cfg(XCHAL_HAVE_TIMER0)]
199    pub fn Timer0(save_frame: &mut crate::exception::Context);
200    #[cfg(XCHAL_HAVE_TIMER1)]
201    pub fn Timer1(save_frame: &mut crate::exception::Context);
202    #[cfg(XCHAL_HAVE_TIMER2)]
203    pub fn Timer2(save_frame: &mut crate::exception::Context);
204    #[cfg(XCHAL_HAVE_TIMER3)]
205    pub fn Timer3(save_frame: &mut crate::exception::Context);
206
207    #[cfg(XCHAL_HAVE_PROFILING)]
208    pub fn Profiling(save_frame: &mut crate::exception::Context);
209
210    #[cfg(XCHAL_HAVE_SOFTWARE0)]
211    pub fn Software0(save_frame: &mut crate::exception::Context);
212    #[cfg(XCHAL_HAVE_SOFTWARE1)]
213    pub fn Software1(save_frame: &mut crate::exception::Context);
214
215    #[cfg(XCHAL_HAVE_NMI)]
216    pub fn NMI(save_frame: &mut crate::exception::Context);
217}
218
219#[doc(hidden)]
220#[unsafe(no_mangle)]
221pub extern "C" fn default_mem_hook() -> bool {
222    true // default to zeroing bss & initializing data
223}
224
225#[doc(hidden)]
226#[macro_export]
227macro_rules! cfg_asm {
228    (@inner, [$($x:tt)*], [$($opts:tt)*], ) => {
229        asm!($($x)* $($opts)*)
230    };
231    (@inner, [$($x:tt)*], [$($opts:tt)*], #[cfg($meta:meta)] $asm:literal, $($rest:tt)*) => {
232        #[cfg($meta)]
233        cfg_asm!(@inner, [$($x)* $asm,], [$($opts)*], $($rest)*);
234        #[cfg(not($meta))]
235        cfg_asm!(@inner, [$($x)*], [$($opts)*], $($rest)*)
236    };
237    (@inner, [$($x:tt)*], [$($opts:tt)*], $asm:literal, $($rest:tt)*) => {
238        cfg_asm!(@inner, [$($x)* $asm,], [$($opts)*], $($rest)*)
239    };
240    ({$($asms:tt)*}, $($opts:tt)*) => {
241        cfg_asm!(@inner, [], [$($opts)*], $($asms)*)
242    };
243}
244
245#[doc(hidden)]
246#[macro_export]
247macro_rules! cfg_global_asm {
248    {@inner, [$($x:tt)*], } => {
249        global_asm!{$($x)*}
250    };
251    (@inner, [$($x:tt)*], #[cfg($meta:meta)] $asm:literal, $($rest:tt)*) => {
252        #[cfg($meta)]
253        cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
254        #[cfg(not($meta))]
255        cfg_global_asm!{@inner, [$($x)*], $($rest)*}
256    };
257    {@inner, [$($x:tt)*], $asm:literal, $($rest:tt)*} => {
258        cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
259    };
260    {$($asms:tt)*} => {
261        cfg_global_asm!{@inner, [], $($asms)*}
262    };
263}