use crate::device::{DeviceContext, Level, RegisterFile};
use super::family::{COMMUTATION_REQUEST, Family, Slot};
use super::fields::{LOCK_LEVEL_1, LOCK_LEVEL_2};
use super::inputs::start_filter;
use super::{BREAK_SOURCES, BreakInput, BreakSource, Controls, Tim, present};
impl<F: Family> Tim<F> {
const LOCKED_AT_1: u64 = Self::DTG.mask()
| Self::BKE.mask()
| Self::BKP.mask()
| Self::BKF.mask()
| Self::BK2E.mask()
| Self::BK2P.mask()
| Self::BK2F.mask()
| Self::AOE.mask();
const LOCKED_AT_2: u64 = Self::OSSR.mask() | Self::OSSI.mask();
const CR2_LOCKED_AT_1: u64 = {
let mut mask = 0;
let mut channel = 0;
while channel < Self::OIS.len() {
mask |= Self::OIS[channel].mask();
channel += 1;
}
let mut channel = 0;
while channel < Self::OIS_N.len() {
mask |= Self::OIS_N[channel].mask();
channel += 1;
}
mask
};
pub(super) fn repetitions(&self, registers: &RegisterFile) -> u64 {
self.reg(registers, Slot::Rcr) & self.kind.repetition_mask()
}
pub(super) fn dead_time(&self, registers: &RegisterFile) -> u128 {
let dtg = Self::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) << Self::CKD.get(self.reg(registers, Slot::Cr1))
}
fn preloaded_ccer(&self) -> u64 {
(0..self.kind.complementary()).fold(0, |mask, channel| {
mask | Self::CC_E[channel].mask()
| Self::CC_NE[channel].mask()
| Self::CC_P[channel].mask()
| Self::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 | Self::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 !Self::CCPC.is_set(self.reg(registers, Slot::Cr2)) {
return;
}
self.power.controls = Some(self.snapshot_controls(registers));
self.flag(registers, Self::COMIF);
if self.kind.dma() && Self::COMDE.is_set(self.reg(registers, Slot::Dier)) {
self.dma_requests.commutation += 1;
self.requested[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, Self::MOE.set(bdtr_value, 0));
self.flag(registers, [Self::BIF, Self::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 || !Self::BKE.is_set(bdtr_value) {
return;
}
self.put(registers, Slot::Bdtr, Self::MOE.set(bdtr_value, 0));
self.flag(
registers,
if self.kind.system_break_flag() {
Self::SBIF
} else {
Self::BIF
},
);
}
fn break_request(&self, which: usize, registers: &RegisterFile) -> bool {
let (enable, polarity, _, slot) = Self::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 enabled = if self.kind.gathers_break_sources() {
Self::BREAK_ENABLES[which][source].is_set(gathered)
} else {
source == BreakSource::Pin.index()
};
let inverted =
Self::BREAK_POLARITIES[which][source].is_some_and(|field| field.is_set(gathered));
enabled
&& 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 Self::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
&& Self::AOE.is_set(bdtr_value)
&& !self.power.active.iter().any(|held| *held)
{
self.put(registers, Slot::Bdtr, Self::MOE.set(bdtr_value, 1));
}
}
pub(super) fn lock_level(&self, registers: &RegisterFile) -> u64 {
Self::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, Self::CR2_LOCKED_AT_1, before);
}
if self.kind.complementary() == 0 {
return;
}
let preloading = Self::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, Self::LOCK.mask(), before);
}
self.power.lock_frozen = true;
let level = Self::LOCK.get(before);
if level >= LOCK_LEVEL_1 {
self.keep(registers, Slot::Bdtr, Self::LOCKED_AT_1, before);
}
if level >= LOCK_LEVEL_2 {
self.keep(registers, Slot::Bdtr, Self::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, Self::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_source(
&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);
}
}