///|
/// Render a single declaration line without surrounding scope directives.
pub fn vcd_declaration(signal : Signal) -> String {
"$var " +
signal.kind +
" " +
signal.width.to_string() +
" " +
signal.id +
" " +
signal.name +
" $end"
}
///|
/// Render a scope opening directive.
pub fn vcd_scope_open(scope : String) -> String {
"$scope module " + scope + " $end"
}
///|
/// Render a scope closing directive.
pub fn vcd_scope_close() -> String {
"$upscope $end"
}
///|
/// Render declarations grouped by their complete scope path.
pub fn trace_to_vcd_grouped(trace : Trace) -> String {
let out = StringBuilder()
write_header(out, trace)
let opened : Array[String] = []
for signal in trace.signals {
let common = common_scope_length(opened, signal.scope)
let mut close_index = opened.length()
while close_index > common {
out.write_string(vcd_scope_close() + "\n")
close_index -= 1
}
while opened.length() > common {
ignore(opened.pop())
}
for scope_index in common.. 0 {
out.write_string(vcd_scope_close() + "\n")
ignore(opened.pop())
}
out.write_string("$enddefinitions $end\n")
write_changes(out, trace.changes)
out.to_string()
}
///|
fn common_scope_length(left : Array[String], right : Array[String]) -> Int {
let bound = Int::min(left.length(), right.length())
let mut index = 0
while index < bound && left[index] == right[index] {
index += 1
}
index
}
///|
fn write_changes(out : StringBuilder, changes : Array[Change]) -> Unit {
let mut current_time : Int64? = None
for change in changes {
if current_time != Some(change.time) {
out.write_char('#')
out.write_string(change.time.to_string())
out.write_char('\n')
current_time = Some(change.time)
}
out.write_string(vcd_assignment(change))
out.write_char('\n')
}
}
///|
/// Render one signal array as indented JSON.
pub fn signals_json_pretty(trace : Trace, indent : String) -> String {
let out : Array[String] = ["["]
for index in 0.. String {
let out : Array[String] = ["["]
for index in 0.. String {
"{\n" +
" \"end_time\": " +
trace.end_time.to_string() +
",\n \"signals\": " +
signals_json_pretty(trace, " ") +
",\n \"changes\": " +
changes_json_pretty(trace, " ") +
"\n}"
}
///|
/// Return a small export inventory for verification reports.
pub fn export_summary(trace : Trace) -> String {
let json = trace_json(trace)
let csv = changes_csv(trace)
let vcd = trace_to_vcd_grouped(trace)
"json_chars=" +
json.length().to_string() +
" csv_chars=" +
csv.length().to_string() +
" vcd_chars=" +
vcd.length().to_string() +
" shape=" +
trace.shape_digest().to_string()
}
///|
/// Return whether a serialized trace can be parsed again.
pub fn export_parseable(trace : Trace) -> Bool {
parse(trace_to_vcd_grouped(trace)) is Ok(_)
}
///|
/// Compare grouped export with the source trace's declaration shape.
pub fn grouped_export_preserves_shape(trace : Trace) -> Bool {
match parse(trace_to_vcd_grouped(trace)) {
Ok(decoded) => same_signal_shape(trace, decoded)
Err(_) => false
}
}
///|
/// Return all declaration paths as a newline-delimited manifest.
pub fn declaration_manifest(trace : Trace) -> String {
trace.signals
.map(fn(signal) {
signal.id + "\t" + signal.width.to_string() + "\t" + signal_path(signal)
})
.join("\n")
}
///|
/// Return the compact JSON byte count for one trace.
pub fn json_size(trace : Trace) -> Int {
trace_json(trace).length()
}
///|
/// Return the CSV header expected by plotting tools.
pub fn csv_header() -> String {
"time,id,value"
}