Одна DRC-дека, три техпроцесса и двенадцать неверных гипотез

от автора

Как мы поверили в «универсальность» движка и почти не стали её проверять

Мы были уверены, что наш DRC-движок универсален.

На SKY130 он совпадал с эталонным KLayout до последнего нарушения: тесты зелёные, расхождения нулевые, поведение стабильное от прогона к прогону. Из этого казалось очевидным, что движок применим к любому техпроцессу. Он же просто исполняет правила из деки и ничего не знает про конкретную фабрику.

Мы эту мысль не проверили. Мы в неё поверили. И это разные вещи, как выяснилось.

Сначала контекст

Мы небольшой командой пишем на Rust редактор топологий микросхем и к нему верификатор. Команда — четыре человека(недавно присоеденился еще один разработчик) человека, я CTO, направляю архитектуру и принимаю решения, в том числе те неправильные, о которых пойдёт речь. Верификатор делает четыре вещи: DRC, LVS, проверку антенн и экстракцию паразитов. Здесь — только про DRC, потому что вся история крутилась вокруг него.

Расшифрую, чтобы дальше не спотыкаться. DRC — это проверка топологии на соответствие правилам фабрики. У каждого техпроцесса есть свой свод правил: минимальная ширина проводника, минимальный зазор между фигурами, на сколько металл должен накрывать переходное отверстие, и так далее — сотни правил. Дизайнер рисует топологию, прогоняет через DRC, и тот говорит, где зазор меньше нормы. Без чистого DRC чип не примут на фабрику. Сам свод правил называется декой, и для каждого процесса она своя.

Мы писали движок на открытом SkyWater SKY130. На нём всё совпадало с эталоном. Отсюда и взялась опасная мысль:

Если движок исполняет правила, значит он должен работать на любой деке.

Звучит логично. Только проверять это никто не сел.

Что значит «универсальный» на самом деле

Первое, на чём мы споткнулись, — на определении. Что именно надо доказать?

Напрашивается простой ответ: «движок считает все правила деки». Мы чуть не пошли этим путём, и хорошо, что остановились. Потому что «считает все правила» — мусорная метрика.

Деку можно импортировать на сто процентов и при этом считать неправильно. Можно показать красивое «224 из 224 правила обработано» и тихо ошибаться на каждом третьем. Количество импортированных правил вообще ничего не говорит о правильности.

Правильное определение оказалось другим:

Движок корректен, если он совпадает с эталоном KLayout на одной и той же геометрии — нарушение к нарушению.

Эталон у нас один: бинарник KLayout, открытого инструмента, который в индустрии считается референсным. Мы прогоняем нашу проверку и проверку KLayout на одной топологии с одной декой и сравниваем нарушение к нарушению. Совпало — хорошо. Не совпало — у нас либо лишнее срабатывание, либо пропущенное. Это единственная метрика, которая нас интересовала.

Инвариант, который не даёт ошибаться тихо

Ещё одно правило мы ввели с самого начала, и не жалею ни секунды:

Ни одно правило деки не имеет права исчезнуть молча.

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

Зачем так строго. Молчаливый пропуск — худшее, что может сделать верификатор. Лишнее срабатывание дизайнер увидит и проигнорирует. А пропущенное нарушение он не увидит никогда — и узнает о нём, когда чип вернётся с фабрики неработающим. Инструмент, который тихо проглатывает правила, опаснее отсутствия инструмента: с отсутствием ты хотя бы знаешь, что проверки нет.

Дальше станет видно, зачем этот инвариант понадобился. Он поймал нас за руку буквально на втором техпроцессе.

Перепись: три техпроцесса вместо одного

Раз цель — совпадение с эталоном на произвольном процессе, надо было выйти за пределы SKY130. Взяли три открытых техпроцесса: тот же SKY130, GlobalFoundries 180 нм и SiGe-процесс от IHP.

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

Перепись сразу опрокинула моё «всё перенесётся с одного процесса на другой».

Диагонали: то, что движок не видел вообще

Почти вся топология микросхем прямоугольная: проводники идут строго по горизонтали и вертикали. Но не вся. На GlobalFoundries в защитных кольцах встречаются диагонали под 45 градусов — фигуры в форме ромбов и восьмиугольников.

И когда мы это измерили, выяснилось, что движок молча терял всю геометрию под углом.

Он не ошибался в расчёте. Он просто не видел диагональные грани — как будто их нет. На прямоугольной топологии это не вылезало никогда, потому что там диагоналей нет. А стоило подсунуть GlobalFoundries с его ромбами — и проверка тихо пропускала целый класс фигур.

