T27.AI

Блог

Три бита, и каждый нужен: performance clock у MMCM на кремнии

2026-10-04 · 6 мин чтения

[один тактовый тракт на одной плате AX7203; светодиоды читались глазом, считывания счётчика нет; сборка nextpnr-xilinx на 115 коммитов позади main, с одним влитым исправлением, применённым локально; быстрый цикл засекался на одном маленьком дизайне] Выход MMCM может дойти до регионального тактового буфера через тракт performance clock, и две строки в базе Project X-Ray говорят, какие биты этот тракт включают. Со строками master prjxray-db счётчик, тактируемый через него, на XC7A200T не идёт. Со строками, которые мы предложили в openXC7/prjxray-db#30, идёт, а два битстрима различаются ровно в трёх битах. Сброс любого одного из трёх снова останавливает счётчик, так что нужен каждый. main nextpnr-xilinx всё ещё фиксирует базу со старыми строками. Попутно цикл от правки до платы сократился с примерно 104–111 с до примерно 19–28 с, в основном за счёт флага роутера, ассемблера из t27-спеков и более быстрого такта JTAG.

#FPGA#OpenToolchain#Verification

Что измерено

MMCM в тайле управления тактами Xilinx 7-series может отдать выход региональным тактовым буферам через тракт performance clock. Тракт состоит из двух мультиплексоров: один в тайле CMT (CLK_PERF0–CLK_PERF3), второй в тайле HCLK_CMT над ним (PERFCLK0–PERFCLK3). В базе Project X-Ray есть строки, которые говорят, какие конфигурационные биты выбирают каждый вход. Для тракта из этого поста строки на master prjxray-db короткие. CLK_PERF2.CLKOUT3 — это один 29_994, а PERFCLK2.MUXED2 — 28_176 29_133. Строки, которые мы предложили в openXC7/prjxray-db#30 на коммите c030ed6, добавляют 28_994 и 29_1022 к первой и 28_180 ко второй.

Эти строки выведены из популяции. cavearr провёл кампанию из 1083 образцов Vivado, и прежде чем писать строки, мы заново получили его числа своим считывателем битов. Популяция говорит, какие биты меняются вместе, но не говорит, что дизайн работает. Этот пост — половина с платой.

Пробник

Плата — ALINX AX7203 (xc7a200tfbg484-2). Тактовый сигнал платы 200 МГц идёт через IBUFDS и BUFG в MMCM. VCO работает на 1000 МГц, CLKOUT3_DIVIDE = 10, так что CLKOUT3 — 100 МГц. CLKOUT3 идёт через CLK_PERF2 и PERFCLK2 <- MUXED2 в BUFR в режиме bypass, а BUFR тактирует 27-битный счётчик. Опорный сигнал — 28-битный счётчик на BUFG. Старший бит каждого счётчика зажигает светодиод, так что оба должны переключаться каждые 1,34 с: 2^27 / 100 МГц = 2^28 / 200 МГц. Третий светодиод часто мигает, пока MMCM выдаёт LOCKED.

Счётчик стоит в обычной логике, а не в столбце ввода-вывода, поэтому такт должен выйти из BUFR через горизонтальный тактовый хребет. Тот же приём использовался для однобитного A/B в одном из прошлых постов, и этот пробник тоже зависит от бита ENABLE_BUFFER, который тот пост проверил.

A/B: одна разводка, две базы, три бита

Один прогон размещения и разводки дал один FASM-файл, и fasm2frames собрал его дважды. Первый раз против master prjxray-db 517d66a, второй — против того же checkout с восемью файлами c030ed6, скопированными поверх. После sync word два битстрима различаются ровно в трёх битах, и все три относятся к PERF: 28_994 и 29_1022 в тайле CMT и 28_180 в тайле HCLK_CMT.

строкиопорный светодиодсветодиод счётчика BUFRLOCKED
master 517d66aмигает, ~1,3 сне меняетсяесть
c030ed6мигает, ~1,3 смигает в такт с опорныместь

