Skip to content

Latest commit

 

History

History
226 lines (146 loc) · 26.1 KB

Implementation steps.md

File metadata and controls

226 lines (146 loc) · 26.1 KB

Этапы реализации проекта в ПЛИС

Для того, чтобы описанное на языке описания аппаратуры устройство было реализовано в ПЛИС, необходимо выполнить несколько этапов:

  1. Elaboration
  2. Synthesis
  3. Implementation
  4. Bitstream generation

В русскоязычной литературе не сложилось устоявшихся терминов для этапов 1 и 3, но elaboration можно назвать как "предобработку" или "развертывание", а implementation как "реализацию" или "построение". Этапы 2 и 4 переводятся дословно: синтез и "генерация двоичного файла конфигурации (битстрима)".

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

Остановимся на каждом шаге подробнее.

Elaboration

На этапе предобработки, САПР разбирает и анализирует HDL-описание вашего устройства, проверяет его на наличие синтаксических ошибок, производит подстановку значений параметров и блоков generate, устанавливает разрядности сигналов и строит иерархию модулей устройства.

Затем, ставит в соответствие отдельным участкам кода соответствующие абстрактные элементы: логические вентили, мультиплексоры, элементы памяти и т.п. Кроме того, производится анализ и оптимизация схемы, например, если какая-то часть логики в конечном итоге не связана ни с одним из выходных сигналов, эта часть логики будет удалена[1].

Итогом предобработки является так называемая топология межсоединений (в быту называемая словом нетлист). Нетлист — это представление цифровой схемы в виде графа, где каждый элемент схемы является вершиной графа, а цепи, соединяющие эти элементы являются его ребрами. Нетлист может храниться как в виде каких-то внутренних файлов САПР-а (так хранится нетлист этапа предобработки), так и в виде HDL-файла, описывающего экземпляры примитивов и связи между ними. Рассмотрим этап предобработки и термин нетлиста на примере.

Допустим, мы хотим реализовать следующую цифровую схему:

../.pic/Introduction/Implementation%20steps/fig_01.drawio.svg

Рисунок 1. Пример простой цифровой схемы.

Её можно описать следующим SystemVerilog-кодом:

module sample(
  input  logic a, b, c, d, sel,
  output logic res
);

logic ab = a & b;
logic xabc = ab ^ c;

assign res = sel? d : xabc;

endmodule

Выполним этап предобработки. Для этого в Vivado на вкладке RTL Analysis выберем Schematic.

Откроются следующие окна:

../.pic/Introduction/Implementation%20steps/fig_02.png

Рисунок 2. Результат этапа предобработки.

В левом окне мы видим наш нетлист. В нижней части обозначены узлы графа (элементы ab_i, res_i, xabc_i, которые представляют собой И, мультиплексор и Исключающее ИЛИ соответственно. Имена этих элементов схожи с именами проводов, присваиванием которым мы создавали данные элементы)

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

Справа вы видите схематикграфическую схему, построенную на основе данного графа (нетлиста).

Synthesis

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

Поскольку в примере схема чисто комбинационная, результат её работы можно рассчитать и выразить в виде таблицы истинности, а значит для её реализации лучше всего подойдут Таблицы Подстановки (LUT-ы). Более того, в ПЛИС xc7a100tcsg324-1 есть пятивходовые LUT-ы, а у нашей схемы именно столько входов. Это означает, работу всей этой схемы можно заменить всего одной таблицей подстановки внутри ПЛИС.

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

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

../.pic/Introduction/Implementation%20steps/fig_03.png

Рисунок 3. Результат этапа синтеза.

Мы видим, что между входами/выходами схемы и её внутренней логикой появились новые примитивы — буферы. Они нужны, чтобы преобразовать уровень напряжения между входами ПЛИС и внутренней логикой (условно говоря, на вход плис могут приходить сигналы с уровнем 3.3 В, а внутри ПЛИС примитивы работают с сигналами уровня 1.2 В).

Сама же логика, как мы и предполагали, свернулась в одну пятивходовую таблицу подстановки.

строка EQN=32'hAAAA3CCC означает, что таблица подстановки будет инициализирована следующим 32-битным значением: 0xAAAA3CCC. Поскольку у схемы 5 входов, у нас может быть 25=32 возможных комбинаций входных сигналов и для каждого нужно свое значение результата.

Можно ли как-то проверить данное 32-битное значение без просчитывания всех 32 комбинаций сигналов "на бумажке"?

Да, можно. Сперва надо понять в каком именно порядке будут идти эти комбинации. Мы видим, что сигналы подключены к таблице подстановки в следующем порядке: `d, c, b, a, sel`. Это означает, что сама таблица примет вид:
|sel| a | b | c | d |  |res|
|---|---|---|---|---|  |---|
| 0 | 0 | 0 | 0 | 0 |  | 0 |
| 0 | 0 | 0 | 0 | 1 |  | 0 |
| 0 | 0 | 0 | 1 | 0 |  | 1 |
| 0 | 0 | 0 | 1 | 1 |  | 1 |
| 0 | 0 | 1 | 0 | 0 |  | 0 |
....
| 1 | 1 | 1 | 1 | 1 |  | 1 |

