ECO Flow в Vivado или работа в режиме редактирования нетлиста

Аннотация


В статье рассмотрен режим работы Vivado, позволяющий вносить изменения в проект на уровне редактирования списка соединений (в дальнейшем – нетлиста). Описаны как сам режим ECO, так и некоторые нюансы, которые появляются во время работы в нём. Приведён демонстрационный пример и описана полная последовательность действий для получения результата, в работоспособности которой может убедиться каждый желающий. Статья будет полезна для «общего развития» FPGA-разработчикам, а особенно — тем, кто часто отлаживает проекты в Logic Analyzer. Надеюсь, работа в этом режиме вызовет интерес у разработчиков, работающих с большими кристаллами, время компиляции в которых может достигать часов (а то и десятков часов), поскольку в этом режиме время, затрачиваемое на имплементацию, при внесении изменений в нетлист может сократиться до буквально пары минут.




Оглавление



В статье очень много картинок не в спойлерах (140 штук). Пожалуйста, будьте внимательны, если заходите с телефона



Введение


Зачастую, когда мне приходится читать лекцию или вести семинар, я всегда стараюсь рассказать несколько больше, нежели предполагает программа. Так было на последних трёх семинарах, посвящённых работе с одноядерными Zynq-7000S. В этот раз было интересно посмотреть, насколько аудитория знает о некоторых «скрытых» режимах работы с Vivado. Вопрос был достаточно прост: «Кто-нибудь из присутствующих знает про режим ECO Flow?» Сразу за вопросом последовал, что называется, «лес рук, чему я особенно не удивился.

Желание несколько просветить разработчиков хотя бы о наличии этого режима в Vivado, не говоря уже о демонстрации работы в нём, появилась у меня очень давно. Но по какой-то загадочной причине, я «впрягся» в написание руководств по сборке проектов с использованием MicroBlaze и работе с ним. Однако, после недавних семинаров стало очевидно, что писать про ECO Flow всё таки нужно.

Цель статьи – дать общее представление о режиме ECO в среде Vivado [1], предоставляемой компанией Xilinx для своих кристаллов и показать на реальном примере работу в этом режиме, стараясь указать «тонкие» моменты и проанализировать его достоинства и недостатки.

Задачи, которые поставлены в этой статье:

  1. разработать тестовый пример, по возможности содержащий и демонстрирующий все (или хотя бы большинство) возможностей работы в режиме ECO;
  2. выполнить имплементацию проекта;
  3. пояснить понятие Design Checkpoint;
  4. описать переход в режим работы ECO;
  5. внести правки в нетлист и получить файл прошивки FPGA;
  6. убедиться в корректности внесенных изменений;
  7. составить сводную таблицу времени, затрачиваемого на стандартное внесение изменений в проект и сравнить его со временем, затрачиваемым в режиме ECO, а также инкрементной имплементации.

К сожалению, физически проверить методологию на достаточно «тяжёлом» кристалле (например, Virtex UltraScale) у меня возможности нет. Но, думаю, даже тот пример, который будет приведен – с проверкой на скромном Artix-7, установленном на плате Arty [2], окажется достаточно показательным. В процессе написания я буду опираться на несколько основных документов, в которых описан режим ECO [3], [4], [5]. Используемая версия Vivado (и, соответственно, документации) – 2017.4.

Небольшое отступление: да в руководстве много картинок и «банальщины» о том, как создать проект, собрать процессорную систему на MicroBlaze, работе в IP Integrator, отладке и т.д. Если Вы опытны и просто хотите прочитать об ECO – пожалуйста, перейдите сразу к главе 4: «Переход в режим ECO». Если же Вы не знаете, как собирать проект на MicroBlaze, ни разу не работали в IP Integrator, или любите руководства в стиле пошаговых иллюстраций – буду только рад, если Вы уделите дополнительные 75-90 минут представленному материалу. И, всё же, я надеюсь, что кто-нибудь выполнит руководство полностью, с проверкой в железе.

1. ECO: краткий обзор


ECO – Engineering change orders [6] («порядок внесения инженерных изменений») – это режим, в котором возможно внести изменения в нетлист, синтезированного или имплементированного проекта с минимальным влиянием на исходный нетлист. В Vivado имеется режим ECO, в котором возможно изменять так называемые Design Checkpoint проекта(см. далее), имплементировать внесённые изменения, выполнять генерацию необходимых отчётов для изменённого нетлиста и генерировать по нему файл прошивки FPGA.

Наиболее типичное применение данного режима:

  • Изменение пробников и подключаемых линий логических анализаторов (ILA – IntegratedLogicAnalyzer) при отладке проекта. Пользователь может изменить набор подключаемых к ILA линий, избежав при этом полной повторной имплементации проекта.
  • Переназначение цепей, подключенных к ножкам ПЛИС. В случае, если разработчиком проекта, схемотехником или разработчиком печатной платы была допущена ошибка в назначении ножек (например, rx перепутан с tx), а проект на FPGA уже был имплементирован, таким способом можно выполнить переназначение портов в нетлисте, избежав повторной полной имплементации (т. е. синтеза, мапинга, оптимизации, размещения, трассировки – со всеми сопутствующими затратами машинного времени и ресурсов) проекта.
  • Выполнение анализа «Что_если?» (редактирование содержимого памяти, изменение функционала LUT, улучшение таймингов и т.д.)

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

Примечание: работа в режиме ECO возможна только с Design Сheckpoint.

2.  Design Сheckpoint


Маршрут проектирования делится на несколько составных частей:, включая синтез и имплементацию. Имплементация в свою очередь делится на подэтапы: различные оптимизации, размещение и трассировку. Промежуточные этапы маршрута проектирования при этом сохраняются в некий «контейнер», который называется Design Checkpoint (DCP) [7]. Это файл, имеющий расширение «.dcp». Design Checkpoint содержит:



  • Текущий нетлист (в зависимости от этапа маршрута проектирования), включая все оптимизации, выполненные до записи dcp-файла.
  •  Ограничения, наложенные на проект (design constraints).

По умолчанию, Vivado создаёт четыре файла dcp: один – на этапе синтеза модуля верхнего уровня проекта (если выполняется синтез в режиме out-of-context, то для всех модулей, которые синтезируются в out-of-context, создается свой файл dcp) и три – на этапе имплементации. Эти файлы можно найти в папках:



«Название_проекта.runs/название_синтеза/название_топ_модуля.dcp»

 «Название_проекта.runs/название_импелементации/».

На рис. 1 показан пример расположения и файлы .dcp, которые создаются по умолчанию для некоторого абстрактного проекта.

1_0

1_1
Рисунок  1 – создаваемые по умолчанию dcp-файлы (1 – «постсинтез-» и 3 – «постимплемент-»: после оптимизации (_opt), после размещения (_placed) и после трассировки (_routed))



В проектном режиме работы с Vivado (Project Mode [8]) файлы .dcp создаются автоматически. Но при работе в непроектном режиме (Non-ProjectMode [8]) пользователь сам должен следить за тем, чтобы «снимки» текущего состояния проекта записывались. Для этого следует использовать соответствующие Tcl команды [9, 10].:



write_checkpoint <file_name>.dcp
read_checkpoint <file_name>.dcp

О том, зачем, как и какие файлы dcp следует открывать, будет рассказано далее.



3. Разработка тестового проекта


Чтобы продемонстрировать на тестовом проекте возможности ECO, он должен содержать следующее:

  1. Элементы, которых нет в исходном нетлисте или элементы, у которых можно изменять функционал. Например, завести светодиод на кнопку – и в исходном проекте он будет загораться по нажатию, а в измененном – по нажатию он будет гаснуть. То есть нужно будет добавить в нетлист инвертор, которого нет в исходном проекте.
  2. Элементы, у которых можно менять содержимое. Например, таблица истинности в LUT или содержимое блочной памяти. Причём изменение содержимого блочной памяти тут будет более предпочтительным, поскольку изменение LUT мы уже выполним в п.1, когда будем создавать дополнительный инвертор.
  3. ILA – для возможности замены подключённых цепей на другие цепи. То есть, не трогая сам ILA, мы попробуемчерез нетлист заменить подключенные к нему выбранные в исходном проекте цепи на другие.
  4. Перепутанные выводы. Предположим, что при проектировании печатной платы ее разработчик выполнил pin-swap двух выводов для удобства разводки, не согласовав это с разработчиком FPGA, т.е. внёс ошибку путаницы rx с tx UART. В режиме ECO мы должны будем восстановить правильность подключения.

3.1. Создание проекта


Находим иконку Vivado и кликаем по ней два раза, откроется окно приветствия (рис. 2)



2
Рисунок 2 – Окно приветствия Vivado

Для создания нового проекта нажимаем кнопку Create Project. Нажатие кнопки вызывает мастер создания нового проекта. После его появления нажимаем кнопку Next (рис. 3).



3
Рисунок 3 – Окно мастера создания нового проекта


Вводим название проекта, в поле Project Name пишем «eco_flow». Указываем, где будет располагаться проект: в поле Project location укажите директорию с проектом. У меня она будет «F:/Projects/FPGA-Systems/eco_flow/projects/vivado». Если установить галочку «create project subdirectory» – будет создана дополнительная папка с именем проекта. Нажимаем Next (рис. 4).



 4