Светодиоды на этой плате активны низким уровнем, так что «не меняется» значит, что счётчик застыл в начальном значении. Со строками master MMCM захватывает частоту и опорный счётчик идёт, но до BUFR такт не доходит.

Нокауты: нужен каждый бит

Со всеми тремя битами счётчик идёт, но это не показывает, что нужен каждый: один может оказаться попутчиком. Поэтому мы взяли рабочие кадры, сбросили ровно один из трёх битов и для каждого случая записали битстрим, без новой разводки. После sync word каждый нокаут отличается от рабочего битстрима в трёх байтах: сам бит и ECC-слово кадра.

сброшенный битчья это строкасветодиод счётчика BUFR
никакой (строки c030ed6)мигает в такт с опорным
28_994CLK_PERF2.CLKOUT3не меняется
29_1022CLK_PERF2.CLKOUT3не меняется
28_180PERFCLK2.MUXED2 (бит used)не меняется
никакой, повторная прошивка после нокаутовмигает в такт с опорным

Сброс любого одного бита останавливает счётчик, а рабочий битстрим, прошитый повторно после нокаутов, снова работает, так что состояние платы между прошивками не менялось. 28_180 — бит, который наша собственная ранняя версия строк пропустила. Это бит «used» тайла HCLK, (26+p)_180 для PERFCLKp. Мы заметили, что его нет, только когда проверка начала считать каждый бит из фиксированного набора, а не только биты, которые упоминает какая-нибудь строка.

perf2.py diff · knockout · the three CLK_PERF2 bits

Записанный прогон, 2026-10-04 в 02:53 UTC+7, после прогонов на плате: perf2.py diff называет три бита, которыми различаются два битстрима A/B, и ни одна строка master не использует ни одного из них. perf2.py knockout заново собирает три файла кадров для нокаутов, каждый байт в байт равен прошитому, и каждый .bit нокаута отличается от рабочего в 3 байтах после sync word. Светодиодов в записи нет. Каждый напечатанный байт настоящий; приглашение и набор команд постановочные, паузы длиннее 2 с сокращены. Сама запись на английском. Открыть запись на отдельной странице.

Декодирование сходится с платой

Базу читают в двух направлениях: fasm2frames кодирует фичи в биты, а bit2fasm декодирует биты обратно в фичи. Мы декодировали оба битстрима обеими базами и оставили PERF-фичи двух тайлов.

битстримдекодирован с masterдекодирован с c030ed6
строки master (счётчик стоит)CLK_PERF2.CLKOUT3, PERFCLK2.MUXED2ничего
строки c030ed6 (счётчик идёт)CLK_PERF2.CLKOUT3, PERFCLK2.MUXED2CLK_PERF2.CLKOUT3, PERFCLK2.MUXED2

Со строками master мёртвый битстрим декодируется так, будто тракт настроен. Каждая строка master — подмножество настоящей, и все её биты на месте. Поэтому декодер на этих строках сообщает о рабочем тактовом тракте, которого в кремнии нет. С c030ed6 декодирование совпадает с платой в обоих случаях.

Что открытый тулчейн поставляет сегодня

prjxray-db#30 открыт. main nextpnr-xilinx фиксирует prjxray-db на 6b8695e — это prjxray-db#13 от cavearr, влитый 7 сентября. На этом коммите CLK_PERF2.CLKOUT3 — это 29_994, а PERFCLK2.MUXED2 — 28_176 29_133: те же строки, что на master 517d66a, с которыми счётчик здесь стоял. Мы прочитали строки на этом коммите. Сам main nextpnr-xilinx мы не собирали и пробник через него не прогоняли.

Как была устроена сборка и что в ней устарело

Этот тракт выбрал не роутер. Если ничего не делать, nextpnr ведёт CLKOUT3 через CLK_PERF0, для которого на master строк нет вообще. Чёрный список pip убрал альтернативы, так что разводке пришлось пройти через CLK_PERF2 <- CLKOUT3 и PERFCLK2 <- MUXED2.

