Привет, Хабр! Меня зовут Алина, я руковожу группой модульной верификации в YADRO. Свой путь в отрасли я начинала со схемотехники и разработки RTL под FPGA. На Хабре даже есть моя статья про использование опций синтеза в Vivado, написанная еще до того, как различные стратегии на основе AI стали нормой. В черновиках лежит вторая часть той статьи, где я делаю вид, что понимаю математику, которая лежит в основе синтеза цифровой схемы из RTL :) Однако тот текст так и остался черновиком, а я ушла в верификацию и работаю в ней уже больше шести лет.

Скорость верификации IP-компонентов зависит не только от верификаторов. Чтобы ее увеличить, ряд полезных практик в свою работу могут внедрить и соседние команды — управления проектами, RTL-дизайна и архитектуры. Далее в статье я такими практиками поделюсь.

Согласно исследованию Wilson Research Group от 2023 года, более 65% ASIC-проектов выходит за изначально планируемые сроки разработки. Более 50% проектов получают хотя бы два респина, прежде чем выйти на рынок. Причина большинства респинов — ошибка в цифровом дизайне. Исследование также показывает, что основной причиной функциональных ошибок являются ошибки проектирования. Многие инженеры по верификации и менеджеры проектов упоминают здесь и проблемы, связанные с изменением, некорректностью и неполнотой спецификаций.

Причины функциональных ошибок в ASIC-проектах
Причины функциональных ошибок в ASIC-проектах

Производители всеми силами стремятся улучшить эту удручающую статистику. Верификация — один из самых ресурсоемких этапов проекта, занимающий больше половины общего времени. Но объем работы верификаторов с годами только растет, как и число логических элементов на микросхеме. В процессе мы обрабатываем очень много артефактов от разных команд — RTL-код, спецификации от архитекторов, референсные модели, готовые тесты. А еще у нас есть собственные артефакты: код верификационного окружения, логи компиляции и запуска, разнообразные отчеты о пассрейте и покрытии тестов, статистика и многое другое.

Мы анализируем все эти артефакты и сопоставляем их, определяя, насколько IP-компонент соответствует заданным требованиям. Артефакты могут быть очень разными, поэтому нам нередко приходится выходить за пределы привычных пайплайнов — например, решать инфраструктурные проблемы.

Доля времени, что тратят на верификацию в разных ASIC-проектах
Доля времени, что тратят на верификацию в разных ASIC-проектах

Артефакты влияют на сроки запуска тестов для прототипа — это один из критериев успеха всего проекта. Верификаторы могут пройти этот путь за несколько недель, а могут биться не один год.

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

Менеджмент требований

Трекинг и маркировка

Бывает, что архитекторы большой подсистемы придумывают фичу или приносят новое требование от бизнеса, для которого эта фича нужна. В фиче определяют узкие места, несут ее разработчику, который фичу имплементирует. А у верификатора падают тесты. И только тогда, в последнюю очередь, он узнает о существовании этой самой фичи. Чтобы такого не случалось, важно иметь налаженную систему трекинга требований и информирования всех заинтересованных.

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

В грамотных системах трекинга все требования хранятся в едином формате и имеют уникальные идентификаторы. А при появлении новых высокоуровневых требований можно сразу посмотреть, какие низкоуровневые изменения для них требуется поддержать. Важность систематизации требований хорошо описана у К. Вигерса и Дж. Битти в «Разработке требований к программному обеспечению». Многие практики из книги можно применить и к аппаратной разработке.

Если вы ведете требования в Confluence, то для систематизации и учета требований можно попробовать плагин Requirement Yogi. Он умеет связывать между собой требования и фильтровать их, фиксировать срезы требований (baseline) в определенный момент. Для продуктивного использования инструмент стоит донастроить: прописать жизненный цикл требований и процесс работы с ним. Гибкость плагина позволяет адаптировать его под уже существующие рабочие процессы.

Requirement Yogi. Источник
Requirement Yogi. Источник

Хороший пример явной связи артефактов верификации и требований — AXI Verification IP от Synopsys. Спецификация протокола AMBA AXI хорошо декомпозирована при помощи многоуровневого списка. В каждом нумерованном разделе описаны определенные требования к протоколу. В комплекте с AXI Verification IP идет иерархический верификационный план (Hierarchy verification plan, HVP), каждый пункт которого имеет явную ссылку на конкретные пункты спецификации AMBA AXI. В HVP уже есть наполнение метриками из VIP, к которым при необходимости можно добавить собственные метрики: точки покрытия, точки проверок, утверждения.

