use crate::device::{DeviceContext, Level, RegisterFile};
use crate::soc::stm32h743::generated::tim1::{af1, cr1, cr2, dier, smcr, sr, tisel};
use super::count::TriggerEvent;
use super::family::{Family, Slot};
use super::fields::{
CC_E, CC_NP, CC_P, CCS_OTHER_INPUT, CCS_OUTPUT, CCS_OWN_INPUT, CCS_TRC, IC_F, IC_PSC, OC_CE,
OC_FE, OCM_FORCE_ACTIVE, OCM_FORCE_INACTIVE, OCM_PWM_1, OCM_PWM_2, SMS, SMS_COMBINED,
SMS_DISABLED, SMS_ENCODER_1, SMS_ENCODER_2, SMS_ENCODER_3, SMS_EXTERNAL_CLOCK_1, SMS_GATED,
SMS_RESET, SMS_TRIGGER, TS, TS_ETRF, TS_FIRST_HIGH_INTERNAL, TS_LAST_INTERNAL,
TS_LAST_LOW_INTERNAL, TS_RESERVED, TS_TI1_EDGES, TS_TI1FP1, TS_TI2FP2,
};
use super::{
Channel, Direction, ETR_SOURCES, FilterEvent, INPUTS, Input, TI_SOURCES, Tim, UpdateSource,
present,
};
const TI_SELECT: [&crate::soc::FieldSpec; INPUTS] =
[tisel::TI1SEL, tisel::TI2SEL, tisel::TI3SEL, tisel::TI4SEL];
#[derive(Debug, Clone, Copy)]
enum Sampling {
Internal,
Dts(u128),
}
const FILTERS: [(Sampling, u128); 16] = [
(Sampling::Dts(1), 0),
(Sampling::Internal, 2),
(Sampling::Internal, 4),
(Sampling::Internal, 8),
(Sampling::Dts(2), 6),
(Sampling::Dts(2), 8),
(Sampling::Dts(4), 6),
(Sampling::Dts(4), 8),
(Sampling::Dts(8), 6),
(Sampling::Dts(8), 8),
(Sampling::Dts(16), 5),
(Sampling::Dts(16), 6),
(Sampling::Dts(16), 8),
(Sampling::Dts(32), 5),
(Sampling::Dts(32), 6),
(Sampling::Dts(32), 8),
];
pub(super) fn start_filter(input: &mut Input, level: Level, clock: u128, delay: u128) {
if level == input.filtered {
input.pending = None;
} else if input.pending.is_none_or(|(_, pending)| pending != level) {
input.pending = Some((clock + delay, level));
}
}
impl<F: Family> Tim<F> {
pub(super) fn sms(&self, registers: &RegisterFile) -> u64 {
if !self.kind.slave() {
return SMS_DISABLED;
}
match SMS.get(self.reg(registers, Slot::Smcr)) {
SMS_ENCODER_1 | SMS_ENCODER_2 | SMS_ENCODER_3 if !self.kind.up_down() => SMS_DISABLED,
code @ SMS_DISABLED..=SMS_COMBINED => code,
_ => SMS_DISABLED,
}
}
pub(super) fn trigger_selection(&self, registers: &RegisterFile) -> u64 {
let selection = TS.get(self.reg(registers, Slot::Smcr));
if selection == TS_ETRF && !self.kind.external_trigger() {
TS_RESERVED
} else {
selection
}
}
pub(super) fn internal_trigger_selected(&self, registers: &RegisterFile) -> bool {
let selection = self.trigger_selection(registers);
selection <= TS_LAST_LOW_INTERNAL
|| (TS_FIRST_HIGH_INTERNAL..=TS_LAST_INTERNAL).contains(&selection)
}
pub(super) fn filter_delay(&self, row: u64, registers: &RegisterFile) -> u128 {
let (sampling, samples) = FILTERS[usize::try_from(row).expect("four bits")];
let dts_division = 1u128 << cr1::CKD.get(self.reg(registers, Slot::Cr1));
let period = match sampling {
Sampling::Internal => 1,
Sampling::Dts(divisor) => dts_division * divisor,
};
period * samples
}
fn pin_level(&self, channel: Channel, registers: &RegisterFile) -> Level {
if channel.index() == 0 && cr2::TI1S.is_set(self.reg(registers, Slot::Cr2)) {
let high = self.inputs[..self.kind.xor_inputs()]
.iter()
.filter(|input| input.level == Level::High)
.count();
Level::from(high % 2 == 1)
} else {
self.inputs[channel.index()].level
}
}
fn refilter(
&mut self,
channel: Channel,
registers: &RegisterFile,
context: &DeviceContext<'_>,
) {
let level = self.pin_level(channel, registers);
let clock = self.clock_cycle(context.now());
let row = IC_F[channel.index()].get(self.reg(registers, channel.ccmr()));
let delay = self.filter_delay(row, registers);
start_filter(&mut self.inputs[channel.index()], level, clock, delay);
}
fn select_inputs(&mut self, registers: &RegisterFile, context: &DeviceContext<'_>) {
let selections = self.reg(registers, Slot::Tisel);
for channel in present(self.kind.channels().min(4)) {
let selection = usize::try_from(TI_SELECT[channel.index()].get(selections))
.expect("four bits")
% TI_SOURCES;
self.inputs[channel.index()].level = self.sources.ti[channel.index()][selection];
}
for channel in present(self.kind.channels().min(4)) {
self.refilter(channel, registers, context);
}
}
pub fn drive_input(
&mut self,
channel: Channel,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.drive_input_source(channel, 0, level, registers, context);
}
#[expect(
clippy::too_many_arguments,
reason = "the channel, the source and the level, and what every call is handed"
)]
pub fn drive_input_source(
&mut self,
channel: Channel,
selection: u8,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let source = usize::from(selection);
if !channel.has_input() || !self.has_channel(channel) || source >= TI_SOURCES {
return;
}
self.settle(registers, context);
self.sources.ti[channel.index()][source] = level;
self.select_inputs(registers, context);
self.settle(registers, context);
self.finish(registers, context);
}
pub fn drive_etr(
&mut self,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.drive_etr_source(0, level, registers, context);
}
pub fn drive_etr_source(
&mut self,
selection: u8,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let source = usize::from(selection);
if !self.kind.external_trigger() || source >= ETR_SOURCES {
return;
}
self.settle(registers, context);
self.sources.etr[source] = level;
self.feed_etr(registers, context);
self.settle(registers, context);
self.finish(registers, context);
}
pub(super) fn feed_etr(&mut self, registers: &RegisterFile, context: &DeviceContext<'_>) {
if !self.kind.external_trigger() {
return;
}
let smcr_value = self.reg(registers, Slot::Smcr);
let source = usize::try_from(af1::ETRSEL.get(self.reg(registers, Slot::Af1)))
.expect("four bits")
% ETR_SOURCES;
let pin = self.sources.etr[source];
let polarized = if smcr::ETP.is_set(smcr_value) {
!pin
} else {
pin
};
if polarized != self.etr_polarized {
self.etr_polarized = polarized;
let every = 1u64 << smcr::ETPS.get(smcr_value);
self.etr.level = match polarized {
Level::High => {
self.etr_edges += 1;
if self.etr_edges.is_multiple_of(every) {
Level::High
} else {
self.etr.level
}
}
Level::Low => Level::Low,
};
}
let clock = self.clock_cycle(context.now());
let delay = self.filter_delay(smcr::ETF.get(smcr_value), registers);
let prescaled = self.etr.level;
start_filter(&mut self.etr, prescaled, clock, delay);
}
pub fn drive_internal_trigger(
&mut self,
trigger: u8,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if !self.kind.slave() {
return;
}
self.settle(registers, context);
let code = if u64::from(trigger) <= TS_LAST_LOW_INTERNAL {
u64::from(trigger)
} else {
u64::from(trigger) + (TS_FIRST_HIGH_INTERNAL - TS_LAST_LOW_INTERNAL - 1)
};
if self.trigger_selection(registers) == code {
if level == Level::High {
self.trigger_edge(registers, context);
}
self.trigger = level;
self.gate(registers, context);
}
self.finish(registers, context);
}
pub(super) fn next_filter_event(&self) -> Option<(u128, FilterEvent)> {
let inputs = self.inputs.iter().enumerate().filter_map(|(index, input)| {
input
.pending
.map(|(cycle, _)| (cycle, FilterEvent::Input(index)))
});
let etr = self.etr.pending.map(|(cycle, _)| (cycle, FilterEvent::Etr));
let breaks = self
.power
.filters
.iter()
.enumerate()
.filter_map(|(index, input)| {
input
.pending
.map(|(cycle, _)| (cycle, FilterEvent::Break(index)))
});
inputs
.chain(etr)
.chain(breaks)
.min_by_key(|(cycle, _)| *cycle)
}
pub(super) fn filtered_edge(
&mut self,
event: FilterEvent,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
match event {
FilterEvent::Etr => {
if let Some((_, new)) = self.etr.pending.take() {
self.etr.filtered = new;
self.etrf_level(new, registers, context);
}
}
FilterEvent::Input(index) => {
if let Some((_, new)) = self.inputs[index].pending.take() {
self.inputs[index].filtered = new;
let source = Channel(u8::try_from(index).expect("four channels"));
self.input_level(source, new, registers, context);
}
}
FilterEvent::Break(index) => {
if let Some((_, new)) = self.power.filters[index].pending.take() {
self.power.filters[index].filtered = new;
self.break_edge(index, new == Level::High, registers);
}
}
}
}
fn etrf_level(
&mut self,
new: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let smcr_value = self.reg(registers, Slot::Smcr);
let selected = self.trigger_selection(registers) == TS_ETRF;
if new == Level::High {
if smcr::ECE.is_set(smcr_value) {
self.external_tick(registers, context);
}
if selected {
self.trigger_edge(registers, context);
}
self.clear_references(self.clock_cycle(context.now()), registers);
}
if selected {
self.trigger = new;
self.gate(registers, context);
}
}
fn input_level(
&mut self,
source: Channel,
new: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
for channel in [source, source.partner()] {
if !channel.has_input() || !self.has_channel(channel) {
continue;
}
let mapped = match self.channel_selection(channel, registers) {
CCS_OWN_INPUT => channel == source,
CCS_OTHER_INPUT => channel.partner() == source,
_ => false,
};
if mapped && self.edge_selected(channel, new, registers) {
self.capture(channel, registers, context);
}
}
self.trigger_from_input(source, new, registers, context);
self.encoder_edge(source, new, registers, context);
}
pub(super) fn edge_selected(
&self,
channel: Channel,
new: Level,
registers: &RegisterFile,
) -> bool {
let ccer_value = self.ccer(registers);
let falling = CC_P[channel.index()].is_set(ccer_value);
let both = CC_NP[channel.index()].is_set(ccer_value);
both || (new == Level::High) != falling
}
fn clear_references(&mut self, clock: u128, registers: &RegisterFile) {
for channel in present(self.kind.channels()) {
let clear_enabled = OC_CE[channel.index()].is_set(self.control(channel, registers));
let mode = self.output_mode(channel, registers);
let forced = matches!(mode, OCM_FORCE_ACTIVE | OCM_FORCE_INACTIVE);
if clear_enabled && !forced && self.channel_selection(channel, registers) == CCS_OUTPUT
{
self.set_reference(channel, false, clock, registers);
let state = &mut self.channels[channel.index()];
state.cleared_for_updates = 1;
state.matched_since_clear = false;
}
}
}
fn trigger_from_input(
&mut self,
source: Channel,
new: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let ts = self.trigger_selection(registers);
let (trigger_level, is_edge) = match (ts, source.index()) {
(TS_TI1_EDGES, 0) => (Level::High, true),
(TS_TI1FP1, 0) | (TS_TI2FP2, 1) => {
let polarized = if CC_P[source.index()].is_set(self.ccer(registers)) {
!new
} else {
new
};
(polarized, self.edge_selected(source, new, registers))
}
_ => return,
};
if !self.kind.slave() {
return;
}
if is_edge {
self.trigger_edge(registers, context);
}
self.trigger = if ts == TS_TI1_EDGES {
Level::Low
} else {
trigger_level
};
self.gate(registers, context);
}
pub(super) fn gate(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let counting = self.should_count(registers);
if self.sms(registers) == SMS_GATED && counting != self.counting {
self.flag(registers, sr::TIF);
self.set_counting(counting, context);
}
}
pub(super) fn trigger_edge(
&mut self,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if !self.kind.slave() {
return;
}
if self.kind.complementary() > 0 && cr2::CCUS.is_set(self.reg(registers, Slot::Cr2)) {
self.commutate(registers, context);
}
if self.internal_trigger_selected(registers) {
for channel in present(self.kind.channels().min(4)) {
if self.channel_selection(channel, registers) == CCS_TRC {
self.capture(channel, registers, context);
}
}
}
let cycle = self.counter_cycle(context.now());
match self.sms(registers) {
SMS_RESET => self.reset_by_trigger(cycle, registers, context),
SMS_TRIGGER => {
self.start_counter(registers, context);
self.flag_trigger(registers);
self.start_pulses(cycle, registers);
}
SMS_COMBINED => {
if self.lost_to_erratum_2_16_1(registers, context) {
self.flag_trigger(registers);
return;
}
self.reset_by_trigger(cycle, registers, context);
self.start_counter(registers, context);
}
SMS_EXTERNAL_CLOCK_1 => {
if !smcr::ECE.is_set(self.reg(registers, Slot::Smcr)) {
self.external_tick(registers, context);
}
self.flag_trigger(registers);
self.start_pulses(cycle, registers);
}
_ => self.start_pulses(cycle, registers),
}
}
fn reset_by_trigger(
&mut self,
cycle: u128,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.update(UpdateSource::Trigger, cycle, registers, context);
self.disarm_outputs();
self.matches_at_segment_start(Some(self.direction), registers);
self.trigger_pulse(registers, TriggerEvent::Reset);
self.flag_trigger(registers);
}
fn start_counter(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let cr1_value = self.reg(registers, Slot::Cr1);
self.put(registers, Slot::Cr1, cr1::CEN.set(cr1_value, 1));
let counting = self.should_count(registers);
self.set_counting(counting, context);
self.fast_enable(self.clock_cycle(context.now()), registers);
}
fn lost_to_erratum_2_16_1(
&self,
registers: &RegisterFile,
context: &DeviceContext<'_>,
) -> bool {
cr1::OPM.is_set(self.reg(registers, Slot::Cr1))
&& smcr::MSM.is_set(self.reg(registers, Slot::Smcr))
&& self.count_now(context.now()) == self.arr
}
pub(super) fn flag_trigger(&mut self, registers: &mut RegisterFile) {
self.flag(registers, sr::TIF);
if self.kind.dma() && dier::TDE.is_set(self.reg(registers, Slot::Dier)) {
self.dma_requests.trigger += 1;
self.requested[super::TRIGGER_REQUEST] = true;
}
}
fn fast_enable(&mut self, clock: u128, registers: &RegisterFile) {
for channel in present(self.kind.channels()) {
let mode = self.output_mode(channel, registers);
let fast = OC_FE[channel.index()].is_set(self.control(channel, registers));
if fast && matches!(mode, OCM_PWM_1 | OCM_PWM_2) {
self.set_reference(channel, mode == OCM_PWM_2, clock, registers);
}
}
}
fn external_tick(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
if !self.counting {
return;
}
self.external_cycles += 1;
self.advance_counter_to(self.external_cycles, registers, context);
}
fn encoder_edge(
&mut self,
source: Channel,
new: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let sms = self.sms(registers);
if !matches!(sms, SMS_ENCODER_1 | SMS_ENCODER_2 | SMS_ENCODER_3) {
return;
}
let counted = matches!(
(sms, source.index()),
(SMS_ENCODER_1, 0) | (SMS_ENCODER_2, 1) | (SMS_ENCODER_3, 0 | 1)
);
let ccer_value = self.ccer(registers);
let polarized = |channel: Channel, level: Level| {
if CC_P[channel.index()].is_set(ccer_value) {
!level
} else {
level
}
};
let edge = polarized(source, new);
let other = source.partner();
let other_level = polarized(other, self.inputs[other.index()].filtered);
let rising = edge == Level::High;
let up = match source.index() {
0 => rising != (other_level == Level::High),
_ => rising == (other_level == Level::High),
};
let direction = if up { Direction::Up } else { Direction::Down };
if direction != self.direction {
let cycle = self.counter_cycle(context.now());
let count = self.count_at(cycle);
self.direction = direction;
self.segment_cycle = cycle;
self.segment_count = count;
self.set_direction_bit(registers);
self.note_pins(self.clock_cycle(context.now()), registers);
}
if counted {
self.external_tick(registers, context);
}
}
pub(super) fn capture(
&mut self,
channel: Channel,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if !CC_E[channel.index()].is_set(self.ccer(registers)) {
return;
}
let every = 1u64 << IC_PSC[channel.index()].get(self.reg(registers, channel.ccmr()));
let state = &mut self.channels[channel.index()];
state.prescaled_edges += 1;
if !state.prescaled_edges.is_multiple_of(every) {
return;
}
let count = self.count_at(self.counter_cycle(context.now()));
self.put(registers, channel.ccr(), count);
self.channels[channel.index()].shadow = count;
self.flag_capture(channel, registers);
}
pub(super) fn write_smcr(
&mut self,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if !self.kind.slave() {
return;
}
let sms = self.sms(registers);
let external = (self.kind.external_trigger()
&& smcr::ECE.is_set(self.reg(registers, Slot::Smcr)))
|| matches!(
sms,
SMS_EXTERNAL_CLOCK_1 | SMS_ENCODER_1 | SMS_ENCODER_2 | SMS_ENCODER_3
);
if external != self.external {
let count = self.count_now(context.now());
self.external = external;
self.external_cycles = 0;
self.set_count(count, context);
}
for channel in present(self.kind.channels().min(2)) {
self.refilter(channel, registers, context);
}
self.feed_etr(registers, context);
}
pub(super) fn write_selections(
&mut self,
registers: &RegisterFile,
context: &DeviceContext<'_>,
) {
self.select_inputs(registers, context);
self.feed_etr(registers, context);
}
}