use crate::device::{DeviceContext, Level, RegisterFile};
use crate::soc::FieldSpec;
use crate::time::{NANOS_PER_SECOND, VirtualNanos};
use super::family::{CHANNEL_REQUESTS, Family, Flags, Slot, UPDATE_REQUEST, Wakeups};
use super::fields::{
CCS_OUTPUT, CMS_EDGE_ALIGNED, MMS_COMPARE_PULSE, MMS_ENABLE, MMS_FIRST_REFERENCE, MMS_RESET,
MMS_UPDATE, MMS2_FIRST_REFERENCE, MMS2_LAST_REFERENCE, SMS_DISABLED, SMS_GATED,
};
use super::{
Channel, CounterEvent, Direction, HALF_WORD, INPUTS, MAX_LINES, Match, Pending, Tim,
UpdateSource, present,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum TriggerEvent {
Reset,
Update,
Compare,
}
impl TriggerEvent {
const fn code(self) -> u64 {
match self {
Self::Reset => MMS_RESET,
Self::Update => MMS_UPDATE,
Self::Compare => MMS_COMPARE_PULSE,
}
}
}
impl<F: Family> Tim<F> {
const LINE_FLAGS: [(Flags, &'static [(&'static FieldSpec, &'static FieldSpec)]); 4] = [
(
Flags::Break,
&[
(Self::BIF, Self::BIE),
(Self::B2IF, Self::BIE),
(Self::SBIF, Self::BIE),
],
),
(Flags::Update, &[(Self::UIF, Self::UIE)]),
(
Flags::TriggerAndCommutation,
&[(Self::TIF, Self::TIE), (Self::COMIF, Self::COMIE)],
),
(
Flags::CaptureCompare,
&[
(Self::CC_IF[0], Self::CC_IE[0]),
(Self::CC_IF[1], Self::CC_IE[1]),
(Self::CC_IF[2], Self::CC_IE[2]),
(Self::CC_IF[3], Self::CC_IE[3]),
],
),
];
pub(super) fn clock_cycle(&self, now: VirtualNanos) -> u128 {
let since = u128::from(now.0.saturating_sub(self.epoch.at.0));
self.epoch.cycle + since * u128::from(self.clock_hz) / u128::from(NANOS_PER_SECOND)
}
pub(super) fn instant_of_clock_cycle(&self, cycle: u128) -> Option<VirtualNanos> {
if self.clock_hz == 0 {
return None;
}
let since = cycle.saturating_sub(self.epoch.cycle);
let nanos = (since * u128::from(NANOS_PER_SECOND)).div_ceil(u128::from(self.clock_hz));
let at = u128::from(self.epoch.at.0) + nanos;
Some(VirtualNanos(
u64::try_from(at).expect("the virtual clock outlasts any run"),
))
}
pub(super) fn counter_cycle(&self, now: VirtualNanos) -> u128 {
if self.external {
self.external_cycles
} else if self.counting {
self.clock_cycle(now) - self.paused
} else {
self.stopped_at - self.paused
}
}
fn instant_of_counter_cycle(&self, cycle: u128) -> Option<VirtualNanos> {
self.instant_of_clock_cycle(cycle + self.paused)
}
fn ticks_since_segment(&self, cycle: u128) -> u64 {
if self.arr == 0 {
return 0;
}
let elapsed = cycle.saturating_sub(self.segment_cycle);
u64::try_from(elapsed / (u128::from(self.psc) + 1)).expect("ticks fit the counter's course")
}
pub(super) fn count_at(&self, cycle: u128) -> u64 {
let ticks = self.ticks_since_segment(cycle);
match self.direction {
Direction::Up => self.segment_count.wrapping_add(ticks) & self.width.mask(),
Direction::Down => self.segment_count.wrapping_sub(ticks) & self.width.mask(),
}
}
fn cycle_of_tick(&self, tick: u64) -> u128 {
self.segment_cycle + u128::from(tick) * (u128::from(self.psc) + 1)
}
pub(super) fn count_now(&self, now: VirtualNanos) -> u64 {
self.count_at(self.counter_cycle(now))
}
pub(super) fn center_aligned(&self, registers: &RegisterFile) -> bool {
self.kind.up_down() && Self::CMS.get(self.reg(registers, Slot::Cr1)) != CMS_EDGE_ALIGNED
}
fn boundary_tick(&self, registers: &RegisterFile) -> Option<(u64, UpdateSource)> {
if self.arr == 0 {
return None;
}
let center = self.center_aligned(registers);
Some(match self.direction {
Direction::Up if center => (
self.arr - self.segment_count.min(self.arr),
UpdateSource::Overflow,
),
Direction::Up if self.segment_count <= self.arr => {
(self.arr - self.segment_count + 1, UpdateSource::Overflow)
}
Direction::Up => (
self.width.mask() - self.segment_count + 1,
UpdateSource::Overflow,
),
Direction::Down if center => (self.segment_count, UpdateSource::Underflow),
Direction::Down => (self.segment_count + 1, UpdateSource::Underflow),
})
}
fn match_tick(&self, channel: Channel, registers: &RegisterFile) -> Option<(u64, u64)> {
if self.arr == 0 {
return None;
}
if self.channel_selection(channel, registers) != CCS_OUTPUT {
return None;
}
let value = self.channels[channel.index()].shadow;
let tick = match self.direction {
Direction::Up => value.checked_sub(self.segment_count)?,
Direction::Down => self.segment_count.checked_sub(value)?,
};
if tick == 0 {
return None;
}
let cycle = self.cycle_of_tick(tick);
match self.channels[channel.index()].last_match {
Some((last, _)) if cycle <= last => None,
_ => Some((tick, value)),
}
}
pub(super) fn advance_counter_to(
&mut self,
cycle: u128,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
while let Some((event_cycle, event)) = self.next_counter_event(registers) {
if event_cycle > cycle {
break;
}
match event {
CounterEvent::Boundary(source) => {
self.boundary(event_cycle, source, registers, context);
}
CounterEvent::Match(found) => {
self.compare_match(found, Some(self.direction), registers);
}
}
}
}
fn next_counter_event(&self, registers: &RegisterFile) -> Option<(u128, CounterEvent)> {
if !self.counting {
return None;
}
let boundary = self
.boundary_tick(registers)
.map(|(tick, source)| (self.cycle_of_tick(tick), CounterEvent::Boundary(source)));
let matches = present(self.kind.channels()).filter_map(|channel| {
let (tick, value) = self.match_tick(channel, registers)?;
let within = boundary
.is_none_or(|(boundary_cycle, _)| self.cycle_of_tick(tick) < boundary_cycle);
within.then(|| {
let cycle = self.cycle_of_tick(tick);
let found = Match {
channel,
cycle,
value,
};
(cycle, CounterEvent::Match(found))
})
});
boundary
.into_iter()
.chain(matches)
.min_by_key(|(cycle, event)| (*cycle, event.order()))
}
fn boundary(
&mut self,
cycle: u128,
source: UpdateSource,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let center = self.center_aligned(registers);
let old_arr = self.arr;
if self.power.repetition == 0 {
self.update(source, cycle, registers, context);
} else {
self.power.repetition -= 1;
}
if center {
self.direction = match source {
UpdateSource::Overflow => Direction::Down,
_ => Direction::Up,
};
self.segment_count = match self.direction {
Direction::Down => self.arr,
Direction::Up => 0,
};
self.set_direction_bit(registers);
} else {
self.segment_count = match self.direction {
Direction::Up => 0,
Direction::Down => self.arr,
};
}
self.segment_cycle = cycle;
if source == UpdateSource::Overflow || !center {
for channel in present(self.kind.channels()) {
let output = self.channel_selection(channel, registers) == CCS_OUTPUT;
if output && self.channels[channel.index()].shadow > old_arr {
self.flag_compare(channel, registers);
}
}
}
self.matches_at_segment_start(None, registers);
}
pub(super) fn set_direction_bit(&self, registers: &mut RegisterFile) {
let cr1_value = self.reg(registers, Slot::Cr1);
self.put(
registers,
Slot::Cr1,
Self::DIR.set(cr1_value, u64::from(self.direction == Direction::Down)),
);
}
pub(super) fn update(
&mut self,
source: UpdateSource,
cycle: u128,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let cr1_value = self.reg(registers, Slot::Cr1);
let reinitialize = matches!(source, UpdateSource::Software | UpdateSource::Trigger);
let center = self.center_aligned(registers);
if reinitialize {
self.segment_cycle = cycle;
self.segment_count = match self.direction {
Direction::Down if !center => self.arr,
_ => 0,
};
}
self.power.repetition = self.repetitions(registers);
if Self::UDIS.is_set(cr1_value) {
return;
}
self.reload_shadows(source, registers);
self.rearm_outputs(source, registers);
self.auto_output(registers);
if reinitialize && self.direction == Direction::Down && !center {
self.segment_count = self.arr;
}
let flagged = !Self::URS.is_set(cr1_value)
|| matches!(source, UpdateSource::Overflow | UpdateSource::Underflow);
if flagged {
self.flag_update(registers);
}
self.trigger_pulse(registers, TriggerEvent::Update);
if Self::OPM.is_set(cr1_value) {
self.put(registers, Slot::Cr1, Self::CEN.set(cr1_value, 0));
self.set_counting(false, context);
}
}
fn reload_shadows(&mut self, source: UpdateSource, registers: &RegisterFile) {
self.psc = self.reg(registers, Slot::Psc) & HALF_WORD;
self.arr = self.reg(registers, Slot::Arr) & self.width.mask();
for channel in present(self.kind.channels()) {
let output = self.channel_selection(channel, registers) == CCS_OUTPUT;
if output && Self::OC_PE[channel.index()].is_set(self.control(channel, registers)) {
self.channels[channel.index()].shadow =
self.reg(registers, channel.ccr()) & self.width.mask();
}
let state = &mut self.channels[channel.index()];
if state.cleared_for_updates > 0 {
if F::PART.errata.clear_outlasts_reset
&& source == UpdateSource::Trigger
&& !state.matched_since_clear
{
state.cleared_for_updates = 1;
} else {
state.cleared_for_updates -= 1;
}
}
}
self.load_group(registers, true);
}
fn flag_update(&mut self, registers: &mut RegisterFile) {
self.flag(registers, Self::UIF);
if !self.kind.dma() {
return;
}
let dier_value = self.reg(registers, Slot::Dier);
if Self::UDE.is_set(dier_value) {
self.dma_requests.update += 1;
self.requested[UPDATE_REQUEST] = true;
}
if Self::CCDS.is_set(self.reg(registers, Slot::Cr2)) {
for channel in present(self.kind.channels().min(4)) {
if Self::CC_DE[channel.index()].is_set(dier_value) {
self.dma_requests.channels[channel.index()] += 1;
self.requested[CHANNEL_REQUESTS[channel.index()]] = true;
}
}
}
}
pub(super) fn trigger_pulse(&mut self, registers: &RegisterFile, event: TriggerEvent) {
let cr2_value = self.reg(registers, Slot::Cr2);
if self.kind.master() && Self::MMS.get(cr2_value) == event.code() {
self.trigger_pulses += 1;
}
if self.kind.second_trigger() && Self::MMS2.get(cr2_value) == event.code() {
self.trigger2_pulses += 1;
}
}
pub(super) fn set_counting(&mut self, counting: bool, context: &mut DeviceContext<'_>) {
if counting == self.counting {
return;
}
let clock = self.clock_cycle(context.now());
if counting {
self.paused += clock - self.stopped_at;
} else {
self.stopped_at = clock;
}
self.counting = counting;
}
pub(super) fn should_count(&self, registers: &RegisterFile) -> bool {
let cen = Self::CEN.is_set(self.reg(registers, Slot::Cr1));
let gated = self.sms(registers) == SMS_GATED;
cen && (!gated || self.trigger == Level::High)
}
pub(super) fn settle(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let now = context.now();
self.seen = now;
loop {
let counter = (!self.external)
.then(|| self.next_counter_event(registers))
.flatten()
.and_then(|(cycle, event)| {
self.instant_of_counter_cycle(cycle)
.map(|at| (at, Pending::Counter(cycle, event)))
});
let filter = self.next_filter_event().and_then(|(cycle, event)| {
self.instant_of_clock_cycle(cycle)
.map(|at| (at, Pending::Filter(event)))
});
let Some((instant, pending)) =
counter.into_iter().chain(filter).min_by_key(|(at, _)| *at)
else {
break;
};
if instant > now {
break;
}
match pending {
Pending::Counter(cycle, CounterEvent::Boundary(source)) => {
self.boundary(cycle, source, registers, context);
}
Pending::Counter(_, CounterEvent::Match(found)) => {
self.compare_match(found, Some(self.direction), registers);
}
Pending::Filter(event) => self.filtered_edge(event, registers, context),
}
}
}
pub(super) fn finish(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let counting = self.should_count(registers);
self.set_counting(counting, context);
if self.kind.breaks() > 0 {
self.refresh_breaks(registers);
}
self.refresh_interrupt(registers, context);
self.refresh_trigger_outputs(registers);
self.drive_requests(registers, context);
self.schedule(registers, context);
}
fn refresh_interrupt(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let status = self.reg(registers, Slot::Sr);
let enables = self.reg(registers, Slot::Dier);
let mut wanted = [Level::Low; MAX_LINES];
for (flags, pairs) in Self::LINE_FLAGS {
let raised = pairs
.iter()
.any(|(flag, enable)| flag.is_set(status) && enable.is_set(enables));
if raised {
wanted[F::line(flags)] = Level::High;
}
}
for (line, (level, current)) in wanted
.iter()
.zip(self.interrupts.iter_mut())
.enumerate()
.take(F::LINES)
{
if level != current {
*current = *level;
context.interrupt(line, *level);
}
}
}
fn refresh_trigger_outputs(&mut self, registers: &RegisterFile) {
let cr2_value = self.reg(registers, Slot::Cr2);
let count = usize::from(self.kind.channels());
let pins = self.channels.map(|state| state.pin == Level::High);
let reference = |index: usize| index < count && pins[index];
self.trigger_output = if self.kind.master() {
match Self::MMS.get(cr2_value) {
MMS_ENABLE => Level::from(self.counting),
MMS_RESET | MMS_UPDATE | MMS_COMPARE_PULSE => Level::Low,
code => {
let index = usize::try_from(code - MMS_FIRST_REFERENCE).expect("three bits");
Level::from(reference(index))
}
}
} else {
Level::Low
};
self.trigger2_output = if self.kind.second_trigger() {
match Self::MMS2.get(cr2_value) {
MMS_ENABLE => Level::from(self.counting),
code @ MMS2_FIRST_REFERENCE..=MMS2_LAST_REFERENCE => {
let index = usize::try_from(code - MMS2_FIRST_REFERENCE).expect("four bits");
Level::from(reference(index))
}
_ => Level::Low,
}
} else {
Level::Low
};
}
fn drive_requests(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let enables = self.reg(registers, Slot::Dier);
for (output, enable) in Self::REQUEST_ENABLES.into_iter().enumerate() {
let wanted = Level::from(self.requested[output] && enable.is_set(enables));
if wanted != self.driven[output] {
self.driven[output] = wanted;
context.request(output, wanted);
}
}
}
fn events_matter(&self, registers: &RegisterFile) -> bool {
let cr2_value = self.reg(registers, Slot::Cr2);
let ccer_value = self.ccer(registers);
let outputs = (0..INPUTS.min(usize::from(self.kind.channels()))).any(|channel| {
Self::CC_E[channel].is_set(ccer_value)
|| Self::CC_NE
.get(channel)
.is_some_and(|enable| enable.is_set(ccer_value))
});
self.reg(registers, Slot::Dier) != 0
|| outputs
|| (self.kind.master() && Self::MMS.get(cr2_value) >= MMS_UPDATE)
|| (self.kind.second_trigger() && Self::MMS2.get(cr2_value) > MMS_ENABLE)
|| Self::OPM.is_set(self.reg(registers, Slot::Cr1))
|| self.sms(registers) != SMS_DISABLED
|| self.kind.breaks() > 0
}
fn schedule(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let woken = match F::PART.wakeups {
Wakeups::Every => true,
Wakeups::WhenSeen => self.events_matter(registers),
};
let counter = (!self.external && woken)
.then(|| self.next_counter_event(registers))
.flatten()
.and_then(|(cycle, _)| self.instant_of_counter_cycle(cycle));
let filter = self
.next_filter_event()
.and_then(|(cycle, _)| self.instant_of_clock_cycle(cycle));
let next = counter.into_iter().chain(filter).min();
if next != self.wakeup.map(|(at, _)| at) {
self.wakeup = next.map(|at| (at, context.schedule(at)));
}
}
fn start_segment(&mut self, count: u64, context: &DeviceContext<'_>) {
let cycle = self.counter_cycle(context.now());
let phase = cycle.saturating_sub(self.segment_cycle) % (u128::from(self.psc) + 1);
self.segment_cycle = cycle - phase;
self.segment_count = count & self.width.mask();
}
pub(super) fn set_count(&mut self, count: u64, context: &DeviceContext<'_>) {
self.start_segment(count, context);
for channel in &mut self.channels {
channel.last_match = None;
}
}
pub(super) fn rebase(&mut self, context: &DeviceContext<'_>) {
let count = self.count_now(context.now());
self.start_segment(count, context);
}
}