Это был ровно тот молчаливый пропуск, которого мы поклялись не допускать. Только мы про него не знали, пока не измерили на втором процессе. Инвариант не предотвратил баг — он сделал его видимым, как только мы прогнали GF180.

Почему диагонали нельзя было «добавить сверху»

Первая реакция — расширить существующую модель, обобщить прямоугольную логику на углы.

Не сработало. Каждая попытка «обобщить» проверенный прямоугольный путь его ломала. Чинишь диагонали — разваливается то, что работало для прямых углов.

В итоге решение оказалось архитектурным: диагонали стали отдельным каналом, который добавляется поверх прямоугольного, а не вместо него. Включается только там, где есть углы, прямоугольный путь не трогает.

На GF180 в итоге закрылись все семь видов проблемных конструкций с ромбами, до последней. Общий счёт совпадений с эталоном на этом процессе — 6242 нарушения. Прямоугольная топология при этом осталась бит в бит такой же, как была. Это было железное условие любого изменения: целевая правка двигает только то, на что нацелена, всё остальное обязано не дрогнуть.

Сама история с диагоналями заняла много раундов и тянет на отдельную статью. Если коротко — почти каждый раунд начинался с уверенной гипотезы «вот сейчас одним куском закроем», и измерение её опровергало. Я насчитал больше десятка таких за всю кампанию. Не потому что плохо думали — а потому что геометрия и деки разных процессов ведут себя не так, как подсказывает интуиция.

SKY130: где 100% оказывается иллюзией

На SKY130, где мы выросли, история получилась поучительная в другую сторону. Тут движок и так считал хорошо — но «хорошо» надо было превратить в число.

Разложили деку по полочкам. Всего 224 правила. Из них 12 — проверки уплотнительного кольца по краю кристалла, той самой защитной рамки вокруг чипа. В обычном тестовом блоке этой рамки нет, она появляется только на финальной сборке. Этим 12 правилам в нашем контексте проверять физически нечего: они не «не работают», им просто нечего ловить. Честный учёт требует вынести их за скобки — не «мы их не считаем», а «в этом контексте неприменимо».

Остаётся 212 применимых правил. На старте мы покрывали 204 из них. Дальше — про оставшиеся восемь, в них вся соль.

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

Итог — 210 из 212, то есть 99,1 процента применимых правил. Последние два — узкая конструкция, которая упирается не в геометрию, а в стопку мелких возможностей разборщика, которых пока нет. Закрыть можно, но это работа, непропорциональная ради двух правил. Мы честно записали её в потолок, а не сделали вид, что её нет.

И вот тут принципиальное. У нас 99,1 процента, а не 100. Я специально не стал натягивать до ста.

Любой, кто работал с верификацией, знает: «100 процентов на чужой деке» означает либо враньё, либо что вы не тестировали по-настоящему. Честные 99,1 с тремя названными исключениями — 12 неприменимы, два упираются в импорт, плюс граница метода ниже — вызывают больше доверия, чем круглая цифра без оговорок. Потолок, который ты можешь назвать поимённо, — это доказательство того, что ты в материале, а не недоделка.

Дырки на настоящей геометрии

Маленький эпизод, который мне нравится как иллюстрация.

Часть правил DRC работает с дырками в фигурах: в большом куске металла вырезано отверстие, надо проверить его площадь. У нас такие правила были покрыты тестами — но синтетическими. Мы сами рисовали фигуру с дыркой и проверяли на ней.

Синтетический тест доказывает, что код в принципе работает. Он не доказывает, что код работает на настоящей геометрии настоящего чипа. Разница иногда болезненная.

Нашли в реальном блоке Caravel небольшой кусок на 85 килобайт, где есть десяток настоящих отверстий в металле ровно на нужном слое. Заморозили на нём эталон KLayout, прогнали нашу проверку.

Шесть отверстий из восьми. Два пропустили.

Первый порыв — расстроиться. Но разобрались, и оказалось интересно: два пропущенных существуют только тогда, когда чип «расплющен» в единый слой без иерархии. Это отдельный известный класс задач — расхождение между послойной обработкой и расплющенной, — и к самому механизму работы с дырками отношения не имеет. То есть наша работа с дырками на настоящей геометрии подтвердилась (шесть отверстий, ноль ложных срабатываний – чего синтетика показать не могла в принципе), а заодно мы точно, числом, измерили границу: вот эти два мы не видим, и вот почему.

Это повторяющийся сюжет всей работы. Измерение почти всегда даёт не «всё совпало», а конкретное число с конкретной границей. И это полезнее зелёной галочки.

IHP: когда дека перестаёт быть набором правил

