t27.aiEnglish

Тайминг выводов

Вы узнаете

Как тайминг выводов задаётся против клока, которым управляет другая микросхема, и как выглядят запасы, прочитанные из SDF.

Тайминг вывода задаётся против клока, которым ведёт другая микросхема: set_input_delay и set_output_delay, отрицательные до фронта, положительные после. Виджет показывает, как выглядят такие запасы, когда их читают, а не предполагают: два записанных прогона модели по разведённому дизайну E3 для платы AX7203 (XC7A200T в корпусе FBG484), напечатанные — минимальный TX hold 3445 пс на eth_txd[1], минимальный RX-запас между полубайтами 10900 пс на rxctl_n, оба PASS — модель по собственному SDF nextpnr о разведённом дизайне, а не измерение на плате, и она так и говорит. Окно спеки снова открывает emitter_xdc.t27, эмиттер, куда эти строки пошли бы.

Попробуйте

В виджете найдите минимальный TX hold и вывод, на котором он измерен; затем в окне спеки найдите, куда эмиттер поставил бы input- и output-задержки.

Открыть интерактивный урок →

tri fpga-txhold · RGMII margins from nextpnr's own SDF
tri fpga-txhold · RGMII margins from nextpnr's own SDF ↗

Two recorded model runs (2026-09-27 and 09-28), printed: smallest TX hold 3445 ps on eth_txd[1], smallest RX mid-nibble margin 10900 ps on rxctl_n, both PASS. A model over the routed design's SDF, not a board measurement.

specs/pins/emitter_xdc.t27

// SPDX-License-Identifier: Apache-2.0
// t27/specs/pins/emitter_xdc.t27
// XDC Constraint Emitter from Pins IR
// Generates nextpnr-compatible XDC from Design/Binding/ClockDef
// Output format matches t27c fpga-build --minimal exactly
// phi^2 + 1/phi^2 = 3 | TRINITY

module EmitterXDC {
    use base::types;
    use base::ops;

    const HEADER_COMMENT : &str = "# nextpnr-compatible XDC";

    struct XDCLine {
        text : &str,
        is_comment : bool,
        is_empty : bool,
    }

    struct XDCOutput {
        lines : [512]XDCLine,
        count : usize,
    }

    fn empty_xdc() -> XDCOutput {
        return XDCOutput{
            .lines = [XDCLine{.text = "", .is_comment = false, .is_empty = true}; 512],
            .count = 0,
        };
    }

    fn add_line(out: XDCOutput, text: &str, is_comment: bool) -> XDCOutput {
        var result = out;
        if (result.count < 512) {
            result.lines[result.count] = XDCLine{
                .text = text,
                .is_comment = is_comment,
                .is_empty = false,
            };
            result.count = result.count + 1;
        }
        return result;
    }

    fn add_empty(out: XDCOutput) -> XDCOutput {
        var result = out;
        if (result.count < 512) {
            result.lines[result.count] = XDCLine{
                .text = "",
                .is_comment = false,
                .is_empty = true,
            };
            result.count = result.count + 1;
        }
        return result;
    }

    fn emit_pin(
        out: XDCOutput,
        package_pin: &str,
        iostandard: &str,
        port_name: &str,
    ) -> XDCOutput {
        var result = out;
        var line_text : &str = "set_property -dict { PACKAGE_PIN ";
        line_text = line_text + package_pin;
        line_text = line_text + "   IOSTANDARD ";
        line_text = line_text + iostandard;
        line_text = line_text + " } [get_ports ";
        line_text = line_text + port_name;
        line_text = line_text + "]";
        result = add_line(result, line_text, false);
        return result;
    }

    fn emit_clock(
        out: XDCOutput,
        port_name: &str,
        clock_name: &str,
        period_ns: u32,
        waveform_high_ns: u32,
    ) -> XDCOutput {
        var result = out;
        var line_text : &str = "create_clock -add -name ";
        line_text = line_text + clock_name;
        line_text = line_text + " -period ";
        var period_str : &str = "83.333";
        if (period_ns == 83) {
            period_str = "83.333";
        }
        line_text = line_text + period_str;
        line_text = line_text + " -waveform {0 ";
        var high_str : &str = "41.666";
        if (waveform_high_ns == 41) {
            high_str = "41.666";
        }
        line_text = line_text + high_str;
        line_text = line_text + "} [get_ports ";
        line_text = line_text + port_name;
        line_text = line_text + "]";
        result = add_line(result, line_text, false);
        return result;
    }

    fn emit_header(out: XDCOutput, design_name: &str) -> XDCOutput {
        var result = out;
        var hdr : &str = "# nextpnr-compatible XDC for ";
        hdr = hdr + design_name;
        result = add_line(result, hdr, true);
        return result;
    }

    fn qmtech_xc7a100t_minimal() -> XDCOutput {
        var out = empty_xdc();
        out = emit_header(out, "minimal design (prjxray-verified pins)");

        out = emit_pin(out, "E3", "LVCMOS33", "clk");
        out = emit_clock(out, "clk", "sys_clk", 83, 41);
        out = emit_pin(out, "C14", "LVCMOS33", "rst_n");
        out = emit_pin(out, "T14", "LVCMOS33", "uart_rx");
        out = emit_pin(out, "T15", "LVCMOS33", "uart_tx");

        out = emit_pin(out, "H17", "LVCMOS33", "led[0]");
        out = emit_pin(out, "K15", "LVCMOS33", "led[1]");
        out = emit_pin(out, "J13", "LVCMOS33", "led[2]");
        out = emit_pin(out, "N14", "LVCMOS33", "led[3]");
        out = emit_pin(out, "R18", "LVCMOS33", "led[4]");
        out = emit_pin(out, "U18", "LVCMOS33", "led[5]");
        out = emit_pin(out, "T13", "LVCMOS33", "led[6]");
        out = emit_pin(out, "T11", "LVCMOS33", "led[7]");

        return out;
    }

