use std::num::NonZeroU64;
use crate::device::{Device, DeviceContext, Level, RegisterFile};
use crate::soc::FieldSpec;
use crate::time::{EventId, VirtualNanos};
use crate::soc::stm32f427::generated::tim2::{
Register, ccer, ccmr1_input, ccmr1_output, ccmr2_input, ccmr2_output, cr1, cr2, dcr, dier, egr,
smcr, sr,
};
const NANOS_PER_SECOND: u128 = 1_000_000_000;
const CHANNEL_COUNT: u8 = 4;
const CHANNELS: usize = CHANNEL_COUNT as usize;
const GLOBAL_INTERRUPT: usize = 0;
const COUNTER_MASK: u64 = u32::MAX as u64;
const HALF_WORD: u64 = 0xFFFF;
const CMS_EDGE_ALIGNED: u64 = 0b00;
const CMS_FLAGS_DOWN: u64 = 0b01;
const CMS_FLAGS_UP: u64 = 0b10;
const MMS_RESET: u64 = 0b000;
const MMS_ENABLE: u64 = 0b001;
const MMS_UPDATE: u64 = 0b010;
const MMS_COMPARE_PULSE: u64 = 0b011;
const MMS_FIRST_REFERENCE: u64 = 0b100;
const SMS_ENCODER_1: u64 = 0b001;
const SMS_ENCODER_2: u64 = 0b010;
const SMS_ENCODER_3: u64 = 0b011;
const SMS_RESET: u64 = 0b100;
const SMS_GATED: u64 = 0b101;
const SMS_TRIGGER: u64 = 0b110;
const SMS_EXTERNAL_CLOCK_1: u64 = 0b111;
const TS_LAST_INTERNAL: u64 = 0b011;
const TS_TI1_EDGES: u64 = 0b100;
const TS_TI1FP1: u64 = 0b101;
const TS_TI2FP2: u64 = 0b110;
const TS_ETRF: u64 = 0b111;
const CC_S: [&FieldSpec; CHANNELS] = [
ccmr1_output::CC1S,
ccmr1_output::CC2S,
ccmr2_output::CC3S,
ccmr2_output::CC4S,
];
const IC_PSC: [&FieldSpec; CHANNELS] = [
ccmr1_input::ICPCS,
ccmr1_input::IC2PCS,
ccmr2_input::IC3PSC,
ccmr2_input::IC4PSC,
];
const IC_F: [&FieldSpec; CHANNELS] = [
ccmr1_input::IC1F,
ccmr1_input::IC2F,
ccmr2_input::IC3F,
ccmr2_input::IC4F,
];
const OC_FE: [&FieldSpec; CHANNELS] = [
ccmr1_output::OC1FE,
ccmr1_output::OC2FE,
ccmr2_output::OC3FE,
ccmr2_output::OC4FE,
];
const OC_PE: [&FieldSpec; CHANNELS] = [
ccmr1_output::OC1PE,
ccmr1_output::OC2PE,
ccmr2_output::OC3PE,
ccmr2_output::OC4PE,
];
const OC_M: [&FieldSpec; CHANNELS] = [
ccmr1_output::OC1M,
ccmr1_output::OC2M,
ccmr2_output::OC3M,
ccmr2_output::OC4M,
];
const OC_CE: [&FieldSpec; CHANNELS] = [
ccmr1_output::OC1CE,
ccmr1_output::OC2CE,
ccmr2_output::OC3CE,
ccmr2_output::OC4CE,
];
const CCS_OUTPUT: u64 = 0b00;
const CCS_OWN_INPUT: u64 = 0b01;
const CCS_OTHER_INPUT: u64 = 0b10;
const CCS_TRC: u64 = 0b11;
const OCM_ACTIVE_ON_MATCH: u64 = 0b001;
const OCM_INACTIVE_ON_MATCH: u64 = 0b010;
const OCM_TOGGLE: u64 = 0b011;
const OCM_FORCE_INACTIVE: u64 = 0b100;
const OCM_FORCE_ACTIVE: u64 = 0b101;
const OCM_PWM_1: u64 = 0b110;
const OCM_PWM_2: u64 = 0b111;
const CC_E: [&FieldSpec; CHANNELS] = [ccer::CC1E, ccer::CC2E, ccer::CC3E, ccer::CC4E];
const CC_P: [&FieldSpec; CHANNELS] = [ccer::CC1P, ccer::CC2P, ccer::CC3P, ccer::CC4P];
const CC_NP: [&FieldSpec; CHANNELS] = [ccer::CC1NP, ccer::CC2NP, ccer::CC3NP, ccer::CC4NP];
const CC_IF: [&FieldSpec; CHANNELS] = [sr::CC1IF, sr::CC2IF, sr::CC3IF, sr::CC4IF];
const CC_OF: [&FieldSpec; CHANNELS] = [sr::CC1OF, sr::CC2OF, sr::CC3OF, sr::CC4OF];
const CC_DE: [&FieldSpec; CHANNELS] = [dier::CC1DE, dier::CC2DE, dier::CC3DE, dier::CC4DE];
const INTERRUPTS: [(&FieldSpec, &FieldSpec); 6] = [
(sr::UIF, dier::UIE),
(sr::CC1IF, dier::CC1IE),
(sr::CC2IF, dier::CC2IE),
(sr::CC3IF, dier::CC3IE),
(sr::CC4IF, dier::CC4IE),
(sr::TIF, dier::TIE),
];
const CC_G: [&FieldSpec; CHANNELS] = [egr::CC1G, egr::CC2G, egr::CC3G, egr::CC4G];
const BURST_REGISTERS: [Option<Register>; 20] = [
Some(Register::Cr1),
Some(Register::Cr2),
Some(Register::Smcr),
Some(Register::Dier),
Some(Register::Sr),
Some(Register::Egr),
Some(Register::Ccmr1Input),
Some(Register::Ccmr2Input),
Some(Register::Ccer),
Some(Register::Cnt),
Some(Register::Psc),
Some(Register::Arr),
None,
Some(Register::Ccr1),
Some(Register::Ccr2),
Some(Register::Ccr3),
Some(Register::Ccr4),
None,
Some(Register::Dcr),
Some(Register::Dmar),
];
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),
];
#[derive(Debug, Clone, Copy)]
enum Sampling {
Internal,
Dts(u128),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Channel(u8);
impl Channel {
#[must_use]
pub const fn new(number: u8) -> Option<Self> {
if number >= 1 && number <= CHANNEL_COUNT {
Some(Self(number - 1))
} else {
None
}
}
const fn index(self) -> usize {
self.0 as usize
}
fn all() -> impl Iterator<Item = Self> {
(0..CHANNEL_COUNT).map(Self)
}
const fn partner(self) -> Self {
Self(self.0 ^ 1)
}
const fn ccr(self) -> Register {
[
Register::Ccr1,
Register::Ccr2,
Register::Ccr3,
Register::Ccr4,
][self.index()]
}
const fn ccmr(self) -> Register {
if self.0 < 2 {
Register::Ccmr1Input
} else {
Register::Ccmr2Input
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Direction {
Up,
Down,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum UpdateSource {
Overflow,
Underflow,
Software,
Trigger,
}
#[derive(Debug, Clone, Copy, Default)]
struct Input {
level: Level,
filtered: Level,
pending: Option<(u128, Level)>,
}
#[derive(Debug, Clone, Copy, Default)]
struct ChannelState {
shadow: u64,
reference: bool,
cleared_for_updates: u8,
matched_since_clear: bool,
prescaled_edges: u64,
last_match: Option<(u128, u64)>,
selection: u64,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DmaRequests {
pub update: u64,
pub channels: [u64; CHANNELS],
pub trigger: u64,
}
#[derive(Debug)]
pub struct Tim {
clock_hz: NonZeroU64,
external: bool,
counting: bool,
paused: u128,
stopped_at: u128,
external_cycles: u128,
psc: u64,
arr: u64,
segment_cycle: u128,
segment_count: u64,
direction: Direction,
channels: [ChannelState; CHANNELS],
inputs: [Input; CHANNELS],
etr_pin: Level,
etr_polarised: Level,
etr_edges: u64,
etr: Input,
trigger: Level,
trigger_output: Level,
trigger_pulses: u64,
dma_requests: DmaRequests,
burst_index: u64,
halted: bool,
interrupt: Level,
wakeup: Option<(VirtualNanos, EventId)>,
}
impl Tim {
#[must_use]
pub fn with_clock(clock_hz: NonZeroU64) -> Self {
Self {
clock_hz,
external: false,
counting: false,
paused: 0,
stopped_at: 0,
external_cycles: 0,
psc: 0,
arr: COUNTER_MASK,
segment_cycle: 0,
segment_count: 0,
direction: Direction::Up,
channels: [ChannelState::default(); CHANNELS],
inputs: [Input::default(); CHANNELS],
etr_pin: Level::Low,
etr_polarised: Level::Low,
etr_edges: 0,
etr: Input::default(),
trigger: Level::Low,
trigger_output: Level::Low,
trigger_pulses: 0,
dma_requests: DmaRequests::default(),
burst_index: 0,
halted: false,
interrupt: Level::Low,
wakeup: None,
}
}
pub fn drive_input(
&mut self,
channel: Channel,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.settle(registers, context);
self.inputs[channel.index()].level = level;
self.refilter(channel, registers, context);
if channel.index() < 3 && cr2::TI1S.is_set(registers.get(Register::Cr2)) {
self.refilter(Channel(0), 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.settle(registers, context);
self.etr_pin = level;
self.feed_etr(registers, context);
self.settle(registers, context);
self.finish(registers, context);
}
fn feed_etr(&mut self, registers: &RegisterFile, context: &DeviceContext<'_>) {
let smcr_value = registers.get(Register::Smcr);
let polarised = if smcr::ETP.is_set(smcr_value) {
!self.etr_pin
} else {
self.etr_pin
};
if polarised != self.etr_polarised {
self.etr_polarised = polarised;
let every = 1u64 << smcr::ETPS.get(smcr_value);
self.etr.level = match polarised {
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 filter = filter_delay(smcr::ETF.get(smcr_value), registers);
let prescaled = self.etr.level;
start_filter(&mut self.etr, prescaled, clock, filter);
}
pub fn drive_internal_trigger(
&mut self,
trigger: u8,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.settle(registers, context);
if smcr::TS.get(registers.get(Register::Smcr)) == u64::from(trigger) {
if level == Level::High {
self.trigger_edge(registers, context);
}
self.trigger = level;
self.gate(registers, context);
}
self.finish(registers, context);
}
pub fn halt(
&mut self,
halted: bool,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.settle(registers, context);
self.halted = halted;
self.finish(registers, context);
}
#[must_use]
pub fn output(&self, channel: Channel, registers: &RegisterFile) -> Option<Level> {
ccer_field(channel, &CC_E, registers).then(|| {
let active_low = ccer_field(channel, &CC_P, registers);
Level::from(self.channels[channel.index()].reference != active_low)
})
}
fn counter(&self, now: VirtualNanos) -> u64 {
self.count_at(self.counter_cycle(now))
}
#[must_use]
pub fn trigger_output(&self) -> (Level, u64) {
(self.trigger_output, self.trigger_pulses)
}
#[must_use]
pub fn dma_requests(&self) -> DmaRequests {
self.dma_requests
}
fn clock_cycle(&self, now: VirtualNanos) -> u128 {
u128::from(now.0) * u128::from(self.clock_hz.get()) / NANOS_PER_SECOND
}
fn instant_of_clock_cycle(&self, cycle: u128) -> VirtualNanos {
let nanos = (cycle * NANOS_PER_SECOND).div_ceil(u128::from(self.clock_hz.get()));
VirtualNanos(u64::try_from(nanos).expect("the virtual clock outlasts any run"))
}
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) -> VirtualNanos {
self.instant_of_clock_cycle(cycle + self.paused)
}
fn ticks_since_segment(&self, cycle: u128) -> u64 {
let elapsed = cycle.saturating_sub(self.segment_cycle);
u64::try_from(elapsed / (u128::from(self.psc) + 1)).expect("ticks fit the counter's course")
}
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) & COUNTER_MASK,
Direction::Down => self.segment_count.wrapping_sub(ticks) & COUNTER_MASK,
}
}
fn cycle_of_tick(&self, tick: u64) -> u128 {
self.segment_cycle + u128::from(tick) * (u128::from(self.psc) + 1)
}
fn centre_aligned(registers: &RegisterFile) -> bool {
cr1::CMS.get(registers.get(Register::Cr1)) != CMS_EDGE_ALIGNED
}
fn boundary_tick(&self, registers: &RegisterFile) -> Option<(u64, UpdateSource)> {
if self.arr == 0 {
return None;
}
let centre = Self::centre_aligned(registers);
Some(match self.direction {
Direction::Up if centre => (
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 => (
COUNTER_MASK - self.segment_count + 1,
UpdateSource::Overflow,
),
Direction::Down if centre => (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 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)),
}
}
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 = Channel::all().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 centre = Self::centre_aligned(registers);
let old_arr = self.arr;
self.update(source, cycle, registers, context);
if centre {
self.direction = match source {
UpdateSource::Overflow => Direction::Down,
_ => Direction::Up,
};
self.segment_count = match self.direction {
Direction::Down => self.arr,
Direction::Up => 0,
};
set_direction_bit(self.direction, registers);
} else {
self.segment_count = match self.direction {
Direction::Up => 0,
Direction::Down => self.arr,
};
}
self.segment_cycle = cycle;
if source == UpdateSource::Overflow || !centre {
for channel in Channel::all() {
let output = 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);
}
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_7_3(channel, cycle, value, registers);
self.channels[channel.index()].last_match = Some((cycle, value));
let cms = cr1::CMS.get(registers.get(Register::Cr1));
let gated = matches!(
(cms, counted),
(CMS_FLAGS_DOWN, Some(Direction::Up)) | (CMS_FLAGS_UP, Some(Direction::Down))
);
let lost = missed && Self::centre_aligned(registers);
if !gated && !lost {
self.flag_compare(channel, registers);
}
let state = &mut self.channels[channel.index()];
if !missed {
state.matched_since_clear = true;
}
match output_mode(channel, registers) {
OCM_ACTIVE_ON_MATCH => state.reference = true,
OCM_INACTIVE_ON_MATCH => state.reference = false,
OCM_TOGGLE if !missed => state.reference = !state.reference,
_ => {}
}
self.refresh_pwm(channel, self.count_at(cycle), registers);
}
fn flag_compare(&mut self, channel: Channel, registers: &mut RegisterFile) {
flag(registers, CC_IF[channel.index()]);
let dier_value = registers.get(Register::Dier);
let on_update = cr2::CCDS.is_set(registers.get(Register::Cr2));
if CC_DE[channel.index()].is_set(dier_value) && !on_update {
self.dma_requests.channels[channel.index()] += 1;
}
if channel.index() == 0 && cr2::MMS.get(registers.get(Register::Cr2)) == MMS_COMPARE_PULSE {
self.trigger_pulses += 1;
}
}
fn missed_by_erratum_2_7_3(
&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::centre_aligned(registers) {
(last_value + 1 == self.arr && value == self.arr) || (last_value == 1 && value == 0)
} else {
last_value == self.arr && value == 0
}
}
fn matches_at_segment_start(
&mut self,
counted: Option<Direction>,
registers: &mut RegisterFile,
) {
let cycle = self.segment_cycle;
for channel in Channel::all() {
if 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 Channel::all() {
self.refresh_pwm(channel, self.segment_count, registers);
}
}
fn refresh_pwm(&mut self, channel: Channel, count: u64, registers: &RegisterFile) {
let mode = output_mode(channel, registers);
let state = &mut self.channels[channel.index()];
let value = state.shadow;
let active = match (mode, self.direction) {
(OCM_PWM_1, Direction::Up) => count < value,
(OCM_PWM_1, Direction::Down) => count <= value,
(OCM_PWM_2, Direction::Up) => count >= value,
(OCM_PWM_2, Direction::Down) => count > value,
_ => return,
};
if state.cleared_for_updates == 0 {
state.reference = active;
}
}
fn update(
&mut self,
source: UpdateSource,
cycle: u128,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let cr1_value = registers.get(Register::Cr1);
let reinitialise = matches!(source, UpdateSource::Software | UpdateSource::Trigger);
let centre = Self::centre_aligned(registers);
if reinitialise {
self.segment_cycle = cycle;
self.segment_count = match self.direction {
Direction::Down if !centre => self.arr,
_ => 0,
};
}
if cr1::UDIS.is_set(cr1_value) {
return;
}
self.reload_shadows(source, registers);
if reinitialise && self.direction == Direction::Down && !centre {
self.segment_count = self.arr;
}
let flagged = !cr1::URS.is_set(cr1_value)
|| matches!(source, UpdateSource::Overflow | UpdateSource::Underflow);
if flagged {
self.flag_update(registers);
}
if cr2::MMS.get(registers.get(Register::Cr2)) == MMS_UPDATE {
self.trigger_pulses += 1;
}
if cr1::OPM.is_set(cr1_value) {
registers.set(Register::Cr1, cr1::CEN.set(cr1_value, 0));
self.set_counting(false, context);
}
}
fn reload_shadows(&mut self, source: UpdateSource, registers: &RegisterFile) {
self.psc = registers.get(Register::Psc) & HALF_WORD;
self.arr = registers.get(Register::Arr) & COUNTER_MASK;
for channel in Channel::all() {
let output = channel_selection(channel, registers) == CCS_OUTPUT;
if output && ccmr_field(channel, &OC_PE, registers) {
self.channels[channel.index()].shadow = registers.get(channel.ccr()) & COUNTER_MASK;
}
let state = &mut self.channels[channel.index()];
if state.cleared_for_updates > 0 {
if source == UpdateSource::Trigger && !state.matched_since_clear {
state.cleared_for_updates = 1;
} else {
state.cleared_for_updates -= 1;
}
}
}
}
fn flag_update(&mut self, registers: &mut RegisterFile) {
flag(registers, sr::UIF);
let dier_value = registers.get(Register::Dier);
if dier::UDE.is_set(dier_value) {
self.dma_requests.update += 1;
}
if cr2::CCDS.is_set(registers.get(Register::Cr2)) {
for channel in Channel::all() {
if CC_DE[channel.index()].is_set(dier_value) {
self.dma_requests.channels[channel.index()] += 1;
}
}
}
}
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;
}
fn should_count(&self, registers: &RegisterFile) -> bool {
let cen = cr1::CEN.is_set(registers.get(Register::Cr1));
let gated = smcr::SMS.get(registers.get(Register::Smcr)) == SMS_GATED;
cen && !self.halted && (!gated || self.trigger == Level::High)
}
fn pin_level(&self, channel: Channel, registers: &RegisterFile) -> Level {
if channel.index() == 0 && cr2::TI1S.is_set(registers.get(Register::Cr2)) {
let bits = self.inputs[..3]
.iter()
.filter(|input| input.level == Level::High)
.count();
Level::from(bits % 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 filter = filter_delay(ccmr_value_of(channel, &IC_F, registers), registers);
start_filter(&mut self.inputs[channel.index()], level, clock, filter);
}
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));
inputs.chain(etr).min_by_key(|(cycle, _)| *cycle)
}
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);
}
}
}
}
fn etrf_level(
&mut self,
new: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let smcr_value = registers.get(Register::Smcr);
if new == Level::High {
if smcr::ECE.is_set(smcr_value) {
self.external_tick(registers, context);
}
if smcr::TS.get(smcr_value) == TS_ETRF {
self.trigger_edge(registers, context);
}
self.clear_references(registers);
}
if smcr::TS.get(smcr_value) == TS_ETRF {
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()] {
let mapped = match channel_selection(channel, registers) {
CCS_OWN_INPUT => channel == source,
CCS_OTHER_INPUT => channel.partner() == source,
_ => false,
};
if mapped && 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);
}
fn clear_references(&mut self, registers: &RegisterFile) {
for channel in Channel::all() {
let clear_enabled = ccmr_field(channel, &OC_CE, registers);
let mode = output_mode(channel, registers);
let forced = matches!(mode, OCM_FORCE_ACTIVE | OCM_FORCE_INACTIVE);
if clear_enabled && !forced && channel_selection(channel, registers) == CCS_OUTPUT {
let state = &mut self.channels[channel.index()];
state.reference = false;
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 = smcr::TS.get(registers.get(Register::Smcr));
let (trigger_level, is_edge) = match (ts, source.index()) {
(TS_TI1_EDGES, 0) => (Level::High, true),
(TS_TI1FP1, 0) | (TS_TI2FP2, 1) => {
let polarised = if ccer_field(source, &CC_P, registers) {
!new
} else {
new
};
(polarised, edge_selected(source, new, registers))
}
_ => 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);
}
fn gate(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let counting = self.should_count(registers);
if smcr::SMS.get(registers.get(Register::Smcr)) == SMS_GATED && counting != self.counting {
flag(registers, sr::TIF);
self.set_counting(counting, context);
}
}
fn trigger_edge(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let smcr_value = registers.get(Register::Smcr);
if smcr::TS.get(smcr_value) <= TS_LAST_INTERNAL {
for channel in Channel::all() {
if channel_selection(channel, registers) == CCS_TRC {
self.capture(channel, registers, context);
}
}
}
match smcr::SMS.get(smcr_value) {
SMS_RESET => {
let cycle = self.counter_cycle(context.now());
self.update(UpdateSource::Trigger, cycle, registers, context);
self.matches_at_segment_start(Some(self.direction), registers);
if cr2::MMS.get(registers.get(Register::Cr2)) == MMS_RESET {
self.trigger_pulses += 1;
}
self.flag_trigger(registers);
}
SMS_TRIGGER => {
let cr1_value = registers.get(Register::Cr1);
registers.set(Register::Cr1, cr1::CEN.set(cr1_value, 1));
let counting = self.should_count(registers);
self.set_counting(counting, context);
self.fast_enable(registers);
self.flag_trigger(registers);
}
SMS_EXTERNAL_CLOCK_1 => {
if !smcr::ECE.is_set(registers.get(Register::Smcr)) {
self.external_tick(registers, context);
}
self.flag_trigger(registers);
}
_ => {}
}
}
fn flag_trigger(&mut self, registers: &mut RegisterFile) {
flag(registers, sr::TIF);
if dier::TDE.is_set(registers.get(Register::Dier)) {
self.dma_requests.trigger += 1;
}
}
fn fast_enable(&mut self, registers: &RegisterFile) {
for channel in Channel::all() {
let mode = output_mode(channel, registers);
if ccmr_field(channel, &OC_FE, registers) && matches!(mode, OCM_PWM_1 | OCM_PWM_2) {
self.channels[channel.index()].reference = mode == OCM_PWM_2;
}
}
}
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 = smcr::SMS.get(registers.get(Register::Smcr));
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 polarised = |channel: Channel, level: Level| {
if ccer_field(channel, &CC_P, registers) {
!level
} else {
level
}
};
let edge = polarised(source, new);
let other = source.partner();
let other_level = polarised(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;
set_direction_bit(direction, registers);
}
if counted {
self.external_tick(registers, context);
}
}
fn capture(
&mut self,
channel: Channel,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if !ccer_field(channel, &CC_E, registers) {
return;
}
let every = 1u64 << ccmr_value_of(channel, &IC_PSC, registers);
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()));
registers.set(channel.ccr(), count);
self.channels[channel.index()].shadow = count;
self.flag_capture(channel, registers);
}
fn flag_capture(&mut self, channel: Channel, registers: &mut RegisterFile) {
if CC_IF[channel.index()].is_set(registers.get(Register::Sr)) {
flag(registers, CC_OF[channel.index()]);
}
self.flag_compare(channel, registers);
}
fn settle(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let now = context.now();
loop {
let counter = (!self.external)
.then(|| self.next_counter_event(registers))
.flatten()
.map(|(cycle, event)| {
(
self.instant_of_counter_cycle(cycle),
Pending::Counter(cycle, event),
)
});
let filter = self
.next_filter_event()
.map(|(cycle, event)| (self.instant_of_clock_cycle(cycle), 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),
}
}
}
fn finish(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let counting = self.should_count(registers);
self.set_counting(counting, context);
self.refresh_interrupt(registers, context);
self.refresh_trigger_output(registers);
self.schedule(registers, context);
}
fn refresh_interrupt(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let status = registers.get(Register::Sr);
let enables = registers.get(Register::Dier);
let wanted = Level::from(
INTERRUPTS
.iter()
.any(|(flag, enable)| flag.is_set(status) && enable.is_set(enables)),
);
if wanted != self.interrupt {
self.interrupt = wanted;
match wanted {
Level::High => context.raise(GLOBAL_INTERRUPT),
Level::Low => context.lower(GLOBAL_INTERRUPT),
}
}
}
fn refresh_trigger_output(&mut self, registers: &RegisterFile) {
let mms = cr2::MMS.get(registers.get(Register::Cr2));
self.trigger_output = match mms {
MMS_ENABLE => Level::from(self.counting),
MMS_RESET | MMS_UPDATE | MMS_COMPARE_PULSE => Level::Low,
reference => {
let index = usize::try_from(reference - MMS_FIRST_REFERENCE).expect("three bits");
Level::from(self.channels[index].reference)
}
};
}
fn schedule(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let counter = (!self.external)
.then(|| self.next_counter_event(registers))
.flatten()
.map(|(cycle, _)| self.instant_of_counter_cycle(cycle));
let filter = self
.next_filter_event()
.map(|(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 & COUNTER_MASK;
}
fn set_count(&mut self, count: u64, context: &DeviceContext<'_>) {
self.start_segment(count, context);
for channel in &mut self.channels {
channel.last_match = None;
}
}
fn rebase(&mut self, context: &DeviceContext<'_>) {
let count = self.counter(context.now());
self.start_segment(count, context);
}
fn write_cr1(
&mut self,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let centre = cr1::CMS.get(value) != CMS_EDGE_ALIGNED;
let encoder = matches!(
smcr::SMS.get(registers.get(Register::Smcr)),
SMS_ENCODER_1 | SMS_ENCODER_2 | SMS_ENCODER_3
);
if centre || encoder {
registers.set(
Register::Cr1,
cr1::DIR.set(value, u64::from(self.direction == Direction::Down)),
);
} else {
let direction = if cr1::DIR.is_set(value) {
Direction::Down
} else {
Direction::Up
};
if direction != self.direction {
let count = self.counter(context.now());
self.direction = direction;
self.set_count(count, context);
}
}
let arr = registers.get(Register::Arr) & COUNTER_MASK;
if !cr1::ARPE.is_set(value) && arr != self.arr {
self.rebase(context);
self.arr = arr;
}
}
fn write_smcr(
&mut self,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let sms = smcr::SMS.get(value);
let external = smcr::ECE.is_set(value)
|| matches!(
sms,
SMS_EXTERNAL_CLOCK_1 | SMS_ENCODER_1 | SMS_ENCODER_2 | SMS_ENCODER_3
);
if external != self.external {
let count = self.counter(context.now());
self.external = external;
self.external_cycles = 0;
self.set_count(count, context);
}
for channel in Channel::all().take(2) {
self.refilter(channel, registers, context);
}
self.feed_etr(registers, context);
}
fn write_egr(
&mut self,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if egr::UG.is_set(value) {
let cycle = self.counter_cycle(context.now());
self.update(UpdateSource::Software, cycle, registers, context);
self.matches_at_segment_start(Some(self.direction), registers);
if cr2::MMS.get(registers.get(Register::Cr2)) == MMS_RESET {
self.trigger_pulses += 1;
}
}
for channel in Channel::all() {
if CC_G[channel.index()].is_set(value) {
if channel_selection(channel, registers) == CCS_OUTPUT {
self.flag_compare(channel, registers);
} else {
let count = self.counter(context.now());
registers.set(channel.ccr(), count);
self.channels[channel.index()].shadow = count;
self.flag_capture(channel, registers);
}
}
}
if egr::TG.is_set(value) {
self.flag_trigger(registers);
}
registers.set(Register::Egr, 0);
}
fn write_ccmr(
&mut self,
register: Register,
value: u64,
registers: &mut RegisterFile,
context: &DeviceContext<'_>,
) {
let first = if register == Register::Ccmr1Input {
Channel(0)
} else {
Channel(2)
};
let pair = [first, first.partner()];
let mut held = value;
for channel in pair {
if ccer_field(channel, &CC_E, registers) {
held = CC_S[channel.index()].set(held, self.channels[channel.index()].selection);
}
}
registers.set(register, held);
let count = self.counter(context.now());
for channel in pair {
let selection = channel_selection(channel, registers);
if selection == CCS_OUTPUT && !ccmr_field(channel, &OC_PE, registers) {
self.channels[channel.index()].shadow = registers.get(channel.ccr()) & COUNTER_MASK;
}
let state = &mut self.channels[channel.index()];
state.selection = selection;
match output_mode(channel, registers) {
OCM_FORCE_ACTIVE => state.reference = true,
OCM_FORCE_INACTIVE => state.reference = false,
_ => {}
}
self.refresh_pwm(channel, count, registers);
}
}
fn write_ccr(&mut self, channel: Channel, registers: &mut RegisterFile) {
if channel_selection(channel, registers) != CCS_OUTPUT {
registers.set(channel.ccr(), self.channels[channel.index()].shadow);
return;
}
if !ccmr_field(channel, &OC_PE, registers) {
self.channels[channel.index()].shadow = registers.get(channel.ccr()) & COUNTER_MASK;
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CounterEvent {
Boundary(UpdateSource),
Match(Match),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Match {
channel: Channel,
cycle: u128,
value: u64,
}
impl CounterEvent {
const fn order(self) -> u8 {
match self {
Self::Boundary(_) => 0,
Self::Match(..) => 1,
}
}
}
#[derive(Debug, Clone, Copy)]
enum FilterEvent {
Input(usize),
Etr,
}
#[derive(Debug, Clone, Copy)]
enum Pending {
Counter(u128, CounterEvent),
Filter(FilterEvent),
}
impl Device for Tim {
type Register = Register;
fn read(
&mut self,
register: Register,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64> {
self.settle(registers, context);
let answer = match register {
Register::Cnt => Some(self.counter(context.now())),
Register::Ccr1 | Register::Ccr2 | Register::Ccr3 | Register::Ccr4 => {
let channel = Channel::all().find(|channel| channel.ccr() == register)?;
if channel_selection(channel, registers) != CCS_OUTPUT {
let status = registers.get(Register::Sr);
registers.set(Register::Sr, CC_IF[channel.index()].set(status, 0));
}
None
}
Register::Dmar => {
let target = self.burst_step(registers)?;
Some(registers.get(target))
}
_ => None,
};
self.finish(registers, context);
answer
}
fn write(
&mut self,
register: Register,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.settle(registers, context);
match register {
Register::Cr1 => {
let was_counting = self.counting;
self.write_cr1(value, registers, context);
let counting = self.should_count(registers);
self.set_counting(counting, context);
if counting && !was_counting {
self.matches_at_segment_start(Some(self.direction), registers);
}
}
Register::Smcr => self.write_smcr(value, registers, context),
Register::Egr => self.write_egr(value, registers, context),
Register::Ccmr1Input | Register::Ccmr1Output => {
self.write_ccmr(Register::Ccmr1Input, value, registers, context);
}
Register::Ccmr2Input | Register::Ccmr2Output => {
self.write_ccmr(Register::Ccmr2Input, value, registers, context);
}
Register::Ccer => {
for channel in Channel::all() {
if !ccer_field(channel, &CC_E, registers) {
self.channels[channel.index()].prescaled_edges = 0;
}
self.refresh_pwm(channel, self.counter(context.now()), registers);
}
}
Register::Cnt => {
self.set_count(value, context);
self.matches_at_segment_start(Some(self.direction), registers);
}
Register::Arr => {
let arr = value & COUNTER_MASK;
if !cr1::ARPE.is_set(registers.get(Register::Cr1)) && arr != self.arr {
self.rebase(context);
self.arr = arr;
}
}
Register::Ccr1 | Register::Ccr2 | Register::Ccr3 | Register::Ccr4 => {
if let Some(channel) = Channel::all().find(|channel| channel.ccr() == register) {
self.rebase(context);
self.write_ccr(channel, registers);
self.refresh_pwm(channel, self.counter(context.now()), registers);
}
}
Register::Dmar => {
if let Some(target) = self.burst_step(registers) {
registers.write_over_bus(target, value);
self.write(target, value, registers, context);
return;
}
}
_ => {}
}
self.finish(registers, context);
}
fn on_event(
&mut self,
event: EventId,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if self.wakeup.map(|(_, id)| id) != Some(event) {
return;
}
self.settle(registers, context);
self.finish(registers, context);
}
}
impl Tim {
fn burst_step(&mut self, registers: &RegisterFile) -> Option<Register> {
let dcr_value = registers.get(Register::Dcr);
let base = dcr::DBA.get(dcr_value);
let length = dcr::DBL.get(dcr_value);
let index = base + self.burst_index;
self.burst_index = (self.burst_index + 1) % (length + 1);
BURST_REGISTERS
.get(usize::try_from(index).expect("ten bits"))
.copied()
.flatten()
}
}
fn filter_delay(row: u64, registers: &RegisterFile) -> u128 {
let (sampling, samples) = FILTERS[usize::try_from(row).expect("four bits")];
let dts_division = 1u128 << cr1::CKD.get(registers.get(Register::Cr1));
let period = match sampling {
Sampling::Internal => 1,
Sampling::Dts(divisor) => dts_division * divisor,
};
period * samples
}
fn start_filter(input: &mut Input, level: Level, clock: u128, delay: u128) {
if level == input.filtered {
input.pending = None;
} else {
input.pending = Some((clock + delay, level));
}
}
fn edge_selected(channel: Channel, new: Level, registers: &RegisterFile) -> bool {
let falling = ccer_field(channel, &CC_P, registers);
let both = ccer_field(channel, &CC_NP, registers);
both || (new == Level::High) != falling
}
fn ccmr_value_of(
channel: Channel,
fields: &[&FieldSpec; CHANNELS],
registers: &RegisterFile,
) -> u64 {
fields[channel.index()].get(registers.get(channel.ccmr()))
}
fn ccmr_field(channel: Channel, fields: &[&FieldSpec; CHANNELS], registers: &RegisterFile) -> bool {
fields[channel.index()].is_set(registers.get(channel.ccmr()))
}
fn ccer_field(channel: Channel, fields: &[&FieldSpec; CHANNELS], registers: &RegisterFile) -> bool {
fields[channel.index()].is_set(registers.get(Register::Ccer))
}
fn flag(registers: &mut RegisterFile, field: &FieldSpec) {
let status = registers.get(Register::Sr);
registers.set(Register::Sr, field.set(status, 1));
}
fn channel_selection(channel: Channel, registers: &RegisterFile) -> u64 {
ccmr_value_of(channel, &CC_S, registers)
}
fn output_mode(channel: Channel, registers: &RegisterFile) -> u64 {
ccmr_value_of(channel, &OC_M, registers)
}
fn set_direction_bit(direction: Direction, registers: &mut RegisterFile) {
let cr1_value = registers.get(Register::Cr1);
registers.set(
Register::Cr1,
cr1::DIR.set(cr1_value, u64::from(direction == Direction::Down)),
);
}