t27.aiРусский

The global buffer

You will learn

What a global buffer is, why it sits on its own network, and what one real build of the whole flow looked like.

A global buffer, BUFG on a 7-series chip, drives the clock on a dedicated low-skew network so the tree of the last lesson has a clean root. The widget shows the flow a BUFG sits inside: one real XC7A200T build, 116.9 s with openXC7 and 83.5 s through the t27 L3+L4 path, byte-identical bitstreams, and place and route taking 60 percent of it. The buffer is one layer of that picture, and the spec frame opens cts.t27 again, where bufg() is built and bufg_has_higher_fanout() is tested.

Try it

In the widget, find the two build times and what percentage place and route took; then in the spec frame find bufg() and the test that gives it the higher fanout.

Open the interactive lesson →

tri game-selftest: the queen loop checks its own tools
tri game-selftest: the queen loop checks its own tools ↗

6 of 6 self-tests hold: anomalies, blog_drift, lessons, site_gate, tick, vault.

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