cbc8871a0b
this removed the RelocatedProgram construction step from pio uses. there's not all that much to be said for the extra step because the origin can be set on the input program itself, and the remaining information exposed by RelocatedProgram can be exposed from LoadedProgram instead (even though it's already available on the pio_asm programs, albeit perhaps less convenient). we do lose access to the relocated instruction iterator, but we also cannot think of anything this iterator would actually be useful for outside of program loading.
367 lines
8.6 KiB
Rust
367 lines
8.6 KiB
Rust
#![no_std]
|
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
|
|
|
// This mod MUST go first, so that the others see its macros.
|
|
pub(crate) mod fmt;
|
|
|
|
#[cfg(feature = "critical-section-impl")]
|
|
mod critical_section_impl;
|
|
|
|
mod intrinsics;
|
|
|
|
pub mod adc;
|
|
pub mod clocks;
|
|
pub mod dma;
|
|
pub mod flash;
|
|
mod float;
|
|
pub mod gpio;
|
|
pub mod i2c;
|
|
pub mod multicore;
|
|
pub mod pwm;
|
|
mod reset;
|
|
pub mod rom_data;
|
|
pub mod rtc;
|
|
pub mod spi;
|
|
#[cfg(feature = "time-driver")]
|
|
pub mod timer;
|
|
pub mod uart;
|
|
#[cfg(feature = "nightly")]
|
|
pub mod usb;
|
|
pub mod watchdog;
|
|
|
|
// PIO
|
|
// TODO: move `pio_instr_util` and `relocate` to inside `pio`
|
|
pub mod pio;
|
|
pub mod pio_instr_util;
|
|
pub(crate) mod relocate;
|
|
|
|
// Reexports
|
|
pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
|
|
#[cfg(feature = "unstable-pac")]
|
|
pub use rp_pac as pac;
|
|
#[cfg(not(feature = "unstable-pac"))]
|
|
pub(crate) use rp_pac as pac;
|
|
|
|
#[cfg(feature = "rt")]
|
|
pub use crate::pac::NVIC_PRIO_BITS;
|
|
|
|
embassy_hal_internal::interrupt_mod!(
|
|
TIMER_IRQ_0,
|
|
TIMER_IRQ_1,
|
|
TIMER_IRQ_2,
|
|
TIMER_IRQ_3,
|
|
PWM_IRQ_WRAP,
|
|
USBCTRL_IRQ,
|
|
XIP_IRQ,
|
|
PIO0_IRQ_0,
|
|
PIO0_IRQ_1,
|
|
PIO1_IRQ_0,
|
|
PIO1_IRQ_1,
|
|
DMA_IRQ_0,
|
|
DMA_IRQ_1,
|
|
IO_IRQ_BANK0,
|
|
IO_IRQ_QSPI,
|
|
SIO_IRQ_PROC0,
|
|
SIO_IRQ_PROC1,
|
|
CLOCKS_IRQ,
|
|
SPI0_IRQ,
|
|
SPI1_IRQ,
|
|
UART0_IRQ,
|
|
UART1_IRQ,
|
|
ADC_IRQ_FIFO,
|
|
I2C0_IRQ,
|
|
I2C1_IRQ,
|
|
RTC_IRQ,
|
|
SWI_IRQ_0,
|
|
SWI_IRQ_1,
|
|
SWI_IRQ_2,
|
|
SWI_IRQ_3,
|
|
SWI_IRQ_4,
|
|
SWI_IRQ_5,
|
|
);
|
|
|
|
/// Macro to bind interrupts to handlers.
|
|
///
|
|
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
|
|
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
|
|
/// prove at compile-time that the right interrupts have been bound.
|
|
// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
|
|
#[macro_export]
|
|
macro_rules! bind_interrupts {
|
|
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
|
|
$vis struct $name;
|
|
|
|
$(
|
|
#[allow(non_snake_case)]
|
|
#[no_mangle]
|
|
unsafe extern "C" fn $irq() {
|
|
$(
|
|
<$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
|
|
)*
|
|
}
|
|
|
|
$(
|
|
unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
|
|
)*
|
|
)*
|
|
};
|
|
}
|
|
|
|
embassy_hal_internal::peripherals! {
|
|
PIN_0,
|
|
PIN_1,
|
|
PIN_2,
|
|
PIN_3,
|
|
PIN_4,
|
|
PIN_5,
|
|
PIN_6,
|
|
PIN_7,
|
|
PIN_8,
|
|
PIN_9,
|
|
PIN_10,
|
|
PIN_11,
|
|
PIN_12,
|
|
PIN_13,
|
|
PIN_14,
|
|
PIN_15,
|
|
PIN_16,
|
|
PIN_17,
|
|
PIN_18,
|
|
PIN_19,
|
|
PIN_20,
|
|
PIN_21,
|
|
PIN_22,
|
|
PIN_23,
|
|
PIN_24,
|
|
PIN_25,
|
|
PIN_26,
|
|
PIN_27,
|
|
PIN_28,
|
|
PIN_29,
|
|
PIN_QSPI_SCLK,
|
|
PIN_QSPI_SS,
|
|
PIN_QSPI_SD0,
|
|
PIN_QSPI_SD1,
|
|
PIN_QSPI_SD2,
|
|
PIN_QSPI_SD3,
|
|
|
|
UART0,
|
|
UART1,
|
|
|
|
SPI0,
|
|
SPI1,
|
|
|
|
I2C0,
|
|
I2C1,
|
|
|
|
DMA_CH0,
|
|
DMA_CH1,
|
|
DMA_CH2,
|
|
DMA_CH3,
|
|
DMA_CH4,
|
|
DMA_CH5,
|
|
DMA_CH6,
|
|
DMA_CH7,
|
|
DMA_CH8,
|
|
DMA_CH9,
|
|
DMA_CH10,
|
|
DMA_CH11,
|
|
|
|
PWM_CH0,
|
|
PWM_CH1,
|
|
PWM_CH2,
|
|
PWM_CH3,
|
|
PWM_CH4,
|
|
PWM_CH5,
|
|
PWM_CH6,
|
|
PWM_CH7,
|
|
|
|
USB,
|
|
|
|
RTC,
|
|
|
|
FLASH,
|
|
|
|
ADC,
|
|
|
|
CORE1,
|
|
|
|
PIO0,
|
|
PIO1,
|
|
|
|
WATCHDOG,
|
|
}
|
|
|
|
macro_rules! select_bootloader {
|
|
( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => {
|
|
$(
|
|
#[cfg(feature = $feature)]
|
|
#[link_section = ".boot2"]
|
|
#[used]
|
|
static BOOT2: [u8; 256] = rp2040_boot2::$loader;
|
|
)*
|
|
|
|
#[cfg(not(any( $( feature = $feature),* )))]
|
|
#[link_section = ".boot2"]
|
|
#[used]
|
|
static BOOT2: [u8; 256] = rp2040_boot2::$default;
|
|
}
|
|
}
|
|
|
|
select_bootloader! {
|
|
"boot2-at25sf128a" => BOOT_LOADER_AT25SF128A,
|
|
"boot2-gd25q64cs" => BOOT_LOADER_GD25Q64CS,
|
|
"boot2-generic-03h" => BOOT_LOADER_GENERIC_03H,
|
|
"boot2-is25lp080" => BOOT_LOADER_IS25LP080,
|
|
"boot2-ram-memcpy" => BOOT_LOADER_RAM_MEMCPY,
|
|
"boot2-w25q080" => BOOT_LOADER_W25Q080,
|
|
"boot2-w25x10cl" => BOOT_LOADER_W25X10CL,
|
|
default => BOOT_LOADER_W25Q080
|
|
}
|
|
|
|
/// Installs a stack guard for the CORE0 stack in MPU region 0.
|
|
/// Will fail if the MPU is already confgigured. This function requires
|
|
/// a `_stack_end` symbol to be defined by the linker script, and expexcts
|
|
/// `_stack_end` to be located at the lowest address (largest depth) of
|
|
/// the stack.
|
|
///
|
|
/// This method can *only* set up stack guards on the currently
|
|
/// executing core. Stack guards for CORE1 are set up automatically,
|
|
/// only CORE0 should ever use this.
|
|
///
|
|
/// # Usage
|
|
///
|
|
/// ```no_run
|
|
/// #![feature(type_alias_impl_trait)]
|
|
/// use embassy_rp::install_core0_stack_guard;
|
|
/// use embassy_executor::{Executor, Spawner};
|
|
///
|
|
/// #[embassy_executor::main]
|
|
/// async fn main(_spawner: Spawner) {
|
|
/// // set up by the linker as follows:
|
|
/// //
|
|
/// // MEMORY {
|
|
/// // STACK0: ORIGIN = 0x20040000, LENGTH = 4K
|
|
/// // }
|
|
/// //
|
|
/// // _stack_end = ORIGIN(STACK0);
|
|
/// // _stack_start = _stack_end + LENGTH(STACK0);
|
|
/// //
|
|
/// install_core0_stack_guard().expect("MPU already configured");
|
|
/// let p = embassy_rp::init(Default::default());
|
|
///
|
|
/// // ...
|
|
/// }
|
|
/// ```
|
|
pub fn install_core0_stack_guard() -> Result<(), ()> {
|
|
extern "C" {
|
|
static mut _stack_end: usize;
|
|
}
|
|
unsafe { install_stack_guard(&mut _stack_end as *mut usize) }
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
|
|
let core = unsafe { cortex_m::Peripherals::steal() };
|
|
|
|
// Fail if MPU is already configured
|
|
if core.MPU.ctrl.read() != 0 {
|
|
return Err(());
|
|
}
|
|
|
|
// The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will
|
|
// just shorten the valid stack range a tad.
|
|
let addr = (stack_bottom as u32 + 31) & !31;
|
|
// Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want
|
|
let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7));
|
|
unsafe {
|
|
core.MPU.ctrl.write(5); // enable mpu with background default map
|
|
core.MPU.rbar.write((addr & !0xff) | (1 << 4)); // set address and update RNR
|
|
core.MPU.rasr.write(
|
|
1 // enable region
|
|
| (0x7 << 1) // size 2^(7 + 1) = 256
|
|
| (subregion_select << 8)
|
|
| 0x10000000, // XN = disable instruction fetch; no other bits means no permissions
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub mod config {
|
|
use crate::clocks::ClockConfig;
|
|
|
|
#[non_exhaustive]
|
|
pub struct Config {
|
|
pub clocks: ClockConfig,
|
|
}
|
|
|
|
impl Default for Config {
|
|
fn default() -> Self {
|
|
Self {
|
|
clocks: ClockConfig::crystal(12_000_000),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Config {
|
|
pub fn new(clocks: ClockConfig) -> Self {
|
|
Self { clocks }
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn init(config: config::Config) -> Peripherals {
|
|
// Do this first, so that it panics if user is calling `init` a second time
|
|
// before doing anything important.
|
|
let peripherals = Peripherals::take();
|
|
|
|
unsafe {
|
|
clocks::init(config.clocks);
|
|
#[cfg(feature = "time-driver")]
|
|
timer::init();
|
|
dma::init();
|
|
gpio::init();
|
|
}
|
|
|
|
peripherals
|
|
}
|
|
|
|
/// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes.
|
|
trait RegExt<T: Copy> {
|
|
fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
|
fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
|
fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
|
}
|
|
|
|
impl<T: Default + Copy, A: pac::common::Write> RegExt<T> for pac::common::Reg<T, A> {
|
|
fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
|
|
let mut val = Default::default();
|
|
let res = f(&mut val);
|
|
unsafe {
|
|
let ptr = (self.as_ptr() as *mut u8).add(0x1000) as *mut T;
|
|
ptr.write_volatile(val);
|
|
}
|
|
res
|
|
}
|
|
|
|
fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
|
|
let mut val = Default::default();
|
|
let res = f(&mut val);
|
|
unsafe {
|
|
let ptr = (self.as_ptr() as *mut u8).add(0x2000) as *mut T;
|
|
ptr.write_volatile(val);
|
|
}
|
|
res
|
|
}
|
|
|
|
fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
|
|
let mut val = Default::default();
|
|
let res = f(&mut val);
|
|
unsafe {
|
|
let ptr = (self.as_ptr() as *mut u8).add(0x3000) as *mut T;
|
|
ptr.write_volatile(val);
|
|
}
|
|
res
|
|
}
|
|
}
|