Как подружить Vivado и git: с микроблейзом и сабмодулями

от автора

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

Например, проект навигационного приемника под Xilinx Spartan 6, собираемый в уже устаревшей IDE Xilinx ISE, на диске занимает около 5 Гб. При этом большая часть файлов обновляется при любой манипуляции в IDE, часть из файлов — бинарные. Одним словом — ад для систем контроля версий. Заставить разработчик хранить файлы в репозитории было очень тяжело. А «чего нет в гите, того не существует». Без систем контроля версий ломаются все процессы разработки: от работы командой до тестирования.

К счастью, в современной среде Vivado разработчики сделали работу над ошибками. Отделить в проекте разрабатываемое человеком от генерируемого стало проще, появились механизмы сборки скриптами. Наши проекты окончательно перешли в git, а процессы разработки под ПЛИС перестали отличаться от процессов разработки программного обеспечения.

Эта статья написана в продолжение рассказа про организацию автотестирования радиоаппаратуры и отвечает на вопрос «как вы подготовили проект FPGA для хранения в репозитории и автоматической сборки в контейнере?». Она составлена по материалам пятилетней давности, а сам подход выдержал проверку временем.

Требования и пожелания

Чего хотим мы?

  • Иметь возможность откатиться к старой версии проекта.

  • Разворачивать и собирать проект на любой машине на основе одного-двух-N репозиториев.

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

  • Легко переключаться между ветками.

  • Иметь возможность собирать прошивку без использования графического интерфейса для последующей автоматизации.

Особенности наших проектов:

  • Используется Xilinx Zynq, т.е. помимо части с программируемой логикой нужна и прошивка процессорной системы.

  • В разных дизайнах используются одни и те же модули (оформленные в виде HDL, а не IP блоков).

  • Используются родные Xilinx’овские IP модули (сериалайзеры, буферы, шины, сбросы и т.п.).

Project и non-Project workflow

Создатели Vivado выделяют два подхода к ведению проекта: project и non-project.

В первом случае мы активно пользуемся GUI, имеем файл проекта .xpr. Во втором — делаем упор на сборку на основе tcl-скриптов, т.е. реализуем unix-way.

Казалось бы вот оно решение — использовать non-project подход, отличный задел для автоматизации. Но на практике разработчики его отторгают. Людям привычнее и быстрее работать в GUI.

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

Содержимое проекта

Типы файлов, которые Vivado относит к исходным:

  • HDL and netlist files: Verilog (.v), SystemVerilog (.sv), VHDL (.vhd), and EDIF (.edf)

  • C based source files (.c)

  • Tcl files, run scripts, and init.tcl (.tcl)

  • Logical and Physical Constraints (.xdc)

  • IP core files (.xci)

  • IP core container (.xcix)

  • IP integrator block design files (.bd)

  • Design Checkpoint files (.dcp)

  • System Generator subsystems (.sgp)

  • Side files for use by related tools (например, “do”-файлы для симулятора)

  • Block Memory Map files (.bmm, .mmi)

  • Executable and Linkable Format files (.elf)

  • Coefficient files (.coe)

Что из этого списка хранить в репозитории? Что «рукописного» мы вносим в проект?

  • Модули на языке Verilog (.v) и SystemVerilog (.sv, .shv)

  • Testbench’и (_tb.v, _tb.sv)

  • Входные тестовые выборки для тестов

  • Настройки Xilinx’овских модулей в Block Diagram (.bd)

  • Описание ограничений (.xdc)

  • Описание портов и их соединение с сигналами модулей (.xdc)

  • Коэффициенты фильтров (.coe)

  • Настройки экранов в симуляторах (.wcfg)

Разделение песочницы и исходных файлов

Когда мы создаем новый дизайн в Vivado, то получаем по-умолчанию структуру каталогов, в которой перемешаны генерируемые и исходные файлы. Есть директории .srcs, .cache, .runs, .data, .hw, .ip_users_files, .sim и т.д. Идея заключается в том, чтобы выкинуть вовне исходные файлы и держать их в системе контроля версий, а в каталоге проекта оставить только генерируемые:

При выделении исходных файлов в отдельные каталоги конечная структура определяется разработчиком исходя из собственных предпочтений. В репозиторий кладутся только исходные файлы и tcl-скрипт, который позволяет воссоздать каталоги песочницы с генерируемыми файлами. Например:

