В статье рассмотрен режим работы Vivado, позволяющий вносить изменения в проект на уровне редактирования списка соединений (в дальнейшем – нетлиста). Описаны как сам режим ECO, так и некоторые нюансы, которые появляются во время работы в нём. Приведён демонстрационный пример и описана полная последовательность действий для получения результата, в работоспособности которой может убедиться каждый желающий. Статья будет полезна для «общего развития» FPGA-разработчикам, а особенно — тем, кто часто отлаживает проекты в Logic Analyzer. Надеюсь, работа в этом режиме вызовет интерес у разработчиков, работающих с большими кристаллами, время компиляции в которых может достигать часов (а то и десятков часов), поскольку в этом режиме время, затрачиваемое на имплементацию, при внесении изменений в нетлист может сократиться до буквально пары минут.  В статье очень много картинок не в спойлерах (140 штук). Пожалуйста, будьте внимательны, если заходите с телефона  Маршрут проектирования делится на несколько составных частей:, включая синтез и имплементацию. Имплементация в свою очередь делится на подэтапы: различные оптимизации, размещение и трассировку. Промежуточные этапы маршрута проектирования при этом сохраняются в некий «контейнер», который называется Design Checkpoint (DCP) [7]. Это файл, имеющий расширение «.dcp». Design Checkpoint содержит: По умолчанию, Vivado создаёт четыре файла dcp: один – на этапе синтеза модуля верхнего уровня проекта (если выполняется синтез в режиме out-of-context, то для всех модулей, которые синтезируются в out-of-context, создается свой файл dcp) и три – на этапе имплементации. Эти файлы можно найти в папках: 
Рисунок  1 – создаваемые по умолчанию dcp-файлы (1 – «постсинтез-» и 3 – «постимплемент-»: после оптимизации (_opt), после размещения (_placed) и после трассировки (_routed)) В проектном режиме работы с Vivado (Project Mode [8]) файлы .dcp создаются автоматически. Но при работе в непроектном режиме (Non-ProjectMode [8]) пользователь сам должен следить за тем, чтобы «снимки» текущего состояния проекта записывались. Для этого следует использовать соответствующие Tcl команды [9, 10].: О том, зачем, как и какие файлы dcp следует открывать, будет рассказано далее. Находим иконку Vivado и кликаем по ней два раза, откроется окно приветствия (рис. 2) Для создания нового проекта нажимаем кнопку Create Project. Нажатие кнопки вызывает мастер создания нового проекта. После его появления нажимаем кнопку Next (рис. 3). 
Вводим название проекта, в поле Project Name пишем «eco_flow». Указываем, где будет располагаться проект: в поле Project location укажите директорию с проектом. У меня она будет «F:/Projects/FPGA-Systems/eco_flow/projects/vivado». Если установить галочку «create project subdirectory» – будет создана дополнительная папка с именем проекта. Нажимаем Next (рис. 4).  
Рисунок 4 – Ввод имени проекта и его расположения Создаем мы обыкновенный проект, поэтому просто выбираем тип проекта RTL. На текущем этапе не будем добавлять какие-либо файлы в проект, поэтому поставим галку “Do not specify sources at this time” и нажимаем Next (рис. 5). 
Рисунок 5 – Выбор типа создаваемого проекта Работать мы будем с платой Arty [2], поэтому выберем кристалл, который на ней установлен: xc7a35tcsg324-1. Нажимаем Next (рис. 6).
Примечание: я специально не выбираю готовую плату из шаблона доступных плат. Это сделано для того, чтобы можно было вручную делать ошибки, которые потом мы будем исправлять. 
Рисунок  6 – Выбор кристалла xc7a35tcsg324-1 Заключительным в мастере настройки нового проекта будет окно Summary создаваемого проекта. Нажимаем Finish (рис. 7).  
Рисунок 7 – Окно кратких сведений создаваемого проекта Здесь мы создадим два модуля: просто мигающий светодиод и блочную память, которая постоянно считывается (фактически, это имитация памяти коэффициентов фильтра, значения которых мы потом попробуем изменить).

