use crate::typelevel::Sealed;
use super::*;
use core::convert::Infallible;
pub struct SioGpioBank0 {
_private: (),
}
pub struct SioFifo {
_private: (),
}
pub struct SioGpioQspi {
_private: (),
}
pub struct HwDivider {
_private: (),
}
pub struct DivResult<T> {
pub quotient: T,
pub remainder: T,
}
pub struct Sio {
_sio: pac::SIO,
pub gpio_bank0: SioGpioBank0,
pub gpio_qspi: SioGpioQspi,
pub hwdivider: HwDivider,
pub fifo: SioFifo,
pub interp0: Interp0,
pub interp1: Interp1,
}
impl Sio {
pub fn new(sio: pac::SIO) -> Self {
Self {
_sio: sio,
gpio_bank0: SioGpioBank0 { _private: () },
gpio_qspi: SioGpioQspi { _private: () },
fifo: SioFifo { _private: () },
hwdivider: HwDivider { _private: () },
interp0: Interp0 {
lane0: Interp0Lane0 { _private: () },
lane1: Interp0Lane1 { _private: () },
},
interp1: Interp1 {
lane0: Interp1Lane0 { _private: () },
lane1: Interp1Lane1 { _private: () },
},
}
}
pub fn core() -> u8 {
unsafe { (*pac::SIO::ptr()).cpuid.read().bits() as u8 }
}
}
impl SioFifo {
pub fn is_read_ready(&mut self) -> bool {
let sio = unsafe { &(*pac::SIO::ptr()) };
sio.fifo_st.read().vld().bit_is_set()
}
pub fn is_write_ready(&mut self) -> bool {
let sio = unsafe { &(*pac::SIO::ptr()) };
sio.fifo_st.read().rdy().bit_is_set()
}
pub fn status(&self) -> u32 {
let sio = unsafe { &(*pac::SIO::ptr()) };
sio.fifo_st.read().bits()
}
pub fn write(&mut self, value: u32) {
let sio = unsafe { &(*pac::SIO::ptr()) };
sio.fifo_wr.write(|w| unsafe { w.bits(value) });
cortex_m::asm::sev();
}
pub fn read(&mut self) -> Option<u32> {
if self.is_read_ready() {
let sio = unsafe { &(*pac::SIO::ptr()) };
Some(sio.fifo_rd.read().bits())
} else {
None
}
}
pub fn drain(&mut self) {
while self.read().is_some() {
}
}
pub fn write_blocking(&mut self, value: u32) {
while !self.is_write_ready() {
cortex_m::asm::nop();
}
self.write(value);
cortex_m::asm::sev();
}
pub fn read_blocking(&mut self) -> u32 {
loop {
if let Some(data) = self.read() {
return data;
} else {
cortex_m::asm::wfe();
}
}
}
}
#[cfg(target_arch = "arm")]
core::arch::global_asm!(
".macro hwdivider_head",
"ldr r2, =(0xd0000000)", "ldr r3, [r2, #0x078]", "lsrs r3, #2", "bcs 2f",
"1:",
".endm",
".macro hwdivider_tail",
"b 3f",
"3: b 3f",
"3: b 3f",
"3: b 3f",
"3:",
"ldr r1, [r2, #0x074]", "ldr r0, [r2, #0x070]", "bx lr",
"2:",
"push {{r4-r6, lr}}",
"ldr r3, [r2, #0x060]", "ldr r4, [r2, #0x064]", "ldr r5, [r2, #0x074]", "ldr r6, [r2, #0x070]", "bl 1b",
"str r3, [r2, #0x060]", "str r4, [r2, #0x064]", "str r5, [r2, #0x074]", "str r6, [r2, #0x070]", "pop {{r4-r6, pc}}",
".endm",
);
macro_rules! division_function {
(
$name:ident $($intrinsic:ident)* ( $argty:ty ) {
$($begin:literal),+
}
) => {
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
core::arch::global_asm!(
// Mangle the name slightly, since this is a global symbol.
concat!(".global _rphal_", stringify!($name)),
concat!(".type _rphal_", stringify!($name), ", %function"),
".align 2",
concat!("_rphal_", stringify!($name), ":"),
$(
concat!(".global ", stringify!($intrinsic)),
concat!(".type ", stringify!($intrinsic), ", %function"),
concat!(stringify!($intrinsic), ":"),
)*
"hwdivider_head",
$($begin),+ ,
"hwdivider_tail",
);
#[cfg(all(target_arch = "arm", feature = "disable-intrinsics"))]
core::arch::global_asm!(
// Mangle the name slightly, since this is a global symbol.
concat!(".global _rphal_", stringify!($name)),
concat!(".type _rphal_", stringify!($name), ", %function"),
".align 2",
concat!("_rphal_", stringify!($name), ":"),
"hwdivider_head",
$($begin),+ ,
"hwdivider_tail",
);
#[cfg(target_arch = "arm")]
extern "aapcs" {
#[link_name = concat!("_rphal_", stringify!($name)) ]
fn $name(n: $argty, d: $argty) -> u64;
}
#[cfg(not(target_arch = "arm"))]
#[allow(unused_variables)]
unsafe fn $name(n: $argty, d: $argty) -> u64 { 0 }
};
}
division_function! {
unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) {
"str r0, [r2, #0x060]", "str r1, [r2, #0x064]" }
}
division_function! {
signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) {
"str r0, [r2, #0x068]", "str r1, [r2, #0x06c]" }
}
fn divider_unsigned(n: u32, d: u32) -> DivResult<u32> {
let packed = unsafe { unsigned_divmod(n, d) };
DivResult {
quotient: packed as u32,
remainder: (packed >> 32) as u32,
}
}
fn divider_signed(n: i32, d: i32) -> DivResult<i32> {
let packed = unsafe { signed_divmod(n, d) };
DivResult {
quotient: packed as u32 as i32,
remainder: (packed >> 32) as u32 as i32,
}
}
impl HwDivider {
pub fn unsigned(&self, dividend: u32, divisor: u32) -> DivResult<u32> {
divider_unsigned(dividend, divisor)
}
pub fn signed(&self, dividend: i32, divisor: i32) -> DivResult<i32> {
divider_signed(dividend, divisor)
}
}
intrinsics! {
extern "C" fn __udivsi3(n: u32, d: u32) -> u32 {
divider_unsigned(n, d).quotient
}
extern "C" fn __umodsi3(n: u32, d: u32) -> u32 {
divider_unsigned(n, d).remainder
}
extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
let quo_rem = divider_unsigned(n, d);
if let Some(rem) = rem {
*rem = quo_rem.remainder;
}
quo_rem.quotient
}
extern "C" fn __divsi3(n: i32, d: i32) -> i32 {
divider_signed(n, d).quotient
}
extern "C" fn __modsi3(n: i32, d: i32) -> i32 {
divider_signed(n, d).remainder
}
extern "C" fn __divmodsi4(n: i32, d: i32, rem: &mut i32) -> i32 {
let quo_rem = divider_signed(n, d);
*rem = quo_rem.remainder;
quo_rem.quotient
}
}
pub trait SpinlockValid: Sealed {}
pub struct Spinlock<const N: usize>(core::marker::PhantomData<()>)
where
Spinlock<N>: SpinlockValid;
impl<const N: usize> Spinlock<N>
where
Spinlock<N>: SpinlockValid,
{
pub fn try_claim() -> Option<Self> {
let sio = unsafe { &*pac::SIO::ptr() };
let lock = sio.spinlock[N].read().bits();
if lock > 0 {
Some(Self(core::marker::PhantomData))
} else {
None
}
}
pub fn claim() -> Self {
loop {
if let Some(result) = Self::try_claim() {
break result;
}
}
}
pub fn claim_async() -> nb::Result<Self, Infallible> {
Self::try_claim().ok_or(nb::Error::WouldBlock)
}
pub unsafe fn release() {
let sio = &*pac::SIO::ptr();
sio.spinlock[N].write_with_zero(|b| b.bits(1));
}
}
impl<const N: usize> Drop for Spinlock<N>
where
Spinlock<N>: SpinlockValid,
{
fn drop(&mut self) {
unsafe { Self::release() }
}
}
macro_rules! spinlock {
($first:expr, $($rest:tt),+) => {
spinlock!($first);
spinlock!($($rest),+);
};
($id:expr) => {
$crate::paste::paste! {
pub type [<Spinlock $id>] = Spinlock<$id>;
impl SpinlockValid for Spinlock<$id> {}
impl Sealed for Spinlock<$id> {}
}
};
}
spinlock!(
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30
);
#[cfg(feature = "critical-section-impl")]
pub(crate) type Spinlock31 = Spinlock<31>;
#[cfg(not(feature = "critical-section-impl"))]
pub type Spinlock31 = Spinlock<31>;
impl SpinlockValid for Spinlock<31> {}
impl Sealed for Spinlock<31> {}
pub fn spinlock_state() -> [bool; 32] {
let sio = unsafe { &*pac::SIO::ptr() };
let register = sio.spinlock_st.read().bits();
let mut result = [false; 32];
#[allow(clippy::needless_range_loop)]
for i in 0..32 {
result[i] = (register & (1 << i)) > 0;
}
result
}
pub unsafe fn spinlock_reset() {
const SIO_BASE: u32 = 0xd0000000;
const SPINLOCK0_PTR: *mut u32 = (SIO_BASE + 0x100) as *mut u32;
const SPINLOCK_COUNT: usize = 32;
for i in 0..SPINLOCK_COUNT {
SPINLOCK0_PTR.wrapping_add(i).write_volatile(1);
}
}
pub struct LaneCtrl {
pub clamp: bool,
pub blend: bool,
pub force_msb: u8,
pub add_raw: bool,
pub cross_result: bool,
pub cross_input: bool,
pub signed: bool,
pub mask_msb: u8,
pub mask_lsb: u8,
pub shift: u8,
}
impl LaneCtrl {
pub const fn new() -> Self {
Self {
clamp: false,
blend: false,
force_msb: 0,
add_raw: false,
cross_result: false,
cross_input: false,
signed: false,
mask_msb: 31,
mask_lsb: 0,
shift: 0,
}
}
pub const fn encode(&self) -> u32 {
assert!(!(self.blend && self.clamp));
assert!(self.force_msb < 0b100);
assert!(self.mask_msb < 0b100000);
assert!(self.mask_lsb < 0b100000);
assert!(self.mask_msb >= self.mask_lsb);
assert!(self.shift < 0b100000);
((self.clamp as u32) << 22)
| ((self.blend as u32) << 21)
| ((self.force_msb as u32) << 19)
| ((self.add_raw as u32) << 18)
| ((self.cross_result as u32) << 17)
| ((self.cross_input as u32) << 16)
| ((self.signed as u32) << 15)
| ((self.mask_msb as u32) << 10)
| ((self.mask_lsb as u32) << 5)
| (self.shift as u32)
}
}
pub trait Lane: Sealed {
fn pop(&mut self) -> u32;
fn peek(&self) -> u32;
fn set_accum(&mut self, v: u32);
fn get_accum(&self) -> u32;
fn set_base(&mut self, v: u32);
fn get_base(&self) -> u32;
fn set_ctrl(&mut self, v: u32);
fn get_ctrl(&self) -> u32;
fn add_accum(&mut self, v: u32);
fn read_raw(&self) -> u32;
}
pub trait Interp: Sealed {
fn pop(&mut self) -> u32;
fn peek(&self) -> u32;
fn set_base(&mut self, v: u32);
fn get_base(&self) -> u32;
fn set_base_1and0(&mut self, v: u32);
}
macro_rules! interpolators {
(
$($interp:ident : ( $( [ $lane:ident,$lane_id:expr ] ),+ ) ),+
) => {
$crate::paste::paste! {
$(
$(
#[doc = "The lane " $lane_id " of " $interp]
pub struct [<$interp $lane>]{
_private: (),
}
impl Lane for [<$interp $lane>]{
fn pop(&mut self) ->u32{
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _pop_ $lane:lower>].read().bits()
}
fn peek(&self) ->u32{
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _peek_ $lane:lower>].read().bits()
}
fn set_accum(&mut self,v:u32){
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _accum $lane_id>].write(|w| unsafe { w.bits(v) });
}
fn get_accum(&self)->u32{
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _accum $lane_id>].read().bits()
}
fn set_base(&mut self, v:u32){
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _base $lane_id>].write(|w| unsafe { w.bits(v) });
}
fn get_base(&self)->u32{
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _base $lane_id>].read().bits()
}
fn set_ctrl(&mut self, v:u32){
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _ctrl_lane $lane_id>].write(|w| unsafe { w.bits(v) });
}
fn get_ctrl(&self)->u32{
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _ctrl_lane $lane_id>].read().bits()
}
fn add_accum(&mut self, v:u32){
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _accum $lane_id _add>].write(|w| unsafe { w.bits(v) });
}
fn read_raw(&self)->u32{
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _accum $lane_id _add>].read().bits()
}
}
impl Sealed for [<$interp $lane>] {}
)+
#[doc = "Interpolator " $interp]
pub struct $interp {
$(
[<$lane:lower>]: [<$interp $lane>],
)+
}
impl $interp{
$(
pub fn [<get_ $lane:lower>](&mut self)->&mut [<$interp $lane>]{
&mut self.[<$lane:lower>]
}
)+
}
impl Interp for $interp{
fn pop(&mut self) ->u32{
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _pop_full>].read().bits()
}
fn peek(&self) ->u32{
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _peek_full>].read().bits()
}
fn set_base(&mut self, v:u32){
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _base2>].write(|w| unsafe { w.bits(v)});
}
fn get_base(&self)->u32{
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _base2>].read().bits()
}
fn set_base_1and0(&mut self, v:u32){
let sio = unsafe { &*pac::SIO::ptr() };
sio.[<$interp:lower _base_1and0>].write(|w| unsafe { w.bits(v)});
}
}
impl Sealed for $interp {}
)+
}
}
}
interpolators!(
Interp0 : ([Lane0,0],[Lane1,1]),
Interp1 : ([Lane0,0],[Lane1,1])
);