Рисунок 4 – Ввод имени проекта и его расположения



Создаем мы обыкновенный проект, поэтому просто выбираем тип проекта RTL. На текущем этапе не будем добавлять какие-либо файлы в проект, поэтому поставим галку “Do not specify sources at this time” и нажимаем Next (рис. 5).



5
Рисунок 5 – Выбор типа создаваемого проекта



Работать мы будем с платой Arty [2], поэтому выберем кристалл, который на ней установлен: xc7a35tcsg324-1. Нажимаем Next (рис. 6).
Примечание: я специально не выбираю готовую плату из шаблона доступных плат. Это сделано для того, чтобы можно было вручную делать ошибки, которые потом мы будем исправлять.



6
Рисунок  6 – Выбор кристалла xc7a35tcsg324-1



Заключительным в мастере настройки нового проекта будет окно Summary создаваемого проекта. Нажимаем Finish (рис. 7).



 7
Рисунок 7 – Окно кратких сведений создаваемого проекта



3.2. Создание и добавление HDL файлов в проект


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

Для создания и добавления нового файла в проект воспользуемся мастером, который вызывается по нажатию на синий плюс (рис. 8).



 8
Рисунок 8 – Вызов мастера создания добавления файлов в проект


В появившемся окне выбираем «Add or create design sources» и кликаем Next (рис. 9).

9
Рисунок 9 – Выбор типа, создаваемого или добавляемого файла

Выбираем Create file, после чего в появившемся окне в поле File name вводим имя создаваемого файла flash_led, нажимаем ОК (рис. 10).



10
Рисунок 10 – Создание нового файла и ввод его имени



После этого фал появится в списке добавляемых. Нажимаем Finish (рис. 11)



11
Рисунок 11 – Список добавляемых или создаваемых файлов



Теперь появился мастер создания шаблона для файла. Поскольку я использую VHDL, то я могу изменить имя архитектуры на rtl. Создаем два пина нашего модуля: iclk с направлением «in» (тактовый сигнал нашего модуля) и oled с направлением «out» (выход, подключаемый к светодиоду). Нажимаем ОК (рис. 12).



 12
Рисунок 12 – Мастер создания шаблона модуля (для VHDL)

Теперь наш модуль находится в дереве проекта (рис. 13).



13
Рисунок 13 – Созданный модуль flash_led



Модуль должен выполнять простую функцию: просто моргать светодиодом с периодом в 1 секунду. Забегая вперед, скажу, что тактовая частота нашего проекта будет равна 100 МГц, а сам модуль Вам ещё пригодится при выполнении домашнего задания.
Замените содержимое файла на следующее (листинг 1 (текстовый варинт листинга 1 см. в приложении А)). Код достаточно прост, и не требует дополнительных комментариев для пояснения его работы.



л1
Листинг 1 – Код модуля flash_led

Теперь создайте новый модуль, который должен называться brom_reader, его порты iclk с направлением «in», и odout[7:0] с направлением «out» (повторите действия с рис. 8 по рис. 12).
Если все сделано правильно, то в дереве проекта должен появиться модуль brom_reader (рис. 14).



14
Рисунок   14 – Модуль brom_reader в дереве проекта

Замените содержимое модуля следующим текстом (листинг 2 (текстовый вариант листинга 2 см. в приложении Б)). Здесь потребуется несколько комментариев:



  1. Строка 13: типу std_logic_vector создается псевдоним. Те, кто работает с VHDL часто используют тип данных «std_logic_vector()». Чтобы каждый раз не писать эти длинные названия, можно объявить псевдоним (alias) и потом использовать его на протяжении всего кода модуля.
  2. Строки 14-20: стандартное объявление двухмерного массива натуральных чисел и инициализация массива (создается память с числами).
  3. Строка 22: использование псевдонима (alias) slv для объявления сигнала
  4. Строки 23-24: использование атрибута синтеза [11]. Для чего он здесь прописан? Мы создали достаточно маленький двумерный массив (строки 15-20) – и, вероятнее всего, во время синтеза он будет оптимизирован и реализован в виде распределённой памяти на LUT. А так как мы хотим разместить массив именно в блочной памяти (BRAM- Block RAM), нам необходимо об этом в явном виде сказать синтезатору, что и делается с помощью атрибутов синтеза. Подробнее о них читайте в руководстве по синтезу Vivado в [11].

В остальном все должно быть понятно: мы создали ROM-память, из которой непрерывно, последовательно и циклически считывается ее содержимое.




л2
Листинг   2 – код модуля brom_reader

3.3.  Создание проекта MicroBlaze и работа в IP Integrator


Теперь мы создадим проект с MicroBlaze. Еще раз обращу Ваше внимание на то, что есть пошаговое руководство на русском по созданию проектов на софт‑процессоре MicroBlaze для новичков [16].
Для создания блочного проекта необходимо создать Block Design. Выбираем Create Block Design, вводим имя system и нажимаем ОК (рис. 15).



 15
Рисунок   15 – Создание нового Block Design и задание его имени


На поле Diagram добавляются ядра из IP каталога Vivado, либо RTL модули, написанные на VHDL/Verilog/SystemVerilog. Найдем в IP каталоге модуль MicroBlaze, для этого нажмите синий крестик и в поле поиска введите “MicroBlaze” и выберите его (рис. 16).

 16

Рисунок   16 – Добавление IP ядра MicroBlaze на рабочее поле Diagram

После добавления MicroBlaze на рабочее поле, воспользуемся экспресс настройками софт-процессора. Выберите Run Block Automation и выставите настройки в соответствии с рис. 17. Нажмите ОК.



17
Рисунок   17 – Экспресс настройки MicroBlaze




После этого на рабочем поле Diagram появятся несколько новых IP ядер, включая генератор тактовых частот и локальную память процессора [11, 12]. Нажмите кнопку Regenerate для оптимизации рабочего поля (рис. 18).

 
Рисунок   18 – Базовое включение MicroBlaze

Выполним настройку некоторых модулей в соответствии с нашей платой Arty. Настроим модуль генерирования сетки тактовых частот clk_wiz_1. Для вызова настроек модуля кликаем по нему два раза левой кнопкой мышки. В окне настроек устанавливаем значение входной тактовой частоты 100МГц, поскольку именно генератор на 100МГц установлен на плате [12]. Также устанавливаем тип источника как однополярный (рис. 19). Перейдём во вкладку Output Clocks, где настроим выходные частоты модуля.





Рисунок   19 – Настройка параметров входной частоты


Во вкладке Output Clocks мы зададим только одну частоту, основную частоту нашей процессорной системы и остальных модулей. Установим её равной 100МГц (рис. 20).

 
Рисунок   20 – настройка параметров выходной частоты



Прокрутите вниз для настройки дополнительных служебных сигналов.  Мы уберем сигнал сброса Reset, который не будем использовать. Снимите с него галочку (рис. 21). Остальные настройки нам не нужны, нажимаем ОК.




Рисунок   21 – Настройка служебных сигналов

Теперь объявляем вход clk_in1 модуля clk_wiz_1 внешним, фактически делаем из него input нашего Block Design. Для этого нажимаем на clk_in1 правой кнопкой мыши и выбираем Make External (рис. 22).



 
Рисунок   22 – Делаем порт clk_in1 внешним



Как видим появился порт clk_in1_0 (рис. 23).



 
Рисунок   23 – Входной порт clk_in1_0



В модуле управления сбросом нашей процессорной системы подключим два неиспользуемых входа (внешний сброс и дополнительный сброс) к неактивному логическому уровню «1». Сделаем это с помощью IP блока, который называется constant. Для этого щелкнем синем крестике вверху, затем в строке поиска вводим «const»» и выберем модуль constant.



 
Рисунок   24 – Поиск IP блока constant  в списке доступных IP

Выполним настройку модуля xlconstant_0, щелкнув по нему дважды левой кнопкой мыши. В строке значение (Const val ) вводим 1, в строке ширина (Const Width) вводим 1, нажимаем ОК (рис. 25)




Рисунок   25 – Настройка модуля xlconstant_0

Выполним подключение выхода dout модуля xlconstant_0 ко входам ext_reset_in и aux_reset_in модуля rst_clk_wiz_1_100M. Просто соедините эти порты мышкой (рис. 26).




Рисунок   26 – Подключение неиспользуемых портов к константе


Добавим модуль UART, найдя его в каталоге доступных IP ядер (рис.27).


Рисунок   27 – Поиск модуля UART в списке доступных IP блоков

Выполним настройку модуля axi_uartlite_0, установив настройки передачи в соответствии с рис. 28. Затем нажимаем ОК.




Рисунок   28 – Настройки модуля axi_uartlite_0

Теперь подключим модуль axi_uartlite_0 к процессору. Воспользуемся для этого автоматизированным методом. Нажимаем в верху Run Connection Automation и выбираем что к чему подключить (AXI вход UART к AXI MicroBlaze) рис. 29.



 
Рисунок   29 – Подключение axi_uartlite_0 к процессору