Те логические модули, что используются в разных проектах (прошивках разных устройств), вынесены в отдельные сабмодули и подключаются наподобии библиотек. Они при этом являются самостоятельными Vivado-проектами и могут разрабатываться (в том числе тестироваться) независимо.

Блок-дизайны для различных плат вынесены в отдельную директорию bd. Там хранятся непосредственно .bd-файлы, которые являются текстовыми xml-файлами. Вспомогательные бинарные файлы генерируются из них уже вивадой.

К сожалению, вспомогательные файлы блок-дизайна генерируются в каталоге с bd-файлом, поэтому их приходится добавлять в игнор:

~/Oryx/src/fpga/.gitignore
# Always ignore journal and log files *.log *.jou *.str  # Ignore trash in bd directory except .BD files /bd/**/* !/bd/**/*.bd  # Ignore editor's temporary files *~ *#  # Ignore sendboxes /prj*/  # Ignore Vivado temporary files .Xil/

Шаг 1. Получаем исходные файлы

Рассмотрим процесс работы с таким проектом на примере типичной задачи: получение исходных кодов, внесение изменений, сборка bitstream-файла, прошивка устройства.

Первым шагом получим исходные коды из репозитория. Заводим каталог:

korogodin@Diod:~/$ mkdir Oryx korogodin@Diod:~/$ cd Oryx

Мы готовы клонировать git-репозиторий. Для этого потребуется аккаунт и права доступа к проекту:

korogodin@Diod:~/Oryx$ git clone ssh://git@krgd.ru:123/git/src Cloning into 'src'... remote: Counting objects: 20490, done. remote: Compressing objects: 100% (8449/8449), done. remote: Total 20490 (delta 13181), reused 18342 (delta 11414) Receiving objects: 100% (20490/20490), 787.09 MiB | 143.00 KiB/s, done. Resolving deltas: 100% (13181/13181), done. Checking connectivity... готово.

Среди прочего, мы получили желанные исходные файлы прошивки PL-части нашего СНК. Они расположены в каталоге fpga:

korogodin@Diod:~/$ cd src/fpga korogodin@Diod:~/Oryx/src/fpga$ ls bd  constr  prj_somz.tcl  sub  verilog

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

korogodin@Diod:~/Oryx/src/fpga$ git submodule update --init korogodin@Diod:~/Oryx/src/fpga$ tree -L 2 sub . ├── acquisition │   ├── bin │   ├── doc │   ├── IPgen │   ├── matlab │   ├── sdk │   ├── tb │   └── verilog ├── correlator │   ├── tb │   └── verilog ├── dsp │   ├── tb │   └── verilog ├── serializer_zynq │   └── verilog └── sync     └── verilog

В дереве каталогов есть все нужные для сборки прошивки исходные файлы.

Шаг 2. Разворачиваем проект Vivado

Чтобы запустить графический интерфейс Vivado и работать через него с нашими исходными кодами, мы должны развернуть Vivado-проект, т.е. создать xpr-файл и структуру временных каталогов. Делает это отдельный tcl-скрипт проекта, который можно сгенерировать из GUI (да-да, тут курица и яйцо).

