use crate::device::{Device, DeviceContext, RegisterFile};
use crate::soc::{Access, FieldSpec, ReadAction, WriteModify};
use crate::time::{EventId, VirtualNanos};
use crate::soc::stm32f427::generated::pwr::{Register, cr, csr};
const fn field(name: &'static str, bit: u8, width: u8) -> FieldSpec {
FieldSpec {
name,
bit,
width,
access: Access::ReadWrite,
read: ReadAction::None,
write: WriteModify::None,
}
}
pub static MRUDS: FieldSpec = field("MRUDS", 11, 1);
pub static LPUDS: FieldSpec = field("LPUDS", 10, 1);
const CSR_HARDWARE: u64 = csr::VOSRDY.mask() | csr::UDRDY.mask();
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Ready {
BackupRegulator = 0,
Scale = 1,
OverDrive = 2,
OverDriveSwitch = 3,
}
impl Ready {
const ALL: [Self; 4] = [
Self::BackupRegulator,
Self::Scale,
Self::OverDrive,
Self::OverDriveSwitch,
];
fn flag(self) -> &'static FieldSpec {
match self {
Self::Scale => csr::VOSRDY,
Self::OverDrive => csr::ODRDY,
Self::OverDriveSwitch => csr::ODSWRDY,
Self::BackupRegulator => csr::BRR,
}
}
fn enable(self) -> Option<(Register, &'static FieldSpec)> {
match self {
Self::Scale => None,
Self::OverDrive => Some((Register::Cr, cr::ODEN)),
Self::OverDriveSwitch => Some((Register::Cr, cr::ODSWEN)),
Self::BackupRegulator => Some((Register::Csr, csr::BRE)),
}
}
fn settling(self) -> VirtualNanos {
VirtualNanos(match self {
Self::OverDriveSwitch => 45_000,
Self::Scale | Self::OverDrive | Self::BackupRegulator => 10_000,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Scale {
One,
Two,
Three,
}
const PVD_THRESHOLDS_MV: [(u32, u32); 8] = [
(2140, 2040),
(2300, 2190),
(2450, 2350),
(2600, 2510),
(2760, 2660),
(2930, 2840),
(3030, 2930),
(3140, 3030),
];
#[derive(Debug)]
pub struct Pwr {
supply_mv: u32,
pending: [Option<EventId>; Ready::ALL.len()],
shadow: Shadow,
}
impl Default for Pwr {
fn default() -> Self {
Self::new()
}
}
impl Pwr {
#[must_use]
pub fn new() -> Self {
Self {
supply_mv: 0,
pending: [None; Ready::ALL.len()],
shadow: Shadow::default(),
}
}
pub fn set_supply(&mut self, millivolts: u32, registers: &mut RegisterFile) {
self.supply_mv = millivolts;
self.refresh_detector(registers);
self.store_shadow(registers);
}
#[must_use]
pub fn scale(registers: &RegisterFile) -> Scale {
match cr::VOS.get(registers.get(Register::Cr)) {
0b10 => Scale::Two,
0b11 => Scale::One,
_ => Scale::Three,
}
}
}
#[derive(Debug)]
struct Shadow {
cr: u64,
csr: u64,
}
impl Default for Shadow {
fn default() -> Self {
Self {
cr: 0x0000_C000,
csr: 0,
}
}
}
impl Device for Pwr {
type Register = Register;
fn read(
&mut self,
_register: Register,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64> {
self.follow_settling(registers, context);
self.store_shadow(registers);
None
}
fn write(
&mut self,
register: Register,
written: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
match register {
Register::Cr => self.write_cr(written, registers, context),
Register::Csr => self.write_csr(registers, context),
}
self.store_shadow(registers);
}
fn on_event(
&mut self,
event: EventId,
registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
let Some(index) = self
.pending
.iter()
.position(|pending| *pending == Some(event))
else {
return;
};
self.pending[index] = None;
let which = Ready::ALL[index];
let csr_value = registers.get(Register::Csr);
registers.set(Register::Csr, which.flag().set(csr_value, 1));
self.store_shadow(registers);
}
}
impl Pwr {
fn store_shadow(&mut self, registers: &RegisterFile) {
self.shadow = Shadow {
cr: registers.get(Register::Cr),
csr: registers.get(Register::Csr),
};
}
fn is_started(registers: &RegisterFile, which: Ready) -> bool {
let asked = which
.enable()
.is_none_or(|(register, enable)| enable.is_set(registers.get(register)));
asked
&& (which != Ready::OverDriveSwitch || csr::ODRDY.is_set(registers.get(Register::Csr)))
}
fn write_cr(
&mut self,
written: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let mut csr_value = registers.get(Register::Csr);
if cr::CSBF.is_set(written) {
csr_value = csr::SBF.set(csr_value, 0);
}
if cr::CWUF.is_set(written) {
csr_value = csr::WUF.set(csr_value, 0);
}
registers.set(Register::Csr, csr_value);
let cr_value = registers.get(Register::Cr);
let cleared = cr::CWUF.set(cr::CSBF.set(cr_value, 0), 0);
registers.set(Register::Cr, cleared);
let cr_value = registers.get(Register::Cr);
if !cr::ODEN.is_set(cr_value) {
registers.set(Register::Cr, cr::ODSWEN.set(cr_value, 0));
}
if cr::VOS.get(self.shadow.cr) != cr::VOS.get(registers.get(Register::Cr)) {
let csr_value = registers.get(Register::Csr);
registers.set(Register::Csr, csr::VOSRDY.set(csr_value, 0));
self.pending[Ready::Scale as usize] = None;
}
self.follow_settling(registers, context);
self.refresh_detector(registers);
}
fn write_csr(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
registers.restore(Register::Csr, CSR_HARDWARE, self.shadow.csr);
if !cr::DBP.is_set(registers.get(Register::Cr)) {
registers.restore(Register::Csr, csr::BRE.mask(), self.shadow.csr);
}
self.follow_settling(registers, context);
}
fn follow_settling(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
for (index, which) in Ready::ALL.iter().enumerate() {
let flag = which.flag();
let csr_value = registers.get(Register::Csr);
if !Self::is_started(registers, *which) {
registers.set(Register::Csr, flag.set(csr_value, 0));
self.pending[index] = None;
continue;
}
if !flag.is_set(csr_value) && self.pending[index].is_none() {
self.pending[index] =
Some(context.schedule(VirtualNanos(context.now().0 + which.settling().0)));
}
}
}
fn refresh_detector(&self, registers: &mut RegisterFile) {
let cr_value = registers.get(Register::Cr);
let csr_value = registers.get(Register::Csr);
if !cr::PVDE.is_set(cr_value) {
registers.set(Register::Csr, csr::PVDO.set(csr_value, 0));
return;
}
let level = usize::try_from(cr::PLS.get(cr_value)).expect("three bits fit a usize");
let (rising_mv, falling_mv) = PVD_THRESHOLDS_MV[level];
let below = if csr::PVDO.is_set(csr_value) {
self.supply_mv < rising_mv
} else {
self.supply_mv < falling_mv
};
registers.set(Register::Csr, csr::PVDO.set(csr_value, u64::from(below)));
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::soc::stm32f427;
#[test]
fn the_shadow_starts_at_the_reset_values() {
let mut soc = crate::mcu::Single::new(stm32f427::builder(), "PWR", Pwr::new()).unwrap();
let registers = soc.registers();
let shadow = Shadow::default();
assert_eq!(shadow.cr, registers.get(Register::Cr));
assert_eq!(shadow.csr, registers.get(Register::Csr));
}
}