Интерфейс UART является стандартным и выделен в отдельный тип интерфейсов в Vivado IP Integrator. В пункте 2 на рис. 29 мы сказали, что хотим сделать rx и tx модуля axi_uartlite_0 внешними. Если вы раскроете интерфейс, то увидите это. Не смущайтесь, что в интерфейсе всего один синий провод, позже, когда создадим HDL обертку проекта, вы увидите, что там два порта (rx и tx).
Нажмите кнопку Regenerate Layout. После этого рабочее поле Block Design будет оптимизировано и схема примет ид, как на рис. 30. Убедитесь, что вы корректно выполнили подключение. Если на этом этапе все нормально, продолжим, если есть ошибки, выполните построение процессорной системы заново.




Рисунок   30 – Промежуточный этап сборки процессорной системы.


Давайте добавим еще один модуль на шину AXI. Это будет модуль GPIO, выход которого мы подключим на светодиод. Найдите в списке доступных IP блоков модуль AXI GPIO и добавьте его на рабочее поле (рис. 31).


Рисунок   31 – Модуль AXI GPIO в списке доступных IP

Выполните настройку модуля, в соответствии с рис. 32 (использовать будем только один канал и один выход).



 
Рисунок   32 – Настройки модуля axi_gpio_0

Выполним подключение модуля axi_gpio_0 к процессору и сделаем выход внешним. Нажмите Run Connection Automation и поставьте все галочки (рис.33).



 
Рисунок   33 – Подключение axi_gpio_0 к процессору 

Нажмите кнопку Regenerate Layout и убедитесь, что все подключения соответствуют рис. 34.




Рисунок   34 – Сборка процессорной cистемы.


Теперь добавим модуль отладки ILA. Предположим, что мы хотим просмотреть транзакции на шине AXI Lite для модуля UART. Находим в списке доступных IP модуль ILA (Integrated Logic Analyzer) рис. 35.

 
Рисунок   35 – IP блок ILA в списке доступных

Здесь мы используем некоторое подобие так называемого HDL Insertion Flow, когда модули отладки мы добавляем непосредственно в HDL код. Напомню, что выполнять поиск цепей для отладки вы можете и в нетлисте после синтеза. Такой подход называется Netlist Insertion Flow.
Так как мы хотим отлаживать AXI транзакции, то должны настроить тип пробника ILA как AXI (параметр Monitor Type в блоке ILA). Этот режим установлен по умолчанию, поэтому просто подключаем вход SLOT_0_AXI блока ila_0 к шине AXI, транзакции на которой мы хотим просмотреть. В нашем случае это шина, идущая от интерконнекта до модуля axi_uart_0 (рис. 36). Также подключаем тактовый сигнал для модуля к clk нашей системы и нажимаем кнопку Regenerate Layout.



 
Рисунок   36 – Подключение ila_0 к AXI шине axi_uart_0

По умолчанию длина записываемых данных установлена 1024, что вполне достаточно для просмотра транзакции.
Теперь добавим наши RTL модули в Block Design, для этого выберите модуль flash_led, нажмите правой кнопкой мыши и затем выберите Add Module to Block Design (это работает только в Vivado не ниже версии 2017.1).




Рисунок   37 – добавление модуля flash_led на поле Diagram

Подключите вход iclk модуля flash_led_0 к тактовому сигналу нашей системы, а порт oled сделайте внешним (правой кнопкой мыши по порту, затем выберите Make Exernal). Нажимаем кнопку Regenerate Layout.



Если всё сделано корректно, то должно получиться как на рис. 38.



 
Рисунок   38 – Промежуточный этап построения проекта в IP Integrator

Повторите аналогичные действия, для добавления модуля brom_reader. Подключите его тактовый вход iclk к тактовой цепи, НО не объявляете выход odout[7:0] внешним. Нажмите Regenerate Layout. Если все сделано корректно, то должно получиться как на рис. 39.




Рисунок   39 – Промежуточный этап построения проекта в IP Integrator

Теперь, добавим еще один ILA и подключим его к выходу odout[7:0] модуля brom_reader_0.
Находим IP блок ILA в списке доступных (рис. 35) и добавляем на поле Diagram. Выполним его настройку, дважды щелкнув по нему мышкой. Устанавливаем Monitor Type в значение Native (отлаживаем мы не шину AXI, а простые цепи). Остальное оставим по умолчанию. Перейдите во вкладку Probe_Ports(0..7) рис. 40.




Рисунок   40 – Настройка ILA


Настроим ширину пробника. Установим значение ширины равное 8 (рис. 41), поскольку именно 8 бит – ширина шины выхода odout[7:0] модуля brom_reader_0. Нажимаем ОК.

 
Рисунок   41 – Настройка ширины пробников ILA

Подключите выход odout[7:0] модуля brom_reader_0 ко входу probe_0[7:0] модуля ila_1, а вход clk модуля ila_1 подключите к тактовой цепи нашего проекта. Нажмите кнопку Regenerate Layout, и, если все корректно, должно получиться как на рис. 42.



 
Рисунок   42 –Промежуточный этап построения проекта в IP Integarator

Осталось только добавить кнопку и напрямую с ней включенный светодиод.
Создадим порт ввода, нажав на пустом месте нашей блок схемы правой кнопкой мыши и выбрав Create Port (рис. 43).



 
Рисунок   43 – Создание порта в IP Integrator



После появления мастера настроек порта, введите его имя ibtn, укажите направление input и, если нужно разрядность. Нажмите OK (рис. 44).





Рисунок     44 – Мастер настройки нового порта  (кнопка ibtn)

После этого порт появится на поле Diagram (рис. 45).



 
Рисунок   45 – созданный порт ibtn



Создайте еще один порт с названием obtn_led, направлением output (повторите действия на рис. 43-44).
Теперь просто соединяем порт ibtn c obtn_led, нажимаем Regenerate Layout. Должно получиться как на рис. 47.




Рисунок   46 – Собранный проект в IP Integrator

Проверим, что нет ошибок в текущем Block Design, нажав на кнопку Validate Design. Если все корректно, но Vivado выдаст соответствующее сообщение. Нажимаем OK и сохраняем текущий Block Design (рис. 48).



 
Рисунок   47 – Проверка корректности собранного проекта в IP Integrator

3.4.  Синтез и имплементация


Перейдите во вкладку Sources, нажмите на system правой кнопкой мыши и выберите пункт Create HDL Wrapper (создать HDL обертку нашего Block Design) рис. 48.

 
Рисунок   48 – Создание обертки проекта



После этого Vivado предложит либо обновлять вручную HDL обертку при внесении изменений в Block Design, либо делать это автоматически. Оставляем автоматическое обновление и нажимаем OK (рис. 49).



 
Рисунок   49 – Варианты обновления HDL обертки



Теперь укажем, что модуль system_wrapper является Top-модулем. Нажимаем правой кнопкой мыши на system_wrapper и выбираем Set as Top (рис. 50).

 
Рисунок   50 – Делаем модуль system_wrapper топ модулем



Теперь выполним синтез модуля system_wrapper, нажав на кнопку Run Synthesis (рис. 51).



Примечание: для подключения блоков отладки мы использовали фактически HDL Insertion Flow [4], то есть фактически вставляли в код наши ILA блоки и подключали к ним цепи. Нет никакой разницы как вы создаете и подключаете цепи для отладки: через HDL или Netlist. В конечном итоге ECO работает именно с синтезированным или имплементированным нетлистом, который хранится в Design Checkpoint.

 
Рисунок   51 – Запуск синтеза проекта

После нажатия на кнопку Run Synthesis нажмите ОК и дождитесь окончания синтеза.



После окончания синтеза будет выведено окно, в котором будет предложено запустить имплементация, открыть результаты синтеза или посмотреть отчеты. Откройте результаты синтеза (рис. 52).

 
Рисунок   52 – Открытие результатов синтеза



Теперь подключим ножки в нашем проекте. Делается это с помощью Pin Planer. Чтобы его открыть нажмите Window→I/O Ports (рис. 54).

 
Рисунок   53 – Открытия окна для назначения пинов



Используя Reference Manual [12] для Arty назначим ножки (рис. 54).



БУДЬТЕ ВНИМАТЕЛЬНЫ!!! Я специально перепутал ножки для rx и tx модуля UART!

Назначьте ножки в соответствии с рис. 54.


Рисунок   54 – Назначение ножек проекта (rx и tx UART перепутаны специально)



Нажмите кнопку сохранить, после чего Vivado укажет, что вы не создавали файл проектных ограничений, и предложит его создать Введите имя файла constr и нажмите ОК (рис. 55).



 
Рисунок   55 – Создание файла проектных ограничений



Теперь мы можем выполнить имплементацию нашего проекта и сгенерировать файл прошивки. Нажмите на кнопку Generate Bistream, затем OK и дождитесь окончания процедуры (рис. 56).



 
Рисунок   56 – Расположение кнопки Generate Bitstream



После окончания генерации Bitstream появится окно дальнейших действий. Нажмите Cancel (рис. 57)



 
Рисунок   57 – Окно дальнейших действий после создания битсрима



3.5.  Написание программы для MicroBlaze


