t27.aiРусский

The CDC report

You will learn

What a CDC report lists and asks of every crossing, and what a report of verified things has in common with it.

A CDC report walks every crossing in the netlist and asks each one: synchronized, and how well? It names the unsynchronized paths, the synchronizers with too few stages, the buses that forgot their Gray. The widget is another report of named things, each checked: 129 files held intact against the newest of 54 content-addressed snapshots -- not a CDC report, but the same shape, a list where every line is a verdict with a source. The spec frame opens timing.t27, the arithmetic such a report leans on when it estimates how safe a synchronizer is.

Try it

In the widget, find how many files the report holds intact and how many snapshots it checks against; then in the spec frame find the estimate a CDC report would run per crossing.

Open the interactive lesson →

tri game-vault: the loop's skills checked against snapshots
tri game-vault: the loop's skills checked against snapshots ↗

129 files intact against the newest of 54 content-addressed snapshots.

specs/fpga/timing.t27

// SPDX-License-Identifier: Apache-2.0
// t27/specs/fpga/timing.t27
// T27 Static Timing Analysis Specification
// Estimates critical path, slack, and Fmax from HIR module structure
// Artix-7 timing model: LUT=0.1ns, BRAM=2.0ns, DSP=2.5ns, routing=0.3ns
// Uses flat arrays + count fields (parser-compatible)
// phi^2 + 1/phi^2 = 3 | TRINITY

module Timing {

    // === Timing arc kind ===

    pub const ArcKind = enum(i8) {
        comb = 0,
        reg_to_reg = 1,
        reg_to_output = 2,
        input_to_reg = 3,
        input_to_output = 4,
    }

    // === Timing arc ===

    pub struct TimingArc {
        source : &str,
        sink : &str,
        delay_ps : u32,
        kind : i8,
    }

    fn comb_arc(source: &str, sink: &str, delay_ps: u32) -> TimingArc {
        return TimingArc{
            .source = source,
            .sink = sink,
            .delay_ps = delay_ps,
            .kind = 0,
        };
    }

    fn reg_to_reg(source: &str, sink: &str, delay_ps: u32) -> TimingArc {
        return TimingArc{
            .source = source,
            .sink = sink,
            .delay_ps = delay_ps,
            .kind = 1,
        };
    }

    fn input_to_reg(source: &str, sink: &str, delay_ps: u32) -> TimingArc {
        return TimingArc{
            .source = source,
            .sink = sink,
            .delay_ps = delay_ps,
            .kind = 3,
        };
    }

    // === Timing path ===

    pub struct TimingPath {
        startpoint : &str,
        endpoint : &str,
        total_delay_ps : u32,
        slack_ps : i64,
        num_arcs : u32,
    }

    fn timing_path(start: &str, end: &str, delay: u32, slack: i64) -> TimingPath {
        return TimingPath{
            .startpoint = start,
            .endpoint = end,
            .total_delay_ps = delay,
            .slack_ps = slack,
            .num_arcs = 1,
        };
    }

    fn is_met(path: TimingPath) -> bool {
        return path.slack_ps >= 0;
    }

    fn is_violated(path: TimingPath) -> bool {
        return path.slack_ps < 0;
    }

    // === Timing constraint ===

    pub struct TimingConstraint {
        name : &str,
        period_ps : u32,
        clock_name : &str,
    }

    fn clock_constraint(name: &str, period_ps: u32) -> TimingConstraint {
        return TimingConstraint{
            .name = name,
            .period_ps = period_ps,
            .clock_name = "clk",
        };
    }

    fn clock_mhz(name: &str, mhz: u32) -> TimingConstraint {
        if mhz == 0 {
            return clock_constraint(name, 10000);
        }
        return TimingConstraint{
            .name = name,
            .period_ps = 1000000000 / mhz,
            .clock_name = "clk",
        };
    }

    // === Timing report ===

    pub struct TimingReport {
        total_paths : u32,
        met_paths : u32,
        violated_paths : u32,
        worst_slack_ps : i64,
        critical_path_ps : u32,
        fmax_mhz : u32,
        has_violations : bool,
    }

    fn timing_ok(critical_ps: u32, fmax: u32) -> TimingReport {
        return TimingReport{
            .total_paths = 1,
            .met_paths = 1,
            .violated_paths = 0,
            .worst_slack_ps = 5000,
            .critical_path_ps = critical_ps,
            .fmax_mhz = fmax,
            .has_violations = false,
        };
    }

    fn timing_fail(critical_ps: u32) -> TimingReport {
        return TimingReport{
            .total_paths = 1,
            .met_paths = 0,
            .violated_paths = 1,
            .worst_slack_ps = -1000,
            .critical_path_ps = critical_ps,
            .fmax_mhz = 0,
            .has_violations = true,
        };
    }

    fn passed(report: TimingReport) -> bool {
        return report.has_violations == false;
    }

    // === Timing model constants ===

    fn lut_delay_ps() -> u32 {
        return 100;
    }

    fn bram_delay_ps() -> u32 {
        return 2000;
    }

    fn dsp_delay_ps() -> u32 {
        return 2500;
    }

    fn routing_delay_ps() -> u32 {
        return 300;
    }

    fn setup_time_ps() -> u32 {
        return 200;
    }

    fn hold_time_ps() -> u32 {
        return 50;
    }

    // === Query functions ===

    fn path_delay(arcs: [TimingArc], count: u32) -> u32 {
        var total : u32 = 0;
        var i : u32 = 0;
        while i < count {
            total = total + arcs[i].delay_ps;
            i = i + 1;
        }
        return total;
    }

