T27.AI

Блог

Задняя половина openXC7 из спека: FASM в кадры и в битстрим

2026-10-03 · 8 мин чтения

[один дизайн на одной плате, эквивалентность, а не лучший битстрим; время на одном занятом ноутбуке, отношения грубые; скорость fpga-assembler — из его README, не наша] openXC7 превращает FASM в битстрим Xilinx 7-series двумя инструментами prjxray: fasm2frames на Python и xc7frames2bit на C++. Мы пересобрали эту половину так, что каждая позиция бита, адрес кадра и слово пакета берутся из трёх t27-спеков. Против двух инструментов она совпадает байт в байт на 22 из 22 файлов FASM и 16 из 16 файлов кадров и быстрее примерно в 6–14 раз на небольших настоящих дизайнах, примерно в 85 раз на дизайне в 121 587 строк и до примерно 200 раз на синтетических тестах. Сравнение нашло тихий заворот битов в fasm2frames (f4pga/prjxray#2574), отсутствующую проверку микросхемы в xc7frames2bit (#2573) и наш собственный баг на kintex7. На плате AX7203 (XC7A200T) .bit, записанный так для дизайна в 121 587 строк, совпал с openXC7 байт в байт, загрузился в SRAM и вернул 403 200 из 403 200 помеченных квитанций.

#FPGA#OpenToolchain#Verification#t27

openXC7 собирает битстрим Xilinx 7-series в две половины. Первая — yosys и nextpnr: на входе Verilog, на выходе FASM. FASM — это текстовый список фич вроде CLBLM_R_X3Y0.SLICEM_X0.ALUT.INIT[63:0] = 64'h.... Вторая половина превращает этот список в битстрим. fasm2frames из prjxray (Python) находит биты каждой фичи и пишет конфигурационные кадры, а xc7frames2bit (C++) упаковывает кадры в конфигурационные пакеты. Мы переписали вторую половину так, что каждая позиция бита, адрес кадра и слово пакета берутся из t27-спека, и сравнили её байт в байт с двумя инструментами, которые она заменяет.

openXC7 сегодняэта ветка (gHashTag/t27#5609)FASMfasm2framesprjxray · Pythonкадрыxc7frames2bitprjxray · C++.bitFASMbitwalk --fasmframes.t27кадрыbitwalk --writepackets.t27 · far.t27.bit= байт в байт 22/22= байт в байт 16/16FASM дают yosys + nextpnr; .bit загружает openFPGALoader
Вторая половина flow. Каждый золотой блок — драйвер, сгенерированный из t27-спеков. Пунктир — проверки на корпусе против инструмента prjxray над ним.

Что лежит в спеках

Драйвер, bitwalk, написан на Rust. t27c gen-rust генерирует его функции-правила из этих спеков. Грамматика FASM, поиск фич, файловый ввод-вывод и текстовый вывод написаны руками; битовая арифметика — нет. Каждый раздел спека ссылается на файл и строку prjxray, которые он воспроизводит (prjxray c9f02d857, prjxray-db 517d66a).

Измерено

ПроверкаКорпусРезультат
FASM в кадры, против fasm2frames22 файла. 13 настоящих дизайнов: 7 из nextpnr на xc7a100t и 6 битстримов Vivado на xc7a35t, прочитанных обратно через bit2fasm. 7 синтетических файлов, 2 из них на kintex7 xc7k325t. 2 файла, которые должны быть отвергнуты22/22 байт в байт. Оба файла на отказ отвергнуты по той же причине
FASM в кадры, против эталонных кадров fpga-assembler5 случаев на совпадение с эталоном из lromor/fpga-assembler#495/5, включая случай с псевдо-PIP на kintex7 и 83-битное значение RXCDR_CFG
Кадры в .bit, против xc7frames2bit16 файлов кадров на xc7a35t, xc7a100t и xc7a200t16/16 байт в байт
CRC, против Vivado6 битстримов VivadoВсе 12 слов CRC воспроизводятся
ECC кадров, против Vivado32 520 кадров, 752 из них с данными0 ошибок
Мутационный гейт20 дефектов, внесённых в три спека по одному20/20 пойманы. Дефект считается пойманным, только если падают и тесты спека, и прогон по корпусу

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

Скорость

0,10 с1,0 с10,0 с100,0 сОдин настоящий дизайнxc7a100t, nextpnr, 1 785 строк1,6 с0,20 с · 8×Синтетический тестxc7a100t, 1 049 строк19,3 с0,14 с · 138×Синтетический тестkintex7 xc7k325t, 1 123 строки71,8 с0,34 с · 211×Все 22 файла корпусаподряд150,7 с3,3 с · 45×fasm2frames (prjxray, Python)bitwalk --fasm (t27-спеки)секунды, лог. шкала
Секунды на файл, логарифмическая шкала. fasm2frames засекали, когда собирался каждый файл корпуса; bitwalk — лучший из 3 прогонов на том же ноутбуке. В обоих случаях машина была занята другими задачами, так что отношения стоит читать как грубые.
Файлfasm2framesbitwalk --fasmОтношение
Один настоящий дизайн, xc7a100t, nextpnr, 1 785 строк1,6 с0,20 с8×
Синтетический тест, xc7a100t, 1 049 строк19,3 с0,14 с138×
Синтетический тест, kintex7 xc7k325t, 1 123 строки71,8 с0,34 с211×
Все 22 файла корпуса, подряд150,7 с3,3 с45×

Синтетические файлы не длиннее настоящего дизайна, около 1 100 строк против 1 785, но fasm2frames тратит на них в 12–45 раз больше времени. Мы не профилировали, почему, и о причине ничего не утверждаем. Ещё мы перезасекли fasm2frames на трёх файлах при той же нагрузке, что и наши прогоны: 3,4 с, 32,5 с и 104,4 с — это делает отношения больше, а не меньше. На графике оставлены меньшие.

Что ещё делает эту работу

ИнструментЯзыкШагГде живут правила битов
fasm2frames (prjxray)PythonFASM в кадрыКод на Python поверх prjxray-db
xc7frames2bit (prjxray)C++кадры в .bitКод на C++
fpga-assembler (lromor)C++FASM в .bitКод на C++ поверх prjxray-db. Его README сообщает примерно 10-кратную скорость против fasm2frames; здесь мы его не засекали
bitwalk (gHashTag/t27#5609)Rust, сгенерированный из t27, плюс написанный руками драйверобаt27-спеки поверх prjxray-db

Найдено по дороге

Дифференциальное сравнение полезно только тогда, когда стороны расходятся. Три раза они разошлись, и каждый раз дефект был у инструмента, с которым мы сравнивали.

Один дефект был наш. В сетке тайлов kintex7 есть четыре тайла, которые начинаются на 2 слова ниже своего кадра, а наш драйвер читал смещение как беззнаковое и останавливался. Теперь спек говорит, как размещаются биты такого тайла (seg_shift), и к этому есть тест.

На плате

Совпадение файлов — это утверждение о файлах. Чтобы проверить цепочку на железе, мы собрали один настоящий дизайн для ALINX AX7203 (XC7A200T): trinet node 0, 121 587 строк FASM, 20 230 кадров. На этом FASM bitwalk снова совпал с openXC7 байт в байт и по кадрам, и по .bit, и занял около 0,8 с там, где fasm2frames занял около 68 с, на хосте, который всё это время был занят. Затем openFPGALoader загрузил .bit от bitwalk в SRAM по JTAG, без записи во флеш, и FPGA сообщила DONE.

Прогон на AX7203Результат
Загрузка .bit от bitwalk в SRAMdone 1 за 16,7 с
Все 42 тернарные матрицы модели, каждый ответ помечен ключом узлаreceipts verified (tag) : 403200/403200, 33 792/33 792 строк бит в бит
Один слой с int8-активациямиreceipts verified (tag) : 51840/51840, 320/320 строк бит в бит

tri x7-board · t27 back half on the AX7203

Записанная сессия: tri x7-board compare, load и receipts, запущенные ещё раз для записи. Каждый напечатанный байт настоящий и появляется тогда, когда появился; приглашение и набор команд постановочные, паузы длиннее 2 с сокращены, и пока это происходит, в заголовке окна стоит пометка. В этой сессии fasm2frames перезасечён: 33,3 с против 0,43 с, при нагрузке 23 на 8 ядрах. Сама запись на английском. Открыть запись на отдельной странице.

Поскольку два файла .bit идентичны, это не показывает ничего, чего не показал бы файл openXC7. Утверждение — эквивалентность на одном дизайне на одной плате, а не лучший битстрим. Передняя половина, yosys и nextpnr-xilinx, принадлежит openXC7 и не менялась.

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

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

Пруфы

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

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

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