Теперь выполним разработку ПО для MicroBlaze. Это выполняется в среде Xilinx Software Development Kit (SDK). Для того чтобы сообщить SDK информацию о собранной процессорной системе (IP ядрах, их адресации на шине AXI) необходимо выполнить экспорт в SDK. Это делается с помощь File → Export → Export Hardware (рис. 58).





Рисунок   58 – Экспорт информации о процессорной системе в SDK



В появившемся окне не устанавливайте галочку Include Bitstream. Отставьте параметры по умолчанию и нажмите OK (рис. 59).

 



Рисунок   59 – Окно параметров экспорта



Теперь запустим SDK. Для этого выберите File → Launch SDK



 
Рисунок   60 – запуск SDK



Дождитесь окончания выполнения служебных операций SDK. После их окончания можем приступить к созданию нового проекта. Выбираем File → New → Application Project.


Рисунок   61 – Создание нового проекта в SDK



Введите название нового проекта MB_run, нажмите Next (рис. 62)




Рисунок   62 – Настройка нового проекта



В окне готовых шаблонов выберите создание приложения Hello World и нажмите Finish (рис. 63)

 
Рисунок   63 – Выбор шаблона создаваемого проекта



Откройте файл helloworld.c (расположение которого показано на рис. 64) и замените его содержимое кодом программы, показанным в листинге 3, и сохраните результат.

 
Рисунок   64 – Расположение файла helloworld.c


Листинг   3 – Заменённое содержимое файла helloworld.c
 



Программа отсылает «Hello World: cycle» примерно 1 раз в 2 секунды и моргает светодиодом LD1 (красной компонентой) также приблизительно 1 раз в 2 секунды.

3.6.  Запуск программы и отладка


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



  1. Светодиод LD4 загорается по нажатию кнопки BTN0
  2. Процессор работает и шлет «Hello World: cycle» в консоль, однако мы не видим слов в консоли, поскольку выполнили неверное подключение rx и tx. Дополнительным сигнализатором работы процессора, является мигающий светодиод LD1.
  3. Выполняются транзакции по интерфейсу AXI-Lite от процессора до UART
  4. Блочная память считывается и выдает корректные значения.

Для начала подключаем Arty к компьютеру. Выполним настройку терминала, в котором должны будут показываться сообщения UART. Это можно сделать стандартными средствами SDK. В SDK имеется терминал, расположенный внизу (рис. 65).
Если терминала нет, то его можно найти в Window → Show View → Others → Xilinx → SDK Terminal (рис. 65).



 
Рисунок   65 – Открытие встроенного терминала в SDK

Установите настройки терминала, в соответствии с рис. 66. Номер COM-порта может отличаться.



 
Рисунок   66 – Настройка SDK терминала

Теперь перейдём в Vivado и выполним программирование FPGA.



Нажимаем Open Hardware Manager и переходим в режим программирования и отладки. Выбираем Open Target и нажимаем автоматическое подсоединение Auto Connect (рис. 67)

 
Рисунок   67 – Открытие Hardware Manager и подключение в ПЛИС



Выбираем из списка наш кристалл и заливаем в него прошивку (рис. 68)





Рисунок   68 – Программирование ПЛИС



В появившемся диалоговом окне следует проверить, что выбраны нужные файлы прошивки .bit и список подключаемых цепей .ltx (рис.69) и затем нажать кнопку Program.



 
Рисунок   69 – Выбор файла прошивки и списка цепей

Если все сделано корректно, светодиод LD4 должен мигать, а по нажатию на кнопку BTN0 должен загораться светодиод LD4.
Теперь заставим процессор выполнять нашу программу по отправке данных в UART и мигать красной компонентой светодиода LD1.
Переключаемся обратно в SDK, кликаем правой кнопкой по нашему проекту MB_run, после чего выбираем Run As → Launch on Hardware (System Debugger), как показано на рис. 70. Мы заливаем сразу релиз программы, и не будем выполнять пошаговую отладку.



 
Рисунок   70 – Запуск исполняемой программы процессора

Подождав несколько секунд, видим, что светодиод LD1 мигает, но сообщения в SDK Terminal не появляются, поскольку rx и tx перепутаны.
Теперь давайте убедимся в том, что, транзакции до модуля UART всё-таки доходят.
Переключаемся в Vivado. Надеюсь, что вы помните, что мы подключали два ILA: один на транзакцию по шине AXI для UART, а второй к выходу блочной памяти. Но у нас в списке оказалось три ILA (рис. 71).



 
Рисунок   71 – Окно подключенных ILA



К сожалению, в результате оптимизации и синтеза, такое может случаться и это следует учитывать, как и то, что в результате оптимизации и различных настроек синтеза могут меняться названия цепей. Тем не менее, мы сможем провести отладку и убедиться, что транзакции до модуля UART все-таки доходят. Но нам нужно их «поймать», то есть настроить триггер, условие, по которому начнется запись состояния линий, к которым подключен ILA.
Дважды кликните мышкой по hw_ila_1 чтобы открыть окно временных диаграмм и окна настроек.
В окне Trigger Setup установите, триггер. Нажмите на синий крестик и в списке доступных цепей выберите цепь, показанную на рис. 72. И нажмите OK.



 
Рисунок   72 – Выбор триггера для запуска ILA

Примечание: мы работаем с интерфейсом AXI-Lite, в котором достаточно много сигналов.  Не буду расписывать здесь функции, которые возложены на те или иные сигналы интерфейса. Если Вам интересно подробнее узнать об этом, пожалуйста, обратитесь к руководству [14].

Теперь установим параметры срабатывания триггера. Поскольку активный уровень сигала RVALID это «1», то скажем, что необходимо начать запись, когда этот сигнал будет равен «1». Установите значение срабатывания, как показано на рис. 73. И затем нажмите кнопку запуска триггера.




Рисунок   73 – Запись данных с ILA по срабатыванию триггера

Теперь, из этого гигантского списка цепей, которые имеются в интерфейсе найдем шину, которая отвечает за данные. Эта шина показана на рис. 74




Рисунок   74 – Цепь с данными на шине AXI-Lite



Измените отображаемое состояние шины на ASCII. Для этого кликните правой кнопкой мыши по шине, затем выберите Radix, затем ASCII (рис. 76).



 
Рисунок   75 – Выбор представления данных на шине *WDATA[31:0]

После этого мы увидим на шине часть нашего сообщения, которое отсылает процессор. Это частично подтверждает, что транзакция проходит корректно (рис. 76).



 
Рисунок   76 – ASCII представление данных на шине *WDATA[31:0]

Попробуйте повторить самостоятельно действия с рис.71 по рис. 76 для hw_ila_2, который подключён к выходу модуля чтения блочной памяти. Устанавливать триггер не требуется. Если всё будет сделано корректно, то картинка должна быть аналогичной рис. 77.



 
Рисунок   77 – данные, считываемые из блочной памяти

На этом сборка тестового проекта завершена, и мы можем приступить к редактированию нашего нетлиста и работе в режиме ECO.




4.  Переход в режим ECO


Как мы убедились выше:



  1. По нажатию кнопки BTN0 загорается светодиод
  2. Посылки по UART отправляются, но мы не видим их в консоли (специально перепутаны rx и tx)
  3. Содержимое блочной памяти считывается циклически и непрерывно (содержимое блочной памяти в ASCII:”author: KeisN13”).

Для исправления ошибок, редактирования нетлиста и изменения содержимого и функционала некоторых компонентов мы воспользуемся режимом ECO, который доступен при работе с Design Checkpoint (DCP). Мы будем использовать DCP, который получается после этапа трассировки кристалла (post route).
При каждом новом запуске синтеза или имплементации Vivado удаляет DCP, поэтому общей практикой работы с DCP является «копипаста» необходимой DCP в новую директорию.
Давайте создадим папку edited_dcp. Я создам ее рядом с папкой проекта (рис. 78)



 
Рисунок   78 – Создание папки для сохранения DCP

Скопируем необходимый для дальнейшей работы DCP файл, который расположен «папка_проекта/название_проекта.runs/название_имплементации/имя_топ_модуля_routed.dcp» (рис. 79) в папку edited_dcp.
 




Рисунок   79 – Расположение DCP

Переходим в Vivado и выполняем открытие DCP. Для этого нажмите File → Open Checkpoint (рис. 80)



 
Рисунок   80 – Открытие DCP

Выберите расположение скопированного DCP файла в папке edited_dcp и нажмите OK.



После открытия DCP, по умолчанию, будет открыто представление (перспектива) Default или, та которую Вы использовали в последний раз, если открывали DCP (рис. 81).

 
Рисунок   81 – Перспектива Debug открытого DCP

Существует несколько режимов работы с DCP, однако сегодня нас интересует именно ECO Flow. Для перехода в режим ECO необходимо сменить представление. Для этого в верхнем правом углу из выпадающего списка выберите ECO (рис. 82).



 
Рисунок   82 – Переход в режим ECO



Как видим, графическое представление при переходе в режим ECO изменилось (рис. 83). Появились новые элементы управления и графического интерфейса, которые рассмотрим далее.



 
Рисунок   83 – Представление в режиме ECO

