///|
/// Header and declaration facts used for compatibility reports.
pub struct CompatibilitySummary {
has_date : Bool
has_version : Bool
has_timescale : Bool
has_comment : Bool
variable_kinds : Array[(String, Int)]
supports_dumpvars : Bool
supports_real : Bool
} derive(Debug, Eq)
///|
/// Summarize syntax features found in a parsed trace and its source text.
pub fn compatibility_summary(
source : String,
trace : Trace,
) -> CompatibilitySummary {
let kinds : Array[(String, Int)] = []
let supports_real = trace.signals.any(fn(signal) {
signal.kind.to_lower() == "real"
}) ||
trace.changes.any(fn(change) { change.value is Real(_) })
for signal in trace.signals {
match kinds.search_by(fn(item) { item.0 == signal.kind }) {
Some(index) => kinds[index] = (signal.kind, kinds[index].1 + 1)
None => kinds.push((signal.kind, 1))
}
}
{
has_date: trace.header.date is Some(_),
has_version: trace.header.version is Some(_),
has_timescale: trace.header.timescale is Some(_),
has_comment: trace.header.comment is Some(_),
variable_kinds: kinds,
supports_dumpvars: source.contains("$dumpvars"),
supports_real,
}
}
///|
/// Return whether a source uses the standard `$dumpvars` block.
pub fn has_dumpvars(source : String) -> Bool {
source.contains("$dumpvars")
}
///|
/// Return whether all values fit their signal declarations.
pub fn values_fit_declarations(trace : Trace) -> Bool {
trace.changes.all(fn(change) {
match find_signal(trace, change.id) {
Some(signal) =>
change.value.width() == 0 || change.value.width() <= signal.width
None => false
}
})
}
///|
/// Return whether serialization preserves declarations and observable changes.
pub fn round_trip_equal(trace : Trace) -> Bool {
match parse(trace_to_vcd_grouped(trace)) {
Ok(decoded) =>
same_signal_shape(trace, decoded) && traces_equal(trace, decoded)
Err(_) => false
}
}
///|
/// Serialize, parse, and return the canonical trace when possible.
pub fn canonical_round_trip(trace : Trace) -> Trace? {
match parse(trace_to_vcd_grouped(trace)) {
Ok(decoded) => Some(compact_trace(decoded))
Err(_) => None
}
}
///|
/// Return counts of declared variable kinds.
pub fn variable_kind_counts(trace : Trace) -> Array[(String, Int)] {
compatibility_summary("", trace).variable_kinds
}
///|
/// Return a concise compatibility label.
pub fn compatibility_label(source : String, trace : Trace) -> String {
let summary = compatibility_summary(source, trace)
let features : Array[String] = []
if summary.has_date {
features.push("date")
}
if summary.has_version {
features.push("version")
}
if summary.has_timescale {
features.push("timescale")
}
if summary.supports_dumpvars {
features.push("dumpvars")
}
if summary.supports_real {
features.push("real")
}
if features.length() == 0 {
"basic"
} else {
features.join(",")
}
}
///|
/// Return whether all declarations have supported variable kinds.
pub fn supported_variable_kinds(trace : Trace) -> Bool {
trace.signals.all(fn(signal) {
match signal.kind.to_lower() {
"wire" | "reg" | "logic" | "real" | "integer" | "parameter" => true
_ => false
}
})
}
///|
/// Produce a stable compatibility report.
pub fn compatibility_report(source : String, trace : Trace) -> String {
let summary = compatibility_summary(source, trace)
let out : Array[String] = [
"compatibility: " + compatibility_label(source, trace),
"declarations: " + trace.signals.length().to_string(),
"changes: " + trace.changes.length().to_string(),
"widths: " + values_fit_declarations(trace).to_string(),
"variable-kinds: " + supported_variable_kinds(trace).to_string(),
]
for item in summary.variable_kinds {
out.push("kind " + item.0 + "=" + item.1.to_string())
}
out.join("\n")
}