Параллельные процессы

Часто бывает, что разработка и верификация идут бок о бок: спецификацию создают вместе с RTL и параллельно отлаживают на верификационном окружении. Так продвигают сразу несколько IP-блоков, и требования к ним уточняют по ходу проекта. Это нормально в гибких методологиях, но в разработке IP-компонентов вы не сможете предсказать окончание верификации, пока спецификация не будет зафиксирована всеми сторонами.

План верификации, который составляют параллельно спецификации, устаревает с каждым обновлением в требованиях. Поэтому и планировать ресурсы на верификацию в этот период едва ли возможно — ни инженерные, ни машинные. Планы устаревают в момент создания :)

Этот процесс почти неизбежен, но его можно контролировать. Для этого все изменения спецификации после ее фиксирования должны проходить через явный процесс Change Requests. В нем должно быть явно сформулировано изменение требований или новое требование. А после — проведен анализ влияния на трудозатраты и инфраструктуру со стороны всех вовлеченных команд. Так можно выявить требования, которые кажутся хорошими и нужными на бумаге, но затраты на их имплементацию и верификацию превысят пользу на текущем этапе. Далее такие требования можно отсеять или выставить им корректный приоритет.

Golden-сценарии

Хорошая практика — когда разработчик, верификатор и архитектор договариваются о наборе «золотых» сценариев, которые всегда должны отрабатывать в соответствии с ожиданиями. Это помогает командам сфокусироваться и всегда поддерживать хотя бы ограниченную работоспособность разрабатываемого IP. Если принятые golden-сценарии достаточно соответствуют целевым, мы всегда будем уверены, что в любом случае новые фичи не поломают IP-компонент.

Набор золотых сценариев на одной из моих предыдущих работ называли check-in regression. Его запускали каждые четыре часа. Результаты рассылали не только разработчикам и верификаторам RTL, но и руководству — в том числе владельцу продукта, которого называли chip owner. Любые падения в «золотом» наборе приводили к разбирательствам, чинить нужно было безотлагательно. Однажды мне, тогда мидл-инженеру, пришлось откатить правки своего техлида, чтобы исправить падения в check-in.

Менеджмент конфигураций

RTL-разработку можно разделить на два больших направления. Первое — это реконфигурируемые IP-компоненты, которые могут как быть универсальными, так и иметь специальное предназначение (процессорный кластер, модули периферии, модули ускорения цифровой обработки и т. д.). Второе — это сборка IP-компонентов в одну большую SoC, куда входит архитектура тактового древа и сбросов, системы отладки, внешние интерфейсы и многое другое. Все это в конечном счете влияет и на реконфигурируемые IP-компоненты.

Define или defparam

Чтобы помочь в верификации больших подсистем, необходимо выровнять подход к менеджменту конфигураций среди различных блоков. Конфигурирование RTL-блоков — установка размера шины, буферов и т. д. — может проходить на уровне define либо через параметры, специальные конструкции SystemVerilog на более высоком уровне абстракции.

Пример с `define:

`define ADDER_IN_W 8 
`define ADDER_OUT_W (`ADDERS_IN_W+1) 