5.  ECO: описание интерфейса


Работа в режиме ECO имеет свой маршрут, который изображен на рис. 84. Вся работа выполняется в соответствующей DCP. После открытия DCP выполняются необходимые изменения в нетлисте, с помощью инструментов графического интерфейса и/или Tcl-команд. Изменения сохраняются, и, в зависимости от того уже размещен полностью проект в кристалле или нет, выполняются размещение и трассировка либо только трассировка. Затем выполняется разводка кристалла, после чего генерируются новые файлы прошивки (битстрима, .bit) и цепей логического анализатора (.ltx). Следующим шагом выполняется проверка внесенных изменений «в железе», и если всё нормально, то сделанные изменения вносятся в исходный проект. Если же нет – то внесение изменений в DCP может быть повторено.



 
Рисунок   84 – Маршрут проектирования в режиме ECO

Внесение изменений возможно с помощью инструментов графического интерфейса. Оригинал описания интерфейса вы можете найти в [3] в разделе Vivado ECO Flow.
Графическое представление в режиме ECO разбито на несколько секций, расположение и назначение которых идентично стандартному представлению, в котором выполняется основное проектирование.
С левой стороны экрана расположен ECO Navigator, который представляет инструменты для маршрута проектирования. ECO Navigator состоит из нескольких секций.
Секция Edit (рис. 85): предоставляет доступ к инструментам, необходимым для редактирования нетлиста



 
Рисунок   85 – Команды секции Edit

Create Net:  открывает диалоговое окно, предоставляющее доступ к созданию новых цепей. Цепи могут быть созданы для любого уровня иерархии, путём задания имени иерархии в названии цепи. Могут быть созданы шины. Если выбран pin или port, то цепь может быть автоматически подключена к ним, если установлена галка Connect selected pins and ports (рис. 86).




Рисунок   86 – Диалоговое окно Create Net

Create Cell: открывает диалоговое окно, позволяющее добавлять дополнительные компоненты в текущий нетлист. Также имеется возможность создавать ячейки в необходимом уровне иерархии. Можно создавать как библиотечные компоненты, которые доступны из списка, либо black box. Если вы создаёте LUT, то сразу можете редактировать функцию, которую она должна реализовывать (рис. 87).



 
Рисунок   87 – Диалоговое окно Create Cell



Create Port: вызывает мастер для добавления и настройки дополнительных портов в текущий нетлист. Возможно настроить несколько параметров: направление, ширину шины, стандарт напряжений и т.д. (рис. 88).



 
Рисунок   88 – Диалоговое окно Create Port



Create pin: выполняет добавление и настройку пинов в текущем нетлисте. Пин может быть создан для текущей ячейки (объектов типа cell) на любом уровне иерархии. Пин для иерархии верхнего уровня также может быть создан с помощью команды create_port. Пин не может быть создан, если не указана ячейка и название создаваемого пина (рис. 89).




Рисунок   89 – Диалоговое окно Create Pin

Connect Net: подключеняет цепь к выбранному пину или порту. Вызываемое диалоговое окно позволяет просмотреть список цепей или выполнить их поиск. Выбранная цепь будет подключена через все уровни иерархии, автоматически создавая необходимые пины.
Disconnect Net: отключает выбранную цепь, пин или порт от цепи. В случае, если выбран объект типа cell, то буду отключены все цепи, подключенные к нему.
Replace Debug Probes: позволяет вызвать диалоговое окно, в котором возможно выполнить редактирование портов ILA и/или VIO (Virtual Input Output), которые были созданы ранее, то есть отключить текущие цепи от и подключить новые.
Place Cell:  позволяет разместить объект типа cell в выбранных ресурсах кристалла.
Unplace Cell: убирает выбранный объект типа cell из текущего размещения.

Секция Run
Команды секции Run предоставляет доступ ко всем командам, необходимым для выполнения имплементации.




Check ECO: Выполняет запуск проверки ошибок (DRC – Design Rule Check)
Примечание: Vivado позволяет вносить в нетлист множество изменений, используя команды режима ECO. Однако, внесённые логические изменения, внесённые в проект, могут привести к неосуществимой физической имплементации. Запуск Check ECO следует делать перед тем как вы соберетесь выполнять имплементацию проекта, чтобы устранить ошибки на ранних этапах маршрута ECO Flow.

Optimize Logical Design: в некоторых случаях рекомендуется выполнять оптимизацию нетлиста с помощью команды opt_design и её соответствующих опций [9]. Optimize Logical Design позволяет вызвать диалоговое окно, позволяющее внести соответствующие аргументы Tcl команды opt_design, которые задаются в строке options.

Place Design: Выполняет инкрементное (т.е. основываясь на предыдущем нетлисте) размещение компонентов текущего нетлиста. Отчёт Incremental Placement Summary, который выводится в консоли в конце выполнения команды place_design позволяет просмотреть статистику переиспользования результаттов предыдущего размещения, которое было в оригинальном DCP до внесения изменений. Нажатие на Place Design вызывает окно, в котором могут быть заданы соответствующие опции команды place_design [9]. Подробнее об инкрементной имплементации см. в [3] в разделе Incremental Compile.

Optimize Physical Design: в некоторых случаях может потребоваться выполнить физическую оптимизацию (команда phys_opt_design [9]). Диалоговое окно, вызываемое по нажатию на Optimize Physical Design позволяет ввести соответствующие опции команды phys_opt_design.
Route Design: вызывает диалоговое окно, которое позволяет в зависимости от выбора выполнить инкрементную трассировку внесенных изменений в нетлист, трассировку выбранных пинов или цепей. В случае, если процент переизпользованных разъеденных цепей менее 75%, то будет выпалена обыкновенная трассировка нетлиста.
Подробнее об инкрементной имплементации см. в [3] в разделе Incremental Compile.

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

Секция Program
Инструменты этой секции позволяют сохранить внесённые изменения в новы DCP, создать файл прошивки ПЛИС, создать новый лист отлаживаемых через ILA цепей, в случае, если ILA подвергался изменениям, запрограммировать ПЛИС и выполнить отладку стандартными средствами Hardware Manager Vivado.
 

Вкладка Scratch
Позволяет отслеживать внесенные изменения в нетлист, включая просмотр и подключение незадействованных пинов, портов, цепей. Столбец подключений Con отслеживает статус подключения объектов, PnR отслеживает статус размещения и трассировки объектов.
Нажатием правок кнопкой мыши по вкладке Scratch будет вызвано меню дополнительных действий (рис. 90). Функционал, которые они выполняют, предполагаю интуитивно понятен исходя из их названий. Полный список и действия, которые выполняют команды приведен в [3] в разделе Vivado ECO Flow → Scratch Pad → Scratch Pad Pop-up Menu.




Рисунок   90 – Меню дополнительных действий вкладки Scratch

6.  Внесение изменений в проект


Теперь мы можем приступить к внесению изменений в наш проект. Первое, что мы сделаем, это установка нового компонента в нетлист. Сделаем так, чтобы светодиод LD0 светился, а при нажатии кнопки BTN0 гас. Иными словами, мы должны вставить в нетлист инвертор, которого нет.




6.1.  Создание новых элементов в нетлисте


Для начала, найдите на схеме порт ibtn, нажмите на него левой кнопкой мыши один раз, а затем нажмите F4. Таким образом, мы попробуем отфильтровать все лишнее в схематике, для повышения читаемости нетлиста.




Рисунок   91 – Открытие схематики для выбранного порта

В появившемся окне, мы увидим только один порт ibtn. Теперь отобразим всю цепь от порта ibtn до светодиода LD1 (порта obtn_led). Нажимаем правой кнопкой мыши по порту ibtn, затем Expand Cone и выбираем To Flops or I/Os. И нажимаем кнопку Regenerate Layout (рис. 92)



 
Рисунок   92 – Отображение конечных точек, к котором подключён порт

Теперь мы видим полный «тракт» от кнопки до светодиода (рис. 93).



 
Рисунок   93 – «Тракт» от порта ibtn до obtn_led

Следующий шаг, который необходимо сделать, это определиться, в каком месте разорвать цепь. Предлагаю разорвать её от модуля system_i до выходного буфера. Для этого выбираем цепь от пина obtn_led модуля system_i до пина I буфера obtn_led_OBUF_inst, затем в секции Edit выбираем Disconnect Net.




Рисунок   94 – Выбор разрываемой цепи

После нажатия на кнопку Regenerate Layout видим, что цепь была удалена (рис. 95)



 
Рисунок   95 – Схематика после разрыва цепи

Также обратите внимание на вкладку Scratch Pad, содержимое которой изменилось.
Теперь добавим на схему инвертор. Нажимаем в секции Edit строку Create Cell, вводим название компонента invertor, находим шаблон INV и нажимаем OK (рис. 96)






Рисунок   96 – Создание инвертора

Компонент INV будет добавлен на поле (рис. 97)




Рисунок   97 – Созданный инвертор на поле схематики

Как видите, инвертор реализован на LUT. Чтобы посмотреть логическую функцию, которую реализует LUT и при необходимости её изменить, Вы можете, нажав правой кнопкой мыши по компоненту, выбрать свойства Cell Properties, во вкладке Truth Table нажать Edit LUT Equation… (рис. 98).






