use core::num::NonZeroU16;
use crate::{
command::Command,
error::Error,
fmt,
register::RegisterAddress,
subdevice::{ports::Topology, SubDevice},
MainDevice, SubDeviceRef,
};
async fn latch_dc_times(
maindevice: &MainDevice<'_>,
subdevices: &mut [SubDevice],
) -> Result<(), Error> {
let num_subdevices_with_dc: usize = subdevices
.iter()
.filter(|subdevice| subdevice.dc_support().any())
.count();
Command::bwr(RegisterAddress::DcTimePort0.into())
.with_wkc(num_subdevices_with_dc as u16)
.send(maindevice, 0u32)
.await?;
for subdevice in subdevices
.iter_mut()
.filter(|subdevice| subdevice.dc_support().any())
{
let sl = SubDeviceRef::new(maindevice, subdevice.configured_address(), ());
let dc_receive_time = sl
.read(RegisterAddress::DcReceiveTime)
.ignore_wkc()
.receive::<u64>(maindevice)
.await?;
let [time_p0, time_p1, time_p2, time_p3] = sl
.read(RegisterAddress::DcTimePort0)
.receive::<[u32; 4]>(maindevice)
.await
.map_err(|e| {
fmt::error!(
"Failed to read DC times for SubDevice {:#06x}: {}",
subdevice.configured_address(),
e
);
e
})?;
subdevice.dc_receive_time = dc_receive_time;
fmt::trace!(
"SubDevice {:#06x} DC receive time {} ns",
subdevice.configured_address(),
subdevice.dc_receive_time
);
subdevice
.ports
.set_receive_times(time_p0, time_p3, time_p1, time_p2);
}
Ok(())
}
async fn write_dc_parameters(
maindevice: &MainDevice<'_>,
subdevice: &SubDevice,
dc_system_time: u64,
now_nanos: u64,
) -> Result<(), Error> {
let system_time_offset = -(subdevice.dc_receive_time as i64) + now_nanos as i64;
fmt::trace!(
"Setting SubDevice {:#06x} system time offset to {} ns (system time is {} ns, DC receive time is {}, now is {} ns)",
subdevice.configured_address(),
system_time_offset,
dc_system_time,
subdevice.dc_receive_time,
now_nanos
);
Command::fpwr(
subdevice.configured_address(),
RegisterAddress::DcSystemTimeOffset.into(),
)
.ignore_wkc()
.send(maindevice, system_time_offset)
.await?;
Command::fpwr(
subdevice.configured_address(),
RegisterAddress::DcSystemTimeTransmissionDelay.into(),
)
.ignore_wkc()
.send(maindevice, subdevice.propagation_delay)
.await?;
Ok(())
}
fn find_subdevice_parent(
parents: &[SubDevice],
subdevice: &SubDevice,
) -> Result<Option<u16>, Error> {
if parents.is_empty() {
return Ok(None);
}
let mut parents_it = parents.iter().rev();
if let Some(parent) = parents_it.next() {
if parent.ports.topology() == Topology::LineEnd {
let split_point = parents_it
.find(|subdevice| subdevice.ports.topology().is_junction())
.ok_or_else(|| {
fmt::error!(
"Did not find fork parent for SubDevice {:#06x}",
subdevice.configured_address()
);
Error::Topology
})?;
Ok(Some(split_point.index))
}
else {
Ok(Some(parent.index))
}
} else {
fmt::error!(
"Did not find parent for SubDevice {:#06x}",
subdevice.configured_address()
);
Err(Error::Topology)
}
}
fn configure_subdevice_offsets(
subdevice: &mut SubDevice,
parents: &[SubDevice],
delay_accum: &mut u32,
) {
{
let time_p0 = subdevice.ports.0[0].dc_receive_time;
let time_p3 = subdevice.ports.0[1].dc_receive_time;
let time_p1 = subdevice.ports.0[2].dc_receive_time;
let time_p2 = subdevice.ports.0[3].dc_receive_time;
let d03 = time_p3.saturating_sub(time_p0);
let d31 = time_p1.saturating_sub(time_p3);
let d12 = time_p2.saturating_sub(time_p1);
fmt::debug!(
"--> Topology {:?}, {}",
subdevice.ports.topology(),
subdevice.ports
);
fmt::debug!(
"--> Receive times {} ns ({} ns) {} ({} ns) {} ({} ns) {} (total {})",
time_p0,
if subdevice.ports.0[1].active { d03 } else { 0 },
time_p3,
if subdevice.ports.0[2].active { d31 } else { 0 },
time_p1,
if subdevice.ports.0[3].active { d12 } else { 0 },
time_p2,
subdevice.ports.total_propagation_time().unwrap_or(0)
);
}
let parent = subdevice
.parent_index
.and_then(|parent_index| parents.iter().find(|parent| parent.index == parent_index));
if let Some(parent) = parent {
let parent_port = fmt::unwrap_opt!(
parent.ports.port_assigned_to(subdevice),
"Parent assigned port"
);
let this_port = subdevice.ports.entry_port();
fmt::debug!(
"--> Parent ({:?}) {} port {} assigned to {} port {} (SubDevice is child of parent: {:?})",
parent.ports.topology(),
parent.name(),
parent_port.number,
subdevice.name(),
this_port.number,
subdevice.is_child_of(parent)
);
let parent_prop_time = parent.ports.total_propagation_time().unwrap_or(0);
let this_prop_time = subdevice.ports.total_propagation_time().unwrap_or(0);
fmt::debug!(
"--> Parent propagation time {}, my prop. time {} delta {}",
parent_prop_time,
this_prop_time,
parent_prop_time - this_prop_time
);
let propagation_delay = match parent.ports.topology() {
Topology::Passthrough => (parent_prop_time - this_prop_time) / 2,
Topology::Fork => {
if subdevice.is_child_of(parent) {
let children_loop_time =
parent.ports.propagation_time_to(parent_port).unwrap_or(0);
(children_loop_time - this_prop_time) / 2
} else {
(parent_prop_time - this_prop_time) / 2
}
}
Topology::Cross => {
if subdevice.is_child_of(parent) {
let children_loop_time =
parent.ports.intermediate_propagation_time_to(parent_port);
(children_loop_time - this_prop_time) / 2
} else {
parent_prop_time - *delay_accum
}
}
Topology::LineEnd => 0,
};
*delay_accum += propagation_delay;
fmt::debug!(
"--> Propagation delay {} (delta {}) ns",
delay_accum,
propagation_delay,
);
subdevice.propagation_delay = *delay_accum;
}
}
fn assign_parent_relationships(subdevices: &mut [SubDevice]) -> Result<(), Error> {
let mut delay_accum = 0;
for i in 0..subdevices.len() {
let (parents, rest) = subdevices.split_at_mut(i);
let subdevice = rest.first_mut().ok_or(Error::Internal)?;
subdevice.parent_index = find_subdevice_parent(parents, subdevice)?;
fmt::debug!(
"SubDevice {:#06x} {} DC support {:?}",
subdevice.configured_address(),
subdevice.name,
subdevice.dc_support()
);
if let Some(parent_idx) = subdevice.parent_index {
let parent =
fmt::unwrap_opt!(parents.iter_mut().find(|parent| parent.index == parent_idx));
fmt::unwrap_opt!(
NonZeroU16::new(subdevice.index)
.and_then(|index| parent.ports.assign_next_downstream_port(index)),
"no free ports on parent"
);
}
if subdevice.dc_support().any() {
configure_subdevice_offsets(subdevice, parents, &mut delay_accum);
} else {
fmt::trace!(
"--> Skipping DC config for slSubDeviceave {:#06x}: DC not supported",
subdevice.configured_address()
);
}
}
#[cfg(feature = "std")]
if option_env!("PRINT_TEST_CASE").is_some() {
for subdevice in subdevices.iter() {
let p = subdevice.ports.0;
let ports = format!(
"{}, {}, {}, {}, {}, {}, {}, {}",
p[0].active,
p[0].dc_receive_time,
p[1].active,
p[1].dc_receive_time,
p[2].active,
p[2].dc_receive_time,
p[3].active,
p[3].dc_receive_time,
);
println!(
r#"SubDevice {{
index: {},
configured_address: {:#06x},
name: "{}".try_into().unwrap(),
ports: ports(
{}
),
dc_receive_time: {},
..defaults.clone()
}},"#,
subdevice.index,
subdevice.configured_address(),
subdevice.name,
ports,
subdevice.dc_receive_time,
);
}
for subdevice in subdevices.iter() {
println!(
"// Index {}: {} ({:?})",
subdevice.index,
subdevice.name(),
subdevice.ports.topology()
);
print!("(");
print!("[");
for p in subdevice.ports.0 {
print!("{:?}, ", p.downstream_to);
}
print!("],");
print!(
"{:?}, {}, ",
subdevice.parent_index, subdevice.propagation_delay
);
print!("),");
println!();
}
}
Ok(())
}
pub(crate) async fn configure_dc<'subdevices>(
maindevice: &MainDevice<'_>,
subdevices: &'subdevices mut [SubDevice],
now: impl Fn() -> u64,
) -> Result<Option<&'subdevices SubDevice>, Error> {
latch_dc_times(maindevice, subdevices).await?;
assign_parent_relationships(subdevices)?;
let first_dc_subdevice = subdevices
.iter()
.find(|subdevice| subdevice.dc_support().any());
if let Some(first_dc_subdevice) = first_dc_subdevice.as_ref() {
let now_nanos = now();
for subdevice in subdevices.iter().filter(|sl| sl.dc_support().any()) {
write_dc_parameters(
maindevice,
subdevice,
first_dc_subdevice.dc_receive_time,
now_nanos,
)
.await?;
}
} else {
fmt::debug!("No SubDevices with DC support found");
}
fmt::debug!("Distributed clock config complete");
Ok(first_dc_subdevice)
}
pub(crate) async fn run_dc_static_sync(
maindevice: &MainDevice<'_>,
dc_reference_subdevice: &SubDevice,
iterations: u32,
) -> Result<(), Error> {
fmt::debug!(
"Performing static drift compensation using SubDevice {:#06x} {} as reference. This can take some time...",
dc_reference_subdevice.configured_address(),
dc_reference_subdevice.name
);
for _ in 0..iterations {
Command::frmw(
dc_reference_subdevice.configured_address(),
RegisterAddress::DcSystemTime.into(),
)
.receive_wkc::<u64>(maindevice)
.await?;
}
fmt::debug!("Static drift compensation complete");
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
subdevice::ports::{tests::make_ports, Port, Ports},
DcSupport,
};
fn ports_passthrough() -> Ports {
make_ports(true, true, false, false)
}
fn ports_fork() -> Ports {
make_ports(true, true, true, false)
}
fn ports_eol() -> Ports {
make_ports(true, false, false, false)
}
#[test]
fn parent_is_ek1100() {
let subdevice_defaults = SubDevice {
configured_address: 0x0000,
ports: Ports::default(),
name: "Default".try_into().unwrap(),
..Default::default()
};
let parents = [
SubDevice {
configured_address: 0x1000,
ports: ports_passthrough(),
name: "LAN9252".try_into().unwrap(),
index: 0,
..subdevice_defaults.clone()
},
SubDevice {
configured_address: 0x1100,
ports: ports_fork(),
name: "EK1100".try_into().unwrap(),
index: 1,
..subdevice_defaults.clone()
},
SubDevice {
configured_address: 0x2004,
ports: ports_passthrough(),
name: "EL2004".try_into().unwrap(),
index: 2,
..subdevice_defaults.clone()
},
SubDevice {
configured_address: 0x3004,
ports: ports_eol(),
name: "EL3004".try_into().unwrap(),
index: 3,
..subdevice_defaults.clone()
},
];
let me = SubDevice {
configured_address: 0x9252,
ports: ports_eol(),
name: "LAN9252".try_into().unwrap(),
index: 4,
..subdevice_defaults
};
let parent_index = find_subdevice_parent(&parents, &me);
assert_eq!(parent_index.unwrap(), Some(1));
}
#[test]
fn two_ek1100() {
let subdevice_defaults = SubDevice {
configured_address: 0x0000,
ports: Ports::default(),
name: "Default".try_into().unwrap(),
..Default::default()
};
let parents = [
SubDevice {
configured_address: 0x1100,
ports: ports_fork(),
name: "EK1100".try_into().unwrap(),
index: 1,
..subdevice_defaults.clone()
},
SubDevice {
configured_address: 0x2004,
ports: ports_passthrough(),
name: "EL2004".try_into().unwrap(),
index: 2,
..subdevice_defaults.clone()
},
SubDevice {
configured_address: 0x3004,
ports: ports_eol(),
name: "EL3004".try_into().unwrap(),
index: 3,
..subdevice_defaults.clone()
},
SubDevice {
configured_address: 0x1100,
ports: ports_fork(),
name: "EK1100_2".try_into().unwrap(),
index: 4,
..subdevice_defaults.clone()
},
SubDevice {
configured_address: 0x2004,
ports: ports_passthrough(),
name: "EL2828".try_into().unwrap(),
index: 5,
..subdevice_defaults.clone()
},
SubDevice {
configured_address: 0x3004,
ports: ports_eol(),
name: "EL2889".try_into().unwrap(),
index: 6,
..subdevice_defaults.clone()
},
];
let ek1100_2_parents = &parents[0..3];
let ek1100_2 = &parents[3];
let el2828_parents = &parents[0..4];
let el2828 = &parents[4];
assert_eq!(
find_subdevice_parent(ek1100_2_parents, ek1100_2).unwrap(),
Some(1)
);
assert_eq!(
find_subdevice_parent(el2828_parents, el2828).unwrap(),
Some(ek1100_2.index)
);
}
#[test]
fn first_in_chain() {
let subdevice_defaults = SubDevice {
configured_address: 0x1000,
ports: Ports::default(),
name: "Default".try_into().unwrap(),
..Default::default()
};
let parents = [];
let me = SubDevice {
configured_address: 0x1100,
ports: ports_eol(),
name: "EK1100".try_into().unwrap(),
index: 4,
..subdevice_defaults
};
let parent_index = find_subdevice_parent(&parents, &me);
assert_eq!(parent_index.unwrap(), None);
}
#[test]
fn p0_p1_times_only() {
let ports = Ports([
Port {
active: true,
dc_receive_time: 641524306,
number: 0,
downstream_to: None,
},
Port {
active: false,
dc_receive_time: 1413563250,
number: 1,
downstream_to: None,
},
Port {
active: false,
dc_receive_time: 0,
number: 2,
downstream_to: None,
},
Port {
active: false,
dc_receive_time: 0,
number: 3,
downstream_to: None,
},
]);
assert_eq!(ports.topology(), Topology::LineEnd);
let mut subdevice = SubDevice {
configured_address: 0x1000,
ports,
name: "Default".try_into().unwrap(),
..Default::default()
};
let parents = [];
let mut delay_accum = 0u32;
configure_subdevice_offsets(&mut subdevice, &parents, &mut delay_accum);
assert_eq!(subdevice.dc_receive_time, 0u64);
}
#[allow(clippy::too_many_arguments)]
fn ports(
active0: bool,
t0: u32,
active3: bool,
t3: u32,
active1: bool,
t1: u32,
active2: bool,
t2: u32,
) -> Ports {
let mut ports = Ports::new(active0, active3, active1, active2);
ports.set_receive_times(t0, t3, t1, t2);
ports
}
#[test]
fn propagation_delay_calc_fork() {
crate::test_logger();
let defaults = SubDevice {
configured_address: 0x999,
name: "CHANGEME".try_into().unwrap(),
ports: Ports::default(),
dc_receive_time: 0,
index: 0,
dc_support: DcSupport::Bits64,
..SubDevice::default()
};
let mut subdevices = [
SubDevice {
index: 0,
configured_address: 0x1000,
name: "EK1100".try_into().unwrap(),
ports: ports(
true, 3380373882, false, 1819436374, true, 3380374482, true, 3380375762,
),
dc_receive_time: 402812332410,
..defaults.clone()
},
SubDevice {
index: 1,
configured_address: 0x1001,
name: "EK1122".try_into().unwrap(),
ports: ports(
true, 3384116362, false, 1819436374, false, 1717989224, true, 3384116672,
),
dc_receive_time: 402816074890,
..defaults.clone()
},
SubDevice {
index: 2,
configured_address: 0x1002,
name: "EL9560".try_into().unwrap(),
ports: ports(
true, 3383862982, false, 1819436374, false, 1717989224, false, 0,
),
dc_receive_time: 0,
..defaults.clone()
},
SubDevice {
index: 3,
configured_address: 0x1003,
name: "EK1914".try_into().unwrap(),
ports: ports(
true, 3373883962, false, 1819436374, true, 3373884272, false, 0,
),
dc_receive_time: 0,
..defaults.clone()
},
SubDevice {
index: 4,
configured_address: 0x1004,
name: "EL1008".try_into().unwrap(),
ports: ports(
true, 3375060602, false, 1819436374, false, 1717989224, false, 0,
),
dc_receive_time: 0,
..defaults.clone()
},
];
let downstreams = [
([None, None, Some(1), Some(3)], None, 0),
([None, None, None, Some(2)], Some(0), 145),
([None, None, None, None], Some(1), 300),
([None, None, Some(4), None], Some(0), 1085),
([None, None, None, None], Some(3), 1240),
];
let expected = {
let mut expected = subdevices.clone();
expected.iter_mut().zip(downstreams).for_each(
|(subdevice, ([d0, d3, d1, d2], parent_index, propagation_delay))| {
subdevice.ports.set_downstreams(d0, d3, d1, d2);
subdevice.parent_index = parent_index;
subdevice.propagation_delay = propagation_delay;
},
);
expected
};
assign_parent_relationships(&mut subdevices).expect("assign");
pretty_assertions::assert_eq!(subdevices, expected);
}
#[test]
fn propagation_delay_calc_cross() {
crate::test_logger();
let defaults = SubDevice {
configured_address: 0x999,
name: "CHANGEME".try_into().unwrap(),
ports: Ports::default(),
dc_receive_time: 0,
index: 0,
dc_support: DcSupport::Bits64,
..SubDevice::default()
};
let mut subdevices = [
SubDevice {
index: 0,
configured_address: 0x1000,
name: "EK1100".try_into().unwrap(),
ports: ports(
true, 3493061450, false, 1819436374, true, 3493064460, false, 0,
),
dc_receive_time: 3493061450,
..defaults.clone()
},
SubDevice {
index: 1,
configured_address: 0x1001,
name: "EK1122".try_into().unwrap(),
ports: ports(
true, 3493293220, true, 3493294570, true, 3493295650, true, 3493295940,
),
dc_receive_time: 3493293220,
..defaults.clone()
},
SubDevice {
index: 2,
configured_address: 0x1002,
name: "EK1914".try_into().unwrap(),
ports: ports(
true, 3485337450, false, 1819436374, true, 3485337760, false, 0,
),
dc_receive_time: 0,
..defaults.clone()
},
SubDevice {
index: 3,
configured_address: 0x1003,
name: "EL1008".try_into().unwrap(),
ports: ports(
true, 3488375400, false, 1819436374, false, 1717989224, false, 0,
),
dc_receive_time: 0,
..defaults.clone()
},
SubDevice {
index: 4,
configured_address: 0x1004,
name: "EK1101".try_into().unwrap(),
ports: ports(
true, 3485087810, false, 1819436374, false, 1717989224, false, 0,
),
dc_receive_time: 3485087810,
..defaults.clone()
},
SubDevice {
index: 5,
configured_address: 0x1005,
name: "EL9560".try_into().unwrap(),
ports: ports(
true, 3494335890, false, 1819436374, false, 1717989224, false, 0,
),
dc_receive_time: 0,
..defaults.clone()
},
];
let downstreams = [
([None, None, Some(1), None], None, 0),
([None, Some(2), Some(4), Some(5)], Some(0), 145),
([None, None, Some(3), None], Some(1), 665),
([None, None, None, None], Some(2), 820),
([None, None, None, None], Some(1), 2035),
([None, None, None, None], Some(1), 2720),
];
let expected = {
let mut expected = subdevices.clone();
expected.iter_mut().zip(downstreams).for_each(
|(subdevice, ([d0, d3, d1, d2], parent_index, propagation_delay))| {
subdevice.ports.set_downstreams(d0, d3, d1, d2);
subdevice.parent_index = parent_index;
subdevice.propagation_delay = propagation_delay;
},
);
expected
};
assign_parent_relationships(&mut subdevices).expect("assign");
pretty_assertions::assert_eq!(subdevices, expected);
}
}