Пример скрипта регенерации проекта
#!/usr/bin/tclsh # # Vivado (TM) v2015.3 (64-bit) # # prj_somz.tcl: Tcl script for re-creating project 'somz' # # Generated by Vivado on Tue Mar 22 10:11:05 +0300 2016 # IP Build 1367837 on Mon Sep 28 08:56:14 MDT 2015 # # This file contains the Vivado Tcl commands for re-creating the project to the state* # when this script was generated. In order to re-create the project, please source this # file in the Vivado Tcl Shell. # # * Note that the runs in the created project will be configured the same way as the #   original project, however they will not be launched automatically. To regenerate the #   run results please launch the synthesis/implementation runs as needed. #  # Set the reference directory for source file relative paths (by default the value is script directory path) set origin_dir          "." set sub_dir             "sub" set prj_name            "somz" set prj_dir_name        "prj_somz" set topmodule_name      "mainboard_facq"  # Acquisition Microblaze firmware set facq_prj_name       "mcs_facq" set facq_bsp_name       "mcs_facq_bsp" set facq_proc_name      "microblaze_0" set hw_platform_name    "$topmodule_name\_hw_platform_0"   # Use origin directory path location variable, if specified in the tcl shell if { [info exists ::origin_dir_loc] } {   set origin_dir $::origin_dir_loc }  variable script_file set script_file "prj_$prj_name.tcl"  # Help information for this script proc help {} {   variable script_file   puts "\nDescription:"   puts "Recreate a Vivado project from this script. The created project will be"   puts "functionally equivalent to the original project for which this script was"   puts "generated. The script contains commands for creating a project, filesets,"   puts "runs, adding/importing sources and setting properties on various objects.\n"   puts "Syntax:"   puts "$script_file"   puts "$script_file -tclargs \[--origin_dir <path>\]"   puts "$script_file -tclargs \[--help\]\n"   puts "Usage:"   puts "Name                   Description"   puts "-------------------------------------------------------------------------"   puts "\[--origin_dir <path>\]  Determine source file paths wrt this path. Default"   puts "                       origin_dir path value is \".\", otherwise, the value"   puts "                       that was set with the \"-paths_relative_to\" switch"   puts "                       when this script was generated.\n"   puts "\[--help\]               Print help information for this script"   puts "-------------------------------------------------------------------------\n"   exit 0 }  if { $::argc > 0 } {   for {set i 0} {$i < [llength $::argc]} {incr i} {     set option [string trim [lindex $::argv $i]]     switch -regexp -- $option {       "--origin_dir" { incr i; set origin_dir [lindex $::argv $i] }       "--help"       { help }       default {         if { [regexp {^-} $option] } {           puts "ERROR: Unknown option '$option' specified, please type '$script_file -tclargs --help' for usage info.\n"           return 1         }       }     }   } }  # Set the directory path for the original project from where this script was exported #set orig_proj_dir "[file normalize "$origin_dir/mainboard_facq_release"]"  # Create project create_project $prj_name ./$prj_dir_name  # Set the directory path for the new project set proj_dir [get_property directory [current_project]]  # Set project properties set obj [get_projects $prj_name] set_property "default_lib" "xil_defaultlib" $obj set_property "part" "xc7z045fbg676-2" $obj set_property "sim.ip.auto_export_scripts" "1" $obj set_property "simulator_language" "Mixed" $obj set_property "source_mgmt_mode" "DisplayOnly" $obj  # Create 'sources_1' fileset (if not found) if {[string equal [get_filesets -quiet sources_1] ""]} {   create_fileset -srcset sources_1 }  # Set 'sources_1' fileset object set obj [get_filesets sources_1] set files [list \  "[file normalize "$origin_dir/verilog/bus_interface.v"]"\  "[file normalize "$origin_dir/verilog/$topmodule_name.v"]"\  "[file normalize "$origin_dir/$sub_dir/serializer_zynq/verilog/zynq_deser_main.v"]"\  "[file normalize "$origin_dir/$sub_dir/serializer_zynq/verilog/gearbox_4_to_7.v"]"\  "[file normalize "$origin_dir/$sub_dir/serializer_zynq/verilog/n_x_serdes_1_to_7_mmcm_idelay_ddr.v"]"\  "[file normalize "$origin_dir/$sub_dir/serializer_zynq/verilog/serdes_1_to_7_slave_idelay_ddr.v"]"\  "[file normalize "$origin_dir/$sub_dir/serializer_zynq/verilog/serdes_1_to_7_mmcm_idelay_ddr.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/correlator_common.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/common_param.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/flag_sync.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/flag_sync_n.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/channel_adder.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/correlator.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/channel_sin_table.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/channel_synthesizer.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/channel_param.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/channel_cos_table.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/correlator_channel.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/channel_delay_reg.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/signal_mux_adc.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/common_sync.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/common_timegen.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/channel_shift_reg.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/channel_cmplx_table.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/time_generator.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/channel_regfile.v"]"\  "[file normalize "$origin_dir/$sub_dir/correlator/verilog/common_regfile.v"]"\  "[file normalize "$origin_dir/$sub_dir/sync/verilog/ed_det.v"]"\  "[file normalize "$origin_dir/$sub_dir/sync/verilog/conv_reg.v"]"\  "[file normalize "$origin_dir/$sub_dir/sync/verilog/signal_sync.v"]"\  "[file normalize "$origin_dir/$sub_dir/sync/verilog/data_sync.v"]"\  "[file normalize "$origin_dir/$sub_dir/sync/verilog/latency.v"]"\  "[file normalize "$origin_dir/$sub_dir/sync/verilog/level_sync.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/bin/$facq_prj_name.elf"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/DDS_I_Q.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/quant_level_table.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/arg_max.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/bram_dat_out_mux.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/poisk_IP.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/dat_in_sign_conv.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/doppler_dds.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/bram_addr_mux.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/pre_ader_adaptive.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/multi_sum.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/abs.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/lim_qnt.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/CORE.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/dop_shifter.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/sum_1_step.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/bram_multi_controller.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/poisk_time_sync.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/bram_block.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/reset_poisk_sync.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/accum.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/acq_regfile.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/psp_rep_dds.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/bram_dat_mux.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/adaptive_quantizer.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/multi_core_correlator_conveer.v"]"\  "[file normalize "$origin_dir/$sub_dir/acquisition/verilog/main/facq_ecpu.v"]"\  "[file normalize "$origin_dir/$sub_dir/dsp/verilog/hist_sig_mag.v"]"\ ] add_files -norecurse -fileset $obj $files  # Set 'sources_1' fileset file properties for remote files # None  # Set 'sources_1' fileset file properties for local files set file "$origin_dir/$sub_dir/acquisition/bin/$facq_prj_name.elf" set file_obj [get_files -of_objects [get_filesets sources_1] [list "$file"]] set_property "scoped_to_cells" "microblaze_0" $file_obj set_property "scoped_to_ref" "zynq" $file_obj set_property "used_in" "implementation" $file_obj set_property "used_in_simulation" "0" $file_obj   # Set 'sources_1' fileset properties set obj [get_filesets sources_1] set_property "include_dirs" "$origin_dir/$sub_dir/correlator/verilog $origin_dir/$sub_dir/acquisition/verilog/inc $origin_dir/verilog" $obj set_property "top" "$topmodule_name" $obj  # Create 'zynq' fileset (if not found) if {[string equal [get_filesets -quiet zynq] ""]} {   create_fileset -blockset zynq }  # Set 'zynq' fileset object set obj [get_filesets zynq] set files [list \  "[file normalize "$origin_dir/bd/$prj_name/zynq.bd"]"\ ] add_files -norecurse -fileset $obj $files  # Set 'zynq' fileset file properties for remote files set file "$origin_dir/bd/$prj_name/zynq.bd" set file [file normalize $file] set file_obj [get_files -of_objects [get_filesets zynq] [list "*$file"]] if { ![get_property "is_locked" $file_obj] } {   set_property "synth_checkpoint_mode" "Singular" $file_obj }   # Set 'zynq' fileset properties set obj [get_filesets zynq] set_property "include_dirs" "$origin_dir/$sub_dir/correlator/verilog $origin_dir/verilog $origin_dir/$sub_dir/acquisition/verilog/inc" $obj set_property "top" "zynq" $obj  # Create 'constrs_1' fileset (if not found) if {[string equal [get_filesets -quiet constrs_1] ""]} {   create_fileset -constrset constrs_1 }  # Set 'constrs_1' fileset object set obj [get_filesets constrs_1]  # Add/Import constrs file and set constrs file properties set file "[file normalize "$origin_dir/constr/$topmodule_name.xdc"]" set file_added [add_files -norecurse -fileset $obj $file] set file "$origin_dir/constr/$topmodule_name.xdc" set file [file normalize $file] set file_obj [get_files -of_objects [get_filesets constrs_1] [list "*$file"]] set_property "file_type" "XDC" $file_obj  # Set 'constrs_1' fileset properties set obj [get_filesets constrs_1] set_property "target_constrs_file" "[file normalize "$origin_dir/constr/$topmodule_name.xdc"]" $obj  # Create 'sim_1' fileset (if not found) if {[string equal [get_filesets -quiet sim_1] ""]} {   create_fileset -simset sim_1 }  # Set 'sim_1' fileset object set obj [get_filesets sim_1] # Empty (no sources present)  # Set 'sim_1' fileset properties set obj [get_filesets sim_1] set_property "source_set" "" $obj set_property "top" "$topmodule_name" $obj set_property "xelab.nosort" "1" $obj set_property "xelab.unifast" "" $obj  # Create 'synth_1' run (if not found) if {[string equal [get_runs -quiet synth_1] ""]} {   create_run -name synth_1 -part xc7z045fbg676-2 -flow {Vivado Synthesis 2014} -strategy "Vivado Synthesis Defaults" -constrset constrs_1 } else {   set_property strategy "Vivado Synthesis Defaults" [get_runs synth_1]   set_property flow "Vivado Synthesis 2014" [get_runs synth_1] } set obj [get_runs synth_1] set_property "part" "xc7z045fbg676-2" $obj  # Create 'zynq_synth_1' run (if not found) if {[string equal [get_runs -quiet zynq_synth_1] ""]} {   create_run -name zynq_synth_1 -part xc7z045fbg676-2 -flow {Vivado Synthesis 2014} -strategy "Vivado Synthesis Defaults" -constrset zynq } else {   set_property strategy "Vivado Synthesis Defaults" [get_runs zynq_synth_1]   set_property flow "Vivado Synthesis 2014" [get_runs zynq_synth_1] } set obj [get_runs zynq_synth_1] set_property "constrset" "zynq" $obj set_property "part" "xc7z045fbg676-2" $obj  # set the current synth run current_run -synthesis [get_runs synth_1]  # Create 'impl_2' run (if not found) if {[string equal [get_runs -quiet impl_2] ""]} {   create_run -name impl_2 -part xc7z045fbg676-2 -flow {Vivado Implementation 2014} -strategy "Performance_Explore" -constrset constrs_1 -parent_run synth_1 } else {   set_property strategy "Performance_Explore" [get_runs impl_2]   set_property flow "Vivado Implementation 2014" [get_runs impl_2] } set obj [get_runs impl_2] set_property "part" "xc7z045fbg676-2" $obj set_property "steps.opt_design.args.directive" "Explore" $obj set_property "steps.place_design.args.directive" "Explore" $obj set_property "steps.phys_opt_design.is_enabled" "1" $obj set_property "steps.phys_opt_design.args.directive" "Explore" $obj set_property "steps.route_design.args.directive" "Explore" $obj set_property "steps.write_bitstream.args.readback_file" "0" $obj set_property "steps.write_bitstream.args.verbose" "0" $obj  # Create 'zynq_impl_1' run (if not found) if {[string equal [get_runs -quiet zynq_impl_1] ""]} {   create_run -name zynq_impl_1 -part xc7z045fbg676-2 -flow {Vivado Implementation 2014} -strategy "Vivado Implementation Defaults" -constrset zynq -parent_run zynq_synth_1 } else {   set_property strategy "Vivado Implementation Defaults" [get_runs zynq_impl_1]   set_property flow "Vivado Implementation 2014" [get_runs zynq_impl_1] } set obj [get_runs zynq_impl_1] set_property "constrset" "zynq" $obj set_property "part" "xc7z045fbg676-2" $obj set_property "steps.write_bitstream.args.readback_file" "0" $obj set_property "steps.write_bitstream.args.verbose" "0" $obj  # set the current impl run current_run -implementation [get_runs impl_2]  puts "INFO: Project created:somz"  puts "INFO: Generate all targets from BD" set file "$origin_dir/bd/$prj_name/zynq.bd" set file [file normalize $file] generate_target all [get_files  $file]  puts "INFO: Export HW" file mkdir [file normalize "$proj_dir/$prj_name.sdk"] write_hwdef -force  -file [file normalize "$proj_dir/$prj_name.sdk/$topmodule_name.hdf"]  puts "INFO: Create HW Platform and BSP" exec xsdk -batch -eval "sdk set_workspace [file normalize "$proj_dir/$prj_name.sdk"]; sdk create_hw_project -name $hw_platform_name -hwspec [file normalize "$proj_dir/$prj_name.sdk/$topmodule_name.hdf"]; sdk create_bsp_project -name $facq_bsp_name -hwproject $hw_platform_name -proc $facq_proc_name -os standalone; exec xsdk -eclipseargs -application org.eclipse.cdt.managedbuilder.core.headlessbuild -import [file normalize "$origin_dir/sub/acquisition/sdk/$facq_prj_name/"] -data [file normalize "$proj_dir/$prj_name.sdk"] -vmargs -Dorg.eclipse.cdt.core.console=org.eclipse.cdt.core.systemConsole; exit"  puts "INFO: Project is regenerated"