Рисунок   98 – Свойство и таблица  логической функции LUT

Выполним соединение пинов. Выберите или на схематике, или во вкладке Scratch Pad пины I0 компонента invertor и obtn_led компонента system_i.




Рисунок   99 – Выбор соединяемых пинов

Теперь создадим между ними цепь. Для этого в секции Edit нажимаем кнопку Create Net. В диалоговом окне вводим имя создаваемой цепи btn_led, устанавливаем галочку автоматического соединения двух портов и нажимаем ОК (рис. 100)




Рисунок   100 – Параметры создаваемой цепи

После этого два пина буду автоматически соединены. Нажмите Regenerate Layout (рис. 101)



 
Рисунок   101 – Созданная цепь

Повторим действия для двух оставшихся пинов и соединим пин O компонента invertor с пином I компонента obtn_led_OBUF_inst. Цепь назовём по-другому, поскольку не может быть в проекте двух цепей с одинаковыми названиями на одном уровне иерархии. Назовём цепь btn_led_o. Результат подсоединения показан на рис. 102




Рисунок   102 – Схематика с созданным инвертором



Теперь сохраняем наш DCP и проверяем наличие ошибок, перед тем как запустить размещение и трассировку новых компонентов (рис. 103).



 
Рисунок   103 – Проверка ECO на ошибки

Из-за того, что мы не выполняли размещение и трассировку, появится ряд ошибок, говорящих об этом.

Теперь попробуем сгенерировать bit файл прошивки, и посмотреть, получилось ли внести изменения корректно. Для этого необходимо последовательно выполнить шаги из секции Run, не все разумеется и без каких-либо опций. Всё остаётся по умолчанию (рис. 104).

Нажимаем Place Design и не вводя никаких опций нажимаем OK. Дождавшись окончания выполнения операций нажимаем Route Design и выбрав Incremental Route в появившемся окне, нажимаем ОК.

После этого генерируем bit файл, нажав на Generate Bitstream. Убедитесь, что в поле пути к файлу указана папка edited_dcp. После этого открываем Hardware Manager для прошивки нашей ПЛИС.




Рисунок   104 – Последовательность действий для получения файла прошивки .bit

При открытии следует учесть, что должен будет открыться новый экземпляр Vivado. Но у меня он не открылся, поэтому я просто открываю новый экземпляр Vivado и запускаю Hardware Manager (рис. 105). При этом будьте внимательны, при выборе файла прошивки .bit и файла цепей для ILA .ltx



 
Рисунок   105 – Открытие Hardware Manager из начального окна Vivado



Выполните последовательность действий, согласно рис.67-69 и запрограммируйте ПЛИС. Обратите внимание, что прошивать мы будем файлом, расположенным в папке edited_dcp (рис. 106).



 
Рисунок   106 – Файл прошивки ПЛИС с изменённым нетлистом



Если всё было выполнено корректно, то светодиод LD0 должен будет светиться, а по нажатию кнопки BTN0 гаснуть.




6.2.  Изменение свойств/параметров компонентов


В режиме ECO также возможно изменять содержимое компонентов и менять их параметры. Сейчас мы попробуем изменить содержимое блочной памяти модуля brom_reader, которая имитирует у нас, например, коэффициенты фильтра. Давайте найдём нашу BROM и просмотрим её свойство INIT, отвечающее за начальную инициализацию.
Перейдите во вкладку Netlist и найдите блочную память в модуле brom_reader (рис.107).



 
Рисунок   107 – Расположение блочной памяти модуля brom_reader в нетлисте

Нажав правой кнопкой по этому компоненту, мы можем просмотреть его свойства, выбрав Cell Properties (рис. 108). Затем перейдите во вкладку Properties и пролистайте до свойств INIT.



 
Рисунок   108 – Свойства INIT выбранной блочной памяти


Как видите, в свойствах INIT хранятся значения инициализации блочной памяти. Среди множества этих свойств нас интересуют только INIT_00 и INIT_01. Если скопировать содержимое этих двух свойств конвертор HEX to ASCII [15], то получим надпись, которую мы видели на рис. 77, но записанную в обратном порядке (рис. 109)

 
Рисунок   109 – Содержимое свойств INIT в формате ASCII

Изменить свойство компонента можно, если щелкнуть на значок карандашика и вписать новые значения или же воспользовавшись Tcl консолью и командой set_ptoperty.
Замените значения свойств согласно рис.110 и сохраните результат:




INIT_00: 256'h0061006700700066002E00770077007700200020002000200020002000200020



INIT_01: 256'h0020002000200020002000750072002E0073006D00650074007300790073002D




Рисунок   110 – Изменённое содержимое свойств INIT в формате ASCII

Поскольку мы не добавляли новых компонентов в нетлист и не переподключали цепи, то нет необходимости выполнять размещение и трассировку. Просто нажимаем Generate Bitstream и ожидаем окончания выполнения операции. На всякий случай сгенерируйте файл .ltx, который мы подцепим во время отладки (рис. 111).




Рисунок   111 – Генерация битсрима и файла цепей для отладки

Перейдите в Hardware manager и запрограммируйте FPGA только что сгенерированным файлом .bit  и созданным списком пробников .ltx (рис.112). Обратите внимание, что файлы взяты из папки edit_dcp.




Рисунок   112 – Файлы прошивки измененного нетлиста и списка цепей

Выполните последовательность действий, описанных на рис. 73-75 (откройте hw_ila_2, переведите тип отображаемых значений в ASCII и просмотрите выводимую надпись). Если всё получилось корректно, то должна появиться надпись рис. 113. 



 
Рисунок   113 – Измененное содержимое блочной памяти

Как видите, если Вам необходимо изменить содержимое или параметры компонентов не только блочной памяти, а в принципе любых, не требующих вмешательств в размещение и трассировку, то делается это очень быстро и достаточно просто. Однако следует быть осторожным, когда Вы изменяете параметры, отвечающие за рабочую частоту, например, когда вносите изменения в настройки MMCM или PLL Вашего проекта. В этом случае, прежде чем создавать файл прошивки, убедитесь, что тайминги проекта сходятся, а соответствующие отчёты не выдают ошибок (Report Timing Summary и т.д.).




6.3.  Подключение других цепей к пробникам и ILA


Пожалуй, самым полезным вариантом использования ECO является замена в уже имплементированном проекте цепей, подключенных к пробникам. Единственным ограничением тут является то, что виртуальные пробники должны быть подключены все, ни один не должен «висеть в воздухе». Это ограничение обычно несущественно, так как неиспользуемые пробники всегда можно просто подключить к gnd или vcc.

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

Как мы знаем из описания интерфейса ECO, в нём есть инструмент, позволяющий выполнить переподключение пробников; им и воспользуемся.



Примечание: несмотря на наличие специального инструмента редактирования пробников и подключённых цепей, выполнять эти действия можно и «совсем вручную», подобно тому как мы делали при добавлении компонента: отсоединить одни цепи и подсоединить другие. Однако тут есть некоторые нюансы: по не совсем понятным причинам, иногда цепь от пина отсоединить не удается, нажатие на Disconnect Net не выполняется. В этом случае можно руками или через скрипты снять с цепи свойство «DONT_TOUCH» или отменить её трассировку (выполнить «unrote») – тогда Disconnect Net будет выполнятся. При работе мастером таких проблем не наблюдается.



Работа с мастером требует, чтобы мы знали название цепей, которые мы собираемся подключать. Давайте попробуем найти цепи, отвечающие за выставление адреса для блочной памяти в модуле brom_reader. Это можно сделать через схематическое представление или через сам нетлист. Поскольку модуль маленький, то просто просмотрим название через нетлист (рис. 114). Необходимая цепь называется cnt, это шина, шириной 5.



 
Рисунок   114 – Цепь адреса блочной памяти в нетлисте




Выбираем режим Replace Debug Probes. Поскольку наш счётчик считает только до 31, то требуется всего 5 линий, поэтому выбираем первые пять линий в ila_1, затем нажимаем правой кнопкой мыши и выбираем Edit Probes (рис. 115). Выбрать можно любой ila, но они достаточно громоздкие. Для руководства, выбраны попроще.



 
Рисунок   115 – Выбор заменяемых пробников

Выполняем поиск по списку цепей, введя название искомой цепи *cnt[*]. Пролистав вниз находим их, и нажимаем кнопку добавления в пробники. Обратите внимание, что мы одновременно заменяем 5 пробников, поэтому должно быть выбрано 5 цепей. Замена типа поиска с contains на match обусловлена правилами поиска в tcl и для текущего примера позволяет выводить более точный результат. При использовании поиска цепей рекомендуется хотя бы немного понимать и знать символы подстановки в языке Tcl и правила, по которым выполняется поиск.



 
Рисунок   116 – Поиск подключаемых к пробникам цепей

Оставшиеся три цепи мы подключим к «0», в Vivado цепи, подключенные к gnd называются const0. Выберем три оставшиеся цепи, правой кнопкой Edit Probes (рис.117).



 
Рисунок   117 – Доукомплектовывание ila_1

