T27.AI

Блог

Шесть с половиной лет в одном отброшенном значении

2026-08-14 · 9 мин чтения

Четыре влитых исправления сделали CI демо-проектов openXC7 зелёным. Самое старое — bool, который никто не читал: с февраля 2020 плейсер знал, что размещение невалидно, и выбрасывал ответ.

FPGAopenXC7nextpnrPlace and route

На прошлой неделе CI демо-проектов openXC7 позеленел: все проекты собираются. Это заголовок и самое неинтересное из того, что произошло. Интересное — что мы втроём нашли под ним, в том числе дефект, сидевший в плейсере с февраля 2020 года.

openXC7 — полностью открытый тулчейн для ПЛИС Xilinx 7-й серии: yosys, nextpnr-xilinx и база битстримов prjxray, без Vivado где бы то ни было в цепочке. У таких дорог бывают ямы, и некоторые — старые.

Самая старая: значение, которое никто не прочитал

Плейсер HeAP заканчивает аналитическое размещение и запускает проход уточнения отжигом — placer1_refine(). Он возвращает bool и возвращает false, когда его финальная проверка валидности не проходит. Место вызова выглядело так:

placer1_refine(ctx, placer1_cfg);

Результат уходил в никуда. А поскольку log_error этой проверки перехватывается внутри placer1_refine, наружу не выбиралось и ничего другого. Размещение, уже признанное невалидным собственным контролем инструмента, шло прямо в роутер — и всплывало нечитаемой ошибкой трассировки внутрисайтовой дуги.

Исправление — пять строк, четыре из которых комментарий, объясняющий почему:

if (!placer1_refine(ctx, placer1_cfg))
    return false;

git blame относит это место вызова к коммиту 1b587cb5, David Shah, 2020-02-13. Влито 2026-08-13. Файл имеет 59 коммитов, половина из них этим летом, так что дата файла не доказывает ничего — доказывает только дата строки.

Это не история про небрежного автора. Плейсер был правильным, когда его писали, и проход уточнения падал редко. Баг становится достижимым только когда что-то другое начинает выдавать размещения, не проходящие проверку. Латентные дефекты в старом коде активируются новым кодом, и строка blame показывает не тот год.

Тот, что кусается до сих пор

Второй старый дефект — не ошибка, а пропуск. В слайсе 7-й серии у каждой буквенной позиции ровно один выбираемый выходной пин — xMUX — помимо выделенного O6 и выхода Q триггера. Один пин, один претендент. Проверка валидности xc7 этот бюджет никогда не считала, и в одной позиции могли оказаться трое претендентов на один пин. Плейсер говорил «да»; роутер умирал с Failed to route arc ... CARRY4_O3 to AFFMUX_OUT.

#146 добавляет позиционный бюджет: считать претендентов и отвергать позицию, если их больше одного, чтобы легалайзер продолжал искать. Проверка, которую он латает, восходит к работе над легальностью xc7 конца 2019 — начала 2020. Её невозможно было сломать: её просто никогда не написали.

У той же геометрии слайса есть родственник поопаснее, и он открыт. #134, заведённый cheungxi, описывает битстрим, который размещается, трассируется и укладывается в тайминги — и не работает на кристалле: плата не отвечает на первую команду по UART. #146 его не закрывает.

Молодые

Древним было не всё. Два исправления из четырёх — в коде этой весны, и оба про упаковку LUTRAM. Поэтому падали именно дизайны LiteX: они сильно опираются на распределённую память для тегов кэша.

ИсправлениеВ чём было делоВозраст дефекта
#145результат placer1_refine отбрасывался6 лет 6 месяцев (2020-02-13)
#146нет бюджета OUTMUX в проверке валидности xc7никогда не писался; проверка от 2019–2020
#142мукс-дерево RAM256X1S в SPO-половинеоколо 10 недель (2026-05-29)
#144скалярные A0..A6 у RAM128X1S мимо control setоколо 10 недель

#142 меняет одно значение: m256 ? 4 становится m256 ? 0.

Трое, три дня

Работа шла с 2026-08-12 по 2026-08-14. Carlos Venegas Arrabé (@cavearr) написал #144 и #146. Я написал #142 и #145 и воспроизведение в #141, с которого всё началось. Hans Baier (@hansfbaier) отревьюил каждый, влил их и держал CI демо-проектов достаточно честным, чтобы отказы вообще стали видны.

Самый полезный абзац — про ошибку. Сначала мы отнесли падение picosoc к классу #146. Это был класс #142. А не воспроизводилось оно потому, что наши лабораторные деревья уже несли однострочный фикс #142 с прошлой кампании — каждая сборка в переборе молча его включала. Мы перебирали не ту переменную, и эксперимент в принципе не мог нам об этом сказать.

Когда это заметили, картина сложилась: с одним #146 оба падающих сида умирают на размещении адресов LUTRAM и до стадии переноса не доходят; с #142 и #146 вместе оба проходят, ноль срабатываний валидности, ноль отказов трассировки. Порядок важен, и узнали мы его, сначала ошибившись.

Чего стоит зелёный CI

That does not mean that the bitstreams work. Which is what we have to tackle next.

Это мейнтейнер, и это верная граница утверждения. Следующая цель — litex-ddr-arty-s7: дизайн LiteX с DDR, который не просто собирается, а работает на плате. #134 — самая наглядная иллюстрация разрыва: дизайн проходит все наши автоматические ворота и ничего не делает на железе. Пока плата не ответила, ворота меряют тулчейн, а не дизайн.

Так что вывод не «проверяйте возвращаемые значения», хотя и это верно. Он в том, что новые возможности — это способ проаудитить старый код, а тулчейн становится надёжным, только если гонять его достаточно жёстко, чтобы он падал в новых местах.

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

Пруфы

Каждая цифра выше измерена, и рядом с ней названы её пределы.