Скрипт регенерации содержит:

  • Название проекта и относительное расположение песочницы (временных файлов).

  • Указание платформы.

  • Указание топового модуля.

  • Настройка симулятора.

  • Подключение к проекту различных наборов файлов — Verilog, TB, IP, .xdc и т.д.

  • Настройки синтеза (с указанием кристалла!).

  • Настройки имплементации (с указанием кристалла!).

Кроме того, в конец этого скрипта я внес перекомпиляцию Block Design при первом запуске, создание HW Platform, BSP для SDK и подключение проекта прошивки для Microblaze (используется блоком поиска).

Новый скрипт восстановления из существующего проекта можно получить через GUI Vivado (File->Write Project Tcl) или через TCL-консоль командой write_project_tcl:

pwd cd [get_property DIRECTORY [current_project]] pwd write_project_tcl -force prj_somz.tcl

Скрипт регенерации песочницы проще всего запустить непосредственно через Vivado. При этом скрипт (а так же block design, IP-ядра и т.д.) подходит только к определенной версии среды, поэтому первым делом следует узнать требуемую версию в заголовке файла:

korogodin@Diod:~/Oryx/src/fpga$ head -n 2 prj_somz.tcl # # Vivado (TM) v2015.3 (64-bit)

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

Скрипт можно запустить из консоли операционной системы:

korogodin@Diod:~/Oryx/src/fpga$ /opt/Xilinx/Vivado/2015.3/bin/vivado -source prj_somz.tcl

а можно из консоли Vivado:

cd ~/Oryx/src/fpga source prj_somz.tcl

Скрипт создает песочницу и файл проекта. Добавляет к проекту внешние исходные файлы. Настраивает правила синтеза и имплементации. Подключается уже собранный Elf-файл для прошивки MicroBlaze (лежит в репозитории в acquisition/bin, скопированный туда руками ранее).

После этого перекомпилируется Block Design. Для проекта прошивки MicroBlaze в песочнице создается somz.sdk, в него добавляется HW Platform и собирается BSP (microblaze_0, standalone). К проекту подключаются исходники прошивки MicroBlaze из сабмодуля acquisition.

Шаг 3. Правим исходные файлы и собираем прошивку

По завершению выполнения скрипта регенирации проекта (prj_somz.tcl в нашем примере) мы имеем готовую к работе настроенную среду:

Можно вносить изменения в исходные файлы и вносить их в коммиты.

Помимо осноного процессор СНК, мы используем ядро небольшого процессора MicroBlaze, размещаемого непосредственно в ПЛИС. Если нужны правки в прошивке MicroBlaze’а, то придется открывать SDK. Для этого в Vivado следует нажать File->Launch SDK. Проект прошивки блока поиска (mcs_facq) изменяется и компилируется в XSDK.