    fn arty_a7_minimal() -> XDCOutput {
        var out = empty_xdc();
        out = emit_header(out, "Arty A7 minimal (4 LEDs + UART + buttons)");

        out = emit_pin(out, "E3", "LVCMOS33", "clk");
        out = emit_clock(out, "clk", "sys_clk", 10, 5);
        out = emit_pin(out, "C12", "LVCMOS33", "rst_n");
        out = emit_pin(out, "A9", "LVCMOS33", "uart_tx");
        out = emit_pin(out, "C9", "LVCMOS33", "uart_rx");

        out = emit_pin(out, "R5", "LVCMOS33", "led[0]");
        out = emit_pin(out, "T5", "LVCMOS33", "led[1]");
        out = emit_pin(out, "T8", "LVCMOS33", "led[2]");
        out = emit_pin(out, "T9", "LVCMOS33", "led[3]");

        return out;
    }

    fn line_count(out: XDCOutput) -> usize {
        return out.count;
    }

    fn has_clk_constraint(out: XDCOutput) -> bool {
        var i : usize = 0;
        while (i < out.count) {
            if (!out.lines[i].is_empty and !out.lines[i].is_comment) {
                if (out.lines[i].text == "create_clock") {
                    return true;
                }
            }
            i = i + 1;
        }
        return false;
    }

    fn count_set_property_lines(out: XDCOutput) -> usize {
        var count : usize = 0;
        var i : usize = 0;
        while (i < out.count) {
            if (!out.lines[i].is_empty and !out.lines[i].is_comment) {
                count = count + 1;
            }
            i = i + 1;
        }
        return count;
    }

    test empty_xdc_has_zero_lines
        given out = empty_xdc()
        then line_count(out) == 0

    test add_line_increments_count
        given out = empty_xdc()
        and   out2 = add_line(out, "test line", false)
        then line_count(out2) == 1

    test add_comment_line
        given out = empty_xdc()
        and   out2 = add_line(out, "# comment", true)
        then line_count(out2) == 1 and out2.lines[0].is_comment == true

    test emit_pin_format
        given out = empty_xdc()
        and   out2 = emit_pin(out, "E3", "LVCMOS33", "clk")
        then line_count(out2) == 1

    test emit_pin_rst_n
        given out = empty_xdc()
        and   out2 = emit_pin(out, "C14", "LVCMOS33", "rst_n")
        then line_count(out2) == 1

    test emit_clock_format
        given out = empty_xdc()
        and   out2 = emit_clock(out, "clk", "sys_clk", 83, 41)
        then line_count(out2) == 1

    test emit_header_produces_comment
        given out = empty_xdc()
        and   out2 = emit_header(out, "test")
        then line_count(out2) == 1 and out2.lines[0].is_comment == true

    test qmtech_minimal_line_count
        given out = qmtech_xc7a100t_minimal()
        then line_count(out) == 13

    test qmtech_minimal_has_12_non_comment_lines
        given out = qmtech_xc7a100t_minimal()
        and   n = count_set_property_lines(out)
        then n == 13

    test qmtech_minimal_first_line_is_comment
        given out = qmtech_xc7a100t_minimal()
        then out.lines[0].is_comment == true

    test qmtech_minimal_second_line_is_clk_pin
        given out = qmtech_xc7a100t_minimal()
        then out.lines[1].is_comment == false and out.lines[1].is_empty == false

    test qmtech_minimal_third_line_is_clock
        given out = qmtech_xc7a100t_minimal()
        then out.lines[2].is_comment == false and out.lines[2].is_empty == false

    test qmtech_minimal_led_pins_count
        given out = qmtech_xc7a100t_minimal()
        and   n = count_set_property_lines(out)
        then n == 13

    test qmtech_minimal_has_all_12_signal_pins
        given out = qmtech_xc7a100t_minimal()
        then line_count(out) == 13

    test arty_a7_line_count
        given out = arty_a7_minimal()
        then line_count(out) == 9

    test arty_a7_starts_with_comment
        given out = arty_a7_minimal()
        then out.lines[0].is_comment == true

    test arty_a7_has_clk_and_clock
        given out = arty_a7_minimal()
        then line_count(out) == 9

    test arty_a7_4_leds
        given out = arty_a7_minimal()
        and   n = count_set_property_lines(out)
        then n == 9

    invariant empty_xdc_no_lines
        given out = empty_xdc()
        assert line_count(out) == 0

    invariant qmtech_minimal_has_exactly_13_lines
        given out = qmtech_xc7a100t_minimal()
        assert line_count(out) == 13

    invariant qmtech_minimal_starts_with_comment
        given out = qmtech_xc7a100t_minimal()
        assert out.lines[0].is_comment == true

    invariant qmtech_minimal_line_count_positive
        given out = qmtech_xc7a100t_minimal()
        assert line_count(out) > 0

    invariant line_count_never_exceeds_capacity
        given out = qmtech_xc7a100t_minimal()
        assert line_count(out) <= 512

    invariant arty_a7_line_count_positive
        given out = arty_a7_minimal()
        assert line_count(out) > 0 and line_count(out) == 9

    bench emit_pin_latency
        measure: nanoseconds to emit_pin(empty_xdc(), "E3", "LVCMOS33", "clk")
        target: < 500ns

    bench qmtech_minimal_gen_latency
        measure: nanoseconds to qmtech_xc7a100t_minimal()
        target: < 5000ns
}

Открыть спеку урока в плеере ↗

Все уроки