#![no_main]
#![no_std]
static DEFAULT_IMAGE: &[u8] = include_bytes!("../appbins/app-loader.bin");
#[rtic::app(device = nrf52840_hal::pac, dispatchers = [SWI0_EGU0])]
mod app {
use core::sync::atomic::Ordering;
use cortex_m::singleton;
use defmt::unwrap;
use groundhog_nrf52::GlobalRollingTimer;
use nrf52840_hal::{
clocks::{ExternalOscillator, Internal, LfOscStopped},
pac::TIMER0,
usbd::{UsbPeripheral, Usbd},
Clocks,
};
use kernel::{
alloc::HEAP,
monotonic::MonoTimer,
drivers::usb_serial::{UsbUartParts, setup_usb_uart, UsbUartIsr, enable_usb_interrupts},
syscall::{syscall_clear, try_recv_syscall},
loader::validate_header,
traits::BlockStorage,
};
use usb_device::{
class_prelude::UsbBusAllocator,
device::{UsbDeviceBuilder, UsbVidPid},
};
use usbd_serial::{SerialPort, USB_CLASS_CDC};
use groundhog::RollingTimer;
use super::{DEFAULT_IMAGE, letsago};
#[monotonic(binds = TIMER0, default = true)]
type Monotonic = MonoTimer<TIMER0>;
#[shared]
struct Shared {}
#[local]
struct Local {
usb_isr: UsbUartIsr,
machine: kernel::traits::Machine,
prog_loaded: Option<(*const u8, usize)>,
}
#[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
let device = cx.device;
let clocks = Clocks::new(device.CLOCK);
let clocks = clocks.enable_ext_hfosc();
let clocks =
unwrap!(singleton!(: Clocks<ExternalOscillator, Internal, LfOscStopped> = clocks));
let mono = Monotonic::new(device.TIMER0);
GlobalRollingTimer::init(device.TIMER1);
HEAP.init().ok();
syscall_clear();
enable_usb_interrupts(&device.USBD);
let (usb_dev, usb_serial) = {
let usb_bus = Usbd::new(UsbPeripheral::new(device.USBD, clocks));
let usb_bus = defmt::unwrap!(singleton!(:UsbBusAllocator<Usbd<UsbPeripheral>> = usb_bus));
let usb_serial = SerialPort::new(usb_bus);
let usb_dev = UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("OVAR Labs")
.product("Anachro Pellegrino")
.serial_number("ajm001")
.device_class(USB_CLASS_CDC)
.max_packet_size_0(64) .build();
(usb_dev, usb_serial)
};
let UsbUartParts { isr, sys } = defmt::unwrap!(setup_usb_uart(usb_dev, usb_serial));
let pins = kernel::map_pins(device.P0, device.P1);
let qsp = kernel::qspi::QspiPins {
qspi_copi_io0: pins.qspi_d0.degrade(),
qspi_cipo_io1: pins.qspi_d1.degrade(),
qspi_io2: pins.qspi_d2.degrade(),
qspi_io3: pins.qspi_d3.degrade(),
qspi_csn: pins.qspi_csn.degrade(),
qspi_sck: pins.qspi_sck.degrade(),
};
let qspi = kernel::qspi::Qspi::new(device.QSPI, qsp);
let mut block = defmt::unwrap!(kernel::drivers::gd25q16::Gd25q16::new(qspi));
let prog_loaded = if let Some(blk) = kernel::MAGIC_BOOT.read_clear() {
unsafe {
extern "C" {
static _app_start: u32;
static _app_len: u32;
}
defmt::println!("Told to boot block {=u32}!", blk);
let app_start = (&_app_start) as *const u32 as *const u8 as *mut u8;
let app_len = (&_app_len) as *const u32 as usize;
block.block_load_to(blk, app_start, app_len).ok()
}
} else {
None
};
let mut hg = defmt::unwrap!(HEAP.try_lock());
let box_uart = defmt::unwrap!(hg.alloc_box(sys));
let leak_uart = box_uart.leak();
let to_uart: &'static mut dyn kernel::traits::Serial = leak_uart;
let box_block = defmt::unwrap!(hg.alloc_box(block));
let leak_block = box_block.leak();
let to_block: &'static mut dyn kernel::traits::BlockStorage = leak_block;
let machine = kernel::traits::Machine {
serial: to_uart,
block_storage: Some(to_block),
};
(
Shared {},
Local {
usb_isr: isr,
machine,
prog_loaded,
},
init::Monotonics(mono),
)
}
#[task(binds = SVCall, local = [machine], priority = 1)]
fn svc(cx: svc::Context) {
let machine = cx.local.machine;
if let Ok(()) = try_recv_syscall(|req| {
machine.handle_syscall(req)
}) {
}
}
#[task(binds = USBD, local = [usb_isr], priority = 2)]
fn usb_tick(cx: usb_tick::Context) {
cx.local.usb_isr.poll();
}
#[idle(local = [prog_loaded])]
fn idle(cx: idle::Context) -> ! {
defmt::println!("Hello, world!");
let timer = GlobalRollingTimer::default();
let start = timer.get_ticks();
while timer.millis_since(start) < 1000 { }
defmt::println!("!!! - ENTERING USERSPACE - !!!");
let loaded = *cx.local.prog_loaded;
let pws = if let Some((ptr, len)) = loaded {
{
let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
validate_header(slice)
}.map(|rh| {
rh.ram_ram_setup()
}).ok()
} else {
None
};
let pws = pws.unwrap_or_else(|| {
let rh = validate_header(DEFAULT_IMAGE).unwrap();
rh.oc_flash_setup(DEFAULT_IMAGE)
});
core::sync::atomic::compiler_fence(Ordering::SeqCst);
unsafe {
letsago(pws.stack_start, pws.entry_point);
}
}
}
use core::arch::asm;
use cortex_m::register::{control, psp};
#[inline(always)]
unsafe fn letsago(sp: u32, entry: u32) -> ! {
let mut cur_ctl = control::read();
cur_ctl.set_npriv(control::Npriv::Unprivileged);
cur_ctl.set_spsel(control::Spsel::Psp);
let cur_ctl = cur_ctl.bits();
psp::write(sp);
asm!(
"msr CONTROL, {}",
"isb",
"bx {}",
in(reg) cur_ctl,
in(reg) entry,
options(noreturn, nomem, nostack),
);
}