use crate::device::{DeviceContext, Level, RegisterFile};
use crate::soc::FieldSpec;
use crate::soc::stm32h743::generated::tim1::{af1, af2, bdtr, cr1, cr2, dier, sr};
use super::family::{Family, Slot};
use super::fields::{CC_E, CC_NE, CC_NP, CC_P, LOCK_LEVEL_1, LOCK_LEVEL_2, OC_M, OIS, OIS_N};
use super::inputs::start_filter;
use super::{BREAK_SOURCES, BreakInput, BreakSource, Controls, Tim, present};
const BREAK_ENABLES: [[&FieldSpec; BREAK_SOURCES]; 2] = [
[af1::BKINE, af1::BKCMP1E, af1::BKCMP2E, af1::BKDF1BK0E],
[af2::BK2INE, af2::BK2CMP1E, af2::BK2CMP2E, af2::BK2DF1BK1E],
];
const BREAK_POLARITIES: [[Option<&FieldSpec>; BREAK_SOURCES]; 2] = [
[
Some(af1::BKINP),
Some(af1::BKCMP1P),
Some(af1::BKCMP2P),
None,
],
[
Some(af2::BK2INP),
Some(af2::BK2CMP1P),
Some(af2::BK2CMP2P),
None,
],
];
const BREAK_CONTROLS: [(&FieldSpec, &FieldSpec, &FieldSpec, Slot); 2] = [
(bdtr::BKE, bdtr::BKP, bdtr::BKF, Slot::Af1),
(bdtr::BK2E, bdtr::BK2P, bdtr::BK2F, Slot::Af2),
];
const LOCKED_AT_1: u64 = bdtr::DTG.mask()
| bdtr::BKE.mask()
| bdtr::BKP.mask()
| bdtr::BKF.mask()
| bdtr::BK2E.mask()
| bdtr::BK2P.mask()
| bdtr::BK2F.mask()
| bdtr::AOE.mask();
const LOCKED_AT_2: u64 = bdtr::OSSR.mask() | bdtr::OSSI.mask();
const fn idle_levels() -> u64 {
let mut mask = 0;
let mut channel = 0;
while channel < OIS.len() {
mask |= OIS[channel].mask();
channel += 1;
}
let mut channel = 0;
while channel < OIS_N.len() {
mask |= OIS_N[channel].mask();
channel += 1;
}
mask
}
const CR2_LOCKED_AT_1: u64 = idle_levels();
impl<F: Family> Tim<F> {
pub(super) fn repetitions(&self, registers: &RegisterFile) -> u64 {
if !self.kind.repetition() {
return 0;
}
let width = if self.kind.breaks() == 2 {
super::HALF_WORD
} else {
super::fields::BYTE
};
self.reg(registers, Slot::Rcr) & width
}
pub(super) fn dead_time(&self, registers: &RegisterFile) -> u128 {
let dtg = bdtr::DTG.get(self.reg(registers, Slot::Bdtr));
let units = match dtg >> 5 {
0..=3 => dtg,
4 | 5 => (64 + (dtg & 0x3F)) * 2,
6 => (32 + (dtg & 0x1F)) * 8,
_ => (32 + (dtg & 0x1F)) * 16,
};
u128::from(units) << cr1::CKD.get(self.reg(registers, Slot::Cr1))
}
fn preloaded_ccer(&self) -> u64 {
(0..self.kind.complementary()).fold(0, |mask, channel| {
mask | CC_E[channel].mask()
| CC_NE[channel].mask()
| CC_P[channel].mask()
| CC_NP[channel].mask()
})
}
pub(super) fn preloaded_modes(&self, pair: usize) -> u64 {
[pair * 2, pair * 2 + 1]
.into_iter()
.filter(|channel| *channel < self.kind.complementary())
.fold(0, |mask, channel| mask | OC_M[channel].mask())
}
pub(super) fn ccer(&self, registers: &RegisterFile) -> u64 {
let written = self.reg(registers, Slot::Ccer);
match self.power.controls {
Some(controls) => {
let held = self.preloaded_ccer();
(written & !held) | (controls.ccer & held)
}
None => written,
}
}
fn snapshot_controls(&self, registers: &RegisterFile) -> Controls {
Controls {
ccer: self.reg(registers, Slot::Ccer),
ccmr: [
self.reg(registers, Slot::Ccmr1),
self.reg(registers, Slot::Ccmr2),
],
}
}
fn refresh_all_pwm(&mut self, registers: &RegisterFile, context: &DeviceContext<'_>) {
let cycle = self.counter_cycle(context.now());
for channel in present(self.kind.channels()) {
self.refresh_pwm(channel, cycle, registers);
}
}
pub(super) fn commutate(&mut self, registers: &mut RegisterFile, context: &DeviceContext<'_>) {
if !cr2::CCPC.is_set(self.reg(registers, Slot::Cr2)) {
return;
}
self.power.controls = Some(self.snapshot_controls(registers));
self.flag(registers, sr::COMIF);
if self.kind.dma() && dier::COMDE.is_set(self.reg(registers, Slot::Dier)) {
self.dma_requests.commutation += 1;
self.requested[super::COMMUTATION_REQUEST] = true;
}
self.refresh_all_pwm(registers, context);
}
pub(super) fn take_break(&self, which: usize, registers: &mut RegisterFile) {
let bdtr_value = self.reg(registers, Slot::Bdtr);
self.put(registers, Slot::Bdtr, bdtr::MOE.set(bdtr_value, 0));
self.flag(registers, [sr::BIF, sr::B2IF][which]);
}
pub(super) fn software_break(&self, which: usize, registers: &mut RegisterFile) {
if which < self.kind.breaks() {
self.take_break(which, registers);
}
}
pub(super) fn system_break(&self, registers: &mut RegisterFile) {
let bdtr_value = self.reg(registers, Slot::Bdtr);
if self.kind.breaks() == 0 || !bdtr::BKE.is_set(bdtr_value) {
return;
}
self.put(registers, Slot::Bdtr, bdtr::MOE.set(bdtr_value, 0));
self.flag(
registers,
if self.kind.system_break_flag() {
sr::SBIF
} else {
sr::BIF
},
);
}
fn break_request(&self, which: usize, registers: &RegisterFile) -> bool {
let (enable, polarity, _, slot) = BREAK_CONTROLS[which];
let bdtr_value = self.reg(registers, Slot::Bdtr);
if which >= self.kind.breaks() || !enable.is_set(bdtr_value) {
return false;
}
let gathered = self.reg(registers, slot);
let active_high = polarity.is_set(bdtr_value);
(0..BREAK_SOURCES).any(|source| {
let inverted =
BREAK_POLARITIES[which][source].is_some_and(|field| field.is_set(gathered));
BREAK_ENABLES[which][source].is_set(gathered)
&& self.sources.breaks[which][source]
.is_some_and(|level| (level == Level::High) == (active_high != inverted))
})
}
pub(super) fn refresh_breaks(&mut self, registers: &mut RegisterFile) {
let clock = self.clock_cycle(self.seen);
for (which, &(_, _, filter, _)) in
BREAK_CONTROLS.iter().enumerate().take(self.kind.breaks())
{
let request = Level::from(self.break_request(which, registers));
if request == self.power.filters[which].level {
continue;
}
self.power.filters[which].level = request;
let delay = self.filter_delay(filter.get(self.reg(registers, Slot::Bdtr)), registers);
if delay == 0 {
self.power.filters[which].pending = None;
self.power.filters[which].filtered = request;
self.break_edge(which, request == Level::High, registers);
} else {
start_filter(&mut self.power.filters[which], request, clock, delay);
}
}
for (which, held) in self.power.active.into_iter().enumerate() {
if held && which < self.kind.breaks() {
self.take_break(which, registers);
}
}
}
pub(super) fn break_edge(&mut self, which: usize, active: bool, registers: &mut RegisterFile) {
self.power.active[which] = active;
if active {
self.take_break(which, registers);
}
}
pub(super) fn auto_output(&self, registers: &mut RegisterFile) {
let bdtr_value = self.reg(registers, Slot::Bdtr);
if self.kind.breaks() > 0
&& bdtr::AOE.is_set(bdtr_value)
&& !self.power.active.iter().any(|held| *held)
{
self.put(registers, Slot::Bdtr, bdtr::MOE.set(bdtr_value, 1));
}
}
pub(super) fn lock_level(&self, registers: &RegisterFile) -> u64 {
bdtr::LOCK.get(self.reg(registers, Slot::Bdtr))
}
pub(super) fn keep(&self, registers: &mut RegisterFile, slot: Slot, mask: u64, before: u64) {
let written = self.reg(registers, slot);
self.put(registers, slot, (written & !mask) | (before & mask));
}
pub(super) fn write_cr2(
&mut self,
before: u64,
registers: &mut RegisterFile,
context: &DeviceContext<'_>,
) {
if self.lock_level(registers) >= LOCK_LEVEL_1 {
self.keep(registers, Slot::Cr2, CR2_LOCKED_AT_1, before);
}
if self.kind.complementary() == 0 {
return;
}
let preloading = cr2::CCPC.is_set(self.reg(registers, Slot::Cr2));
match (preloading, self.power.controls) {
(true, None) => self.power.controls = Some(self.snapshot_controls(registers)),
(false, Some(_)) => {
self.power.controls = None;
self.refresh_all_pwm(registers, context);
}
_ => {}
}
}
pub(super) fn write_bdtr(&mut self, before: u64, registers: &mut RegisterFile) {
if self.kind.breaks() == 0 {
return;
}
if self.power.lock_frozen {
self.keep(registers, Slot::Bdtr, bdtr::LOCK.mask(), before);
}
self.power.lock_frozen = true;
let level = bdtr::LOCK.get(before);
if level >= LOCK_LEVEL_1 {
self.keep(registers, Slot::Bdtr, LOCKED_AT_1, before);
}
if level >= LOCK_LEVEL_2 {
self.keep(registers, Slot::Bdtr, LOCKED_AT_2, before);
}
if self.power.active.iter().any(|held| *held) {
let bdtr_value = self.reg(registers, Slot::Bdtr);
self.put(registers, Slot::Bdtr, bdtr::MOE.set(bdtr_value, 0));
}
}
pub(super) fn write_alternate(&self, slot: Slot, before: u64, registers: &mut RegisterFile) {
if self.lock_level(registers) >= LOCK_LEVEL_1 {
self.keep(registers, slot, u64::MAX, before);
}
}
#[expect(
clippy::too_many_arguments,
reason = "the break, its source and the level, and what every call is handed"
)]
pub fn drive_break(
&mut self,
input: BreakInput,
source: BreakSource,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if input.index() >= self.kind.breaks() {
return;
}
self.settle(registers, context);
self.sources.breaks[input.index()][source.index()] = Some(level);
self.finish(registers, context);
}
}