use crate::device::{Device, DeviceContext, Level, RegisterFile};
use crate::soc::Layout;
use crate::soc::stm32h743::generated::sdmmc1::{Register, cmdr, dctrl, star};
pub const INSTANCES: [&str; 2] = ["SDMMC1", "SDMMC2"];
const IRQ: usize = 0;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Sdmmc {
interrupt: bool,
}
impl Sdmmc {
fn refresh(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let raised = registers.get(Register::Star) & registers.get(Register::Maskr) != 0;
if raised != self.interrupt {
self.interrupt = raised;
context.interrupt(IRQ, Level::from(raised));
}
}
}
impl Device for Sdmmc {
type Register = Register;
fn write(
&mut self,
register: Register,
written: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
match register {
Register::Cmdr if cmdr::CPSMEN.get(written) != 0 => {
registers.set(Register::Cmdr, cmdr::CPSMEN.set(written, 0));
if dctrl::DTEN.get(registers.get(Register::Dctrl)) == 0 {
let flag = if cmdr::WAITRESP.get(written) == 0 {
star::CMDSENT
} else {
star::CTIMEOUT
};
let flags = registers.get(Register::Star) | flag.mask();
registers.set(Register::Star, flags);
}
}
Register::Icr => {
let clear_bits = Register::Icr
.spec()
.fields
.iter()
.fold(0, |mask, field| mask | field.mask());
let cleared = written & clear_bits;
let flags = registers.get(Register::Star) & !cleared;
registers.set(Register::Star, flags);
registers.set(Register::Icr, 0);
}
_ => {}
}
self.refresh(registers, context);
}
fn reset(&mut self) {
self.interrupt = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mcu::{Operate, Single};
use crate::soc::Width;
use crate::soc::stm32h743::builder;
use crate::soc::stm32h743::generated::SDMMC1;
use crate::soc::stm32h743::generated::sdmmc1::maskr;
use crate::time::VirtualNanos;
const AT: VirtualNanos = VirtualNanos(0);
fn at(register: Register) -> u64 {
SDMMC1.base + u64::from(register.spec().offset)
}
fn slot() -> Single<Sdmmc> {
Single::new(builder(), INSTANCES[0], Sdmmc::default()).unwrap()
}
fn command(index: u64, waitresp: u64) -> u64 {
cmdr::CPSMEN.set(cmdr::WAITRESP.set(index, waitresp), 1)
}
fn status(slot: &mut Single<Sdmmc>) -> u64 {
slot.read(AT, at(Register::Star), Width::W32).unwrap()
}
#[test]
fn a_command_needing_no_response_is_sent() {
let mut slot = slot();
slot.write(AT, at(Register::Cmdr), Width::W32, command(0, 0))
.unwrap();
assert_eq!(status(&mut slot), star::CMDSENT.mask());
let held = slot.read(AT, at(Register::Cmdr), Width::W32).unwrap();
assert_eq!(cmdr::CPSMEN.get(held), 0, "the CPSM is idle again");
}
#[test]
fn a_command_expecting_a_response_times_out() {
let mut slot = slot();
slot.write(AT, at(Register::Cmdr), Width::W32, command(8, 0b01))
.unwrap();
assert_eq!(status(&mut slot), star::CTIMEOUT.mask());
}
#[test]
fn no_command_goes_out_while_a_data_transfer_is_enabled() {
let mut slot = slot();
slot.write(AT, at(Register::Dctrl), Width::W32, dctrl::DTEN.mask())
.unwrap();
slot.write(AT, at(Register::Cmdr), Width::W32, command(0, 0))
.unwrap();
assert_eq!(status(&mut slot), 0);
}
#[test]
fn the_clear_register_clears_the_flags_it_names() {
let mut slot = slot();
slot.write(AT, at(Register::Cmdr), Width::W32, command(0, 0))
.unwrap();
slot.write(AT, at(Register::Cmdr), Width::W32, command(55, 0b01))
.unwrap();
slot.write(AT, at(Register::Icr), Width::W32, star::CMDSENT.mask())
.unwrap();
assert_eq!(status(&mut slot), star::CTIMEOUT.mask());
slot.write(AT, at(Register::Icr), Width::W32, 0xFFFF_FFFF)
.unwrap();
assert_eq!(status(&mut slot), 0);
}
#[test]
fn an_enabled_flag_raises_the_interrupt() {
let mut slot = slot();
slot.write(
AT,
at(Register::Maskr),
Width::W32,
maskr::CTIMEOUTIE.mask(),
)
.unwrap();
slot.write(AT, at(Register::Cmdr), Width::W32, command(8, 0b01))
.unwrap();
assert!(slot.model.interrupt);
slot.write(AT, at(Register::Icr), Width::W32, star::CTIMEOUT.mask())
.unwrap();
assert!(!slot.model.interrupt);
}
#[test]
fn chibios_finds_the_slot_empty() {
let mut slot = slot();
slot.write(AT, at(Register::Icr), Width::W32, 0xFFFF_FFFF)
.unwrap();
slot.write(AT, at(Register::Cmdr), Width::W32, command(0, 0))
.unwrap();
assert_ne!(status(&mut slot) & star::CMDSENT.mask(), 0);
slot.write(AT, at(Register::Icr), Width::W32, star::CMDSENT.mask())
.unwrap();
for index in [8, 55] {
slot.write(AT, at(Register::Cmdr), Width::W32, command(index, 0b01))
.unwrap();
let waited = star::CMDREND.mask() | star::CTIMEOUT.mask() | star::CCRCFAIL.mask();
let seen = status(&mut slot) & waited;
assert_eq!(seen, star::CTIMEOUT.mask(), "CMD{index}");
slot.write(AT, at(Register::Icr), Width::W32, seen).unwrap();
}
assert_eq!(status(&mut slot), 0);
}
}