Давайте посмотрим на логику исходной схемы и данную таблицу истинности: когда sel==1, на выход идет d, это означает, что мы знаем все значения для нижней половины таблицы истинности, в нижней половине таблице истинности самый левый входной сигнал (sel) равен только единице, значит результат будет совпадать с сигналом d, который непрерывно меняется с 0 на 1 и оканчивается значением 1. Это означает, что если читать значения результатов снизу-вверх (от старших значений к младшим), то сначала у нас будет 16 цифр, представляющих 8 пар 10:101010101010, что эквивалентно записи AAAA в шестнадцатеричной записи.

Рассчитывать оставшиеся 16 вариантов тоже не обязательно, если посмотреть на схему, то можно заметить, что когда sel!=1, ни sel, ни d больше не участвуют в управлении выходом. Выход будет зависеть от операции xor, которая дает 1 только когда её входы не равны между собой. Верхний вход xor (выход И) , будет равен единице только когда входы a и b равны единице, причем в данный момент, нас интересуют только ситуации, когда sel!=1. Принимая во внимание, что в таблице истинности значения входов записываются чередующимися степенями двойки (единицами, парами, четверками, восьмерками) единиц и нулей, мы понимаем, что интересующая нас часть таблицы истинности будет выглядеть следующим образом:

       ...

  | a | b | c |
. |---|---|---| .
. | 1 | 1 | 0 | .
. | 1 | 1 | 0 | .
  | 1 | 1 | 1 |
  | 1 | 1 | 1 |

       ...

Только в этой части таблицы истинности мы получим 1 на выходе И, при этом в старшей части, вход c так же равен 1. Это значит, что входы Исключающего ИЛИ будут равны и на выходе будет 0. Значит результат этой таблицы истинности будет равен 0011 или 3 в шестнадцатеричной записи.

Ниже данной части таблицы истинности располагается та часть, где sel==1, выше та часть, где выход И будет равен 0. Это означает, что оставшаяся младшая часть будет повторять значения c, которое сменяется парами нулей и единиц: 00-11-00-11... Эта оставшаяся последовательность будет записана в шестнадцатеричном виде как 0xCCC.

Таким образом, мы и получаем искомое выражение EQN=32'hAAAA3CCC. Если с этой частью возникли сложности, попробуйте составить данную таблицу истинности (без вычисления самих результатов, а затем просмотрите логику быстрого вычисления результата).

Implementation

После получения нетлиста, где в качестве элементов используются ресурсы конкретной ПЛИС, происходит размещение этой схемы на элементы заданной ПЛИС: выбираются конкретные логические ячейки. Затем происходит трассировка (маршрутизация) связей между ними. Для этих процедур часто используется термин place & route (размещение и трассировка). Например, реализация 32-битного сумматора с ускоренным переносом может потребовать 32 LUT-а и 8 примитивов вычисления быстрого переноса (CARRY4). Будет неразумно использовать для этого примитивы, разбросанные по всему кристаллу ПЛИС, ведь тогда придётся выполнять сложную трассировку сигнала, да и временные характеристики устройства так же пострадают (сигналу, идущему от предыдущего разряда к следующему, придётся проходить больший путь). Вместо этого, САПР будет пытаться разместить схему таким образом, чтобы использовались близлежащие примитивы ПЛИС, для получения оптимальных характеристик.

Что именно считается "оптимальным" зависит от двух вещей: настроек САПР и ограничений (constraints), учитываемых при построении итоговой схемы в ПЛИС. Ограничения сужают область возможных решений по размещению примитивов внутри ПЛИС под определенные характеристики (временны́е и физические). Например, можно сказать, внутри ПЛИС схема должна быть размещена таким образом, чтобы время прохождения по критическому пути не превышало 20ns. Это временно́е ограничение. Также нужно сообщить САПР, к какой ножке ПЛИС необходимо подключить входы и выходы нашей схемы — это физическое ограничение.

Ограничения описываются не на языке описания аппаратуры, вместо этого используются текстовые файлы специального формата, зависящего от конкретной САПР.

Пример используемых ограничений для лабораторной по АЛУ под отладочную плату `Nexys A7-100T` (для упрощения восприятия, убраны старшие разряды переключателей и светодиодов, т.к. ограничения для каждого из разряда однотипны).

Строки, начинающиеся с # являются комментариями.

Строки, начинающиеся с set_property являются физическими ограничениями, связывающими входы и выходы нашей схемы с конкретными входами и выходами ПЛИС.

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

## This file is a general .xdc for the Nexys A7-100T

# Clock signal

set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { CLK100 }]; #IO_L12P_T1_MRCC_35 Sch=clk100mhz
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports {CLK100}];