Для создания и добавления нового файла в проект воспользуемся мастером, который вызывается по нажатию на синий плюс (рис. 8). Выбираем Create file, после чего в появившемся окне в поле File name вводим имя создаваемого файла flash_led, нажимаем ОК (рис. 10). 
Рисунок 10 – Создание нового файла и ввод его имени После этого фал появится в списке добавляемых. Нажимаем Finish (рис. 11) 
Рисунок 11 – Список добавляемых или создаваемых файлов Теперь появился мастер создания шаблона для файла. Поскольку я использую VHDL, то я могу изменить имя архитектуры на rtl. Создаем два пина нашего модуля: iclk с направлением «in» (тактовый сигнал нашего модуля) и oled с направлением «out» (выход, подключаемый к светодиоду). Нажимаем ОК (рис. 12). Теперь наш модуль находится в дереве проекта (рис. 13). 
Рисунок 13 – Созданный модуль flash_led Модуль должен выполнять простую функцию: просто моргать светодиодом с периодом в 1 секунду. Забегая вперед, скажу, что тактовая частота нашего проекта будет равна 100 МГц, а сам модуль Вам ещё пригодится при выполнении домашнего задания.
Замените содержимое файла на следующее (листинг 1 (текстовый варинт листинга 1 см. в приложении А)). Код достаточно прост, и не требует дополнительных комментариев для пояснения его работы. Теперь создайте новый модуль, который должен называться brom_reader, его порты iclk с направлением «in», и odout[7:0] с направлением «out» (повторите действия с рис. 8 по рис. 12).
Если все сделано правильно, то в дереве проекта должен появиться модуль brom_reader (рис. 14). Замените содержимое модуля следующим текстом (листинг 2 (текстовый вариант листинга 2 см. в приложении Б)). Здесь потребуется несколько комментариев: В остальном все должно быть понятно: мы создали ROM-память, из которой непрерывно, последовательно и циклически считывается ее содержимое. Теперь мы создадим проект с MicroBlaze. Еще раз обращу Ваше внимание на то, что есть пошаговое руководство на русском по созданию проектов на софт‑процессоре MicroBlaze для новичков [16].
Для создания блочного проекта необходимо создать Block Design. Выбираем Create Block Design, вводим имя system и нажимаем ОК (рис. 15). После добавления MicroBlaze на рабочее поле, воспользуемся экспресс настройками софт-процессора. Выберите Run Block Automation и выставите настройки в соответствии с рис. 17. Нажмите ОК. 
Рисунок   17 – Экспресс настройки MicroBlaze Выполним настройку некоторых модулей в соответствии с нашей платой Arty. Настроим модуль генерирования сетки тактовых частот clk_wiz_1. Для вызова настроек модуля кликаем по нему два раза левой кнопкой мышки. В окне настроек устанавливаем значение входной тактовой частоты 100МГц, поскольку именно генератор на 100МГц установлен на плате [12]. Также устанавливаем тип источника как однополярный (рис. 19). Перейдём во вкладку Output Clocks, где настроим выходные частоты модуля.  
Рисунок   20 – настройка параметров выходной частоты Прокрутите вниз для настройки дополнительных служебных сигналов.  Мы уберем сигнал сброса Reset, который не будем использовать. Снимите с него галочку (рис. 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. Выполним настройку модуля xlconstant_0, щелкнув по нему дважды левой кнопкой мыши. В строке значение (Const val ) вводим 1, в строке ширина (Const Width) вводим 1, нажимаем ОК (рис. 25) Выполним подключение выхода dout модуля xlconstant_0 ко входам ext_reset_in и aux_reset_in модуля rst_clk_wiz_1_100M. Просто соедините эти порты мышкой (рис. 26). Выполним настройку модуля axi_uartlite_0, установив настройки передачи в соответствии с рис. 28. Затем нажимаем ОК. Теперь подключим модуль axi_uartlite_0 к процессору. Воспользуемся для этого автоматизированным методом. Нажимаем в верху Run Connection Automation и выбираем что к чему подключить (AXI вход UART к AXI MicroBlaze) рис. 29. Интерфейс UART является стандартным и выделен в отдельный тип интерфейсов в Vivado IP Integrator. В пункте 2 на рис. 29 мы сказали, что хотим сделать rx и tx модуля axi_uartlite_0 внешними. Если вы раскроете интерфейс, то увидите это. Не смущайтесь, что в интерфейсе всего один синий провод, позже, когда создадим HDL обертку проекта, вы увидите, что там два порта (rx и tx).
Нажмите кнопку Regenerate Layout. После этого рабочее поле Block Design будет оптимизировано и схема примет ид, как на рис. 30. Убедитесь, что вы корректно выполнили подключение. Если на этом этапе все нормально, продолжим, если есть ошибки, выполните построение процессорной системы заново. Выполните настройку модуля, в соответствии с рис. 32 (использовать будем только один канал и один выход). Выполним подключение модуля axi_gpio_0 к процессору и сделаем выход внешним. Нажмите Run Connection Automation и поставьте все галочки (рис.33). Нажмите кнопку Regenerate Layout и убедитесь, что все подключения соответствуют рис. 34. Здесь мы используем некоторое подобие так называемого 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. По умолчанию длина записываемых данных установлена 1024, что вполне достаточно для просмотра транзакции.
Теперь добавим наши RTL модули в Block Design, для этого выберите модуль flash_led, нажмите правой кнопкой мыши и затем выберите Add Module to Block Design (это работает только в Vivado не ниже версии 2017.1). Подключите вход iclk модуля flash_led_0 к тактовому сигналу нашей системы, а порт oled сделайте внешним (правой кнопкой мыши по порту, затем выберите Make Exernal). Нажимаем кнопку Regenerate Layout. Если всё сделано корректно, то должно получиться как на рис. 38. Повторите аналогичные действия, для добавления модуля brom_reader. Подключите его тактовый вход iclk к тактовой цепи, НО не объявляете выход odout[7:0] внешним. Нажмите Regenerate Layout. Если все сделано корректно, то должно получиться как на рис. 39. Теперь, добавим еще один ILA и подключим его к выходу odout[7:0] модуля brom_reader_0.
Находим IP блок ILA в списке доступных (рис. 35) и добавляем на поле Diagram. Выполним его настройку, дважды щелкнув по нему мышкой. Устанавливаем Monitor Type в значение Native (отлаживаем мы не шину AXI, а простые цепи). Остальное оставим по умолчанию. Перейдите во вкладку Probe_Ports(0..7) рис. 40. Подключите выход odout[7:0] модуля brom_reader_0 ко входу probe_0[7:0] модуля ila_1, а вход clk модуля ila_1 подключите к тактовой цепи нашего проекта. Нажмите кнопку Regenerate Layout, и, если все корректно, должно получиться как на рис. 42. Осталось только добавить кнопку и напрямую с ней включенный светодиод.
Создадим порт ввода, нажав на пустом месте нашей блок схемы правой кнопкой мыши и выбрав Create Port (рис. 43).  
Рисунок   43 – Создание порта в IP Integrator После появления мастера настроек порта, введите его имя ibtn, укажите направление input и, если нужно разрядность. Нажмите OK (рис. 44). После этого порт появится на поле Diagram (рис. 45).  
Рисунок   45 – созданный порт ibtn Создайте еще один порт с названием obtn_led, направлением output (повторите действия на рис. 43-44).
Теперь просто соединяем порт ibtn c obtn_led, нажимаем Regenerate Layout. Должно получиться как на рис. 47. Проверим, что нет ошибок в текущем Block Design, нажав на кнопку Validate Design. Если все корректно, но Vivado выдаст соответствующее сообщение. Нажимаем OK и сохраняем текущий Block Design (рис. 48).  
Рисунок   48 – Создание обертки проекта После этого Vivado предложит либо обновлять вручную HDL обертку при внесении изменений в Block Design, либо делать это автоматически. Оставляем автоматическое обновление и нажимаем OK (рис. 49).  
Рисунок   49 – Варианты обновления HDL обертки  
Рисунок   50 – Делаем модуль system_wrapper топ модулем Теперь выполним синтез модуля system_wrapper, нажав на кнопку Run Synthesis (рис. 51).  
Рисунок   51 – Запуск синтеза проекта

После нажатия на кнопку Run Synthesis нажмите ОК и дождитесь окончания синтеза.  
Рисунок   52 – Открытие результатов синтеза  
Рисунок   53 – Открытия окна для назначения пинов Используя Reference Manual [12] для Arty назначим ножки (рис. 54). 
Рисунок   54 – Назначение ножек проекта (rx и tx UART перепутаны специально) Нажмите кнопку сохранить, после чего Vivado укажет, что вы не создавали файл проектных ограничений, и предложит его создать Введите имя файла constr и нажмите ОК (рис. 55).  
Рисунок   55 – Создание файла проектных ограничений Теперь мы можем выполнить имплементацию нашего проекта и сгенерировать файл прошивки. Нажмите на кнопку Generate Bistream, затем OK и дождитесь окончания процедуры (рис. 56).  
Рисунок   56 – Расположение кнопки Generate Bitstream После окончания генерации Bitstream появится окно дальнейших действий. Нажмите Cancel (рис. 57)  
Рисунок   57 – Окно дальнейших действий после создания битсрима Теперь выполним разработку ПО для MicroBlaze. Это выполняется в среде Xilinx Software Development Kit (SDK). Для того чтобы сообщить SDK информацию о собранной процессорной системе (IP ядрах, их адресации на шине AXI) необходимо выполнить экспорт в SDK. Это делается с помощь File → Export → Export Hardware (рис. 58).  Рисунок   58 – Экспорт информации о процессорной системе в SDK   Рисунок   59 – Окно параметров экспорта Теперь запустим SDK. Для этого выберите File → Launch SDK  
Рисунок   60 – запуск SDK 
Рисунок   61 – Создание нового проекта в SDK Введите название нового проекта MB_run, нажмите Next (рис. 62) 
Рисунок   62 – Настройка нового проекта   
Рисунок   63 – Выбор шаблона создаваемого проекта   
Рисунок   64 – Расположение файла helloworld.c


Листинг   3 – Заменённое содержимое файла helloworld.c
  После завершения написания кода и сборки процессорной системы необходимо убедиться, что: Для начала подключаем Arty к компьютеру. Выполним настройку терминала, в котором должны будут показываться сообщения UART. Это можно сделать стандартными средствами SDK. В SDK имеется терминал, расположенный внизу (рис. 65).
Если терминала нет, то его можно найти в Window → Show View → Others → Xilinx → SDK Terminal (рис. 65). Установите настройки терминала, в соответствии с рис. 66. Номер COM-порта может отличаться. Теперь перейдём в Vivado и выполним программирование FPGA.  
Рисунок   67 – Открытие Hardware Manager и подключение в ПЛИС Выбираем из списка наш кристалл и заливаем в него прошивку (рис. 68) Рисунок   68 – Программирование ПЛИС В появившемся диалоговом окне следует проверить, что выбраны нужные файлы прошивки .bit и список подключаемых цепей .ltx (рис.69) и затем нажать кнопку Program. Если все сделано корректно, светодиод LD4 должен мигать, а по нажатию на кнопку BTN0 должен загораться светодиод LD4.
Теперь заставим процессор выполнять нашу программу по отправке данных в UART и мигать красной компонентой светодиода LD1.
Переключаемся обратно в SDK, кликаем правой кнопкой по нашему проекту MB_run, после чего выбираем Run As → Launch on Hardware (System Debugger), как показано на рис. 70. Мы заливаем сразу релиз программы, и не будем выполнять пошаговую отладку. Подождав несколько секунд, видим, что светодиод LD1 мигает, но сообщения в SDK Terminal не появляются, поскольку rx и tx перепутаны.
Теперь давайте убедимся в том, что, транзакции до модуля UART всё-таки доходят.
Переключаемся в Vivado. Надеюсь, что вы помните, что мы подключали два ILA: один на транзакцию по шине AXI для UART, а второй к выходу блочной памяти. Но у нас в списке оказалось три ILA (рис. 71).  
Рисунок   71 – Окно подключенных ILA К сожалению, в результате оптимизации и синтеза, такое может случаться и это следует учитывать, как и то, что в результате оптимизации и различных настроек синтеза могут меняться названия цепей. Тем не менее, мы сможем провести отладку и убедиться, что транзакции до модуля UART все-таки доходят. Но нам нужно их «поймать», то есть настроить триггер, условие, по которому начнется запись состояния линий, к которым подключен ILA.
Дважды кликните мышкой по hw_ila_1 чтобы открыть окно временных диаграмм и окна настроек.
В окне Trigger Setup установите, триггер. Нажмите на синий крестик и в списке доступных цепей выберите цепь, показанную на рис. 72. И нажмите OK. Примечание: мы работаем с интерфейсом AXI-Lite, в котором достаточно много сигналов.  Не буду расписывать здесь функции, которые возложены на те или иные сигналы интерфейса. Если Вам интересно подробнее узнать об этом, пожалуйста, обратитесь к руководству [14].

Теперь установим параметры срабатывания триггера. Поскольку активный уровень сигала RVALID это «1», то скажем, что необходимо начать запись, когда этот сигнал будет равен «1». Установите значение срабатывания, как показано на рис. 73. И затем нажмите кнопку запуска триггера. Теперь, из этого гигантского списка цепей, которые имеются в интерфейсе найдем шину, которая отвечает за данные. Эта шина показана на рис. 74 
Рисунок   74 – Цепь с данными на шине AXI-Lite Измените отображаемое состояние шины на ASCII. Для этого кликните правой кнопкой мыши по шине, затем выберите Radix, затем ASCII (рис. 76). После этого мы увидим на шине часть нашего сообщения, которое отсылает процессор. Это частично подтверждает, что транзакция проходит корректно (рис. 76). Попробуйте повторить самостоятельно действия с рис.71 по рис. 76 для hw_ila_2, который подключён к выходу модуля чтения блочной памяти. Устанавливать триггер не требуется. Если всё будет сделано корректно, то картинка должна быть аналогичной рис. 77. На этом сборка тестового проекта завершена, и мы можем приступить к редактированию нашего нетлиста и работе в режиме ECO. Как мы убедились выше: Для исправления ошибок, редактирования нетлиста и изменения содержимого и функционала некоторых компонентов мы воспользуемся режимом ECO, который доступен при работе с Design Checkpoint (DCP). Мы будем использовать DCP, который получается после этапа трассировки кристалла (post route).
При каждом новом запуске синтеза или имплементации Vivado удаляет DCP, поэтому общей практикой работы с DCP является «копипаста» необходимой DCP в новую директорию.
Давайте создадим папку edited_dcp. Я создам ее рядом с папкой проекта (рис. 78) Скопируем необходимый для дальнейшей работы DCP файл, который расположен «папка_проекта/название_проекта.runs/название_имплементации/имя_топ_модуля_routed.dcp» (рис. 79) в папку edited_dcp.
  Переходим в Vivado и выполняем открытие DCP. Для этого нажмите File → Open Checkpoint (рис. 80) Выберите расположение скопированного DCP файла в папке edited_dcp и нажмите OK. Существует несколько режимов работы с DCP, однако сегодня нас интересует именно ECO Flow. Для перехода в режим ECO необходимо сменить представление. Для этого в верхнем правом углу из выпадающего списка выберите ECO (рис. 82).  
Рисунок   82 – Переход в режим ECO Как видим, графическое представление при переходе в режим ECO изменилось (рис. 83). Появились новые элементы управления и графического интерфейса, которые рассмотрим далее. Работа в режиме ECO имеет свой маршрут, который изображен на рис. 84. Вся работа выполняется в соответствующей DCP. После открытия DCP выполняются необходимые изменения в нетлисте, с помощью инструментов графического интерфейса и/или Tcl-команд. Изменения сохраняются, и, в зависимости от того уже размещен полностью проект в кристалле или нет, выполняются размещение и трассировка либо только трассировка. Затем выполняется разводка кристалла, после чего генерируются новые файлы прошивки (битстрима, .bit) и цепей логического анализатора (.ltx). Следующим шагом выполняется проверка внесенных изменений «в железе», и если всё нормально, то сделанные изменения вносятся в исходный проект. Если же нет – то внесение изменений в DCP может быть повторено. Внесение изменений возможно с помощью инструментов графического интерфейса. Оригинал описания интерфейса вы можете найти в [3] в разделе Vivado ECO Flow.
Графическое представление в режиме ECO разбито на несколько секций, расположение и назначение которых идентично стандартному представлению, в котором выполняется основное проектирование.
С левой стороны экрана расположен ECO Navigator, который представляет инструменты для маршрута проектирования. ECO Navigator состоит из нескольких секций.
Секция Edit (рис. 85): предоставляет доступ к инструментам, необходимым для редактирования нетлиста Create Net:  открывает диалоговое окно, предоставляющее доступ к созданию новых цепей. Цепи могут быть созданы для любого уровня иерархии, путём задания имени иерархии в названии цепи. Могут быть созданы шины. Если выбран pin или port, то цепь может быть автоматически подключена к ним, если установлена галка Connect selected pins and ports (рис. 86). Create Cell: открывает диалоговое окно, позволяющее добавлять дополнительные компоненты в текущий нетлист. Также имеется возможность создавать ячейки в необходимом уровне иерархии. Можно создавать как библиотечные компоненты, которые доступны из списка, либо black box. Если вы создаёте LUT, то сразу можете редактировать функцию, которую она должна реализовывать (рис. 87).  
Рисунок   87 – Диалоговое окно Create Cell Create Port: вызывает мастер для добавления и настройки дополнительных портов в текущий нетлист. Возможно настроить несколько параметров: направление, ширину шины, стандарт напряжений и т.д. (рис. 88).  
Рисунок   88 – Диалоговое окно Create Port Create pin: выполняет добавление и настройку пинов в текущем нетлисте. Пин может быть создан для текущей ячейки (объектов типа cell) на любом уровне иерархии. Пин для иерархии верхнего уровня также может быть создан с помощью команды create_port. Пин не может быть создан, если не указана ячейка и название создаваемого пина (рис. 89). Connect Net: подключеняет цепь к выбранному пину или порту. Вызываемое диалоговое окно позволяет просмотреть список цепей или выполнить их поиск. Выбранная цепь будет подключена через все уровни иерархии, автоматически создавая необходимые пины.
Disconnect Net: отключает выбранную цепь, пин или порт от цепи. В случае, если выбран объект типа cell, то буду отключены все цепи, подключенные к нему.
Replace Debug Probes: позволяет вызвать диалоговое окно, в котором возможно выполнить редактирование портов ILA и/или VIO (Virtual Input Output), которые были созданы ранее, то есть отключить текущие цепи от и подключить новые.
Place Cell:  позволяет разместить объект типа cell в выбранных ресурсах кристалла.
Unplace Cell: убирает выбранный объект типа cell из текущего размещения.

Секция Run
Команды секции Run предоставляет доступ ко всем командам, необходимым для выполнения имплементации. Вкладка Scratch
Позволяет отслеживать внесенные изменения в нетлист, включая просмотр и подключение незадействованных пинов, портов, цепей. Столбец подключений Con отслеживает статус подключения объектов, PnR отслеживает статус размещения и трассировки объектов.
Нажатием правок кнопкой мыши по вкладке Scratch будет вызвано меню дополнительных действий (рис. 90). Функционал, которые они выполняют, предполагаю интуитивно понятен исходя из их названий. Полный список и действия, которые выполняют команды приведен в [3] в разделе Vivado ECO Flow → Scratch Pad → Scratch Pad Pop-up Menu. Теперь мы можем приступить к внесению изменений в наш проект. Первое, что мы сделаем, это установка нового компонента в нетлист. Сделаем так, чтобы светодиод LD0 светился, а при нажатии кнопки BTN0 гас. Иными словами, мы должны вставить в нетлист инвертор, которого нет. Для начала, найдите на схеме порт ibtn, нажмите на него левой кнопкой мыши один раз, а затем нажмите F4. Таким образом, мы попробуем отфильтровать все лишнее в схематике, для повышения читаемости нетлиста. В появившемся окне, мы увидим только один порт ibtn. Теперь отобразим всю цепь от порта ibtn до светодиода LD1 (порта obtn_led). Нажимаем правой кнопкой мыши по порту ibtn, затем Expand Cone и выбираем To Flops or I/Os. И нажимаем кнопку Regenerate Layout (рис. 92) Теперь мы видим полный «тракт» от кнопки до светодиода (рис. 93). Следующий шаг, который необходимо сделать, это определиться, в каком месте разорвать цепь. Предлагаю разорвать её от модуля system_i до выходного буфера. Для этого выбираем цепь от пина obtn_led модуля system_i до пина I буфера obtn_led_OBUF_inst, затем в секции Edit выбираем Disconnect Net. После нажатия на кнопку Regenerate Layout видим, что цепь была удалена (рис. 95) Также обратите внимание на вкладку Scratch Pad, содержимое которой изменилось.
Теперь добавим на схему инвертор. Нажимаем в секции Edit строку Create Cell, вводим название компонента invertor, находим шаблон INV и нажимаем OK (рис. 96) Компонент INV будет добавлен на поле (рис. 97) Как видите, инвертор реализован на LUT. Чтобы посмотреть логическую функцию, которую реализует LUT и при необходимости её изменить, Вы можете, нажав правой кнопкой мыши по компоненту, выбрать свойства Cell Properties, во вкладке Truth Table нажать Edit LUT Equation… (рис. 98). Выполним соединение пинов. Выберите или на схематике, или во вкладке Scratch Pad пины I0 компонента invertor и obtn_led компонента system_i. Теперь создадим между ними цепь. Для этого в секции Edit нажимаем кнопку Create Net. В диалоговом окне вводим имя создаваемой цепи btn_led, устанавливаем галочку автоматического соединения двух портов и нажимаем ОК (рис. 100) После этого два пина буду автоматически соединены. Нажмите Regenerate Layout (рис. 101) Повторим действия для двух оставшихся пинов и соединим пин O компонента invertor с пином I компонента obtn_led_OBUF_inst. Цепь назовём по-другому, поскольку не может быть в проекте двух цепей с одинаковыми названиями на одном уровне иерархии. Назовём цепь btn_led_o. Результат подсоединения показан на рис. 102 
Рисунок   102 – Схематика с созданным инвертором Теперь сохраняем наш DCP и проверяем наличие ошибок, перед тем как запустить размещение и трассировку новых компонентов (рис. 103). Из-за того, что мы не выполняли размещение и трассировку, появится ряд ошибок, говорящих об этом.

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

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

После этого генерируем bit файл, нажав на Generate Bitstream. Убедитесь, что в поле пути к файлу указана папка edited_dcp. После этого открываем Hardware Manager для прошивки нашей ПЛИС. При открытии следует учесть, что должен будет открыться новый экземпляр Vivado. Но у меня он не открылся, поэтому я просто открываю новый экземпляр Vivado и запускаю Hardware Manager (рис. 105). При этом будьте внимательны, при выборе файла прошивки .bit и файла цепей для ILA .ltx  
Рисунок   105 – Открытие Hardware Manager из начального окна Vivado Выполните последовательность действий, согласно рис.67-69 и запрограммируйте ПЛИС. Обратите внимание, что прошивать мы будем файлом, расположенным в папке edited_dcp (рис. 106).  
Рисунок   106 – Файл прошивки ПЛИС с изменённым нетлистом Если всё было выполнено корректно, то светодиод LD0 должен будет светиться, а по нажатию кнопки BTN0 гаснуть. В режиме ECO также возможно изменять содержимое компонентов и менять их параметры. Сейчас мы попробуем изменить содержимое блочной памяти модуля brom_reader, которая имитирует у нас, например, коэффициенты фильтра. Давайте найдём нашу BROM и просмотрим её свойство INIT, отвечающее за начальную инициализацию.
Перейдите во вкладку Netlist и найдите блочную память в модуле brom_reader (рис.107). Нажав правой кнопкой по этому компоненту, мы можем просмотреть его свойства, выбрав Cell Properties (рис. 108). Затем перейдите во вкладку Properties и пролистайте до свойств INIT. Изменить свойство компонента можно, если щелкнуть на значок карандашика и вписать новые значения или же воспользовавшись Tcl консолью и командой set_ptoperty.
Замените значения свойств согласно рис.110 и сохраните результат: INIT_00: 256'h0061006700700066002E00770077007700200020002000200020002000200020 INIT_01: 256'h0020002000200020002000750072002E0073006D00650074007300790073002D Поскольку мы не добавляли новых компонентов в нетлист и не переподключали цепи, то нет необходимости выполнять размещение и трассировку. Просто нажимаем Generate Bitstream и ожидаем окончания выполнения операции. На всякий случай сгенерируйте файл .ltx, который мы подцепим во время отладки (рис. 111). Перейдите в Hardware manager и запрограммируйте FPGA только что сгенерированным файлом .bit  и созданным списком пробников .ltx (рис.112). Обратите внимание, что файлы взяты из папки edit_dcp. Выполните последовательность действий, описанных на рис. 73-75 (откройте hw_ila_2, переведите тип отображаемых значений в ASCII и просмотрите выводимую надпись). Если всё получилось корректно, то должна появиться надпись рис. 113.  Как видите, если Вам необходимо изменить содержимое или параметры компонентов не только блочной памяти, а в принципе любых, не требующих вмешательств в размещение и трассировку, то делается это очень быстро и достаточно просто. Однако следует быть осторожным, когда Вы изменяете параметры, отвечающие за рабочую частоту, например, когда вносите изменения в настройки MMCM или PLL Вашего проекта. В этом случае, прежде чем создавать файл прошивки, убедитесь, что тайминги проекта сходятся, а соответствующие отчёты не выдают ошибок (Report Timing Summary и т.д.). Пожалуй, самым полезным вариантом использования 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, но они достаточно громоздкие. Для руководства, выбраны попроще. Выполняем поиск по списку цепей, введя название искомой цепи *cnt[*]. Пролистав вниз находим их, и нажимаем кнопку добавления в пробники. Обратите внимание, что мы одновременно заменяем 5 пробников, поэтому должно быть выбрано 5 цепей. Замена типа поиска с contains на match обусловлена правилами поиска в tcl и для текущего примера позволяет выводить более точный результат. При использовании поиска цепей рекомендуется хотя бы немного понимать и знать символы подстановки в языке Tcl и правила, по которым выполняется поиск. Оставшиеся три цепи мы подключим к «0», в Vivado цепи, подключенные к gnd называются const0. Выберем три оставшиеся цепи, правой кнопкой Edit Probes (рис.117). Вводим название в строку поиска *const0 и добавляем первую цепь из списка, три раза кликнув по стрелке, поскольку мы должны подключить три линии (рис. 118). БУДЬТЕ ВНИМАТЕЛЬНЫ!!! Не добавляете линии из иерархии Debug Hub (dbg_hub), в этом случае Vivado выдаст ошибку только в самом конце настройки пробников, и вся работа пойдет на смарку. Выберите цепь из любого IP, например, uartlite. Сформированный ila_1 должен выглядеть как на рис. 119. Еще раз отмечу, что не должно быть пустых пробников. В этом случае, будут ошибки на этапе DRC. После нажатия на кнопку 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). Как видим, мы смогли отключить цепи выхода блочной памяти от пробников и подключить новые. Последнее, что остаётся сделать это изменить rx и tx для модуля uart и наконец-то увидеть Hello World в консоли. Следует отметить, что при работе с ECO есть несколько путей, чтобы достичь результата. Я покажу один из них.

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

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