module adder_defines
(
...
input logic [`ADDER_IN_W-1:0] a,
input logic [`ADDER_IN_W-1:0] b, 
output logic [`ADDER_OUT_W-1:0] sum
);

Тот же пример с параметрами:

module adder_params #(
parameter int IN_W = 8, 
parameter int OUT_W = IN_W + 1 
) (
...
input logic [IN_W-1:0] a, 
input logic [IN_W-1:0] b, 
output logic [OUT_W-1:0] sum
);

Конфигурирование блоков может использовать любой из этих подходов. Современные симуляторы поддерживают изменение конфигурации любым способом при помощи опций командной строки. У каждого подхода есть свои преимущества. В случае define мы чуть ускоряем симуляцию — «развертывание» define идет на этапе элаборации, а параметры подставляются только на этапе компиляции. Кроме того, глобальная область видимости `define увеличивает гибкость: конфигурировать блок можно с любого уровня иерархии. Но при таком использовании инструмент становится очень опасным: контроль над конфигурацией можно легко потерять, в результате чего верифицируемая конфигурация IP будет отличаться от той, что инстанцируется на самом деле. Чаще всего так получается, когда конфигурацию начинают задавать внутри тестового окружения, а не в составе RTL.

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

У меня был проект, где RTL конфигурировали с помощью define. Так было записано в том числе и пороговое значение для детектирования сигнала. Однажды утром я обнаружила, что все мои тесты упали, потому что в файле тестового окружения define переопределили на строковое значение. Поскольку этот файл зачитывался после файла с корректно заданным значением, в качестве порога использовался ASCII-код заданной строки, то есть полная ерунда. Практика code review с командой за океаном у нас по разным причинам не была развита, поэтому ломающее обновление сразу попало в основную ветку. Отлаживать такое непросто, поскольку, как я упоминала, у define не ограничен скоуп видимости. Проблемное значение нужно будет прослеживать по изменениям через симуляции, что может занять очень много времени.

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

Мониторинг конфигураций в подсистемах

Верификаторы получают от разных команд блоки на разных уровнях подсистемы. Поэтому очень полезно иметь точную информацию о том, в какой конфигурации блоки инстанцируются в ту или иную систему. Я часто видела, что конфигурации блоков, спускаемых с верхнего уровня, неявно меняются через define и параметры, значения которых заданы через define.

По этой причине, чтобы узнать истинную конфигурацию IP-блока, нужно скомпилировать систему с уровня, на котором задается конфигурация. Например, конфигурация небольшого скремблера может быть получена только после компиляции всей подсистемы тракта цифровой обработки. Это вынуждает нас завязываться на исходники, которые не имеют отношения к нашему IP. Кроме того, отслеживание конфигурации становится трудозатратным: об изменении мы узнаем по факту, когда тесты упадут или верификационное покрытие неожиданно просядет.

Конфигурацией IP-блока в большой подсистеме почти невозможно управлять со стороны верификации. Но, если есть явный сабсет конфигураций, который мы проверили на верификации, или явно известна конфигурация для инстанса проекта, то при помощи SystemVerilog-утверждений в RTL можно явно проверять, что инстанцировна валидная конфигурация.

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

Скорость симуляции

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

Эффективное использование тестового окружения и возможностей симулятора позволит найти больше багов, не переходя к тестированию на аппаратных платформах: косимуляции, прототипах, FPGA или специальных эмуляторах. Скорость верификации на этих платформах выше в десятки раз, поэтому Linux можно загружать несколько часов, а не ждать неделю.

Однако возможности отладки у разных платформ ограничены, требования к инфраструктуре и квалификации инженеров выше, да и стоимость как у новенького «боинга». Про платформы тестирования и прототипирования для pre-silicon верификации вообще можно написать отдельную статью. Для нас же важно запускать в симуляторе как можно больше тестовых сценариев и получать результаты за разумное время.

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

Возможный эффект от перехода на другие платформы
Возможный эффект от перехода на другие платформы

Размещение верификационных конструкций в RTL-коде

Очень хороший паттерн в RTL-разработке — использование SystemVerilog-конструкций assertion и coverpoint, которые проверяют корректность микроархитектурных решений. А также полноту тестовых сценариев: сформировать тестовые воздействия, чтобы «раскачать» схему и попасть в нужное состояние, может быть весьма непросто. Внутренние assertion и coverpoint позволяют не только проверить, что это произошло, но и подскажут разработчику и верификатору необходимый сценарий. 

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

Пример таких конструкций внутри модуля сумматора

module adder (
…
);

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            sum_reg <= '0;
        else
            sum_reg <= a + b;  
    end


`ifdef VERIFICATION_EN

    property p_sum_correct;
        @(posedge clk)
        disable iff (!rst_n)
        (sum_reg == a + b);
    endproperty

    assert property (p_sum_correct)
        else $error("[ASSERTION FAILED] sum_reg != a + b at time %0t", $time);


    covergroup cg_adder @(posedge clk);
…
        }
    endgroup : cg_adder

   cg_adder cg_inst = new();

    always @(posedge clk) begin
        if (rst_n) cg_inst.sample();
    end
`endif // VERIFICATION_EN

endmodule : adder

Здесь под define VERIFICATION_EN размещены две конструкции. Assertion проверяет, что суммирование происходит корректно , а covergroup обеспечивает проверку покрытия общих и краевых условий. Для обычного сумматора такие проверки могут быть избыточными. Но для модулей на 1000+ строк синтезируемого кода верификационная часть может сильно ускорить поиск и отладку краевых случаев. Поскольку define использует часть модуля под VERIFICATION_EN только для верификации, в ней допускается размещение несинтезируемых конструкций, которыми так славится SystemVerilog. 

Такое инвазивное размещение конструкций assertion и coverpoint также расширяет возможности для формальной верификации, особенно если использовать дополнительные конструкции — например, assume и restrict. 

Цели тестов могут быть разными, поэтому отладочные верификационные конструкции могут быть критически важны или не нужны вовсе. В последнем случае с ними лишь замедлится симуляция. Чтобы убедиться, что сценарий отрабатывает верно и нагружает что нужно, нам важно максимальное количество проверок и точек покрытия. Запуская большой набор тестов для отлова багов, мы можем отключить точки покрытия, но оставить assertion. Если далее мы запускаем еще бо́льший набор тестов на стабильном RTL с целью собрать покрытие, то наоборот: можем отключить assertion и оставить точки покрытия. А в очень сложных целевых сценариях типа загрузки Linux точки покрытия нам ни к чему.

Внимание к assertion

Утверждения (assertion) и точки покрытия интересны еще и тем, что их нужно уметь писать: для них предусмотрен специальный синтаксис. Вообще, SystemVerilog можно разделить на три огромных раздела: SystemVerilog Assertions, SystemVerilog Coverage и «основной» SystemVerilog — конструкции функциональной верификации. По каждому разделу можно найти немало книг и пособий применительно к функциональной и формальной верификации. Неумело описав assertion, мы можем получить драматическое снижение скорости симуляции и ничего взамен. Каждый цикл симуляции у нас будет срабатывать новый assertion и бесконечно ожидать своего завершения. 

Таким образом, помимо code review, очень хорошей практикой будет использование своего code style. В некоторых командах для assertion-конструкций даже используются специальные обертки define или обертки функций. Готовые обертки маскируют детали написания assertion. Например, можно объявить обертку, которая на каждом переднем фронте тактового сигнала будет проверять утверждение, переданное ей в качестве аргумента:

`define assert_clk( arg ) \ 
assert property (@(posedge clk) disable iff (!rst_n) arg ) 

ERROR_q_did_not_follow_d:  `assert_clk(q==$past(d)); 

Лейбл ERROR_q_did_not_follow_d позволяет однозначно найти в симуляторе или инструменте формальной верификации это утверждение. Опциями симулятора можно даже отключать утверждения по их лейблам. При отсутствии лейбла у утверждения или точки покрытия инструмент назначит их автоматически. Вот только ориентироваться в этих машинных именах будет гораздо сложнее.

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

Использовать утверждения и точки покрытия конструкций, как и точки функционального покрытия, в симуляционных запусках следует осознанно. На подсистемах среднего и большого уровня держать их включенными по умолчанию точно не стоит. Разве что у вас нет сроков сдачи проекта, зато есть бесконечные вычислительные ресурсы…

Актуальность утверждений и покрытия

Историю с assertion и покрытием продолжает поддержка этих конструкций в актуальном состоянии. Хотя код верификационных конструкций не является синтезируемым, разработчики тратят время на их создание и использование. Поэтому конструкции всегда должны соответствовать набору фичей и параметрам разработанного IP-блока. Иначе, если в симуляцию попадет ненужный assertion, мы вполне можем получить снижение скорости симуляции и километровые файлы логов — это в лучшем случае. А в худшем — баги, непокрытые точки и утверждения, которые придется разгребать.

Неопределенные значения

В стандарте SystemVerilog определены типы данных с четырьмя допустимыми значениями: 0, 1, x (не определено) и z (не имеет значения). Чаще всего используется тип logic. Неопределенные значения могут вводиться не только намеренно, но и при неаккуратном стиле кодинга. Их вызывают, например:

  • неинициализированные после сброса регистры,

  • операции, приводящие к неизвестным результатам,

  • некоторые нарушения синхронизации,

  • висящие провода,

  • провода с несколькими драйверами.

Все эти ситуации — потенциальные источники ошибок и разницы поведения дизайна в кремнии и в симуляторе. Появление неопределенности даже на один такт в одном из регистров может вызвать ее лавинообразное распространение по всему последующему конвейеру. Очень часто неопределенности возникают в операторах if, case или в условных присвоениях. Поиск неопределенных значений в регистрах и проводах можно реализовать набором утверждений, о которых шла речь выше. 

Современные симуляторы умеют работать с неопределенными значениями. Детали реализации обработки неопределенностей чаще всего не раскрываются, но стратегии обработки доступны в документации симуляторов. Обычно эти стратегии из пессимистичных прогнозов, согласно которым принимают решение о появлении X-значения. Часто режим обработки неопределенностей включен по умолчанию — его называют X-propagation.

Однако симуляция с учетом неопределенных значений достаточно требовательна к используемой памяти, а сама скорость симуляции заметно ниже. Для каждой переменной типа logic — а таких переменных подавляющее большинство — в каждый таймслот необходимо определить значение из четырех, а не из двух возможных. В IP среднего размера эта разница может быть не особо заметна, а вот на уровне большой SoC и сложных нагрузках снижение производительности симулятора может быть существенным. При этом, как и в предыдущих пунктах, предполагается, что переход к сложным долгим нагрузкам происходит при достаточно высокой зрелости RTL, когда ошибки, связанные с X-propagation, уже отлажены на более простых нагрузках.

Другие факторы

На скорость симуляции могут влиять и некоторые RTL-конструкции. В целом рекомендацию можно сформулировать так: используйте присвоения всей шине целиком, а не обращение к конкретным битам.

Однажды мне пришлось работать со сгенерированным исходником SystemVerilog, где нужно было выполнить операцию XOR над большей частью бит у 40-битной шины. Кодогенерации не составило труда реализовать XOR между десятком бит в явном виде. Выглядело это примерно так:

c_ecc_out_1= cin[29]^ cin[28]^ cin[27]^ cin[26] ^ cin[25] ^ cin[24]^ cin[23] ^ cin[22] ^ cin[21] ^ cin[20] ^ cin[19] ^ cin[18] ^ cin[17] ^ cin[16] ^ cin[15] ^ cin[14] ^ cin[13] ^ cin[12] ^ cin[11] ^ cin[7] ^ cin[4] ^ cin[1] ^ cin[0];

Гораздо быстрее в симуляторе отработает конструкция, которая использует маску и всю шину целиком:

c_ecc_out_l = ^ (cin & 40'h003ffff893);

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

c_v = c_s << 8

В случае конкатенации нижние биты указаны явно и будет обработано симулятором быстрее:

c_v = { c_s [39:0], 8'h00 };

Code style

Использование линтов

Такие инструменты, как Synopsys Spyglass, а также open source линты — например, у AMIQ Verissimo или симулятора Verilator — помогают найти некоторые типичные ошибки, которые проявляются на функциональной симуляции. Например, если подключить провода различной ширины, на функциональной симуляции часть битов при передаче из одного провода (модуля) в другой будет потеряна и данные в чекпойнтах не сойдутся.

Линты умеют подсвечивать эти проблемы и оценивать их критичность в зависимости от своих настроек. Важно не игнорировать эти ошибки, а разбирать их до передачи RTL на функциональную верификацию. Да, линты могут выдавать ошибки, не актуальные на текущей стадии проекта, но их можно фильтровать с помощью настроек severity и осознанного написания исключений.

Уровень сложности блока

Уровень сложности верифицируемого блока не должен меняться в пределах этого блока. Все особенно сложные компоненты — перемножители матриц или пересинхронизаторы для перехода из одного тактового домена в другой — лучше выносить в отдельные модули и инстанцировать их по необходимости.

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

Если компоненты все-таки размазали, это тоже не конец света. Но при верификации может потребоваться больше времени и деталей реализации блока. Подхода с черным ящиком может оказаться недостаточно, и в ход пойдут разные возможности подглядывания в дизайн. SystemVerilog предоставляет такие возможности конструкциями force-release. Однако их использование — это крайняя мера для отладки, в регулярные запуски их включать не стоит.

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

Опции симулятора

Однажды мы верифицировали цифровой дизайн, и при этом у нас был небольшой трек по запуску некоторых сценариев на gate-level уровне — когда мы запускаем сценарии верификации не на RTL-коде, а на уровне логических примитивов. Примитивы сами по себе редко используются в написании RTL. Список примитивов и их соединений генерируется после синтеза и передается инструментам Place&Route для создания топологии. Такой список называют нетлистом (netlist). Его особенность в том, что логические примитивы вносят задержки, которые отсутствуют при моделировании RTL.

Анализ задержек можно проводить инструментами статического временного анализа (STA), и в большинстве случаев этого достаточно. Однако в некоторых специфичных случаях моделирование нетлиста может дать подсказку по оптимизации RTL, чтобы задержки сошлись. Если STA проходит, то ожидается, что результаты запуска функциональных тестов на RTL и его нетлисте различаться не будут. 

Но у нас тесты с ходу не сошлись, а начали падать на gate-level уровне. Мы, конечно, начали грешить на задержки, внесенные логическими примитивами, и копать в эту сторону. По факту, проблема оказалась гораздо проще: опции симулятора в двух запусках не соответствовали друг другу. После выравнивания опций симулятора результаты сошлись.

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

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

Vendor-specific директивы

Метакомментарии — это обычные комментарии с префиксом // или в скобках /* …*/, которые передают специализированную информацию инструментам EDA-разработки. Информация эта, как правило, выходит за рамки того, что может быть передано в рамках семантики SystemVerilog. Обычно она содержит простые переключатели «вкл/выкл» или настройки режимов инструментов. Выглядит это следующим образом:

// synthesis_off 
<some SV code>
// synthesis_on

В коде на Verilog я видела и более экзотический пример, в котором симулятору явно указано использовать четыре возможных состояния для конкретной переменной, даже если опциями симулятора и стандартом задано другое:

wire /*4value*/ [31:0] tri_data;

Использовать метакомментарии нужно аккуратно, всегда сохраняя возможность полностью от них избавиться. С ними мы становимся зависимы от своих вендоров и инструментов. Нельзя будет гарантировать повторяемость результатов в других инструментах и маршрутах верификации. Внутри команды вы, конечно, вряд ли измените маршрут разработки без предварительного обсуждения с командой. Однако директивы могут сыграть злую шутку при передаче разработанного IP заказчику, использующему другие EDA-инструменты.

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

Унификация code style всего RTL

RTL-разработкой могут заниматься разные команды. Потом результаты их работы интегрируют и передают получившуюся большую систему на верификацию. Мы прогоняем тесты и начинаем отладку ошибок. Когда у всех RTL-команд проекта предусмотрен единый code style, отладка сильно упрощается. В едином коде гораздо проще ориентироваться, особенно во время отладки при переходе между блоками, что часто приходится делать при первичном анализе проблем.

Однажды я участвовала в верификации подсистемы, куда был проинтегрирован блок, разработанный сторонней командой. В блоке были и assertion, и coverpoint — в общем, вся инфраструктура для верификации. Но когда мы включили assertion и запустили тесты, у нас просто не скомпилировался RTL. Мы даже не дошли до запуска тестов.

Впоследствии выяснилось, что assertion стороннего блока использовали внутренний code style команды разработки, который явно разрешал написание assertion, не соответствующих стандарту SystemVerilog, но при этом поддерживаемых в их симуляторе. А при запуске у нас, в другом симуляторе, все упало. 

Симуляторы по-разному относятся к нюансам стандартов. Например, Synopsys VCS позволяет себе больше вольностей, а Cadence Xcelium сразу наказывает за ошибки. В некоторых симуляторах можно самостоятельно настроить эту строгость в соответствии со стандартами SystemVerilog. Однако пользоваться этой возможностью стоит только в крайнем случае — например, при интеграции стороннего IP. Внутри команды лучше воспитывать инженерную культуру и минимальную толерантность к ошибкам. Допустив отход от стандарта, никогда не знаешь, в каком месте это выстрелит. 

Заключение

Рынок диктует свои правила скорости выхода на рынок для полупроводниковых продуктов и выполняемых ими задач. Процесс и проблемы верификации цифровых схем масштабируются не так быстро, потому что здесь сходится много независимых треков: разработка требований, RTL-дизайн, моделирование, инфраструктура. Поэтому крайне важно стремиться оптимизировать и улучшать верификационные процессы. В том числе путем внедрения методологий и правил в процессы других команд. Это позволит сократить время разработки и вывода продукта на рынок, одновременно повысив уровень уверенности в его качестве и надежности.

Если вы занимаетесь верификацией, обратите внимание на наши вакансии — для разных уровней и в разных рабочих группах:

Комментарии (0)