Занимаясь поиском причины некорректно работающего проекта на ПЛИС, несколько раз приходилось погружаться в топологию схемы на уровне логических ячеек. Ошибка проявляла себя любопытным образом - машина состояний не переключалась по заведомо свершившемуся событию. Подключение сигналов машины к отладочным выводам микросхемы приводило к тому, что ошибка пропадала и проект начинал работать правильно. Для поиска несоответствий пришлось сравнивать конфигурацию регистров в нетлистах и проблема была обнаружена - в процессе трассировки программа неверно инициализировала регистр. Копание в ПЛИС на уровне схемы соединений натолкнуло меня на идею сделать небольшой тестовый проект в нетлист-редакторе. В данной статье рассмотрен способ конфигурирования ПЛИС на уровне логики микросхемы. С помощью программы, позволяющей работать с архитектурой кристалла, показана методика создания проектов на этапе трассировки и переноса схемы соединений дизайна в чип. В качестве примера я выбрал два простых “Hello World!” проекта. В первом примере сделан элементарный делитель частоты на регистрах, который моргает светодиодом, а так же добавлен небольшой кусок комбинаторной логики с переключателями. Во втором примере реализован генератор периодического сигнала на комбинаторной логике и имитации линии задержки. Для экспериментов была выбрана ПЛИС MachXO3LF фирмы Lattice Semiconductor, установленная на отладочной плате MachXO3LStarterKit. Это недорогая микросхема с 6900 логическими ячейками и FLASH-памятью для хранения бит-файла конфигурации. В качестве среды разработки используется IDE Lattice Diamond, состоящей из отдельных приложений для синтеза, трансляции, трассировки топологии, прошивки и отладки. Приложения могут работать как в интерактивном, так и в консольном режимах.  В данном эксперименте простой дизайн создан исключительно с помощью программы для редактирования/просмотра конфигурации ПЛИС. Фирма Lattice Semiconductor поставляет для этих целей EPIC Device Editor (Editor for Programmable Integrated Circuits). С её помощью можно анализировать расположение логических блоков, вносить незначительные изменения в схему без дополнительных временных затрат на компиляцию проекта. А также вручную "рисовать" поведение схемы в ПЛИС. Метод, описанный в этой статье, имеет исключительно экспериментально-обучающий характер, и может оказаться полезным для инженеров, начинающих погружаться мир HDL-разработки. Возможно некоторые аспекты помогут лучше разобраться в особенностях временных ограничений и оптимизации проектов. Итак, запустив приложение EPIC, создав новый дизайн (File -> New -> New Design, или Ctrl+N) и выбрав нужную микросхему (в нашем случае LCMXO3LF-6900C), в основном окне программы отобразится схема устройства, представленная на рисунке ниже. В окне показаны имеющиеся в чипе компоненты и соединения. В мелком масштабе узнаётся схема устройства, представленная в документации к этой ПЛИС.   На схеме можно видеть набор логических элементов PFU (Programmable Function Units). Они расположены почти по всей площади микросхемы и сгруппированы в столбцы и строки. Блок PFU состоит из четырёх программируемых ячеек (Slices). Каждая ячейка может быть сконфигурирована в одном из четырёх режимов -  Logic, Ripple, RAM и ROM. В Logic-режиме ячейку можно использовать  для создания комбинаторной логики, настроив таблицу истинности. Ripple-режим позволяет реализовывать элементарные арифметические функции - двуxбитное сложение, вычитание и прочее. С помощью нескольких ячеек в RAM-режиме можно реализовывать блок перезаписываемой памяти, а в режиме ROM - программируемой памяти. В каждой ячейке Slice имеется две таблицы истинности с четырьмя входами, одним выходом (LUT look-up table) и битами переноса, два регистра и мультиплексоры. Ниже на рисунке показаны элементы ячеек из документации (слева) и в редакторе EPIC (справа). Каждый из блоков PFU имеет буквенно-цифровую адресацию, соответствующую строке и столбцу, в котором он расположен (к примеру, R9C18), а каждая ячейка в блоке индексируется с помощью букв A, B, C и D.  Порты вводов-выводов расположены по периметру устройства и сгруппированы в банки. Каждый отдельный банк может быть сконфигурирован на работу вывода микросхемы с определённым стандартным типом сигналов - LVCMOS, LVTTL, LVDS, … Коммутация компонентов производится с помощью буферов и переключателей. Рядом с каждым блоком PFU расположен блок переключателей, входы-выходы которого подключены к логическим ячейкам и к общей шине, соединяющей остальные переключатели в одну общую сеть. Эти буферы позволяют соединять выводы ячеек друг с другом. Такую операцию можно делать вручную в графическом редакторе, выбирая соответствующие входы-выходы компонент и создавая соединения. Но это слишком трудоёмкий процесс, поэтому значительно удобнее пользоваться tcl-командами или скриптом. Tcl-функции приложения EPIC немногочисленны, но позволяют автоматизировать процесс и выполнять все операции, доступные в графической оболочке - создавать, размещать и параметризировать компоненты, создавать сети и соединения. С помощью tcl-скрипта выполнены описываемые в статье примеры. В первом примере создадим делитель частоты, поступающей с внешнего генератора на вход микросхемы. Для этого будем использовать элементарную схему на регистрах, а полученный в результате деления сигнал подключим к одному из восьми светодиодов, смонтированных на плате. В дополнение соединим физические переключатели, установленные на плате, через комбинаторную логику с ещё одним светодиодом. Создадим файл make.tcl и в него будем писать скрипт с командами для EPIC. Для начала выберем используемую на отладочной плате ПЛИС: В нашем проекте используется ПЛИС семейства MachXO3 в корпусе с 256 выводами и "быстродействием" №5. Далее опишем физические выводы. Они проименованы в соответствии со схемой устройства: Два вывода микросхемы подключим к сигналам CLK_OUT (коммутация входной частоты с ножкой микросхемы) и CLK_OUT_DIV (коммутация делённой частоты с ножкой микросхемы) для отладки с помощью осциллографа. Далее конфигурируем два вывода - указываем тип (In/Out), ток, напряжение и прочие параметры: Параметры проще всего скопировать из атрибутов вручную созданной компоненты в графическом редакторе: В данном примере созданы два основных типа: вход и выход ПЛИС. Далее их параметры копируем на остальные порты: Создадим логическую ячейку с именем SLICE0 для демонстрации работы комбинаторной логики, и расположим её в шеснадцатом столбце, тридцать второй колонке в блоке D. Адрес расположения выбран случайно. Сконфигурируем её в виде CCU2 - carry-chain unit.  CCU имеет 9 входов (4х2 логических входов и один вход для бита переноса) и три выхода (2х1 логических выходов, один выход для бита переноса). В качестве инициализирующего параметра CCU-блока принимается шеснадцатибитное значение, которое формирует таблицу истинности для каждого отдельного выхода. К примеру, значение INIT=0xF444 формирует следующую таблицу истинности: Параметры ячейки SLICE0: Соединим четыре входа переключателей с четырьмя входами созданной логической ячейки. А выход SLICE0 подключим непосредственно к выводу ПЛИС со светодиодом. Для экономии электричества отлючим шесть светодиодов, подав логическую единицу на выход ПЛИС, поскольку катод диода подключен к выводу микросхемы, а анод подтянут к питанию 3.3В. Логическую единицу в виде константы можно задать с помощью следующей ячейки: В данном случае выход F0 будет подключен к выходу 4-input look up table K0, которая сконфигурирована константой “1”. Светодиоды LED1-LED6  подключаем к выходу этой логической ячейки В итоге первый светодиод будет загораться только при определённом состоянии переключателей, следующие шесть будут выключены. Поморгаем заключительным, восьмым светодиодом. Для этого поделим входную частоту 12 МГц с ввода ПЛИС, воспользовавшись элементарным делителем на триггерах, схема которого показана ниже. Каждый следующий элемент такой схемы делит частоту на 2. Чтобы вручную не создавать и не описывать десяток компонент, напишем цикл: С помощью констант ROW и LINE задаётся адрес первой ячейки делителя, остальные будут располагаться правее от неё. Константа BITS определяет количество ячеек и, соответственно, разрядность делителя. Вход первой ячейки подключен ко входу тактирующего сигнала, выход последней - к восьмому светодиоду и свободному выходу микросхемы для отображения на осциллографе. В каждой ячейке один из доступных LUT сконфигурирован на инвертирование входного сигнала (K0::H0=~A) а его выход F0 подключен ко входу D1 регистра. Регистр тактируется выходом Q0 предыдущей ячейки, а выход регистра подаётся на тактирующий сигнал регистра следующей ячейки. Так выглядит схема одного элемента в графическом редакторе: В заключении проект сохраняется командой: Итого готовый код выглядит так:  Запустим готовый tcl-скрипт с помощью консольного режима EPIC. Запускаем утилиту fpgac и вводим команду source make.tcl То же самое можно сделать и в графическом режиме, но программа EPIC часто падает из-за большого количества всплывающих окон во время выполнения скрипта.  В результате будет сгенерирован нетлист hello_world.ncd, который можно открыть в графической оболочке и посмотреть топологию получившейся прошивки. Далее файл соединений можно конвертировать в бинарный файл, загружаемый в плис. Для этого в консоли программы fpgac вводится команда Выполнив её, получим файл прошивки hello_world.jed. Прошиваем программатором устройство и проверяем его работу.  20 последовательно соединённых регистров делит входную частоту 12 МГц: Замеряем частоту выходного сигнала на ножке В5 (CLK_DIV) отладочной платы. Измерение даёт такой же результат с небольшой погрешностью. Проверим работу переключателей. Перебрав все 16 вариантов видим, что диод зажигается только при заданных в LUT значениях ячейки SLICE0 (0хF444). С помощью следующего проекта можно продемонстрировать задержку в распространении сигналов внутри ПЛИС. Воспользуемся ей для создания примитивного генератора частоты. Реализуем следующую схему в логике, заменив конденсаторы линией задержки. Компоненты NAND опишем в SLICE0 и SLICE1 и расположим их в левом верхнем углу ПЛИС (колонка 2, столбец 2) и соединим их друг с другом через блок SLICE2, расположенный в правом нижнем углу (колонка 25, столбец 40). Выход CLK_OUT соединим с выходом NAND-ячейки. Следующий tcl-скрипт генерирует вышеописанную схему. Размещая SLICE2 ближе или дальше от SLICE0 и SLICE1 можно увеличивать или уменьшать частоту генерируемого сигнала. В приложении можно оценить приблизительное время задержки каждого сигнала. Для созданных цепей получаем следующие значения: Полученные значения задержки можно использовать для симуляции проекта: Схема в симуляторе начинает генерировать периодический сигнал с частотою около 65 МГц. Создадим файл прошивки данного проекта как в предыдущем примере и загрузим его в ПЛИС.Измеренная с помощью осциллографа частота на выводе CLK_OUT получилась приблизительно 83 МГц. Значительная разница между измеренной и рассчитанной частотой обусловлена тем, что программа завышает расчитанное время задержки распространения сигнала, предоставляя "худший сценарий".  Это объясняет тот факт, что прошивка ПЛИС может работать корректно, даже если в результате трассировки программа сообщает о многочисленных нарушениях временных ограничений.  Данная статья и описанные примеры демонстрируют возможности работы с ПЛИС на уровне элементарных блоков и схемы. Несмотря на практическую бесполезность этих тестов, некоторые из рассмотренных методов возможно применять в разработке. К примеру вносить изменения в готовый нетлист для отладки больших проектов, которые требуют продолжительной компиляции. Так можно соединить интересующий сигнал с выводом микросхемы для измерений без правки кода и дальнейшего синтеза. Можно изменить настройки входов-выходов или проинициализировать Block Ram Memory новыми значениями. Работая с редактором схемы возможно реализовать устройство типа time-to-digital converter. И, конечно же, подобные примеры могут быть полезным методическим материалом при изучении ПЛИС.