t27.aiРусский

Reset fanout

You will learn

Why a reset grows a tree like a clock, and where its cost shows up in a real resource count.

A reset reaches every flop in the design, so it grows a tree like a clock tree and is balanced for the same reason: an uneven release is a skew you built yourself. The widget counts a real routed design against the XC7A200T's own totals, read from the chipdb and prjxray-db, and counts yours if you drop your routed JSON on it. A reset tree lives in the same budget: routing and control-set pressure, visible in exactly those bars. The spec frame opens cts.t27 once more, the tree arithmetic that both trees share.

Try it

In the widget, read the totals the bars are counted against; then in the spec frame find the skew target a reset tree is balanced to meet.

Open the interactive lesson →

tri fpga-rxcap
tri fpga-rxcap ↗

Per-bit setup and hold of an asynchronous RX nibble sampled by RXC, from the routed SDF: the four rxd bits of one nibble change 2387 to 2486 ps apart, so a binary counter tears where a gray one cannot.

specs/fpga/cts.t27

// SPDX-License-Identifier: Apache-2.0
// t27/specs/fpga/cts.t27
// T27 Clock Tree Synthesis Specification
// PLL configuration, clock buffer trees, skew estimation
// Artix-7: BUFH=0.05ns, BUFG=0.1ns, PLL jitter=50ps, max skew=100ps
// Uses flat arrays + count fields (parser-compatible)
// phi^2 + 1/phi^2 = 3 | TRINITY

module CTS {

    pub struct PllConfig {
        name : &str,
        input_mhz : u32,
        output_mhz : u32,
        multiply : u32,
        divide : u32,
        jitter_ps : u32,
    }

    fn pll_config(name: &str, input_mhz: u32, output_mhz: u32) -> PllConfig {
        var m : u32 = 1;
        var d : u32 = 1;
        if input_mhz > 0 {
            d = input_mhz;
            m = output_mhz;
        }
        return PllConfig{
            .name = name,
            .input_mhz = input_mhz,
            .output_mhz = output_mhz,
            .multiply = m,
            .divide = d,
            .jitter_ps = 50,
        };
    }

    fn pll_period_ps(pll: PllConfig) -> u32 {
        if pll.output_mhz == 0 {
            return 0;
        }
        return 1000000000 / pll.output_mhz;
    }

    pub struct ClockBuffer {
        name : &str,
        delay_ps : u32,
        fanout : u32,
    }

    fn bufg(name: &str) -> ClockBuffer {
        return ClockBuffer{ .name = name, .delay_ps = 100, .fanout = 32 };
    }

    fn bufh(name: &str) -> ClockBuffer {
        return ClockBuffer{ .name = name, .delay_ps = 50, .fanout = 16 };
    }

    fn bufg_has_higher_fanout(b: ClockBuffer) -> bool {
        return b.fanout >= 32;
    }

    pub struct ClockTree {
        root : &str,
        num_levels : u32,
        total_buffers : u32,
        max_skew_ps : u32,
    }

    fn clock_tree(root: &str, levels: u32, bufs: u32) -> ClockTree {
        return ClockTree{
            .root = root,
            .num_levels = levels,
            .total_buffers = bufs,
            .max_skew_ps = 100,
        };
    }

    fn tree_delay_ps(tree: ClockTree, buf_delay: u32) -> u32 {
        return tree.num_levels * buf_delay;
    }

    fn skew_ok(tree: ClockTree, max_allowed_ps: u32) -> bool {
        return tree.max_skew_ps <= max_allowed_ps;
    }

    pub struct CtsReport {
        num_clocks : u32,
        num_plls : u32,
        total_buffers : u32,
        worst_skew_ps : u32,
        worst_latency_ps : u32,
        has_violations : bool,
    }

    fn cts_ok(clocks: u32, plls: u32, bufs: u32, skew: u32, latency: u32) -> CtsReport {
        return CtsReport{
            .num_clocks = clocks,
            .num_plls = plls,
            .total_buffers = bufs,
            .worst_skew_ps = skew,
            .worst_latency_ps = latency,
            .has_violations = false,
        };
    }

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

    // === Auto tree estimation ===

    fn est_buffers_needed(num_sinks: u32) -> u32 {
        if num_sinks <= 16 {
            return 1;
        }
        return num_sinks / 16 + 1;
    }

    fn est_tree_levels(num_sinks: u32) -> u32 {
        if num_sinks <= 16 {
            return 1;
        }
        if num_sinks <= 256 {
            return 2;
        }
        return 3;
    }

    // === Validation ===

    fn validate_pll(pll: PllConfig) -> u32 {
        var errors : u32 = 0;
        if pll.name == "" { errors = errors + 1; }
        if pll.output_mhz == 0 { errors = errors + 1; }
        return errors;
    }

    // === Tests ===

    test pll_config_creation
        given p = pll_config("sys_pll", 100, 200)
        then p.input_mhz == 100
        and p.output_mhz == 200
        and pll_period_ps(p) == 5000000

    test bufg_creation
        given b = bufg("clk_buf")
        then b.delay_ps == 100
        and b.fanout == 32
        and bufg_has_higher_fanout(b) == true

    test bufh_creation
        given b = bufh("clk_h")
        then b.delay_ps == 50
        and b.fanout == 16
        and bufg_has_higher_fanout(b) == false

    test clock_tree_creation
        given t = clock_tree("clk", 2, 5)
        then t.root == "clk"
        and t.num_levels == 2
        and t.total_buffers == 5
        and t.max_skew_ps == 100

    test tree_delay
        given t = clock_tree("clk", 3, 8)
        then tree_delay_ps(t, 100) == 300

    test skew_ok_yes
        given t = clock_tree("clk", 2, 5)
        then skew_ok(t, 200) == true

    test skew_ok_no
        given t = clock_tree("clk", 2, 5)
        then skew_ok(t, 50) == false

    test cts_report_ok
        given r = cts_ok(2, 1, 10, 80, 300)
        then passed(r) == true
        and r.has_violations == false

    test est_buffers_one
        then est_buffers_needed(10) == 1

    test est_buffers_many
        then est_buffers_needed(100) == 7

    test est_tree_levels_one
        then est_tree_levels(10) == 1

    test est_tree_levels_two
        then est_tree_levels(100) == 2

    test est_tree_levels_three
        then est_tree_levels(500) == 3

    test validate_pll_ok
        given p = pll_config("ok", 100, 200)
        then validate_pll(p) == 0

    test validate_pll_empty
        given p = PllConfig{.name = "", .input_mhz = 100, .output_mhz = 0, .multiply = 1, .divide = 1, .jitter_ps = 50}
        then validate_pll(p) > 0

    // === Invariants ===

    invariant bufg_delay_positive
        given b = bufg("inv")
        assert b.delay_ps > 0

    invariant skew_non_negative
        given t = clock_tree("inv", 2, 5)
        assert t.max_skew_ps >= 0

    bench buffer_estimation_latency
        measure: nanoseconds to est_buffers_needed(50)
        target: < 50ns

    bench tree_level_estimation_latency
        measure: nanoseconds to est_tree_levels(200)
        target: < 50ns
}

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

Open the lesson's spec in the player ↗

All lessons