Размещали и разводили классическим nextpnr-xilinx. Локальная chip database himbaechel для xc7a200t была сгенерирована из базы без PERF-строк, поэтому у этих pip не было конфигурационных битов, и они выпадали.

Классический бинарник был устаревшим: собран из b608fd2c, на 115 коммитов позади main openXC7/nextpnr-xilinx. Мы узнали это только после первых прогонов на плате, по двум симптомам:

Урок, который мы записали: прежде чем винить инструмент, проверить git log HEAD..origin/main. Ранний черновик этого отчёта утверждал, что классический nextpnr-xilinx так и не получил исправление, которое он на самом деле получил, и исправление было нашим.

Цикл стал быстрее

Каждый нокаут требует прошивки и взгляда на светодиоды, так что время от правки до платы важно. В начале один цикл занимал около 104–111 с. К концу — около 19–28 с. Синтеза в таблице нет: здесь его не засекали.

шагдопослечто изменилось
размещение и разводка49 с13,2 с--router router1 вместо router2
FASM в кадры37–39 с0,43–1,72 сbitwalk, ассемблер из t27-спеков, вместо fasm2frames
кадры в битстрим0,8 с0,8 сбез изменений (xc7frames2bit)
прошивка в SRAM17,5–22,6 с4,5–12,2 сopenFPGALoader --freq 30000000
итого~104–111 с~19–28 с

По логу router2 потратил 38,1 с, но времена по отдельным цепям, которые он сообщает, в сумме дают около 1,5 с, так что большая часть времени ушла не на разводку цепей. Router1 закончил разводку за 2,53 с. Две разводки различаются на 225 строк FASM, но идут через один и тот же PERF-тракт. Битстрим от router1 мы прошили, и два светодиода мигают вместе. Обе разводки показывают максимальную частоту выше 270 МГц на обоих тактах при целевых 12 МГц по умолчанию, так что тайминг ни разу не был под давлением.

bitwalk записал кадры, байт в байт совпадающие с fasm2frames, для обеих баз. Нокауты вовсе обходились без разводки: правка кадров, кадры в битстрим, прошивка — около 6–14 с. На 30 МГц прошивка занимала от 4,5 до 12,2 с для битстримов одного размера, и мы не знаем почему.

После работы с платой записан ещё один цикл. Размещение и разводка в нём заняли 5,95 с, а не 13,2 с, а шаг из кадров в битстрим — 2,11 с, а не 0,8 с. Другие задачи всё это время держали ноутбук на load average около 110, так что отдельный замер здесь гуляет на столько, и ни одно из чисел не точнее другого.

perf2.py loop --flash · place and route to SRAM, timed

Записанный прогон, 2026-10-04 в 02:54 UTC+7: perf2.py loop --flash размещает и разводит пробник через router1, собирает его bitwalk и xc7frames2bit, проверяет, что результат после sync word совпадает с битстримом, который работал на плате, и загружает его в SRAM. В этой записи: размещение и разводка 5,95 с (из них router1 сообщает 0,78 с), FASM в кадры 1,14 с, кадры в битстрим 2,11 с, загрузка в SRAM 9,85 с, всего 19,06 с, при этом другие задачи держали ноутбук на load average около 110 на 8 ядрах. Синтеза в ней нет. Каждый напечатанный байт настоящий; приглашение и набор команд постановочные, паузы длиннее 2 с сокращены. Сама запись на английском. Открыть запись на отдельной странице.

Прошлый пост говорил, что ничто в нём не указывает путь к более быстрой разводке. Это тоже не он, в том смысле, который имел в виду тот пост: это флаг роутера, измеренный на дизайне из нескольких сотен проводов. Выдержит ли router1 дизайн из того поста на 121 587 строк, где размещение и разводка занимают 70,1 с, мы не измеряли.

Чего это не показывает

Чего это не решает

Пруфы

Поработаем вместе

Хотите так же проверить собственный дизайн?

Я аудирую RTL и строю независимые побитово точные модели, затем провожу результат через синтез и, когда это полезно, проверяю на плате Artix-7. Первый модуль проверки — бесплатно.