120 lines
3.3 KiB
Rust
120 lines
3.3 KiB
Rust
//! Low-level access to Xtensa LX processors and peripherals.
|
|
//!
|
|
//! ## Minimum Supported Rust Version (MSRV)
|
|
//!
|
|
//! This crate is guaranteed to compile on stable Rust 1.65 and up. It might
|
|
//! compile with older versions but that may change in any new patch release.
|
|
//!
|
|
//! ## Feature Flags
|
|
#![doc = document_features::document_features!()]
|
|
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
|
#![allow(asm_sub_register)]
|
|
#![feature(asm_experimental_arch)]
|
|
#![no_std]
|
|
|
|
use core::arch::asm;
|
|
|
|
pub mod interrupt;
|
|
pub mod timer;
|
|
|
|
#[macro_use]
|
|
mod macros;
|
|
|
|
const DCR_ENABLEOCD: u32 = 0x01;
|
|
const XDM_OCD_DCR_SET: u32 = 0x10200C;
|
|
|
|
/// Move the vector base
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// *This is highly unsafe!*
|
|
/// It should be used with care, `base` MUST be a valid pointer
|
|
#[inline(always)]
|
|
pub unsafe fn set_vecbase(base: *const u32) {
|
|
asm!("wsr.vecbase {0}", in(reg) base, options(nostack));
|
|
}
|
|
|
|
/// Get the core stack pointer
|
|
#[inline(always)]
|
|
pub fn get_stack_pointer() -> *const u32 {
|
|
let x: *const u32;
|
|
unsafe { asm!("mov {0}, sp", out(reg) x, options(nostack)) };
|
|
x
|
|
}
|
|
|
|
/// Set the core stack pointer
|
|
///
|
|
/// `stack` pointer to the non-inclusive end of the stack (must be 16-byte
|
|
/// aligned)
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// *This is highly unsafe!*
|
|
/// It should be used with care at e.g. program start or when building a task
|
|
/// scheduler
|
|
#[inline(always)]
|
|
pub unsafe fn set_stack_pointer(stack: *mut u32) {
|
|
// FIXME: this function relies on it getting inlined - if it doesn't inline it
|
|
// will try and return from this function using the adress in `a0` which has
|
|
// just been trashed... According to https://nnethercote.github.io/perf-book/inlining.html:
|
|
// "Inline attributes do not guarantee that a function is inlined or not
|
|
// inlined, but in practice, #[inline(always)] will cause inlining in all but
|
|
// the most exceptional cases." Is this good enough? Should we rewrite these
|
|
// as a macro to guarentee inlining?
|
|
|
|
// NOTE: modification of the `sp` & `a0` is not typically allowed inside inline
|
|
// asm!, but because we *need* to modify it we can do so by ommiting it from
|
|
// the clobber
|
|
asm!(
|
|
"movi a0, 0", // trash return register
|
|
"mov sp, {0}", // move stack pointer
|
|
in(reg) stack, options(nostack)
|
|
);
|
|
}
|
|
|
|
/// Get the core current program counter
|
|
#[inline(always)]
|
|
pub fn get_program_counter() -> *const u32 {
|
|
let x: *const u32;
|
|
unsafe {
|
|
asm!("
|
|
mov {1}, {2}
|
|
call0 2f
|
|
.align 4
|
|
2:
|
|
mov {0}, {2}
|
|
mov {2}, {1}
|
|
", out(reg) x, out(reg) _, out(reg) _, options(nostack))
|
|
};
|
|
x
|
|
}
|
|
|
|
/// Get the id of the current core
|
|
#[inline(always)]
|
|
pub fn get_processor_id() -> u32 {
|
|
let mut x: u32;
|
|
unsafe { asm!("rsr.prid {0}", out(reg) x, options(nostack)) };
|
|
x
|
|
}
|
|
|
|
/// Returns true if a debugger is attached
|
|
#[inline(always)]
|
|
pub fn is_debugger_attached() -> bool {
|
|
let mut x: u32;
|
|
unsafe { asm!("rer {0}, {1}", out(reg) x, in(reg) XDM_OCD_DCR_SET, options(nostack)) };
|
|
(x & DCR_ENABLEOCD) != 0
|
|
}
|
|
|
|
/// Insert debug breakpoint
|
|
#[inline(always)]
|
|
pub fn debug_break() {
|
|
unsafe { asm!("break 1, 15", options(nostack)) };
|
|
}
|
|
|
|
/// Used to reexport items for use in macros. Do not use directly.
|
|
/// Not covered by semver guarantees.
|
|
#[doc(hidden)]
|
|
pub mod _export {
|
|
pub use critical_section;
|
|
}
|