///|
/// A trigger condition used by waveform regression checks.
pub enum TriggerCondition {
AnyChange
ScalarLevel(Char)
RisingEdge
FallingEdge
UnknownValue
ExactValue(Value)
} derive(Debug, Eq)
///|
/// A timestamp at which a trigger condition matched.
pub struct TriggerHit {
signal_id : String
time : Int64
value : Value
condition : String
} derive(Debug, Eq)
///|
/// A decoded bus snapshot assembled from several scalar signals.
pub struct BusSnapshot {
time : Int64
value : Value
signal_ids : Array[String]
} derive(Debug, Eq)
///|
/// Find trigger hits for one signal.
pub fn trigger_hits(
trace : Trace,
signal_id : String,
condition : TriggerCondition,
) -> Array[TriggerHit] {
let result : Array[TriggerHit] = []
let events = signal_changes(trace, signal_id)
let mut previous : Value? = None
for change in events {
let matched = match condition {
AnyChange => true
ScalarLevel(level) => change.value == Scalar(level)
RisingEdge =>
match (previous, change.value) {
(Some(Scalar(before)), Scalar(after)) => before == '0' && after == '1'
_ => false
}
FallingEdge =>
match (previous, change.value) {
(Some(Scalar(before)), Scalar(after)) => before == '1' && after == '0'
_ => false
}
UnknownValue => change.value.is_unknown()
ExactValue(value) => change.value == value
}
if matched {
result.push({
signal_id,
time: change.time,
value: change.value,
condition: trigger_name(condition),
})
}
previous = Some(change.value)
}
result
}
///|
/// Return all changes within a time-triggered region.
pub fn trigger_window(
trace : Trace,
signal_id : String,
condition : TriggerCondition,
before : Int64,
after : Int64,
) -> Array[(Int64, Int64)] {
trigger_hits(trace, signal_id, condition).map(fn(hit) {
(hit.time - Int64::max(0L, before), hit.time + Int64::max(0L, after))
})
}
///|
/// Assemble a bus from signals ordered from most to least significant.
pub fn bus_snapshot(
trace : Trace,
signal_ids : Array[String],
timestamp : Int64,
) -> BusSnapshot {
let bits : Array[LogicBit] = []
for signal_id in signal_ids {
let value = value_at(trace, signal_id, timestamp)
match value {
Some(item) => bits.append(item.bits())
None => bits.push(Unknown)
}
}
{ time: timestamp, value: bits_value(bits), signal_ids }
}
///|
/// Assemble bus snapshots at every trace timestamp where a member changes.
pub fn bus_snapshots(
trace : Trace,
signal_ids : Array[String],
) -> Array[BusSnapshot] {
let times : Array[Int64] = [0L]
for change in changes_for_ids(trace, signal_ids) {
if !times.contains(change.time) {
times.push(change.time)
}
}
times.sort()
times.map(fn(time) { bus_snapshot(trace, signal_ids, time) })
}
///|
/// Return the Hamming distance between two known values.
pub fn hamming_distance(left : Value, right : Value) -> Int? {
if left.is_unknown() || right.is_unknown() {
return None
}
let a = left.bits()
let b = right.bits()
let width = Int::max(a.length(), b.length())
let mut distance = 0
for i in 0.. Bool {
let item = frequency_estimate(trace, signal_id)
item.period_count >= 2 && stats(trace, signal_id).unknown_values == 0
}
///|
/// Compare activity between two signals.
pub fn activity_distance(
trace : Trace,
left_id : String,
right_id : String,
) -> Int64 {
let left = activity(trace, left_id)
let right = activity(trace, right_id)
(left.active_time - right.active_time).abs()
}
///|
/// Return the fraction of aligned scalar samples that agree.
pub fn signal_agreement(
trace : Trace,
left_id : String,
right_id : String,
step : Int64,
) -> Double? {
let options = {
start: Some(0L),
end: Some(trace.end_time),
step: Int64::max(1L, step),
include_initial: true,
include_unknown: true,
}
let left = sample_signal(trace, left_id, options)
let right = sample_signal(trace, right_id, options)
let mut compared = 0
let mut equal = 0
for i in 0.. {
compared += 1
if values_equal(a, b, true) {
equal += 1
}
}
_ => ()
}
}
if compared == 0 {
None
} else {
Some(equal.to_double() / compared.to_double())
}
}
///|
fn trigger_name(condition : TriggerCondition) -> String {
match condition {
AnyChange => "any-change"
ScalarLevel(value) => "level-" + value.to_string()
RisingEdge => "rising"
FallingEdge => "falling"
UnknownValue => "unknown"
ExactValue(value) => "value-" + value.display()
}
}
///|
/// Construct an any-change trigger.
pub fn any_change_trigger() -> TriggerCondition {
AnyChange
}
///|
/// Construct a scalar-level trigger.
pub fn scalar_level_trigger(value : Char) -> TriggerCondition {
ScalarLevel(value)
}
///|
/// Construct a rising-edge trigger.
pub fn rising_edge_trigger() -> TriggerCondition {
RisingEdge
}
///|
/// Construct a falling-edge trigger.
pub fn falling_edge_trigger() -> TriggerCondition {
FallingEdge
}
///|
/// Construct an unknown-value trigger.
pub fn unknown_value_trigger() -> TriggerCondition {
UnknownValue
}
///|
/// Construct an exact-value trigger.
pub fn exact_value_trigger(value : Value) -> TriggerCondition {
ExactValue(value)
}