Проект прошивки, BSP, HW Platform подключается автоматически скриптом prj_somz.tcl при регенерации проекта. Для этого в нем используется следующий хак:

exec xsdk -batch -eval "sdk set_workspace [file normalize "$proj_dir/$prj_name.sdk"]; sdk create_hw_project -name $hw_platform_name -hwspec [file normalize "$proj_dir/$prj_name.sdk/$topmodule_name.hdf"]; sdk create_bsp_project -name $facq_bsp_name -hwproject $hw_platform_name -proc $facq_proc_name -os standalone; exec xsdk -eclipseargs -application org.eclipse.cdt.managedbuilder.core.headlessbuild -import [file normalize "$origin_dir/sub/acquisition/sdk/$facq_prj_name/"] -data [file normalize "$proj_dir/$prj_name.sdk"] -vmargs -Dorg.eclipse.cdt.core.console=org.eclipse.cdt.core.systemConsole; exit"

После внесения изменений можно собрать прошивку для ПЛИС:  выбираем число каналов коррелятора, размер ядра блока поиска, тип корреляторов и т.п. и запускам Generate Bitstream слева снизу в Vivado. Процесс сборки пошел!

Если повезет, то через несколько минут/часов/дней мы получим bit-файл, готовый для прошивки в ПЛИС (impl_2 — набор правил имплементации, описан в prj_somz.tcl):

korogodin@Diod:~/Oryx/src/fpga$ find ./ -name *.bit /home/korogodin/Oryx/src/fpga/prj_somz/somz.runs/impl_2/somz.bit

Заключение

Описан способ хранения исходных кодов проекта для СнК Zynq в системе контроля git. В репозитории размещаются текстовые файлы, создаваемые разработчиком, плюс автоматически формируемых скрипт и xml-описание блок-дизайна. Это позволяет контролировать вносимые изменения при процессе слияния веток и быстрее выявлять источники ошибок при их возникновении.

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


ссылка на оригинал статьи https://habr.com/ru/post/683580/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *