xtensa_lx_rt/
lib.rs

1//! Minimal startup/runtime for Xtensa LX CPUs.
2//!
3//! ## Feature Flags
4#![doc = document_features::document_features!()]
5#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
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
78global_asm!(
79    "
80    // _xtensa_lx_rt_zero_fill
81    //
82    // Input arguments:
83    // a2: start address (used as a cursor)
84    // a3: end address
85
86    .section .rwtext,\"ax\",@progbits
87    .global _xtensa_lx_rt_zero_fill
88    .p2align 2
89    .type _xtensa_lx_rt_zero_fill,@function
90_xtensa_lx_rt_zero_fill:
91    entry a1, 0
92    bgeu   a2, a3, .Lfill_done    // If start >= end, skip zeroing
93    movi.n a5, 0
94
95.Lfill_loop:
96    s32i.n a5, a2, 0              // Store the zero at the current cursor
97    addi.n a2, a2, 4              // Increment the cursor by 4 bytes
98    bltu   a2, a3, .Lfill_loop    // If cursor < end, repeat
99.Lfill_done:
100    retw.n
101
102    // _xtensa_lx_rt_copy
103    //
104    // Input arguments:
105    // a2: source address
106    // a3: destination start address (used as a cursor)
107    // a4: destination end address
108
109    .section .rwtext,\"ax\",@progbits
110    .global _xtensa_lx_rt_copy
111    .p2align 2
112    .type _xtensa_lx_rt_copy,@function
113_xtensa_lx_rt_copy:
114    entry a1, 0
115    bgeu   a3, a4, .Lcopy_done   // If start >= end, skip copying
116.Lcopy_loop:
117    l32i.n a5, a2, 0             // Load word from source pointer
118    s32i.n a5, a3, 0             // Store word at destination pointer
119    addi.n a3, a3, 4             // Increment destination pointer by 4 bytes
120    addi.n a2, a2, 4             // Increment source pointer by 4 bytes
121    bltu   a3, a4, .Lcopy_loop   // If cursor < end, repeat
122.Lcopy_done:
123    retw.n
124
125    .section .rwtext,\"ax\",@progbits
126    .global Reset
127    .p2align 2
128    .type Reset,@function
129Reset:
130    entry  a1, 0
131    movi   a0, 0                    // Trash the return address. Debuggers may use this to stop unwinding.
132
133    wsr.intenable a0                // Disable interrupts
134
135    l32r   a5, sym_stack_start_cpu0 // a5 is our temporary value register
136    mov    sp, a5                   // Set the stack pointer.
137
138    l32r   a5, sym__pre_init
139    callx8 a5                       // Call the pre-initialization function.
140
141.Linit_bss:
142    l32r   a5, sym__zero_bss        // Do we need to zero-initialize memory?
143    callx8 a5
144    beqz   a10, .Linit_data         // No -> skip to copying initialized data
145
146    l32r   a10, sym_bss_start        // Set input range to .bss
147    l32r   a11, sym_bss_end          //
148    call8  _xtensa_lx_rt_zero_fill  // Zero-fill
149
150.Linit_data:
151    l32r   a5, sym__init_data       // Do we need to initialize data sections?
152    callx8 a5
153    beqz   a10, .Linit_data_done    // If not, skip initialization
154
155    l32r   a10, sym_sidata           // Arguments - source data pointer
156    l32r   a11, sym_data_start       //           - destination pointer
157    l32r   a12, sym_data_end         //           - destination end pointer
158    call8  _xtensa_lx_rt_copy       // Copy .data section
159
160.Linit_data_done:
161    memw    // Make sure all writes are completed before proceeding. At this point, all static variables have been initialized.
162"
163);
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 interupts are cleared by writing CCOMPARE[i]")
167#[cfg(any(
168    XCHAL_HAVE_TIMER0,
169    XCHAL_HAVE_TIMER1,
170    XCHAL_HAVE_TIMER2,
171    XCHAL_HAVE_TIMER3
172))]
173cfg_global_asm!(
174    #[cfg(XCHAL_HAVE_TIMER0)]
175    "wsr.ccompare0 a0",
176    #[cfg(XCHAL_HAVE_TIMER1)]
177    "wsr.ccompare1 a0",
178    #[cfg(XCHAL_HAVE_TIMER2)]
179    "wsr.ccompare2 a0",
180    #[cfg(XCHAL_HAVE_TIMER3)]
181    "wsr.ccompare3 a0",
182    "isync",
183);
184
185global_asm!(
186    "
187    l32r   a5, sym_init_start // vector table address
188    wsr.vecbase a5
189
190    l32r   a5, sym__post_init
191    callx8 a5
192
193    l32r   a5, sym_main       // program entry point
194    callx8 a5
195    ",
196);
197
198// We redefine these functions to avoid pulling in `xtensa-lx` as a dependency:
199
200// CPU Interrupts
201unsafe extern "C" {
202    #[cfg(XCHAL_HAVE_TIMER0)]
203    pub fn Timer0(save_frame: &mut crate::exception::Context);
204    #[cfg(XCHAL_HAVE_TIMER1)]
205    pub fn Timer1(save_frame: &mut crate::exception::Context);
206    #[cfg(XCHAL_HAVE_TIMER2)]
207    pub fn Timer2(save_frame: &mut crate::exception::Context);
208    #[cfg(XCHAL_HAVE_TIMER3)]
209    pub fn Timer3(save_frame: &mut crate::exception::Context);
210
211    #[cfg(XCHAL_HAVE_PROFILING)]
212    pub fn Profiling(save_frame: &mut crate::exception::Context);
213
214    #[cfg(XCHAL_HAVE_SOFTWARE0)]
215    pub fn Software0(save_frame: &mut crate::exception::Context);
216    #[cfg(XCHAL_HAVE_SOFTWARE1)]
217    pub fn Software1(save_frame: &mut crate::exception::Context);
218
219    #[cfg(XCHAL_HAVE_NMI)]
220    pub fn NMI(save_frame: &mut crate::exception::Context);
221}
222
223#[doc(hidden)]
224#[unsafe(no_mangle)]
225pub extern "C" fn default_mem_hook() -> bool {
226    true // default to zeroing bss & initializing data
227}
228
229#[doc(hidden)]
230#[macro_export]
231macro_rules! cfg_asm {
232    (@inner, [$($x:tt)*], [$($opts:tt)*], ) => {
233        asm!($($x)* $($opts)*)
234    };
235    (@inner, [$($x:tt)*], [$($opts:tt)*], #[cfg($meta:meta)] $asm:literal, $($rest:tt)*) => {
236        #[cfg($meta)]
237        cfg_asm!(@inner, [$($x)* $asm,], [$($opts)*], $($rest)*);
238        #[cfg(not($meta))]
239        cfg_asm!(@inner, [$($x)*], [$($opts)*], $($rest)*)
240    };
241    (@inner, [$($x:tt)*], [$($opts:tt)*], $asm:literal, $($rest:tt)*) => {
242        cfg_asm!(@inner, [$($x)* $asm,], [$($opts)*], $($rest)*)
243    };
244    ({$($asms:tt)*}, $($opts:tt)*) => {
245        cfg_asm!(@inner, [], [$($opts)*], $($asms)*)
246    };
247}
248
249#[doc(hidden)]
250#[macro_export]
251macro_rules! cfg_global_asm {
252    {@inner, [$($x:tt)*], } => {
253        global_asm!{$($x)*}
254    };
255    (@inner, [$($x:tt)*], #[cfg($meta:meta)] $asm:literal, $($rest:tt)*) => {
256        #[cfg($meta)]
257        cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
258        #[cfg(not($meta))]
259        cfg_global_asm!{@inner, [$($x)*], $($rest)*}
260    };
261    {@inner, [$($x:tt)*], $asm:literal, $($rest:tt)*} => {
262        cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
263    };
264    {$($asms:tt)*} => {
265        cfg_global_asm!{@inner, [], $($asms)*}
266    };
267}