use crate::device::{Device, DeviceContext, Level, RegisterFile};
use crate::soc::{FieldSpec, Layout};
use crate::soc::stm32f427::generated::gpioa::{Register, lckr};
const PIN_COUNT: u8 = 16;
const PINS: usize = PIN_COUNT as usize;
const MODE_INPUT: u64 = 0b00;
const MODE_OUTPUT: u64 = 0b01;
const MODE_ALTERNATE: u64 = 0b10;
const MODE_ANALOG: u64 = 0b11;
const PULL_UP: u64 = 0b01;
const PULL_DOWN: u64 = 0b10;
const OPEN_DRAIN: u64 = 1;
const MODE_BITS: u32 = 2;
const FUNCTION_BITS: u32 = 4;
const FUNCTIONS_PER_REGISTER: u8 = 8;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
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)
}
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)]
enum Lock {
#[default]
Open,
KeyWritten(u64),
KeyCleared(u64),
Locked(u64),
}
#[derive(Debug, Clone, Copy)]
struct Frozen {
moder: u64,
otyper: u64,
ospeedr: u64,
pupdr: u64,
afrl: u64,
afrh: u64,
}
#[derive(Debug)]
pub struct Gpio {
external: [Option<Level>; PINS],
function: [Option<Level>; PINS],
sampled: [Level; PINS],
lock: Lock,
frozen: Option<Frozen>,
}
impl Default for Gpio {
fn default() -> Self {
Self {
external: [None; PINS],
function: [None; PINS],
sampled: [Level::Low; PINS],
lock: Lock::Open,
frozen: None,
}
}
}
impl Gpio {
pub fn drive(&mut self, pin: Pin, level: Option<Level>) {
self.external[pin.index()] = level;
self.remember(pin, level);
}
pub fn drive_function(&mut self, pin: Pin, level: Option<Level>) {
self.function[pin.index()] = level;
self.remember(pin, level);
}
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(pin, registers) == MODE_ALTERNATE).then(|| {
let (register, bit) = function_field(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 input_level(&self, pin: Pin, registers: &RegisterFile) -> Option<Level> {
let index = pin.index();
let driven = match mode(pin, registers) {
MODE_ANALOG => return None,
MODE_OUTPUT => Some(Level::from(
field_at(Register::Odr, pin.bit()).is_set(registers.get(Register::Odr)),
)),
MODE_ALTERNATE => self.function[index],
MODE_INPUT => None,
unknown => unreachable!("a two-bit field holds {unknown}"),
};
let open_drain = field_at(Register::Otyper, pin.bit()).get(registers.get(Register::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(pin, registers))
.unwrap_or(self.sampled[index]),
};
Some(level)
}
fn input_data(&mut self, registers: &RegisterFile) -> u64 {
Pin::all()
.filter(|&pin| self.sample(pin, registers) == Some(Level::High))
.map(|pin| field_at(Register::Idr, pin.bit()).mask())
.sum()
}
fn lock(&mut self, value: u64, registers: &mut RegisterFile) {
let key = lckr::LCKK.is_set(value);
let bits = lock_bits(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(Register::Moder),
otyper: registers.get(Register::Otyper),
ospeedr: registers.get(Register::Ospeedr),
pupdr: registers.get(Register::Pupdr),
afrl: registers.get(Register::Afrl),
afrh: registers.get(Register::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) => lckr::LCKK.set(mask, 1),
_ => bits,
};
registers.set(Register::Lckr, shown);
}
fn hold_frozen(&self, register: Register, registers: &mut RegisterFile) {
let (Lock::Locked(mask), Some(frozen)) = (self.lock, self.frozen) else {
return;
};
let held = match register {
Register::Moder => frozen.moder,
Register::Ospeedr => frozen.ospeedr,
Register::Pupdr => frozen.pupdr,
Register::Otyper => frozen.otyper,
Register::Afrl => frozen.afrl,
Register::Afrh => frozen.afrh,
_ => return,
};
let bits = Pin::all()
.filter(|pin| field_at(Register::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 Device for Gpio {
type Register = Register;
fn read(
&mut self,
register: Register,
registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) -> Option<u64> {
(register == Register::Idr).then(|| self.input_data(registers))
}
fn write(
&mut self,
register: Register,
value: u64,
registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
match register {
Register::Bsrr => {
let output = Pin::all().fold(registers.get(Register::Odr), |output, pin| {
let set = field_at(Register::Bsrr, pin.bit()).is_set(value);
let reset =
field_at(Register::Bsrr, u32::from(PIN_COUNT) + pin.bit()).is_set(value);
match (set, reset) {
(true, _) => field_at(Register::Odr, pin.bit()).set(output, 1),
(false, true) => field_at(Register::Odr, pin.bit()).set(output, 0),
(false, false) => output,
}
});
registers.set(Register::Odr, output);
}
Register::Lckr => self.lock(value, registers),
Register::Moder
| Register::Otyper
| Register::Ospeedr
| Register::Pupdr
| Register::Afrl
| Register::Afrh => self.hold_frozen(register, registers),
_ => {}
}
self.input_data(registers);
}
}
fn mode(pin: Pin, registers: &RegisterFile) -> u64 {
field_at(Register::Moder, pin.bit() * MODE_BITS).get(registers.get(Register::Moder))
}
fn pull(pin: Pin, registers: &RegisterFile) -> Option<Level> {
match field_at(Register::Pupdr, pin.bit() * MODE_BITS).get(registers.get(Register::Pupdr)) {
PULL_UP => Some(Level::High),
PULL_DOWN => Some(Level::Low),
_ => None,
}
}
fn function_field(pin: Pin) -> (Register, u32) {
if pin.0 < FUNCTIONS_PER_REGISTER {
(Register::Afrl, pin.bit() * FUNCTION_BITS)
} else {
(
Register::Afrh,
(pin.bit() - u32::from(FUNCTIONS_PER_REGISTER)) * FUNCTION_BITS,
)
}
}
fn field_at(register: Register, bit: u32) -> &'static FieldSpec {
register
.spec()
.fields
.iter()
.find(|field| u32::from(field.bit) == bit)
.expect("every pin has its field in every GPIO register")
}
fn lock_bits(value: u64) -> u64 {
Pin::all().fold(0, |bits, pin| {
bits | (value & field_at(Register::Lckr, pin.bit()).mask())
})
}
fn configuration_field(register: Register, pin: Pin) -> Option<&'static FieldSpec> {
match register {
Register::Moder | Register::Ospeedr | Register::Pupdr => {
Some(field_at(register, pin.bit() * MODE_BITS))
}
Register::Otyper => Some(field_at(register, pin.bit())),
Register::Afrl | Register::Afrh => {
let (holder, bit) = function_field(pin);
(holder == register).then(|| field_at(holder, bit))
}
_ => None,
}
}