///|
/// A matched pair of source and target rising edges.
pub struct LatencySample {
source_signal : String
target_signal : String
source_time : Int64
target_time : Int64?
latency : Int64?
} derive(Debug, Eq)
///|
/// Summary of matched edge latency.
pub struct LatencyReport {
source_signal : String
target_signal : String
samples : Int
matched : Int
minimum : Int64?
maximum : Int64?
mean : Double?
} derive(Debug, Eq)
///|
/// Match every source rising edge with the first target rising edge at or after it.
pub fn correlate_rising_edges(
trace : Trace,
source_signal : String,
target_signal : String,
maximum_latency : Int64,
) -> Array[LatencySample] {
let target_times = edges(trace, target_signal).filter_map(fn(event) {
if event.kind is Rising {
Some(event.time)
} else {
None
}
})
let result : Array[LatencySample] = []
for source in edges(trace, source_signal) {
if source.kind is Rising {
let match_time = target_times
.filter(fn(time) {
time >= source.time && time - source.time <= maximum_latency
})
.get(0)
result.push({
source_signal,
target_signal,
source_time: source.time,
target_time: match_time,
latency: match_time.map(fn(time) { time - source.time }),
})
}
}
result
}
///|
/// Summarize rising-edge latency pairs.
pub fn latency_report(
trace : Trace,
source_signal : String,
target_signal : String,
maximum_latency : Int64,
) -> LatencyReport {
let samples = correlate_rising_edges(
trace, source_signal, target_signal, maximum_latency,
)
let latencies = samples.filter_map(fn(sample) { sample.latency })
let total : Int64 = latencies.fold(init=0L, fn(sum, value) { sum + value })
let minimum = latencies.fold(init=None, fn(current, value) {
match current {
Some(item) => Some(Int64::min(item, value))
None => Some(value)
}
})
let maximum = latencies.fold(init=None, fn(current, value) {
match current {
Some(item) => Some(Int64::max(item, value))
None => Some(value)
}
})
{
source_signal,
target_signal,
samples: samples.length(),
matched: latencies.length(),
minimum,
maximum,
mean: if latencies.length() == 0 {
None
} else {
Some(total.to_double() / latencies.length().to_double())
},
}
}
///|
/// Return source edges that have no target edge within the allowed latency.
pub fn unmatched_rising_edges(
trace : Trace,
source_signal : String,
target_signal : String,
maximum_latency : Int64,
) -> Array[LatencySample] {
correlate_rising_edges(trace, source_signal, target_signal, maximum_latency).filter(fn(
sample,
) {
sample.latency is None
},
)
}
///|
/// Check a setup/hold window around each reference rising edge.
pub fn setup_hold_violations(
trace : Trace,
reference_signal : String,
data_signal : String,
setup : Int64,
hold : Int64,
) -> Array[Int64] {
let result : Array[Int64] = []
let safe_setup = Int64::max(0L, setup)
let safe_hold = Int64::max(0L, hold)
for edge in edges(trace, reference_signal) {
if edge.kind is Rising {
let lower = edge.time - safe_setup
let upper = edge.time + safe_hold
if signal_changes(trace, data_signal).any(fn(change) {
change.time >= lower && change.time <= upper
}) {
result.push(edge.time)
}
}
}
result
}