Вводим название в строку поиска *const0 и добавляем первую цепь из списка, три раза кликнув по стрелке, поскольку мы должны подключить три линии (рис. 118). БУДЬТЕ ВНИМАТЕЛЬНЫ!!! Не добавляете линии из иерархии Debug Hub (dbg_hub), в этом случае Vivado выдаст ошибку только в самом конце настройки пробников, и вся работа пойдет на смарку. Выберите цепь из любого IP, например, uartlite.



 
Рисунок   118 – Добавление константных цепей в пробники

Сформированный ila_1 должен выглядеть как на рис. 119. Еще раз отмечу, что не должно быть пустых пробников. В этом случае, будут ошибки на этапе DRC.



 
Рисунок   119 – Новые цепи в ila_1

После нажатия на кнопку OK появится окно, которое информирует, что атрибут DONT_TOUCH будет снят с цепей, ранее подключенных к пробникам ila_1. Это один из нюансов, которые я описал в примечании в начале этого раздела. Учитывайте это, в случае если вы не используете мастер подключений, а делаете всё вручную. Соглашаемся с изменениями, нажав Unset Property и сохраняем текущее состояние DCP.
Теперь последовательно выполняем размещение, трассировку, генерацию битсрима и создание списка цепей (рис.120). Все вместе занимает примерно 1 минуту на моем компьютере. Быстро, не правда ли!



 
Рисунок   120 – последовательность действий для создания файла прошивки списка цепей для отладки



Теперь открываем Hardware Manger и программируем наш кристалл, на забывая подключить правильные .bit и .ltx файлы.
Открыв hw_ila_2 мы увидим 4 сигнала: шину cnt и три const0. Измените вывод значений шины cnt с hex на unsigned decimal (по аналогии с рис. 75) и запустите запись, нажав синий треугольник. Если все корректно, то Вы увидите изменение на шине cnt с 0 по 31 с инкрементом 1 (рис.121).



 
Рисунок   121 – Значение шины cnt в пробнике ila_1

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



6.4.  Замена портов ввода/вывода


Последнее, что остаётся сделать это изменить rx и tx для модуля uart и наконец-то увидеть Hello World в консоли. Следует отметить, что при работе с ECO есть несколько путей, чтобы достичь результата. Я покажу один из них.

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

Сначала разберемся с входной цепью.

Выберите порт uart_rtl_0_rxd, подключенную у нем цепь и входной буфер. Нажмите unplace cell и затем удалить (рис. 122). После нажатия кнопки удалить, порт останется, просто выберите его и нажмите удалить еще один раз.



 
Рисунок   122 – Выбор входной цепи и детрассировка компонентов

Теперь восстановим удаленные компоненты. Первым делом добавим порт uart_rx, нажав на кнопку Create Port затем выставив настройки в соответствии с рис. 123



 
Рисунок   123 – Параметры создаваемого входного порта

Поскольку ножка A9 уже занята нашим выходным портом, Vivado выдаст сообщение, в которым предложит отменить привязку другого порта к ножке A9. Соглашаемся с этим (рис. 124)



 
Рисунок   124 – Окно информации об уже имеющемся подключении к выбранной ножке

Теперь создадим входной буфер. Нажмем кнопку Create Cell, название создаваемой ячейки ibuf_rx (рис. 125)




Рисунок   125 – Создание входного буфера



Теперь создаем цепь. Это делается в несколько этапов. Сначала цепь подключается к порту, а затем выполняется подключение к буферу. Выберите порт uart_rx и нажмите Create Net. Название цепи rx_net.




Рисунок   126 – Создание цепи

Теперь выберите цепь и порт I входного буфера и нажмите Connect Net. После этого появится цепь (рис. 127)



 
Рисунок   127 – Подключение порта и входного буфера

Теперь создадим цепь, межу буфером и модулем system_i. Выберите выходной пин O буфера и цепь uart_rtl_0_rxd, подключённую к system_i и нажмите Create Net



 
Рисунок   128 – Подключение буфера и цепи

Теперь повторим туже последовательность действий для выходного порта.
Выбираем выходной порт uart_rtl_0_txd, подключенную к нему цепь и выходной буфер. Нажимаем Unplace cell, а затем красный крестик, для удаления компонентов (рис. 122). После нажатия кнопки удалить, порт останется, просто выберите его и нажмите удалить еще один раз.



 
Рисунок   129 – Выбор выходной цепи

Создаём порт uart_tx, нажав кнопку Create Port, задаём направление Output и устанавливаем стандарт LVCMOS33 (рис. 130)



 

Рисунок   130 – Настройка параметров выходного порта



Создадим цепь tx_net, которая будет подключена к порту uart_tx. Выбираем uart_tx и нажимаем Create Net



 
Рисунок   131 – Создание цепи tx_net

Создаём выходной буфер tx_obuf, нажав Create Cell и выбрав тип OBUF (рис. 132)



 
Рисунок   132 – Создание выходного буфера

Выбираем пин O выходного буфера и цепь, подключённую к порту uart_tx и нажимаем Connect Net.



 
Рисунок   133 – Подключение буфера и порта

Теперь подключим буфер к system_i. Выбираем пин I буфера и цепь uart_rtl_0_txd (рис. 134).



 
Рисунок   134 – Подключение буфера и цепи

Сохраняем проект и выполняем последовательно размещение и трассировку. После этого запускам генерацию бистрима (рис. 135).



 
Рисунок   135 – Последовательно действий для получения файла прошивки

Откройте Hardware Manager и запрограммируйте ПЛИС. Перейдите в SDK и выполните запуск приложения (см. рис. 70). Если всё нормально, но в консоли вы увидите сообщение Hello World: cycle (рис. 136).



 
Рисунок   136 – Сообщения Hello World в SDK Terminal

7.  Сравнительный анализ


Проведем небольшой сравнительный анализ разных подходов к внесению изменений в проект по части времени, затрачиваемого на различные операции. Это сравнение будет, конечно, довольно условным, т.к. требуемое время сильно зависит от кристалла, от его загруженности, от количества свободных трассировочных ресурсов и т.д. Проделаны будут операции по изменению HDL файлов, после которого будет заново выполнен синтез, а также в «постсинтез-» нетлисте будут изменены пробники. Результаты приведены в таблице 1. Сразу следует отметить, что инкрементная имплементация даёт выигрыш только при работе с большими кристаллами, для которых получение конечного результата может занимать десяток часов. В учет тут не было включено время, затрачиваемое на внесение разработчиком изменений. Однако учтено то, что мы работали с IP Integrator – а он требует дополнительного времени на синтез IP и RTL.

Таблица 1. Сравнительный анализ времени, затрачиваемого на получение конечного файла прошивки в различных режимах.




Резюмируя сравнительны анализ, ещё раз скажу, что он очень поверхностный. Вы прекрасно понимаете, что FPGA – это сложная система и время того или иного этапа маршрута проектирования может изменяться очень сильно. Тем не менее, ECO не требует синтеза, и внесение небольших изменений в проект выполняется гораздо быстрее, особенно когда требуется сохранить трассировку кристалла и внести изменения только в содержимое компонентов.

8.  Заключение


Применял ли я когда-нибудь на практике режим ECO? Да. Стояла задача, не изменяя трассировку кристалла поменять содержимое блочной памяти, в которой были зашиты некоторые значения, позволяющие защитить продукцию от копирования. Там как раз и пригодился ECO; правда, по большей части работа была выполнена при помощи Tcl-скриптов, а не графического интерфейса. Тем не менее, режим ECO оказывается действительно полезен при работе с большими проектами – особенно в случае, если вы являетесь горячим фанатом внутрисхемной отладки с LogicAnalyser (ChipScope) и любите делать ILA на пару сотен (или даже тысяч) пробников. Возможно, Вы найдете работу в этом режиме полезной для Вас, если просто попробуете сделать что-то большее, чем описано в данном руководстве.
Если Вы нашли для ECO Flow интересное применение, или просто решили попробовать использовать его в своём проекте, оставьте небольшой комментарий: будет любопытно узнать для чего Вам в Vivado пригодился ECO Flow.
Не забудьте сделать и домашнее задание. Удачи!



9.  Домашнее задание


  1. Используя исправленный нетлист, измените логическую функцию LUT, которая реализует инвертор, чтобы она не инвертировала входной сигнал.
  2. Попробуйте запустить различные оптимизации с различными опциями в режиме ECO. Используйте для помощи гайд с Tcl командами Vivado [9].
  3. Просмотрите транзакции на шине AXI-lIte, которые идут к модулю GPIO. В режиме ECO замените цепи, которые подключены к ila_1 от модуля uartlite, на цепи от модуля gpio.
  4. *Попробуйте изменить частоты, которую вырабатывает MMCM, текущее значение 100МГц. Сделайте его 50 МГц. Если все сделано корректно, то светодиод должен мигать в два раза медленней. Не забудьте просмотреть отчет по таймингам, поскольку вы изменили модуль, отвечающий за частоту всего проекта.
  5. *Попробуйте создать Tcl команду или скрипт, который бы автоматически выполнял необходимую последовательность действий при изменении нетлиста в режиме ECO. Скрипт должен сохранять изменения, запускать размещение, трассировку, генерировать фал прошивки и т.д.
  6. **Напишите скрипт, который бы позволял изменять содержимое блочной памяти в блоке brom_reader, записывая любые 32 символа ASCII, вводимые в качестве аргументов разрабатываемой процедуры/скрипта