Выберите порт uart_rtl_0_rxd, подключенную у нем цепь и входной буфер. Нажмите unplace cell и затем удалить (рис. 122). После нажатия кнопки удалить, порт останется, просто выберите его и нажмите удалить еще один раз. Теперь восстановим удаленные компоненты. Первым делом добавим порт uart_rx, нажав на кнопку Create Port затем выставив настройки в соответствии с рис. 123 Поскольку ножка A9 уже занята нашим выходным портом, Vivado выдаст сообщение, в которым предложит отменить привязку другого порта к ножке A9. Соглашаемся с этим (рис. 124) Теперь создадим входной буфер. Нажмем кнопку Create Cell, название создаваемой ячейки ibuf_rx (рис. 125) 
Рисунок   125 – Создание входного буфера Теперь создаем цепь. Это делается в несколько этапов. Сначала цепь подключается к порту, а затем выполняется подключение к буферу. Выберите порт uart_rx и нажмите Create Net. Название цепи rx_net. Теперь выберите цепь и порт I входного буфера и нажмите Connect Net. После этого появится цепь (рис. 127) Теперь создадим цепь, межу буфером и модулем system_i. Выберите выходной пин O буфера и цепь uart_rtl_0_rxd, подключённую к system_i и нажмите Create Net Теперь повторим туже последовательность действий для выходного порта.
Выбираем выходной порт uart_rtl_0_txd, подключенную к нему цепь и выходной буфер. Нажимаем Unplace cell, а затем красный крестик, для удаления компонентов (рис. 122). После нажатия кнопки удалить, порт останется, просто выберите его и нажмите удалить еще один раз. Создаём порт uart_tx, нажав кнопку Create Port, задаём направление Output и устанавливаем стандарт LVCMOS33 (рис. 130) Рисунок   130 – Настройка параметров выходного порта Создадим цепь tx_net, которая будет подключена к порту uart_tx. Выбираем uart_tx и нажимаем Create Net Создаём выходной буфер tx_obuf, нажав Create Cell и выбрав тип OBUF (рис. 132) Выбираем пин O выходного буфера и цепь, подключённую к порту uart_tx и нажимаем Connect Net. Теперь подключим буфер к system_i. Выбираем пин I буфера и цепь uart_rtl_0_txd (рис. 134). Сохраняем проект и выполняем последовательно размещение и трассировку. После этого запускам генерацию бистрима (рис. 135). Откройте Hardware Manager и запрограммируйте ПЛИС. Перейдите в SDK и выполните запуск приложения (см. рис. 70). Если всё нормально, но в консоли вы увидите сообщение Hello World: cycle (рис. 136). Проведем небольшой сравнительный анализ разных подходов к внесению изменений в проект по части времени, затрачиваемого на различные операции. Это сравнение будет, конечно, довольно условным, т.к. требуемое время сильно зависит от кристалла, от его загруженности, от количества свободных трассировочных ресурсов и т.д. Проделаны будут операции по изменению HDL файлов, после которого будет заново выполнен синтез, а также в «постсинтез-» нетлисте будут изменены пробники. Результаты приведены в таблице 1. Сразу следует отметить, что инкрементная имплементация даёт выигрыш только при работе с большими кристаллами, для которых получение конечного результата может занимать десяток часов. В учет тут не было включено время, затрачиваемое на внесение разработчиком изменений. Однако учтено то, что мы работали с IP Integrator – а он требует дополнительного времени на синтез IP и RTL.

