Временные ограничения и статический временной анализ FPGA на примере Microsemi SmartTime

Ещё во время обучения в ВУЗе, проектируя различные тестовые безделушки и выполняя лабораторные работы по цифровой схемотехнике, я попадал в ситуации, когда вроде бы корректный несколько раз перепроверенный проект отказывается работать «в железе». В то время, на заре изучения программируемой логики, мне как-то очень редко доводилось добираться до последних пунктов Design Flow, в чем, вероятно, и крылась беда. Если я нечаянным щелчком мыши открывал Timing Analyzer, то после нескольких секунд беглого просмотра становилось скучно, и я возвращался к издевательствам над отладочной платой и сочинял новые безумства на VHDL.

Когда подошло время более-менее адекватных и серьёзных проектов, проблем стало больше, соответственно, я начал интенсивнее использовать гугл и искать ответы на свои вопросы. Тут мне всё чаще стали попадаться такие страшные словосочетания, как “timing analysis” и “design constraints”, когда я почитал и немного вникнул, пришло осознание того, что я упустил что-то очень важное. Сначала я панически боялся этих неведомых констрейнов, и ведь без них успешно работали первые проекты, благо частота там была не больше пары десятков МГц. Но когда речь зашла о более высоких частотах и более сложных проектах, здесь уже не обойтись без тщательного временного анализа и оптимизации. По мере общения с людьми, с удивлением обнаружил, что далеко не все наши разработчики в достаточной мере знакомы с этими процессами, чему причина, вероятно, очень малое количество документации и разъяснений на русском языке. Поэтому решил поделиться тем, что аккумулировал за время работы с ПЛИС, используя инструменты от компании Microsemi (вероятно, более известной как Actel). Этот пост ни в коем случае не претендует на 100% полноту и точность, просто результат желания разложить знания по полочкам и, возможно, кому-то помочь сделать то же самое. Все замечания и предложения приветствуются.

Синхронные схемы и основные определения


Итак, как правило, мы имеем дело с синхронными схемами. Такие схемы состоят из следующих элементов:

  • порты ввода-вывода;
  • последовательные элементы (триггеры);
  • комбинационная логика (вентили).

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


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

  • задержки на элементах (cell delay);
  • задержки распространения (propagation delay);


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



Естественно, при передаче сигнала очевидным образом появляются две стороны взаимодействия — источник и приёмник. Это конечные точки пути. Конечными точками могут быть порты ввода-вывода и триггеры. Остановимся на триггерах. Они в нашем случае тактируются одним тактовым сигналом, и путь пролегает от выхода Q одного триггера до входа D второго. Хоть тактовый сигнал и один, в данном примере дадим ему два имени:

  • Launch clock — по фронту новые данные поступают на выход Q триггера 1;
  • Latch clock — по фронту триггер 2 запоминает то, что в данный момент на входе D.


Так как данные распространяются с задержкой, вызванной вышеописанными факторами, сигнал на входе D триггера 2 появляется далеко не сразу. Отсюда вытекают следующие характеристики:

  • Setup time (tsu) — время, за которое сигнал должен установиться до фронта clk приёмника;
  • Hold time (th) — время, которое сигнал должен удерживаться после фронта clk приёмника;
  • Slack определяет запас по времени для tsu и th.


tsu и th образуют своеобразный коридор, стержнем которого служит фронт latch clock. Теперь требования к сигналу на входе D приёмника просты — он не должен изменяться в пределах этого коридора. То есть, в идеальном случае, установиться задолго до его левой границы и сменить значение на новое через некоторое время после правой границы. Этот самый запас по времени называется Slack. Если Slack — положительное число, значит всё в порядке, и данные успеют поступить на вход приёмника в требуемое время, если отрицательное — указанный путь не удовлетворяет временным характеристикам, то есть данные поступают на вход вне требуемого интервала времени, а значит, устройство будет работать некорректно.

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

Задание временных ограничений (Timing Constraints)


Прежде чем начать проектировать новое устройство, разработчик должен обладать как можно более полными сведениями о требованиях к этому устройству и его производительности. В первую очередь это временные характеристики этой системы. А когда они известны ему самому, нужно доложить об этом инструменту проектирования, и тут на помощь приходят временные ограничения или временные констрейны. Временные ограничения — это информация о требованиях к временным характеристикам проекта, изложенная на понятном среде языке, коим чаще всего является Synopsis Design Constraints, SDC. Это де-факто стандарт описания временных (и не только) ограничений для ПЛИС, базирующийся на Tcl, который, кстати, и сам по себе повсеместно применяется для автоматизации разработки оборудования.

Эти описания помещаются в *.sdc файл и прикрепляются к проекту. Потребители этого файла — всякого рода оптимизаторы, которые стараются развести кристалл так, чтобы он удовлетворял требованиям разработчика, а также временные анализаторы, о которых речь пойдёт далее. Sdc-файлы несложны, фактически это перечисление команд с аргументами и их значениями. При описании можно (и нужно) использовать синтаксис Tcl, включая специальные символы, например для размещения одной команды на нескольких строках.

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

create_clock -name name -period period_value [-waveform edge_list] source

Этой командой мы определяем в схеме тактовый сигнал и описываем его характеристики:

Имя констрейна
-name name

Период
-period period_value

Скважность (по умолчанию — 2), квадратные скобки указывают на необязательный аргумент
[-waveform edge_list]

Источник сигнала (пин, порт)
source

Знание средств САПР о тактовом сигнале наиболее важно, так как без этого ни о каком анализе и оптимизации речи быть не может. Далее можно ещё более утончить информацию о тактовом сигнале, используя команды set_clock_latency, set_clock_uncertainty и т.д., но здесь мы это рассматривать не будем, полагаясь на установленные в среде значения по умолчанию. В качестве примера:

create_clock -name {my_clock} –period 6 –waveform {0 3} {CLK}

Данная команда создаёт тактовый сигнал с периодом 6 нс, в пределах которого фронт будет на 0 нс, а спад на 3-ей.



Ещё одна полезная команда, относящаяся к тактовому сигналу:

create_generated_clock -name {name -source reference_pin [-divide_by divide_factor] [-multiply_by multiply_factor] [-invert] source

Она описывает тактовый сигнал, который порождается внутри микросхемы, обычно в схемах фазовой автоподстройки частоты (ФАПЧ, PLL). Собственно, аргументы по большей части повторяют настройки, заданные в PLL — источник исходного сигнала, коэффициенты деления и умножения, инверсия сигнала и т.д. Так как PLL используется повсеместно, это тоже достаточно важная команда, которая встречается часто.

Перейдём к рассмотрению команд, задающих собственно ограничения и требования к дизайну. Первая пара команд:

set_input_delay delay_value -clock clock_ref [–max] [–min] [–clock_fall] input_list
set_output_delay delay_value -clock clock_ref [–max] [–min] [–clock_fall] output_list

Важные ограничения, если дизайн взаимодействует с внешними устройствами (а это ведь всегда так). Задаёт задержку для внешнего по отношению к ПЛИС сигналу (входному или выходному) с привязкой к тактовому сигналу. Взаимодействуя с другими устройствами, мы должны учитывать их временные характеристики, для чего и служат два этих констрейна. Например, имеется наша ПЛИС, какое-либо устройство, обменивающееся с нами данными, тактовый генератор, служащим общий источником тактовых импульсов. Для того, чтобы эффективно провести временной анализ и трассировку неплохо бы знать как сигнал будет приходить к нам и как нам выдавать его наружу. Обычно такие сведения описаны в соответствующих даташитах на изделия, так что задача обычно сводится к просмотру документации и переписывание характеристик в наш *.sdc файл.



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

Следующая пара команд задаёт соответственно минимальную и максимальную задержку на внутреннем пути:

set_min_delay delay_value [-from from_list] [-to to_list]
set_max_delay delay_value [-from from_list] [-to to_list]

Аргументы, опять же, простые — значение задержки в наносекундах, начальная точка и конечная точка. Обычно такие ограничения применяются к чисто комбинационным путям от входов микросхемы к выходам. При этом учитываются констрейны set_input_delay и set_output_delay и create_clock, если хоть одна из конечных точек — синхронный элемент. Также может использоваться для схем с несколькими тактовыми доменами, обеспечивая надёжный переход между ними.



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

Multicycle path — такой путь, конечными точками которого являются триггеры, который требует более одного периода тактового сигнала, чтобы проходящие по нему данные достигли точки назначения. Очень важно выявлять такие пути, так как по умолчанию все средства оптимизации рассматривают схему, как однотактовую, то есть пытаются привести все пути типа триггер-триггер к одному циклу тактовой частоты. К примеру, какой-либо источник выдаёт данные с частотой в два раза меньшей, чем тактовая частота. Тогда нет смысла ловить данные на каждом такте, так что этот путь помечается как multicycle и сигналам, проходящим по нему, даётся привилегия задерживаться на 2 такта. Если так не сделать, наш инструмент будет тщетно пытаться оптимизировать этот путь, при этом могут страдать другие, которые как раз требуют прохода за один такт.



Flase path — ложные пути, такие пути хоть и физически существуют, но есть причина, по которой мы хотим исключить их из процессов оптимизации и временного анализа, например, если во время функционирования устройства по ним никогда не пойдет сигнал. Простой пример: у нас есть 4-битный счётчик, но нам нужно досчитать только до 9, затем счётчик всегда сбрасывается. Но оказывается, что инкремент на бОльших числах задействует пути со значительными задержками. Они присутствуют, но, по факту, не нужны. Такие пути помечаются, как false path и, таким образом, исключаются из оптимизации и временного анализа. Как и в примере с multicycle, если оставить всё, как есть, то эти пути будут подвергаться оптимизации со всеми вытекающими последствиями для остальных путей.

Команды для укрощения вышеописанных путей:

set_multicycle_path ncycles [-from from_list] [–through through_list] [-to to_list]
set_false_path [-from from_list] [-through through_list] [-to to_list]

В обеих командах аргументами указываются конечные точки, а в случае multicycle еще количество тактов, которые даются сигналу на прохождение пути.

Итак, мы рассмотрели некоторые команды, с помощью которых можно задавать временные ограничения и описывать требования к временным характеристикам проекта. Их корректное задание и внимательность являются залогом успеха при разработке устройств на ПЛИС, но они с таким же успехом могут создать значительные сложности и ошибки, которые трудно будет отследить и устранить в случае, если описать нереалистичные требования. Конечно, рассмотренное — это лишь капля в море, но и это дает начальное представление и фундамент для будущего изучения. Подробнее об этих командах и их ключах можно почитать например в [2]. Теперь перейдём к практической части и посмотрим, как временной анализ выглядит в среде Libero SoC, инструменте проектирования для ПЛИС Microsemi/Actel.

Временной анализ в Libero SoC SmartTime


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

Сейчас временной анализатор (timing analyzer) включается в каждую современную САПР разработки оборудования. С помощью этой программы разработчик может узнать, соответствуют ли его чаяния возможностям только что рождённого (а может, уже сотню раз перекомпилированного) устройства ещё до прошивки ПЛИС и испытаний на натурном образце. В современных САПР они имеют удобный графический интерфейс и поддаются быстрому освоению.

Рассмотрим временной анализатор на примере включённого в Libero SoC SmartTime. Для этого создадим в среде Libero SoC вариацию классического hello world проекта для ПЛИС с счётчиком и на его примере разберёмся в том, что позволяет временной анализатор.



В проекте выбран простой кристалл третьего поколения ПЛИС Microsemi — ProASIC3 A3P600 со стандартным speedgrade в корпусе PQ 208. Для начала прогоним проект через Design Flow, как есть. При этом в настройках Place&Route нужно выбрать критерий оптимизации по временным характеристикам (Timing-driven).



После этого нам станет доступен инструмент Designer, в котором, помимо прочего, содержится оболочка для управления временными ограничениями и временного анализа — SmartTime. Она представлена двумя подсистемами — Constraints Editor и Timing Analyzer.



Открыв Constraints Editor, мы можем с помощью удобного графического интерфейса задать те самые требования и ограничения, о которых говорилось выше, и далее экспортировать *.sdc файл. Так и поступим. Как было обозначено выше, первым и, безусловно, необходимым констрейном является создание тактовых сигналов с требуемыми характеристиками. У нас такой всего один, для его описания проследуем по меню: Actions -> Constraint -> Clock.



Укажем пин, с которого должен приходить сигнал и представим, что нам требуется, чтобы проект работал на 200 МГц. После нажатия OK мы увидим, как клок появился в редакторе.



Для того чтобы изменения вступили в силу, жмём File -> Commit, а из окна Designer экспортируем файл ограничений путём File -> Export -> Constraint Files…. По умолчанию он ложится в папку constraint в корне проекта. Вернемся в Design Flow и отметим появившийся файл top.sdc как используемый в подпунктах Synthesize и Compile и откроем его.



################################################################################
#  SDC WRITER VERSION "3.1";
#  DESIGN "top";
#  Timing constraints scenario: "Primary";
#  DATE "Mon Feb 16 10:48:26 2015";
#  VENDOR "Actel";
#  PROGRAM "Microsemi Libero Software Release v11.4 SP1";
#  VERSION "11.4.1.17"  Copyright (C) 1989-2014 Actel Corp. 
################################################################################

set sdc_version 1.7

########  Clock Constraints  ########

create_clock  -name { Clock } -period 5.000 -waveform { 0.000 2.500  }  { Clock  } 


########  Generated Clock Constraints  ########

########  Clock Source Latency Constraints #########

########  Input Delay Constraints  ########

########  Output Delay Constraints  ########

########   Delay Constraints  ########

########   Delay Constraints  ########

########   Multicycle Constraints  ########

########   False Path Constraints  ########

########   Output load Constraints  ########

########  Disable Timing Constraints #########

########  Clock Uncertainty Constraints #########

Видим специально отформатированный файл, в котором присутствует наш create_clock, а остальные поля пустые (на их месте могут быть соответствующие команды, если их задать). Что ж, запускаем Design Flow ещё раз, до пункта Verify Timing. Снова открываем Designer и запускаем вторую подсистему — Timing Analyzer. По умолчанию открывается просмотр Maximum Delay Analysis View, то есть временные задержки, вычисленные исходя из худших условий (worst case). Посмотрим на результаты.



У многих давно выработался рефлекс: красные цвет — плохо. Бывают исключения, но не в этом случае. Перейдем в подпункт Register-to-Register, который содержит информацию о путях между триггерами в единственном созданном тактовом домене в табличной форме. Мы имеем несколько плохих результатов по таким путям, появился отрицательный Slack, время поступления сигнала на триггер-приёмник больше рассчитанного максимально допустимого. Чем это грозит описано в теоретической части в начале поста. Благо здесь всё не так уж плохо — всего пять путей показали отрицательный результат. Распределение Slack можно видеть на гистограмме в левой нижней части окна. Начнём разбираться. Для начала вспомним, какие условия мы задавали и посмотрим, что на это сказал анализатор.



Получается, мы погорячились, и наши 200 МГц были понижены до 172 МГц fmax для данного проекта. Теперь рассмотрим подробнее один из плохих путей, для этого сделаем на нём двойной щелчок.



Открывается детализированная информация о пути. Нам показывают сведения о требуемом времени прихода дынных (Data Required Time), времени фактического прихода данных (Data Arrival Time) и запас по времени (Slack). При этом путь раскрывается в виде таблицы с подробным указанием, где и насколько сигнал задерживается, а также в виде изображения соединений между триггером-источником и триггером-приёмником. Инструмент также показывает, каким образом он рассчитывает Data Required Time. В правом верхнем углу также можно посмотреть круговую диаграмму, показывающую соотношение задержек на вентилях и задержек на линиях соединений.

Анализируя результаты, приходим к выводу, что задержки на вентилях комбинационной цепочки на пути от триггера 2-го разряда до триггера 7-го разряда счётчика не позволяют схеме в целом работать на указанной частоте. Слишком длинный и сложный комбинационный путь, данные не успевают поступать в нужное время, Slack имеет отрицательное значение. Возникает такая ситуация на старших разрядах по очевидной причине — для того, чтобы установиться в единицу, седьмому разряду необходимо удостовериться, что все остальные уже установились, соответственно к нему идёт 8 путей (от всех разрядов, включая обратную связь), и велика вероятность, что какие-то из них окажутся неприемлемыми.



Таким образом, из-за всего нескольких путей дизайн не работает на нужной частоте. Обидно. Как же с этим бороться? Наиболее распространённый способ повышения производительности синхронных схем — устранение большого количества комбинационной логики между триггерами посредством разделения процесса на стадии, это называется конвейеризацией. В общем случае при таком подходе поток входных данных поступает как обычно, проходит через несколько ступеней конвейера и появляется на выходе через время, зависящее от глубины конвейера. Глубина, то есть количество ступеней, выбирается исходя из требований к производительности.

Вернемся к нашему проекту и попробуем применить данный подход для достижения цели, которая была поставлена. Разобьем один 8-битный таймер на два 4-битных, добавим выход переноса и вход разрешения тактирования. Соединим выход переноса первого таймера с входом разрешения тактирования второго таймера через D-триггер. Получаем двухступенчатый конвейер, первый таймер представляет младшие разряды, второй таймер — старшие.



Запускаем компиляцию и заходим в SmartTime. Вуаля. Отрицательные Slack ушли, ошибок нет, частота поднялась до 227 МГц, что даже намного больше, чем нам требовалось.




Итак, применив технику конвейеризации, мы разогнали частоту работы проекта со счётчиком с 172 МГц до 227 МГц, при этом функциональные возможности были в полной мере сохранены, как и используемый кристалл.

Заключение


Разумеется, мы рассмотрели очень простой случай, и это всё очень далеко от реальных проектов и реального процесса оптимизации, когда от красного цвета в окне временного анализатора начинает болеть голова, и на отладку проекта уходят целые дни. Когда пример будет становиться чуть-чуть сложнее, будет появляться масса новых вопросов. Как эффективно ловить multicycle и false пути? Как быть с несколькими тактовыми доменами? Может быть можно как-то закрепить разводку некоторых элементов и зафиксировать их временные характеристики?
Но это хорошая отправная точка для начинающих осваивать это нелегкое дело. И, конечно, стоит попробовать проделать то же самое своими руками и попробовать оптимизировать более сложный проект.

Ссылки:


1. www.microsemi.com, actel.ru — официальный сайт Microsemi с документацией, сайт официального дистрибьютора (информация на русском)
2. www.microsemi.com/index.php?option=com_docman&task=doc_download&gid=131597 — о констрейнах.
3. www.vlsi-expert.com/p/static-timing-analysis.html — о статическом временном анализе.
4. vhdlguru.blogspot.ru/2011/01/what-is-pipelining-explanation-with.html — о конвейеризации.
5. www.microsemi.com/index.php?option=com_docman&task=doc_download&gid=130940 — руководство по SmartTime.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 7

    0
    О, Actel теперь это Microsemi… Ну и как, Flash конфигурационная память работает нормально?
      0
      сарказм?)
      +2
      Отлично написано. В своё время тоже страшился всех этих тайминганалазеров, предпочитая менять дизайны))) Очень помог в освоении документ «TimeQuest для чайников», он был отправной точкой (Спасибо Денису). Теперь я даже люблю порелаксировать над проектом бесконечно разбирая слаки, благо инструменты для этого есть в квартусе.

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

        считаю не совсем верным. Конструкцией set_false_path мы просто сообщаем анализатору, что указанный путь анализировать не нужно. Но при этом сигналы по таким путям идут и нормально работают, просто задержки на них для нас некритичны.
          0
          согласен, поправлю!
          +1
          Очень нужная статья, но в то же время, дабы не распугать потенциальных новых плисоводов, стоит заметить что в начале пути на спец инструментарий контроля тайминга можно забить. Вот понимание базовых принципов синхронной логики совершенно необходимо, оно позволяет прикидывать и удерживать в голове тайминг небольших проектов.
            0
            Простой пример: у нас есть 4-битный счётчик, но нам нужно досчитать только до 9, затем счётчик всегда сбрасывается. Но оказывается, что инкремент на бОльших числах задействует пути со значительными задержками.

            Пример, конечно, неплохой, но ИМХО, такая оптимизация не нужная (да и вредная, т.к. не особо интуитивная).
            Чаще всего проблемы в других вещах, чем в счетчиках.

            Only users with full accounts can post comments. Log in, please.