Библиографический список


1. Vivado на сайте Xilinx
2. Описание Arty Board на сайте Digilent
3. UG904 Vivado Design Suite User Guide: Implementation
4. UG908 Vivado Design Suite User Guide Programming and Debugging
5. UG986 Vivado Design Suite Tutorial: Implementation
6. Wiki: ECO
7. UG949 UltraFast Design Methodology Guide
8. UG892 Vivado Design Suite User Guide Design Flows Overview
9. UG835 Vivado Design Suite Tcl Command Reference Guide
10. UG894 Using Tcl Scripting
11. UG901 Vivado Design Suite User Guide Synthesis
12. Arty Reference Manual
13. UG908 Programming and Debugging
14. UG1037 Vivado Design Suite AXI Reference Guide
15. Hex-to-ASCII
16. Руководство: Разработка процессорной системы на базе софт-процессора MicroBlaze в среде Xilinx Vivado IDE/HLx



Приложение А. Листинг модуля flash_led

Скрытый текст
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;


entity flash_led is
    Port ( iclk : in STD_LOGIC;
           oled : out STD_LOGIC);
end flash_led;

architecture rtl of flash_led is
    signal cnt : natural range 0 to 100_000_001 := 0;
    signal led : std_logic := '0';
begin

    process(iclk)
    begin
        if rising_edge(iclk) then
            if cnt = 100_000_000 then
                cnt <= 0;
            else 
                cnt <= cnt + 1;
            end if;
            
            if cnt < 50_000_000 then
                led <= '0';
            else
                led <= '1';
            end if;
        end if;
    end process;
    
    oled <= led;
end rtl;


Приложение Б. Листинг модуля brom_reader

Скрытый текст
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity brom_reader is
    Port ( 
        iclk : in STD_LOGIC;
        odout : out std_logic_vector(7 downto 0)
        );
end brom_reader;

architecture rtl of brom_reader is
    alias slv is std_logic_vector;
    type rom_type is array (0 to 31) of natural;
    signal rom : rom_type := (
		32 ,  32,  32,  32,  97, 117, 116, 104,
		111, 114,  58,  32,  75, 101, 105, 115,
		78 ,  49,  51,  32,  32,  32,  32,  32,
		32 ,  32,  32,  32,  32,  32,  32,  32
	);
    signal cnt : natural range 0 to rom'length-1 := 0;
    signal dout: slv(odout'range) := (others => '0');
    attribute RAM_STYLE : string;
    attribute RAM_STYLE of rom : signal is "BLOCK";
begin
    
    process(iclk) 
    begin
        if rising_edge(iclk) then
           dout <= slv(to_unsigned(rom(cnt), dout'length)); 
           if cnt = (rom'length - 1) then
            cnt <= 0;
           else
            cnt <= cnt + 1;
           end if;
        end if; 
    end process;
    
    odout <= dout;
end rtl;


Приложение В. Листинг программы helloworld
Скрытый текст
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xgpio.h"

XGpio Gpio; /* The Instance of the GPIO Driver */

#define DELAY 10000000

int main()
{
    init_platform();

    int Status;
    volatile int Delay;
    int k = 0;
    /* Initialize the GPIO driver */
    Status = XGpio_Initialize(&Gpio, XPAR_GPIO_0_BASEADDR);
    if (Status != XST_SUCCESS) {
    	xil_printf("Gpio Initialization Failed\r\n");
    	return XST_FAILURE;
    }

    /* Loop forever blinking the LED */
	while (1) {
		/* Set the LED to High */
		XGpio_DiscreteWrite(&Gpio, 1, 1);
		xil_printf("Hello World: cycle %d\n\r", k);
		k++;
		/* Wait a small amount of time so the LED is visible */
		for (Delay = 0; Delay < DELAY; Delay++){};

		/* Clear the LED bit */
		XGpio_DiscreteWrite(&Gpio, 1, 0);

		/* Wait a small amount of time so the LED is visible */
		for (Delay = 0; Delay < DELAY; Delay++){};
    }

    cleanup_platform();
    return 0;
}



PS: Огромное спасибо пользователям intekus Des333, ishevchuk, roman-yanalov которые не только прочитали это 91 страничное руководство (именно столько оно занимает в Word), но и выполнили его и внесли необходимые редакторские правки.

Поделиться публикацией
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 14
  • –2
    Это мужик да? На картинке?
    • +1

      25 часов работы над статьей ради вопроса о гендерной пренадлежности персонажа с картинки. Спасибо, именно на такие комментарии я и рассчитывал :)

      • +1
        Спасибо за статью. Интересно было узнать о такой возможности у Xilinx. А как эта система реагирует на изменения значительно влияющие на архитектуру проекта? Переназначение какого нибудь глобального Enable например. расчет синхронизаций не сыпется?
        • +1

          Надеюсь, что материал был полезен.
          К вопросу таймингов при внесении изменений. Анализ будет выполнен заново. Если Вы откроете ЕСО то увидите, что там есть кнопка расчёта таймингов, однако принцип и последовательность создания отчётов идентична обыкновенным этапам проектирования. Я написал лишь про малую часть возможностей, на самом деле их больше. Я бы рекомендовал обратиться к соответствующим руководствам, ссылки на которые приведены в библиографическом списке. Если этого будет не достаточно, напишите мне, я попробую найти дополнительную информацию.


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

    • 0
      Это прямо целая подробная инструкция или мануал!
      Спасибо за качественный материал, буду вчитываться.
      • +1
        Ну я бы рекомендовал запастись чайком и терпением и, желательно, какой нибудь отладкой (любой, на на которой стоит кристалл от Xilinx не ниже 7-го семейства). Обязательно запустите Vivado, в ECO можно и без отладки работать. Вчитывание без практики в руководство даст только 65% пользы.
        Руководство действительно большое (91 страница), выполнение займет не менее часа, вероятнее даже в районе полутора.
        При работе в режиме ECO, пожалуйста, будьте крайне внимательны. Не всегда с первого раза очевидно, почему не удаляется цепь. Я про эти моменты указал в статье.
        При возникновении трудностей, обращайтесь.

        PS: Если всё-таки пройдете руководство полностью, напишите сколько времени это заняло? Спасибо.
        • 0
          Хорошо, попробую и отпишусь. Вам спасибо)
      • +1
        Потрясающее руководство по столь малоизвестной фиче Vivado. Огромная Вам благодарность.
        Расскажите, как Вы «вышли» на ECO flow? Это была необходимость или в каком-то мануале наткнулись на её описание и копнули глубже?
        • +1
          Спасибо. Надеюсь пригодится Вам при работе.
          Касательно литературы, я указал в начале статьи руководства, на которые опирался при написании:
          В процессе написания я буду опираться на несколько основных документов, в которых описан режим ECO [3], [4], [5]

          Ссылки на них в библиографическом списке кликабельны.

          Как я узнал про ECO. На предпоследнем месте работы нас направили на обучение в тренинг центр Xilinx (в РФ). Собственно там про это и упомянули/рассказали совсем не много. Но значение особо этому не придал. Ну режим как режим. Теперь на лекциях в этом самом тренинг центре я рассказываю про него всем достаточно подробно :)

          Где я его применял/использовал и использовал ли?
          Да, приходилось. В заключении я про это упомянул. Этот проект был с испанцами, которые как оказалось, про режим ECO тоже не особо были в курсе. Я собрал тестовый проект, написал скрипты и показал как оно работает. Там мы изменяли как раз содержимое блочной памяти для защиты от копирования, без какого-либо вмешательства в трассировку кристалла.
          Еще один пример применения, год/полтора назад я помогал в подготовке стендов, используемых для сбора статистики, где мы реализовывали разнообразные физически неклонируемые функции (PUF — physical unclonable function). Здесь я уже использовал ECO именно для отладки, поскольку банально забыл подключить нужные цепи к ILA. Благо проников хватало. Реализации PUF кстати были сделаны на Arty.

          PS: Если всё-таки пройдете руководство полностью, напишите сколько времени это заняло? Спасибо.

          • +3
            Прошёлся по руководству выполняя всё шаг за шагом, сверяя Ваши скриншоты с тем, что получалось у меня, на всё ушло около часа.
            • +2
              Отлично. Спасибо Вам большое за проверку и информацию о времени.
              Если есть какие-то неточности, пожалуйста, дайте знать, чтобы я внес соответствующие правки в руководство.
        • +2
          Спасибо. Очень хорошая статья.
          • +2
            Рад стараться, главное чтобы от всего этого кому-нибудь была польза
          • 0
            Спасибо. У меня проекты небольшие, там и перетрассировать недолго, но вдруг пригодится в будущем. Надо потренироваться, как раз 2018.1 вышел.

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

            Самое читаемое
            Интересные публикации