    fn slack(delay_ps: u32, constraint_ps: u32) -> i64 {
        return constraint_ps as i64 - delay_ps as i64;
    }

    fn fmax_from_delay(delay_ps: u32) -> u32 {
        if delay_ps == 0 {
            return 0;
        }
        return 1000000000 / delay_ps;
    }

    fn est_comb_delay(num_luts: u32) -> u32 {
        return num_luts * lut_delay_ps() + routing_delay_ps();
    }

    fn est_reg_to_reg_delay(num_luts: u32) -> u32 {
        return num_luts * lut_delay_ps() + routing_delay_ps() + setup_time_ps();
    }

    fn worst_path(paths: [TimingPath], count: u32) -> u32 {
        if count == 0 {
            return 0;
        }
        var worst : u32 = paths[0].total_delay_ps;
        var i : u32 = 1;
        while i < count {
            if paths[i].total_delay_ps > worst {
                worst = paths[i].total_delay_ps;
            }
            i = i + 1;
        }
        return worst;
    }

    // === Validation ===

    fn validate_constraint(tc: TimingConstraint) -> u32 {
        var errors : u32 = 0;
        if tc.name == "" {
            errors = errors + 1;
        }
        if tc.period_ps == 0 {
            errors = errors + 1;
        }
        return errors;
    }

    fn validate_arc(arc: TimingArc) -> u32 {
        var errors : u32 = 0;
        if arc.source == "" {
            errors = errors + 1;
        }
        if arc.sink == "" {
            errors = errors + 1;
        }
        return errors;
    }

    // === Tests ===

    test comb_arc_creation
        given a = comb_arc("a", "b", 500)
        then a.source == "a"
        and a.sink == "b"
        and a.delay_ps == 500
        and a.kind == 0

    test reg_to_reg_creation
        given a = reg_to_reg("r1", "r2", 800)
        then a.kind == 1

    test input_to_reg_creation
        given a = input_to_reg("din", "r1", 400)
        then a.kind == 3

    test timing_path_met
        given p = timing_path("r1", "r2", 5000, 5000)
        then is_met(p) == true
        and is_violated(p) == false

    test timing_path_violated
        given p = timing_path("r1", "r2", 12000, -2000)
        then is_met(p) == false
        and is_violated(p) == true

    test clock_constraint_creation
        given c = clock_constraint("clk_fast", 5000)
        then c.period_ps == 5000
        and c.clock_name == "clk"

    test clock_mhz_creation
        given c = clock_mhz("clk_100", 100)
        then c.period_ps == 10000000
        and c.name == "clk_100"

    test clock_mhz_zero
        given c = clock_mhz("bad", 0)
        then c.period_ps == 10000

    test timing_ok_report
        given r = timing_ok(5000, 200)
        then r.critical_path_ps == 5000
        and r.fmax_mhz == 200
        and passed(r) == true

    test timing_fail_report
        given r = timing_fail(15000)
        then r.has_violations == true
        and passed(r) == false

    test path_delay_calc
        given a1 = comb_arc("a", "b", 100)
        and a2 = comb_arc("b", "c", 200)
        and a3 = comb_arc("c", "d", 300)
        then path_delay([a1, a2, a3], 3) == 600

    test slack_positive
        then slack(5000, 10000) == 5000

    test slack_negative
        then slack(15000, 10000) == -5000

    test fmax_from_delay
        then fmax_from_delay(5000) == 200000

    test fmax_zero_delay
        then fmax_from_delay(0) == 0

    test est_comb_delay
        then est_comb_delay(3) == 600

    test est_reg_to_reg_delay
        then est_reg_to_reg_delay(3) == 800

    test worst_path
        given p1 = timing_path("a", "b", 500, 0)
        and p2 = timing_path("c", "d", 1200, 0)
        and p3 = timing_path("e", "f", 800, 0)
        then worst_path([p1, p2, p3], 3) == 1200

    test worst_path_empty
        then worst_path([], 0) == 0

    test validate_constraint_ok
        given c = clock_constraint("clk", 10000)
        then validate_constraint(c) == 0

    test validate_constraint_empty_name
        given c = TimingConstraint{.name = "", .period_ps = 10000, .clock_name = "clk"}
        then validate_constraint(c) > 0

    test validate_arc_ok
        given a = comb_arc("a", "b", 100)
        then validate_arc(a) == 0

    test validate_arc_empty_source
        given a = TimingArc{.source = "", .sink = "b", .delay_ps = 100, .kind = 0}
        then validate_arc(a) > 0

    test timing_model_constants
        then lut_delay_ps() == 100
        and bram_delay_ps() == 2000
        and dsp_delay_ps() == 2500
        and routing_delay_ps() == 300
        and setup_time_ps() == 200

    test hold_time_ps
        then hold_time_ps() == 50

    // === Invariants ===

    invariant slack_consistent_with_fmax
        given d = 5000
        and s = slack(d, 10000)
        assert s >= 0

    invariant timing_constants_positive
        assert lut_delay_ps() > 0
        and bram_delay_ps() > lut_delay_ps()
        and dsp_delay_ps() > lut_delay_ps()

    // === Benchmarks ===

    bench timing_analysis
        measure: nanoseconds for est_reg_to_reg_delay(10)
        target: < 50ns
}

// phi^2 + 1/phi^2 = 3 | TRINITY

Open the lesson's spec in the player ↗

All lessons