# Switches
set_property -dict { PACKAGE_PIN J15   IOSTANDARD LVCMOS33 } [get_ports { SW[0] }]; #IO_L24N_T3_RS0_15 Sch=sw[0]
set_property -dict { PACKAGE_PIN L16   IOSTANDARD LVCMOS33 } [get_ports { SW[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=sw[1]
# ...

### LEDs

set_property -dict { PACKAGE_PIN H17   IOSTANDARD LVCMOS33 } [get_ports { LED[0] }]; #IO_L18P_T2_A24_15 Sch=led[0]
set_property -dict { PACKAGE_PIN K15   IOSTANDARD LVCMOS33 } [get_ports { LED[1] }]; #IO_L24P_T3_RS1_15 Sch=led[1]
# ...

## 7 segment display
set_property -dict { PACKAGE_PIN T10   IOSTANDARD LVCMOS33 } [get_ports { CA }]; #IO_L24N_T3_A00_D16_14 Sch=ca
set_property -dict { PACKAGE_PIN R10   IOSTANDARD LVCMOS33 } [get_ports { CB }]; #IO_25_14 Sch=cb
# ...

# set_property -dict { PACKAGE_PIN H15   IOSTANDARD LVCMOS33 } [get_ports { DP }]; #IO_L19N_T3_A21_VREF_15 Sch=dp
set_property -dict { PACKAGE_PIN J17   IOSTANDARD LVCMOS33 } [get_ports { AN[0] }]; #IO_L23P_T3_FOE_B_15 Sch=an[0]
set_property -dict { PACKAGE_PIN J18   IOSTANDARD LVCMOS33 } [get_ports { AN[1] }]; #IO_L23N_T3_FWE_B_15 Sch=an[1]
# ...

## Buttons
set_property -dict { PACKAGE_PIN C12   IOSTANDARD LVCMOS33 } [get_ports { resetn }]; #IO_L3P_T0_DQS_AD1P_15 Sch=cpu_resetn

После выполнения построения, нетлист и сама цифровая схема остаются неизменными, однако использованные для реализации схемы примитивы получают свой "адрес" внутри ПЛИС:

cell_add../.pic/Introduction/Implementation%20steps/fig_04.png

Рисунок 4. "Адрес" конкретного LUT-а в ПЛИС.

Теперь, мы можем посмотреть на "внутренности" нашей ПЛИС xc7a100tcsg324-1 и то, как через её примитивы будет реализована наша схема. Для этого необходимо открыть построенную схему: Implementation -> Open implemented design. Откроется следующее окно:

../.pic/Introduction/Implementation%20steps/fig_05.png

Рисунок 5. Окно просмотра реализованного устройства.

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

Нас интересует "бледно-голубая точка", расположенная в нижнем левом углу прямоугольника X0Y1 (выделено красным). Если отмасштабировать эту зону, мы найдем используемый нами LUT:

../.pic/Introduction/Implementation%20steps/fig_06.png

Рисунок 6. Расположение выбранного LUT-а внутри ПЛИС.

Кроме того, если поиграться со свойствами этого примитива, мы сможем найти нашу таблицу истинности, инициализирующую этот примитив.

Bitstream generation

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

Итоги главы

Таким образом, маршрут перехода от HDL-описания устройства до его реализации в ПЛИС выглядит следующим образом:

  1. Сперва происходит этап предобработки (elaboration). В основные задачи этого этапа входит:
    1. развертывание иерархии модулей: преобразование иерархической структуры проекта в плоскую, что облегчает дальнейшие этапы обработки;
    2. проверка синтаксиса и семантики HDL-кода;
    3. разрешение параметров и констант;
    4. генерация промежуточного представления проекта, которое затем используется на следующих этапах. Полученное промежуточное представление проекта использует абстрактные элементы (логические вентили, мультиплексоры, регистры), которые не привязаны к конкретной ПЛИС.
  2. Затем выполняется этап синтеза (synthesis) нетлиста, который использует ресурсы конкретной ПЛИС. Все, использовавшиеся на предыдущем этапе структуры (регистры, мультиплексоры и прочие блоки) реализуются через примитивы ПЛИС (LUT-ы, D-триггеры, арифметические блоки и т.п.). Выполняется этап оптимизации логических сетей для минимизации занимаемой площади, уменьшения задержек и энергопотребления.
  3. После выполняется этап построения (implementation) конечной цифровой схемы, выполняющий несколько подэтапов:
    1. Размещение (Placement): определение конкретных местоположений для всех логических элементов в ПЛИС. Если на предыдущем этапе часть схемы была реализована через LUT, то на этом этапе решается какой именно LUT будет использован (имеется в виду не его тип, а какой из множества однотипных элементов будет выбран).
    2. Трассировка (Routing): создание соединений между элементами в соответствии с нетлистом.
    3. Временной анализ (Timing Analysis): Проверка временны́х характеристик для подтверждения, что все сигналы распространяются по цепи в допустимые временны́е рамки. Область допустимых решений для этапов "place & route" сужается путем наложения физических и временны́х ограничений (constraints).
  4. Последним этапом выполняется генерация двоичного файла конфигурации (bitstream generation), который во время прошивки сконфигурирует ПЛИС на реализацию построенной схемы.

Список источников

  1. Форум Xilinx: what exactly is 'elaborating' a design?