С SiGe-процессом от IHP вышла самая отрезвляющая часть, и она лучше всего показывает, зачем вообще нужна перепись.

На двух первых процессах дека — более или менее плоский список правил, который разборщик читает. Дека IHP устроена принципиально иначе. Это не список правил. Это программа на Ruby: три тысячи строк с циклами, условиями, собственными классами и конфигом, который подгружается на лету. Наш разборщик такое не читает в принципе — он рассчитан на плоское описание, а тут полноценный язык программирования.

Мы измерили честно, что это значит, и разложили на две планки.

Первая — учебная. Для университетов и небольших команд, которым нужны базовые проверки на IHP, весь промышленный свод не требуется. Нужен рабочий набор типовых проверок — ширина, зазоры, накрытие, — те, что студент реально нарушает, пока учится рисовать топологию. Таких правил порядка сорока-шестидесяти. И хорошая новость: большинство из них внутри той Ruby-программы — всё-таки плоские проверки, просто завёрнутые в обёртку из циклов и методов. Обёртку можно развернуть текстовым препроцессором, без всякого исполнения Ruby. То есть учебная планка достижима — реальная цель для нашего сегмента.

Вторая планка — полный промышленный свод правил IHP. И тут честный ответ: это отдельный большой проект, не доработка. Минимум треть деки по-настоящему завязана на логику Ruby — вычисляемые параметры, классы-обработчики, поведение, которое нельзя развернуть статически. Чтобы это поддержать, нужен либо интерпретатор Ruby, либо переписывание этих обработчиков. Масштаб отдельного продукта, и я не стал притворяться, что это «ещё немного допилить».

Зачем так подробно про то, чего мы пока не сделали? Затем, что в этом и смысл переписи. Я мог бы сказать «поддерживаем три процесса» и формально не соврать — на учебном уровне это правда для всех трёх. Но честная картина другая: на двух процессах мы близки к полному совпадению с эталоном, на третьем есть рабочая учебная планка и измеренный по стоимости путь к полной. Это не так звонко, как «поддерживаем всё». Зато правда, и под ней я готов подписаться перед любым, кто откроет инструмент и проверит.

Что я как CTO из этого вынес

Кампания заняла прилично времени, и почти каждый раунд начинался с гипотезы, которую измерение потом опровергало. Больше десятка таких. Отсюда выводы, по убыванию важности.

Сначала измерь, потом чини. Самое дешёвое перед дорогой работой — холодный замер на чтение, без единой правки. Перепись трёх процессов стоила несколько дней и сэкономила недели: показала, где движок реально универсален, а где дыра, которой мы не подозревали. Пошли бы «чинить универсальность» по наитию — полировали бы то, что и так работает, и не заметили молчаливую потерю диагоналей.

Импортировано – не значит правильно. Самая коварная метрика в верификации — процент покрытия деки. Можно затащить все правила и считать неправильно. Значение имеет только совпадение с эталоном на конкретной геометрии, нарушение к нарушению. Остальное — самоуспокоение.

Честный потолок сильнее круглой цифры. 99,1 процента с тремя названными исключениями — позиция, которую можно защищать. 100 процентов без оговорок — либо неправда, либо непротестировано, и любой профессионал это считает мгновенно. Границы метода мы документируем как часть результата, а не прячем.

Молчаливый пропуск – это баг, а не упрощение. Каждое правило обязано попасть в одну из трёх корзин. Правило, исчезнувшее без следа, — не «приемлемая аппроксимация», а пропущенное нарушение, которое вернётся с фабрики неработающим чипом. Инвариант мы держим насквозь, и он не раз ловил нас за руку – в первый раз ровно на диагоналях GF180.

Новый класс геометрии нельзя обобщать задним числом. Каждый раз, когда мы пытались «обобщить» существующий код под новую геометрию, мы ломали проверенное. Правильный путь — отдельный канал поверх рабочего, включающийся только там, где нужен, оставляющий старое нетронутым до бита. Проверенное и непроверенное нельзя собирать в одну кучу.

Если совсем коротко: мы не доказали, что движок «поддерживает всё». Мы доказали другое, и оно мне нравится больше. Движок не привязан к процессу, на котором мы его писали: он совпадает с эталоном на трёх разных техпроцессах, на прямоугольной геометрии и на диагональной, без молчаливых потерь. А там, где у него есть граница, мы можем назвать её поимённо и сказать, сколько стоит её сдвинуть.

Самое неприятное в этой истории — не сами ошибки. А то, что почти ни одна из них не была видна до измерения.


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

ссылка на оригинал статьи https://habr.com/ru/articles/1051882/