Глобальный буфер
Вы узнаете
Что такое глобальный буфер, почему он живёт в собственной сети и как выглядела одна настоящая сборка всего потока.
Глобальный буфер, BUFG на кристалле 7-й серии, ведёт тактовый сигнал по выделенной малоперекосной сети, чтобы у дерева из прошлого урока был чистый корень. Виджет показывает поток, внутри которого стоит BUFG: одна настоящая сборка XC7A200T — 116,9 с с openXC7 и 83,5 с через путь t27 L3+L4, побайтово одинаковые битстримы, а разводка занимает 60 процентов времени. Буфер — один слой этой картины, а окно спеки снова открывает cts.t27, где строятся bufg() и тестируется bufg_has_higher_fanout().
Попробуйте
В виджете найдите два времени сборки и долю разводки в процентах; затем в окне спеки найдите bufg() и тест, дающий ей больший фан-аут.

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
Все уроки
Модуль 1 · Что такое тактовый сигнал
Один фронт, один мир: всё, что делит фронт тактового сигнала, делит и мир; период и джиттер настоящего фронта и место, где тактовый сигнал входит в плату.
Модуль 2 · Деревья тактового сигнала
Перекос и задержка прохождения, глобальная сеть буферов и ловушка стробирования клока логикой.
Модуль 3 · PLL и MMCM
Умножить и поделить один тактовый сигнал в другой, сдвинуть его фазу шагами VCO и какие тактовые сигналы анализатор считает родственными.
Модуль 4 · Сбросы
Ассертировать асинхронно, снимать синхронно: три вида сброса, конвейер снятия и дерево, которое растит сброс.
Модуль 5 · Метастабильность
Окно setup-hold, среднее время между отказами в целочисленной арифметике и два триггера, которые это чинят.
Модуль 6 · Пересечение многих бит
Почему двоичная шина рвётся, почему код Грея нет, и рукопожатие, переносящее импульс между мирами.
Модуль 7 · Асинхронный FIFO
Указатели, флаги и глубина: буфер, переносящий поток между двумя тактовыми сигналами.
Модуль 8 · Ограничения
Строки, сообщающие анализатору, что такое тактовый сигнал, какие пути не проверять и чему должны соответствовать выводы.
Модуль 9 · На плате
Отчёт CDC, одно пересечение, захваченное на триггерах, и дифф битстрима, замыкающий курс.