T27.AI

Блог

179 фич, которые не меняют ни бита, и одно решение маршрутизации, которое меняло

2026-08-31 · 7 мин чтения

Отчёт о паритете двух деревьев place-and-route для Xilinx отранжировал собственные задачи наоборот: 179 недостающих фич BRAM оказались нулевыми кодовыми точками, а блокер, не пускавший битстрим на плату, не давал в счётчиках фич никакой разницы.

Гравюрный триптих к статье: 179 фич, которые не меняют ни бита, и одно решение маршрутизации, которое меняло
Открыть полный триптих в исходном размере
#openXC7#FPGA#Bitstream#Upstream#Measurement

27 августа 2026 в openXC7/nextpnr-xilinx#165 появился отчёт о паритете: два дерева place-and-route — порт himbaechel и выпущенный nextpnr-xilinx 0.9.3 — прогнаны по одним и тем же нетлистам, одному XDC, одной prjxray-db, одному yosys и одному бэкенду, так что единственная переменная — бинарник P&R. Самый конкретный вывод отчёта был назван без оговорок: 179 конфигурационных фич BRAM, которые 0.9.3 выдаёт, а порт нет, — «самый конкретный пункт битстрим-паритета и тот, который я чинил бы первым».

Через день тот же автор эту фразу отозвал. Ещё через три дня три битстрима запустились на физической плате. Ни одно из этих измерений не моё — я перечитал артефакты, но не воспроизвёл в них ни одного числа, — и вместе они дают один вывод, который стоит записать: разница, которую можно посчитать, не обязательно та разница, которая важна.

179 фич, которые не меняют ни бита

Отсутствующие фичи реальны в тексте FASM: RAMB18.READ_WIDTH_{A,B}_1, WRITE_WIDTH_{A,B}_1 и RSTREG_PRIORITY_{A,B}_RSTREG, 179 штук в дизайне LiteX, причём RSTREG_PRIORITY_* идёт 88 → 0, WRITE_WIDTH_B_1 44 → 5, READ_WIDTH_B_1 40 → 7 между вариантами. И все они — нулевые кодовые точки: их segbits целиком отрицательные.

BRAM_L.RAMB18_Y0.READ_WIDTH_A_1  !27_35 !27_36 !27_37
BRAM_L.RAMB18_Y0.RSTREG_PRIORITY_A_RSTREG  !27_124

Каждый бит в определении — отрицание, поэтому записать фичу и не записать её дают один и тот же фрейм. Отзыв опирается на три проверки, независимые друг от друга:

То есть пункт работы, поставленный первым, не сдвинул бы в битстриме ничего из того, что задействует LiteX. Это разница в тексте, описывающем конфигурацию, а не в самой конфигурации.

Блокер, который никто не посчитал

То, что на самом деле не давало битстриму дойти до платы, было решением маршрутизации и не давало вообще никакой разницы в счётчиках фич. При сборке blinky для lowRISC Sonata (xc7a50tcsg324, тактовый вход платы на P15 = LIOI3_X0Y23) порт выводит клок с пада через региональную сеть:

pad → HCLK_IOI3_X1Y26.HCLK_IOI_IO_PLL_CLK3_DMUX ← I2IOCLK_BOT1
    → RCLK3 → BUFR divider bypass → CLK_HROW … CK_BUFRCLK_L1 → BUFG

Детерминированно, на восьми сидах, и с router2, и с маршрутизатором по умолчанию. 0.9.3 вместо этого идёт по выделенному хребту CCIO → HCLK_CMT → CLK_HROW → BUFG. Форк умеет это явно: Arch::routeClock() трактует цепь с единственным потребителем, входящую в BUFGCTRL.I0, как глобальную (to_bufg_input, xilinx/arch.cc:1812), с комментарием, что при обычной маршрутизации выход BUFG мёртв и дизайн замирает. У XilinxImpl::route_clocks() в порту пять форм, и ни одна из них не эта.

Увидеть это было тяжело потому, что один и тот же маршрут ломается двумя по-разному выглядящими способами в зависимости от ревизии базы. С той ревизией, которую везёт 0.9.3 (ab1fc60), у пипа DMUX вообще нет записи segbits, и fasm2frames отвергает дизайн с FasmLookupError — это читается как дыра в базе. С текущим master базы (77e52f10, после prjxray-db#7, влитого 26 августа) тот же ключ — строка default из одних нулей, дизайн собирается без замечаний и даёт битстрим — а это читается как успех.

…но доставляет ли этот региональный путь работающий клок до BUFG — неизвестно.

Эта фраза, написанная 28 августа, и есть честное состояние битстрима, который собирается без единой жалобы. Решить вопрос не могло ничто, кроме кремния.

Три битстрима, одна плата

30 августа круг замкнулся. Джонатан (jrrk2) прогнал три битстрима blinky на физической Sonata — тот же нетлист, тот же XDC, тот же бэкенд prjxray, различается только P&R — и все три мигают.

плечодеревомаршрутизаторпуть клока
Anextpnr-xilinx 0.9.3 (68aeeb39, выпущенный пакет)router2выделенный CCIO → CMT → HROW → BUFG
Bhimbaechel 2212c004router1выделенный путь (router1 обходит датапат BUFR)
Chimbaechel + openXC7/nextpnr#1 (2bf0e1f9)router2выделенный путь (предикат из PR отказывается от датапата BUFR)

Информацию несёт плечо C. Раньше оно давало либо FasmLookupError, либо клок неизвестной живости — в зависимости от базы; с влитым предикатом из openXC7/nextpnr#1 оно мигает на маршрутизаторе по умолчанию. Утверждение о том, что это значит для порта, было сделано вместе с оговоркой, и его стоит привести дословно, а не пересказывать:

B и C — насколько нам известно, первые проверенные на плате битстримы порта в A/B-комплекте против форка.

Одна заметка для тех, у кого такая же плата: Sonata прошивается сбросом файла UF2 на её загрузочный том, а не через JTAG, поэтому .bit нужно завернуть в UF2 с family id 0x6ce29e6b.

Что именно починено

Стоит быть точным в том, что закрылось, а что нет. openXC7/nextpnr#1 делает так, что маршрут через датапат BUFR обязывает поставить BUFR, — то есть учит маршрутизатор отказываться от пути, который он не может честно собрать, и размещение откатывается на выделенный хребет. Сам региональный путь работать не заставили. Смежная задача размещения — поставить BUFIO, питаемый падом, на сайт, до которого этот пад дотягивается, — это openXC7/nextpnr-xilinx#168, и она открыта; открыт и #149, issue, с которого началась эта линия: BUFIO не пакуется, а разрешения HCLK_L не выдаются никогда.

Итог недели: посчитанная разница в 179 фич оказалась косметикой, а непосчитанная разница в одном решении маршрутизации и была тем, что стояло между портом и платой. Диффы по фичам дёшево считать, и поэтому они выглядят удобной очередью работ. Эта очередь отранжировала свои пункты ровно наоборот, а поправка пришла из segbits, кругового прогона и эталона Vivado — не из диффа побольше.

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

Пруфы

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

Нужно довести FPGA/RTL-задачу до замеров на железе?

Работаю по контракту и part-time с hardware-AI, FPGA/RTL и ML-системами — от спецификации и открытого тулчейна до воспроизводимых измерений.