Таблица 1. Сравнительный анализ времени, затрачиваемого на получение конечного файла прошивки в различных режимах.
 Применял ли я когда-нибудь на практике режим ECO? Да. Стояла задача, не изменяя трассировку кристалла поменять содержимое блочной памяти, в которой были зашиты некоторые значения, позволяющие защитить продукцию от копирования. Там как раз и пригодился ECO; правда, по большей части работа была выполнена при помощи Tcl-скриптов, а не графического интерфейса. Тем не менее, режим ECO оказывается действительно полезен при работе с большими проектами – особенно в случае, если вы являетесь горячим фанатом внутрисхемной отладки с LogicAnalyser (ChipScope) и любите делать ILA на пару сотен (или даже тысяч) пробников. Возможно, Вы найдете работу в этом режиме полезной для Вас, если просто попробуете сделать что-то большее, чем описано в данном руководстве.
Если Вы нашли для ECO Flow интересное применение, или просто решили попробовать использовать его в своём проекте, оставьте небольшой комментарий: будет любопытно узнать для чего Вам в Vivado пригодился ECO Flow.
Не забудьте сделать и домашнее задание. Удачи! 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
 PS: Огромное спасибо пользователям intekus Des333, ishevchuk, roman-yanalov которые не только прочитали это 91 страничное руководство (именно столько оно занимает в Word), но и выполнили его и внесли необходимые редакторские правки. Вождь FPGA комунити @fpgasystems