use std::marker::PhantomData;
use crate::device::{Device, DeviceContext, Level, RegisterFile};
use crate::soc::{FieldSpec, Layout};
use crate::time::VirtualNanos;
const PIN_COUNT: u8 = 16;
const PINS: usize = PIN_COUNT as usize;
pub(crate) const MODE_INPUT: u64 = 0b00;
pub(crate) const MODE_OUTPUT: u64 = 0b01;
pub(crate) const MODE_ALTERNATE: u64 = 0b10;
pub(crate) const MODE_ANALOG: u64 = 0b11;
pub(crate) const PULL_UP: u64 = 0b01;
pub(crate) const PULL_DOWN: u64 = 0b10;
pub(crate) const OPEN_DRAIN: u64 = 1;
const MODE_BITS: u32 = 2;
const FUNCTION_BITS: u32 = 4;
const FUNCTIONS_PER_REGISTER: u8 = 8;
const LCKK_BIT: u32 = PIN_COUNT as u32;
pub trait Port: Layout + PartialEq + 'static {
const MODER: Self;
const OTYPER: Self;
const OSPEEDR: Self;
const PUPDR: Self;
const IDR: Self;
const ODR: Self;
const BSRR: Self;
const LCKR: Self;
const AFRL: Self;
const AFRH: Self;
const AT_RESET: Option<Level>;
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
pub struct Pin(u8);
impl Pin {
#[must_use]
pub const fn new(index: u8) -> Option<Self> {
if index < PIN_COUNT {
Some(Self(index))
} else {
None
}
}
fn all() -> impl Iterator<Item = Self> {
(0..PIN_COUNT).map(Self)
}
#[must_use]
pub const fn index(self) -> usize {
self.0 as usize
}
#[must_use]
pub const fn bit(self) -> u32 {
self.0 as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
enum Lock {
#[default]
Open,
KeyWritten(u64),
KeyCleared(u64),
Locked(u64),
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Frozen {
moder: u64,
otyper: u64,
ospeedr: u64,
pupdr: u64,
afrl: u64,
afrh: u64,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(bound = "")]
pub struct Gpio<P> {
external: [Option<Level>; PINS],
function: [Option<Level>; PINS],
sampled: [Level; PINS],
announced: [Option<Level>; PINS],
lock: Lock,
frozen: Option<Frozen>,
port: PhantomData<P>,
}
impl<P: Port> Default for Gpio<P> {
fn default() -> Self {
Self {
external: [None; PINS],
function: [None; PINS],
sampled: [Level::Low; PINS],
announced: [P::AT_RESET; PINS],
lock: Lock::Open,
frozen: None,
port: PhantomData,
}
}
}
impl<P: Port> Gpio<P> {
pub fn drive(
&mut self,
pin: Pin,
level: Option<Level>,
registers: &RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.external[pin.index()] = level;
self.remember(pin, level);
self.announce(registers, context);
}
pub fn drive_function(
&mut self,
pin: Pin,
level: Option<Level>,
registers: &RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.function[pin.index()] = level;
self.remember(pin, level);
self.announce(registers, context);
}
fn remember(&mut self, pin: Pin, level: Option<Level>) {
if let Some(level) = level {
self.sampled[pin.index()] = level;
}
}
#[must_use]
pub fn level(&self, pin: Pin, registers: &RegisterFile) -> Level {
self.input_level(pin, registers)
.unwrap_or_else(|| self.external[pin.index()].unwrap_or(self.sampled[pin.index()]))
}
#[must_use]
pub fn function(pin: Pin, registers: &RegisterFile) -> Option<u8> {
(mode::<P>(pin, registers) == MODE_ALTERNATE).then(|| {
let (register, bit) = function_field::<P>(pin);
let number = field_at(register, bit).get(registers.get(register));
u8::try_from(number).expect("a four-bit field fits a byte")
})
}
fn sample(&mut self, pin: Pin, registers: &RegisterFile) -> Option<Level> {
let level = self.input_level(pin, registers);
if let Some(level) = level {
self.sampled[pin.index()] = level;
}
level
}
fn held(&self, pin: Pin, registers: &RegisterFile) -> Option<Level> {
let index = pin.index();
let held = match mode::<P>(pin, registers) {
MODE_ANALOG => return Some(Level::Low),
MODE_OUTPUT => true,
MODE_ALTERNATE => self.function[index].is_some(),
_ => false,
} || self.external[index].is_some()
|| pull::<P>(pin, registers).is_some();
held.then(|| self.level(pin, registers))
}
fn input_level(&self, pin: Pin, registers: &RegisterFile) -> Option<Level> {
let index = pin.index();
let driven = match mode::<P>(pin, registers) {
MODE_ANALOG => return None,
MODE_OUTPUT => Some(Level::from(
field_at(P::ODR, pin.bit()).is_set(registers.get(P::ODR)),
)),
MODE_ALTERNATE => self.function[index],
MODE_INPUT => None,
unknown => unreachable!("a two-bit field holds {unknown}"),
};
let open_drain = field_at(P::OTYPER, pin.bit()).get(registers.get(P::OTYPER)) == OPEN_DRAIN;
let level = match driven {
Some(Level::Low) => Level::Low,
Some(Level::High) if !open_drain => Level::High,
_ => self.external[index]
.or_else(|| pull::<P>(pin, registers))
.unwrap_or(self.sampled[index]),
};
Some(level)
}
fn input_data(&mut self, registers: &RegisterFile) -> u64 {
input_bits::<P>(|pin| self.sample(pin, registers))
}
fn announce(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
for pin in Pin::all() {
let Some(level) = self.held(pin, registers) else {
continue;
};
if self.announced[pin.index()] != Some(level) {
self.announced[pin.index()] = Some(level);
context.request(pin.index(), level);
}
}
}
fn lock(&mut self, value: u64, registers: &mut RegisterFile) {
let key = field_at(P::LCKR, LCKK_BIT).is_set(value);
let bits = lock_bits::<P>(value);
self.lock = match (self.lock, key) {
(Lock::Locked(mask), _) => Lock::Locked(mask),
(Lock::KeyCleared(same), true) if same == bits => {
self.frozen = Some(Frozen {
moder: registers.get(P::MODER),
otyper: registers.get(P::OTYPER),
ospeedr: registers.get(P::OSPEEDR),
pupdr: registers.get(P::PUPDR),
afrl: registers.get(P::AFRL),
afrh: registers.get(P::AFRH),
});
Lock::Locked(same)
}
(_, true) => Lock::KeyWritten(bits),
(Lock::KeyWritten(same), false) if same == bits => Lock::KeyCleared(same),
(_, false) => Lock::Open,
};
let shown = match self.lock {
Lock::Locked(mask) => field_at(P::LCKR, LCKK_BIT).set(mask, 1),
_ => bits,
};
registers.set(P::LCKR, shown);
}
fn hold_frozen(&self, register: P, registers: &mut RegisterFile) {
let (Lock::Locked(mask), Some(frozen)) = (self.lock, self.frozen) else {
return;
};
let held = [
(P::MODER, frozen.moder),
(P::OTYPER, frozen.otyper),
(P::OSPEEDR, frozen.ospeedr),
(P::PUPDR, frozen.pupdr),
(P::AFRL, frozen.afrl),
(P::AFRH, frozen.afrh),
]
.into_iter()
.find_map(|(frozen, value)| (frozen == register).then_some(value));
let Some(held) = held else {
return;
};
let bits = Pin::all()
.filter(|pin| field_at(P::LCKR, pin.bit()).is_set(mask))
.filter_map(|pin| configuration_field(register, pin))
.fold(0, |bits, field| bits | field.mask());
let written = registers.get(register);
registers.set(register, (written & !bits) | (held & bits));
}
}
impl<P: Port> Device for Gpio<P> {
type Register = P;
fn read(
&mut self,
register: P,
registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) -> Option<u64> {
(register == P::IDR).then(|| self.input_data(registers))
}
fn peek(&self, register: P, registers: &RegisterFile, _now: VirtualNanos) -> Option<u64> {
(register == P::IDR).then(|| input_bits::<P>(|pin| self.input_level(pin, registers)))
}
fn write(
&mut self,
register: P,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if register == P::BSRR {
let output = Pin::all().fold(registers.get(P::ODR), |output, pin| {
let set = field_at(P::BSRR, pin.bit()).is_set(value);
let reset = field_at(P::BSRR, u32::from(PIN_COUNT) + pin.bit()).is_set(value);
match (set, reset) {
(true, _) => field_at(P::ODR, pin.bit()).set(output, 1),
(false, true) => field_at(P::ODR, pin.bit()).set(output, 0),
(false, false) => output,
}
});
registers.set(P::ODR, output);
} else if register == P::LCKR {
self.lock(value, registers);
} else {
self.hold_frozen(register, registers);
}
self.input_data(registers);
self.announce(registers, context);
}
fn reset(&mut self) {
self.lock = Lock::Open;
self.frozen = None;
self.announced = [P::AT_RESET; PINS];
self.function = [None; PINS];
}
}
fn input_bits<P: Port>(mut level: impl FnMut(Pin) -> Option<Level>) -> u64 {
Pin::all()
.filter(|&pin| level(pin) == Some(Level::High))
.map(|pin| field_at(P::IDR, pin.bit()).mask())
.sum()
}
fn mode<P: Port>(pin: Pin, registers: &RegisterFile) -> u64 {
field_at(P::MODER, pin.bit() * MODE_BITS).get(registers.get(P::MODER))
}
fn pull<P: Port>(pin: Pin, registers: &RegisterFile) -> Option<Level> {
match field_at(P::PUPDR, pin.bit() * MODE_BITS).get(registers.get(P::PUPDR)) {
PULL_UP => Some(Level::High),
PULL_DOWN => Some(Level::Low),
_ => None,
}
}
fn function_field<P: Port>(pin: Pin) -> (P, u32) {
if pin.0 < FUNCTIONS_PER_REGISTER {
(P::AFRL, pin.bit() * FUNCTION_BITS)
} else {
(
P::AFRH,
(pin.bit() - u32::from(FUNCTIONS_PER_REGISTER)) * FUNCTION_BITS,
)
}
}
fn field_at<P: Port>(register: P, bit: u32) -> &'static FieldSpec {
register
.field_at(bit)
.expect("every pin has its field in every GPIO register")
}
fn lock_bits<P: Port>(value: u64) -> u64 {
Pin::all().fold(0, |bits, pin| {
bits | (value & field_at(P::LCKR, pin.bit()).mask())
})
}
fn configuration_field<P: Port>(register: P, pin: Pin) -> Option<&'static FieldSpec> {
if [P::MODER, P::OSPEEDR, P::PUPDR].contains(®ister) {
Some(field_at(register, pin.bit() * MODE_BITS))
} else if register == P::OTYPER {
Some(field_at(register, pin.bit()))
} else if register == P::AFRL || register == P::AFRH {
let (holder, bit) = function_field::<P>(pin);
(holder == register).then(|| field_at(holder, bit))
} else {
None
}
}