use cortex_m::peripheral::SYST;
#[cfg(not(feature = "defmt"))]
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
#[cfg(feature = "defmt")]
#[allow(unused_imports)]
use defmt::{debug, error, info, trace};
use core::arch::asm;
use embassy_rp::gpio::Pin;
use rp_pac as pac;
fn exact_low(pin: &impl Pin, low_cycles: u32) {
let pin_num = pin.pin() as usize;
let mask = 1u32 << pin_num;
let so = pac::SIO.gpio_out(0);
so.value_clr().write_value(1 << pin_num);
let soe = pac::SIO.gpio_oe(0);
let soe_set = soe.value_set().as_ptr();
let soe_clr = soe.value_clr().as_ptr();
match (low_cycles, low_cycles % 3) {
(0, _) => {
}
(1, _) => unsafe {
asm!(
"str {mask}, [{soe_set}]",
"str {mask}, [{soe_clr}]",
mask = in(reg) mask,
soe_set = in(reg) soe_set,
soe_clr = in(reg) soe_clr,
options(nostack, readonly),
);
},
(2, _) => unsafe {
asm!(
"str {mask}, [{soe_set}]",
"nop",
"str {mask}, [{soe_clr}]",
mask = in(reg) mask,
soe_set = in(reg) soe_set,
soe_clr = in(reg) soe_clr,
options(nostack, readonly),
);
},
(_, 0) => unsafe {
asm!(
"str {mask}, [{soe_set}]",
"000:",
"subs {d}, 3",
"bne 000b",
"str {mask}, [{soe_clr}]",
mask = in(reg) mask,
soe_set = in(reg) soe_set,
soe_clr = in(reg) soe_clr,
d = in(reg) low_cycles,
options(nostack, readonly),
);
},
(_, 1) => unsafe {
asm!(
"str {mask}, [{soe_set}]",
"subs {d}, 1",
"000:",
"subs {d}, 3",
"bne 000b",
"str {mask}, [{soe_clr}]",
mask = in(reg) mask,
soe_set = in(reg) soe_set,
soe_clr = in(reg) soe_clr,
d = in(reg) low_cycles,
options(nostack, readonly),
);
},
(_, 2) => unsafe {
asm!(
"str {mask}, [{soe_set}]",
"subs {d}, 1",
"subs {d}, 1",
"000:",
"subs {d}, 3",
"bne 000b",
"str {mask}, [{soe_clr}]",
mask = in(reg) mask,
soe_set = in(reg) soe_set,
soe_clr = in(reg) soe_clr,
d = in(reg) low_cycles,
options(nostack, readonly),
);
},
(_, 3..) => unreachable!(),
}
}
#[allow(dead_code)]
fn time_rise_noasm(pin: &mut impl Pin, _low_cycles: u32) -> u8 {
let pin_num = pin.pin() as usize;
let mask = 1u32 << pin_num;
let pad = pac::PADS_BANK0.gpio(pin_num);
let gpio_in = pac::SIO.gpio_in(0);
let gpio_out = pac::SIO.gpio_out(0);
let gpio_oe = pac::SIO.gpio_oe(0);
pad.modify(|s| s.set_pue(true));
gpio_out.value_clr().write_value(mask);
gpio_oe.value_set().write_value(mask);
gpio_oe.value_clr().write_value(mask);
let mut c = 0u8;
while gpio_in.read() & mask == 0 {
c = c.wrapping_add(1);
}
pad.modify(|s| s.set_pue(false));
c
}
fn time_rise(pin: &mut impl Pin, low_cycles: u32) -> u8 {
let pin_num = pin.pin() as usize;
let mask = 1u32 << pin_num;
let pad = pac::PADS_BANK0.gpio(pin_num);
let gpio_in = pac::SIO.gpio_in(0).as_ptr();
pad.modify(|s| s.set_pue(true));
exact_low(pin, low_cycles);
let x0: u32;
let x1: u32;
let x2: u32;
let x3: u32;
let x4: u32;
unsafe {
asm!(
"mov r10, r7",
"222:",
"ldr {x0}, [{gpio_in}]",
"ldr {x1}, [{gpio_in}]",
"ldr {x2}, [{gpio_in}]",
"ldr {x3}, [{gpio_in}]",
"ldr {x4}, [{gpio_in}]",
"ldr r7, [{gpio_in}]",
"ands r7, {mask}",
"beq 222b",
"mov r7, r10",
mask = in(reg) mask,
gpio_in = in(reg) gpio_in,
x0 = out(reg) x0,
x1 = out(reg) x1,
x2 = out(reg) x2,
x3 = out(reg) x3,
x4 = out(reg) x4,
out("r10") _,
options(nostack, readonly),
);
}
let result = (x0 & mask)
| (x1 & mask).rotate_left(1)
| (x2 & mask).rotate_left(2)
| (x3 & mask).rotate_left(3)
| (x4 & mask).rotate_left(4)
| mask.rotate_left(5);
let result = result.rotate_right(pin_num as u32) as u8;
pad.modify(|s| s.set_pue(false));
result
}
pub fn lsb(v: u8) -> u8 {
for i in 0..u8::BITS {
if v & 1 << i != 0 {
return i as u8;
}
}
8
}
struct SyTi<'t> {
syst: &'t mut SYST,
t1: u32,
}
impl<'t> SyTi<'t> {
fn new(syst: &'t mut SYST) -> Self {
assert!(syst.get_clock_source() == cortex_m::peripheral::syst::SystClkSource::Core);
syst.clear_current();
syst.enable_counter();
Self {
syst,
t1: SYST::get_reload(),
}
}
fn done(self) -> Result<u32, ()> {
let t2 = SYST::get_current();
if self.syst.has_wrapped() {
error!("SYST wrapped");
return Err(());
}
self.syst.disable_counter();
Ok(self.t1 - t2)
}
}
pub struct RawNoise<'a, P: Pin> {
pin: &'a mut P,
low_cycles: u32,
_setup: PinSetup,
}
impl<'a, P: Pin> RawNoise<'a, P> {
pub fn new(pin: &'a mut P, low_cycles: u32) -> Self {
let setup = PinSetup::new(pin.pin());
Self {
pin,
low_cycles,
_setup: setup,
}
}
pub fn next_with_systick(&mut self, syst: &mut SYST) -> Result<u32, ()> {
critical_section::with(|_cs| {
let t = SyTi::new(syst);
let r = time_rise(self.pin, self.low_cycles);
let t = t.done()?;
let t = t + lsb(r) as u32;
Ok(t)
})
}
}
impl<P: Pin> Iterator for RawNoise<'_, P> {
type Item = (u8, bool);
fn next(&mut self) -> Option<Self::Item> {
let r = critical_section::with(|_cs| {
time_rise(self.pin, self.low_cycles)
});
let valid = (r & 1) == 0;
Some((r, valid))
}
}
struct PinSetup {
pin: u8,
schmitt: bool,
ie: bool,
pde: bool,
pue: bool,
func: u8,
}
impl PinSetup {
fn new(pin_num: u8) -> Self {
let (schmitt, ie, pde, pue) = pac::PADS_BANK0.gpio(pin_num as usize).modify(|s| {
let prev = (s.schmitt(), s.ie(), s.pde(), s.pue());
s.set_schmitt(false);
s.set_ie(true);
s.set_pde(false);
prev
});
let func = pac::IO_BANK0.gpio(pin_num as usize).ctrl().modify(|s| {
let func = s.funcsel();
s.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0.into());
func
});
PinSetup {
pin: pin_num,
schmitt,
ie,
pde,
pue,
func,
}
}
}
impl Drop for PinSetup {
fn drop(&mut self) {
pac::PADS_BANK0.gpio(self.pin as usize).modify(|s| {
s.set_ie(self.ie);
s.set_schmitt(self.schmitt);
s.set_pde(self.pde);
s.set_pue(self.pue);
});
pac::IO_BANK0
.gpio(self.pin as usize)
.ctrl()
.modify(|s| s.set_funcsel(self.func));
}
}