t27.aiРусский

Compare two traces

You will learn

How to diff two runs and have the trace name the earliest cycle where they split.

Two runs of the same testbench, one before a change and one after: the fastest question is where they split. A trace diff answers at the resolution of one signal change: the earliest cycle where the golden run says 1 and the new run says 0. The widget diffs files this way, frame by frame, and refuses to say 'same' when a byte differs. The lesson's spec is the testbench that drives the VCD comparison tool -- expected against actual, with the mismatch printed, not eyeballed.

Try it

Open the diff widget on two files of your own; then find in the compare testbench where the earliest mismatching cycle would be printed.

Open the interactive lesson →

Bitstream diff: two 7-series .bit files, frame by frame
Bitstream diff: two 7-series .bit files, frame by frame ↗

Drop two Xilinx 7-series bitstreams: header, packets and every 101-word frame compared in your browser, the first differing frame address decoded. Nothing is uploaded.

specs/fpga/testbench/vcd_conformance_compare_tb.t27

// SPDX-License-Identifier: Apache-2.0
// t27/specs/fpga/testbench/vcd_conformance_compare_tb.t27
// VCD Conformance Compare Testbench
// Tests the conformance comparison engine: batch compare, masking, value extraction
// phi^2 + 1/phi^2 = 3 | TRINITY

module VcdConformanceCompare_Testbench {
    use fpga::vcd_conformance_compare::VcdConformanceCompare;

    const CLK_PERIOD : u32 = 20;
    const MAX_COMPARES : u32 = 128;

    var clk : bool = false;
    var rst_n : bool = false;
    var compare_en : bool = false;
    var total_checks : u32 = 0;
    var pass_count : u32 = 0;
    var fail_count : u32 = 0;

    var test_passed : u32 = 0;
    var test_failed : u32 = 0;

    fn tick() {
        clk = false;
        clk = true;
    }

    fn reset() {
        rst_n = false;
        tick();
        tick();
        rst_n = true;
        tick();
        total_checks = 0;
        pass_count = 0;
        fail_count = 0;
    }

    fn run_compare(vcd: [u32], vcd_count: u32, expected_vals: [u32], expected_count: u32) -> u32 {
        var matches : u32 = 0;
        var i : u32 = 0;
        while i < expected_count {
            if i < vcd_count and vcd[i] == expected_vals[i] {
                matches = matches + 1;
            }
            i = i + 1;
        }
        return matches;
    }

    test test_reset_clears_state {
        reset();
        invariant total_checks == 0;
        invariant pass_count == 0;
        invariant fail_count == 0;
    }

    test test_run_compare_all_match {
        var vcd : [u32] = [10, 20, 30];
        var expected : [u32] = [10, 20, 30];
        var result : u32 = run_compare(vcd, 3, expected, 3);
        invariant result == 3;
    }

    test test_run_compare_partial_match {
        var vcd : [u32] = [10, 20, 30];
        var expected : [u32] = [10, 99, 30];
        var result : u32 = run_compare(vcd, 3, expected, 3);
        invariant result == 2;
    }

    test test_run_compare_no_match {
        var vcd : [u32] = [10, 20];
        var expected : [u32] = [99, 88];
        var result : u32 = run_compare(vcd, 2, expected, 2);
        invariant result == 0;
    }

    test test_run_compare_empty {
        var result : u32 = run_compare([], 0, [], 0);
        invariant result == 0;
    }

    test test_run_compare_vcd_shorter {
        var vcd : [u32] = [10];
        var expected : [u32] = [10, 20];
        var result : u32 = run_compare(vcd, 1, expected, 2);
        invariant result == 1;
    }

    test test_max_compares {
        invariant MAX_COMPARES == 128;
    }

    test test_clk_period {
        invariant CLK_PERIOD == 20;
    }

    invariant max_compares_positive : MAX_COMPARES > 0;

    bench bench_vcd_compare {
        reset();
        compare_en = true;
        var i : u32 = 0;
        while i < 100 {
            tick();
            i = i + 1;
        }
        compare_en = false;
    }
}

Open the lesson's spec in the player ↗

All lessons