use crate::device::{Level, RegisterFile};
use crate::edge::Pulse;
use crate::soc::FieldSpec;
use crate::soc::stm32h743::generated::tim1::{bdtr, ccr5, cr1, cr2};
use crate::time::VirtualNanos;
use super::count::TriggerEvent;
use super::family::{Family, Slot};
use super::fields::{
CC_DE, CC_E, CC_IF, CC_NE, CC_NP, CC_P, CCS_OUTPUT, CMS_FLAGS_DOWN, CMS_FLAGS_UP,
MMS2_OC4_EDGES, MMS2_OC4_OR_OC6_RISING, MMS2_OC4_RISING_OC6_FALLING, MMS2_OC5_OR_OC6_RISING,
MMS2_OC5_RISING_OC6_FALLING, MMS2_OC6_EDGES, OC_M, OC_PE, OCM_ACTIVE_ON_MATCH,
OCM_ASYMMETRIC_1, OCM_ASYMMETRIC_2, OCM_COMBINED_1, OCM_COMBINED_2, OCM_FROZEN,
OCM_INACTIVE_ON_MATCH, OCM_PWM_1, OCM_PWM_2, OCM_RETRIGGERABLE_1, OCM_RETRIGGERABLE_2,
OCM_TOGGLE, OIS, OIS_N,
};
use super::{Channel, Direction, Match, NANOS_PER_SECOND, Tim, UpdateSource, present};
const FIFTH: usize = 4;
const GROUP: [&FieldSpec; 3] = [ccr5::GC5C1, ccr5::GC5C2, ccr5::GC5C3];
pub(super) fn group_mask() -> u64 {
GROUP.iter().fold(0, |mask, field| mask | field.mask())
}
impl<F: Family> Tim<F> {
pub(super) fn control(&self, channel: Channel, registers: &RegisterFile) -> u64 {
let written = self.reg(registers, channel.ccmr());
let pair = channel.index() / 2;
match self.power.controls {
Some(controls) if pair < controls.ccmr.len() => {
let held = self.preloaded_modes(pair);
(written & !held) | (controls.ccmr[pair] & held)
}
_ => written,
}
}
pub(super) fn channel_selection(&self, channel: Channel, registers: &RegisterFile) -> u64 {
if channel.has_input() {
super::fields::CC_S[channel.index()].get(self.reg(registers, channel.ccmr()))
} else {
CCS_OUTPUT
}
}
pub(super) fn output_mode(&self, channel: Channel, registers: &RegisterFile) -> u64 {
let mode = OC_M[channel.index()].get(self.control(channel, registers));
if self.kind.has_mode(mode) {
mode
} else {
OCM_FROZEN
}
}
fn grouped(&self, channel: Channel) -> bool {
GROUP
.get(channel.index())
.is_some_and(|field| field.is_set(self.group))
}
pub(super) fn load_group(&mut self, registers: &RegisterFile, at_update: bool) {
if self.kind.channels() < 5 {
return;
}
let written = self.reg(registers, Slot::Ccr5);
for channel in present(GROUP.len().try_into().expect("three channels")) {
let preloaded = OC_PE[channel.index()].is_set(self.control(channel, registers));
if preloaded == at_update {
let field = GROUP[channel.index()];
self.group = field.set(self.group, field.get(written));
}
}
}
fn combined_reference(&self, channel: Channel, registers: &RegisterFile) -> bool {
let own = self.channels[channel.index()].reference;
let partner = self.channels[channel.partner().index()].reference;
let combined = match self.output_mode(channel, registers) {
OCM_COMBINED_1 => own || partner,
OCM_COMBINED_2 => own && partner,
OCM_ASYMMETRIC_1 | OCM_ASYMMETRIC_2 => match self.direction {
Direction::Up => own,
Direction::Down => partner,
},
_ => own,
};
if self.grouped(channel) {
combined && self.channels[FIFTH].reference
} else {
combined
}
}
pub(super) fn set_reference(
&mut self,
channel: Channel,
active: bool,
clock: u128,
registers: &RegisterFile,
) {
self.channels[channel.index()].reference = active;
self.note_pins(clock, registers);
}
pub(super) fn note_pins(&mut self, clock: u128, registers: &RegisterFile) {
let mut edges = [None; super::CHANNELS];
for channel in present(self.kind.channels()) {
let new = Level::from(self.combined_reference(channel, registers));
let state = &mut self.channels[channel.index()];
if new != state.pin {
state.pin = new;
state.pin_since = clock;
edges[channel.index()] = Some(new == Level::High);
}
}
for (index, rising) in edges.into_iter().enumerate() {
if let Some(rising) = rising {
self.second_trigger_edge(index, rising, registers);
}
}
}
fn second_trigger_edge(&mut self, index: usize, rising: bool, registers: &RegisterFile) {
let (oc4, oc5, oc6) = (3, 4, 5);
let selected = match cr2::MMS2.get(self.reg(registers, Slot::Cr2)) {
MMS2_OC4_EDGES => index == oc4,
MMS2_OC6_EDGES => index == oc6,
MMS2_OC4_OR_OC6_RISING => rising && (index == oc4 || index == oc6),
MMS2_OC4_RISING_OC6_FALLING => (index == oc4 && rising) || (index == oc6 && !rising),
MMS2_OC5_OR_OC6_RISING => rising && (index == oc5 || index == oc6),
MMS2_OC5_RISING_OC6_FALLING => (index == oc5 && rising) || (index == oc6 && !rising),
_ => false,
};
if selected && self.kind == super::Kind::Advanced {
self.trigger2_pulses += 1;
}
}
pub(super) fn compare_match(
&mut self,
found: Match,
counted: Option<Direction>,
registers: &mut RegisterFile,
) {
let Match {
channel,
cycle,
value,
} = found;
let missed = self.missed_by_erratum_2_16_2(channel, cycle, value, registers);
self.channels[channel.index()].last_match = Some((cycle, value));
let cms = cr1::CMS.get(self.reg(registers, Slot::Cr1));
let gated = self.kind.up_down()
&& matches!(
(cms, counted),
(CMS_FLAGS_DOWN, Some(Direction::Up)) | (CMS_FLAGS_UP, Some(Direction::Down))
);
let lost = missed && self.center_aligned(registers);
if !gated && !lost {
self.flag_compare(channel, registers);
}
let mode = self.output_mode(channel, registers);
let state = &mut self.channels[channel.index()];
if !missed {
state.matched_since_clear = true;
}
let reference = state.reference;
let clock = cycle + self.paused;
match mode {
OCM_ACTIVE_ON_MATCH => self.set_reference(channel, true, clock, registers),
OCM_INACTIVE_ON_MATCH => self.set_reference(channel, false, clock, registers),
OCM_TOGGLE if !missed => self.set_reference(channel, !reference, clock, registers),
_ => {}
}
self.refresh_pwm(channel, cycle, registers);
}
pub(super) fn flag_compare(&mut self, channel: Channel, registers: &mut RegisterFile) {
self.flag(registers, CC_IF[channel.index()]);
if channel.has_input() && self.kind.dma() {
let on_update = cr2::CCDS.is_set(self.reg(registers, Slot::Cr2));
let enabled = CC_DE[channel.index()].is_set(self.reg(registers, Slot::Dier));
if enabled && !on_update {
self.dma_requests.channels[channel.index()] += 1;
self.requested[super::CHANNEL_REQUESTS[channel.index()]] = true;
}
}
if channel.index() == 0 {
self.trigger_pulse(registers, TriggerEvent::Compare);
}
}
fn missed_by_erratum_2_16_2(
&self,
channel: Channel,
cycle: u128,
value: u64,
registers: &RegisterFile,
) -> bool {
let Some((last_cycle, last_value)) = self.channels[channel.index()].last_match else {
return false;
};
if cycle != last_cycle + u128::from(self.psc) + 1 || value == last_value {
return false;
}
if self.center_aligned(registers) {
self.repetitions(registers) == 0
&& ((last_value + 1 == self.arr && value == self.arr)
|| (last_value == 1 && value == 0))
} else {
last_value == self.arr && value == 0
}
}
pub(super) fn matches_at_segment_start(
&mut self,
counted: Option<Direction>,
registers: &mut RegisterFile,
) {
let cycle = self.segment_cycle;
for channel in present(self.kind.channels()) {
if self.channel_selection(channel, registers) != CCS_OUTPUT {
continue;
}
let value = self.channels[channel.index()].shadow;
let already = matches!(self.channels[channel.index()].last_match, Some((last, _)) if last >= cycle);
if value == self.segment_count && !already {
let found = Match {
channel,
cycle,
value,
};
self.compare_match(found, counted, registers);
}
}
for channel in present(self.kind.channels()) {
self.refresh_pwm(channel, self.segment_cycle, registers);
}
}
pub(super) fn refresh_pwm(&mut self, channel: Channel, cycle: u128, registers: &RegisterFile) {
let mode = self.output_mode(channel, registers);
let count = self.count_at(cycle);
let state = &self.channels[channel.index()];
let value = state.shadow;
let up = self.direction == Direction::Up;
let first_mode = |count: u64| if up { count < value } else { count <= value };
let second_mode = |count: u64| if up { count >= value } else { count > value };
let second = matches!(mode, OCM_PWM_2 | OCM_COMBINED_2 | OCM_ASYMMETRIC_2)
|| (mode == OCM_RETRIGGERABLE_2 && up);
let active = match mode {
OCM_RETRIGGERABLE_1 if state.armed => up,
OCM_RETRIGGERABLE_2 if state.armed => !up,
OCM_PWM_1 | OCM_COMBINED_1 | OCM_ASYMMETRIC_1 | OCM_RETRIGGERABLE_1 | OCM_PWM_2
| OCM_COMBINED_2 | OCM_ASYMMETRIC_2 | OCM_RETRIGGERABLE_2 => {
if second {
second_mode(count)
} else {
first_mode(count)
}
}
_ => return,
};
if state.cleared_for_updates == 0 {
self.set_reference(channel, active, cycle + self.paused, registers);
}
}
pub(super) fn rearm_outputs(&mut self, source: UpdateSource, registers: &RegisterFile) {
if source == UpdateSource::Trigger {
return;
}
for channel in present(self.kind.channels()) {
if matches!(
self.output_mode(channel, registers),
OCM_RETRIGGERABLE_1 | OCM_RETRIGGERABLE_2
) {
self.channels[channel.index()].armed = true;
}
}
}
pub(super) fn disarm_outputs(&mut self) {
for state in &mut self.channels {
state.armed = false;
}
}
pub(super) fn start_pulses(&mut self, cycle: u128, registers: &RegisterFile) {
let waiting = self.channels.map(|state| state.armed);
self.disarm_outputs();
for channel in present(self.kind.channels()) {
if waiting[channel.index()] {
self.refresh_pwm(channel, cycle, registers);
}
}
}
pub(super) fn flag_capture(&mut self, channel: Channel, registers: &mut RegisterFile) {
if channel.has_input() && CC_IF[channel.index()].is_set(self.reg(registers, Slot::Sr)) {
self.flag(registers, super::fields::CC_OF[channel.index()]);
}
self.flag_compare(channel, registers);
}
fn complementary_enabled(&self, index: usize, ccer_value: u64) -> bool {
index < self.kind.complementary() && CC_NE[index].is_set(ccer_value)
}
fn outputs(
&self,
channel: Channel,
registers: &RegisterFile,
) -> (Option<Level>, Option<Level>) {
let index = channel.index();
if !channel.has_input() || !self.has_channel(channel) {
return (None, None);
}
let ccer_value = self.ccer(registers);
let enabled = CC_E[index].is_set(ccer_value);
let active_low = CC_P[index].is_set(ccer_value);
let reference = self.channels[index].pin == Level::High;
if self.kind.breaks() == 0 {
return (enabled.then(|| Level::from(reference != active_low)), None);
}
let complementary = self.complementary_enabled(index, ccer_value);
let complementary_low = CC_NP[index].is_set(ccer_value);
let bdtr_value = self.reg(registers, Slot::Bdtr);
let cr2_value = self.reg(registers, Slot::Cr2);
if !bdtr::MOE.is_set(bdtr_value) {
if !bdtr::OSSI.is_set(bdtr_value) || (!enabled && !complementary) {
return (None, None);
}
if self.power.active[1] && !self.power.active[0] {
return (
Some(Level::from(active_low)),
Some(Level::from(complementary_low)),
);
}
let idle = Level::from(OIS[index].is_set(cr2_value));
let idle_n = OIS_N
.get(index)
.map(|field| Level::from(field.is_set(cr2_value)));
return (Some(idle), idle_n);
}
let off_state = bdtr::OSSR.is_set(bdtr_value);
let settled = self
.clock_cycle(self.seen)
.saturating_sub(self.channels[index].pin_since)
>= self.dead_time(registers);
match (enabled, complementary) {
(false, false) => (None, None),
(false, true) => (
off_state.then(|| Level::from(active_low)),
Some(Level::from(reference != complementary_low)),
),
(true, false) => (
Some(Level::from(reference != active_low)),
(off_state && index < self.kind.complementary())
.then(|| Level::from(complementary_low)),
),
(true, true) => (
Some(Level::from((reference && settled) != active_low)),
Some(Level::from((!reference && settled) != complementary_low)),
),
}
}
#[must_use]
pub fn output(&self, channel: Channel, registers: &RegisterFile) -> Option<Level> {
self.outputs(channel, registers).0
}
#[must_use]
pub fn complementary_output(
&self,
channel: Channel,
registers: &RegisterFile,
) -> Option<Level> {
self.outputs(channel, registers).1
}
#[must_use]
pub fn reference(&self, channel: Channel) -> Level {
if self.has_channel(channel) {
self.channels[channel.index()].pin
} else {
Level::Low
}
}
#[must_use]
pub fn pulse(&self, channel: Channel, registers: &RegisterFile) -> Option<Pulse> {
self.pulse_of(channel, registers, false)
}
#[must_use]
pub fn complementary_pulse(&self, channel: Channel, registers: &RegisterFile) -> Option<Pulse> {
self.pulse_of(channel, registers, true)
}
fn pulse_of(
&self,
channel: Channel,
registers: &RegisterFile,
complementary: bool,
) -> Option<Pulse> {
let driven = if complementary {
self.complementary_output(channel, registers)
} else {
self.output(channel, registers)
};
driven?;
let index = channel.index();
let ccer_value = self.ccer(registers);
let pair = self.complementary_enabled(index, ccer_value);
let enabled = if complementary {
pair
} else {
CC_E[index].is_set(ccer_value)
};
let plain = self.counting
&& !self.external
&& self.clock_hz != 0
&& !self.center_aligned(registers)
&& self.channel_selection(channel, registers) == CCS_OUTPUT
&& enabled
&& !self.grouped(channel)
&& (self.kind.breaks() == 0 || bdtr::MOE.is_set(self.reg(registers, Slot::Bdtr)));
if !plain {
return None;
}
let period = self.arr + 1;
let compare = self.channels[index].shadow;
let below = match self.direction {
Direction::Up => compare.min(period),
Direction::Down => (compare + 1).min(period),
};
let active = match self.output_mode(channel, registers) {
OCM_PWM_1 => below,
OCM_PWM_2 => period - below,
_ => return None,
};
let per_count = u128::from(self.psc) + 1;
let period_cycles = u128::from(period) * per_count;
let active_cycles = u128::from(active) * per_count;
let both = pair && CC_E[index].is_set(ccer_value);
let dead = self.dead_time(registers);
let (carried, inverted) = if complementary {
let own = if CC_E[index].is_set(ccer_value) {
period_cycles - active_cycles
} else {
active_cycles
};
(
if both { own.saturating_sub(dead) } else { own },
CC_NP[index].is_set(ccer_value),
)
} else {
(
if pair {
active_cycles.saturating_sub(dead)
} else {
active_cycles
},
CC_P[index].is_set(ccer_value),
)
};
let high_cycles = if inverted {
period_cycles - carried
} else {
carried
};
let nanos = |cycles: u128| {
VirtualNanos(
u64::try_from(cycles * NANOS_PER_SECOND / u128::from(self.clock_hz))
.expect("a period fits the virtual clock"),
)
};
Some(Pulse {
width: nanos(high_cycles),
period: nanos(period_cycles),
})
}
}