Сценарии сборки - fpga-lib/site_scons GitHub Wiki

Сценарии сборки

Общие сведения

Сценарием сборки называется описание способов получения целей сборки из источников (как правило, файлов), которые называются зависимостями. Установление связей между целями и зависимостями осуществляется разными способами, основным из которых является использование билдеров (builders, см. ниже), а так же явное указание зависимости с помощью функции Depends(). Помимо обычных (основных) целей существуют так называемые мнимые цели (phony targets), которые не являются файлами или директориями, а, как правило, обозначают действия — например, запуск какого-либо инструмента безусловно.

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

Сборочное окружение (Construction Environment)

Сборочное окружение (СО) — это объект SCons, содержащий всё необходимое для реализации сборки по требуемому сценарию. Основу СО составляют переменные сборочного окружения, которые содержат различные настройки, начиная от названий и путей внешних инструментов, опций их запуска, и заканчивая вспомогательными параметрами разного назначения (название проекта, расширения файлов, пути, определяющие структуру проекта и т.п.).

Помимо переменных сборочное окружение содержит набор билдеров (см. ниже), сканеров и других необходимых для осуществления процесса сборки объектов. Создаётся сборочное окружение с помощью конструктора специального объекта SCons:

envx = Environment()   # создание сборочного окружения по умолчанию, имя объекта может быть произвольным

Такое СО содержит набор билдеров и переменных по умолчанию, большинство из которых относится к миру традиционного ПО. Например, с помощью такого СО очень легко организовать сборку исполняемого файла из исходных файлов на языках C/C++ (билдер Program, см. официальную документацию на SCons). Можно было бы легко создать СО без этих переменных и билдеров, указав аргументом конструктору tools = { }, но смысла в этом немного, а эти инструменты могут пригодиться в какой-либо ситуации, если внезапно потребуется собрать исполняемую программу — например, для Verilog/DPI.

Поведение инструментов (tools) можно легко изменить, указав соответствующие значения переменным окружения. То же самое относится и к специализированным инструментам, реализованным для поддержки работы SCons с проектами на FPGA, речь о которых пойдёт ниже.

Доступ к переменными СО может осуществляться как непосредственно в нотации словаря языка Python:

envx['TOP_NAME'] = 'top_level_module'

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

envx.Append(VLOG_FLAGS = ' -O5 -timescale=1ns/1ps')
...
envx.Append(USER_DEFINED_PARAMS = {'ROOT_DIR'      : envx['ROOT_PATH']})
envx.Append(USER_DEFINED_PARAMS = {'CFG_DIR'       : envx['CFG_PATH']})
envx.Append(USER_DEFINED_PARAMS = {'BUILD_SRC_DIR' : envx['BUILD_SRC_PATH']})

модифицирующих переменную, добавляя к ней новые объекты (переменная в этом случае является списком). Вместо Append() можно использовать Prepend(), которая добавляет аргументы не в конец, а в начало списка. Более подробную справочную информацию по способам работы с переменными сборочных окружений можно найти в официальной документации на SCons.

Логика работы SCons

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

Работа SCons выполняется в два этапа:

  1. чтение скриптов, начиная с корневого SConstruct и далее по иерархии сборки, выявление целей и зависимостей, построение графа зависимостей и определение того, какие действия необходимо выполнить для обеспечения актуальности целей;
  2. запуск внешних или внутренних (скриптов) инструментов в требуемой последовательности для реализации плана действий, составленного в п.1.

Работа SCons всегда начинается с чтения и анализа файла SConstruct, в котором содержатся общие действия, такие как обработка аргументов командной строки, создание сборочного окружения (СО), установление значений (при необходимости) некоторых переменных СО, управление сборкой по иерархии — передача управления в SConscript/*.scons файлы сборочных вариантов, в которых описываются основные сценарии сборки.

При чтении SConscript/*.scons файла происходит переход в директорию сборочного варианта, которая с этого момента является текущей. Это обстоятельство используется для автоматического определения имени сборочного варианта, которое в дальнейшем служат для соответствующего именования исполнительного окружения в директории build.

Следует отметить, что при этом происходит передача объекта сборочного окружения из корневого файла в файл сборочного варианта:

#
#  SConstruct
#
...
SConscript(variant_path, exports='envx')
...
#
#  SConscript/*.scons
#
Import('envx')
...

По окончании чтения и анализа SConscript/*.scons файла осуществляется переход к непосредственным действиям. При этом SCons переходит в корневую директорию проекта (туда, где находится файл SConsctruct), и вызов всех инструментов производится из этой директории. Учитывая вышеописанное и во избежание проблем с путями к файлам и директориям, которые будут непосредственно участвовать в процессах запусках инструментов, необходимо, чтобы пути были либо от корневой директории проекта, либо абсолютными. Сборочные сценарии и их компоненты (в частности, билдеры) должны учитывать эту особенность, что, впрочем, достигается без особых сложностей.

Builders

Билдер — это специальный объект SCons, обеспечивающий связь цели и зависимости при построении графа зависимостей, а так же реализующий непосредственное действие, приводящее цель в актуальное состояние. Билдер имеет ассоциированную с ним исполнительную функцию (action function), которая собственно и отвечает за действия, направленные на приведение цели в актуальное состояние. Функция принимает три агрумента:

def action_function(target, source, env):
   ...

первые два из которых являются списками целей и источников (зависимостей) соответственно.

Конструктор билдера имеет два аргумента, а запуск билдера устанавливает непосредственную связь между целью и зависимостями:

trg = SomeBuilder(target, source)

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

Псевдобилдеры

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

Для решения описанных задач SCons предусматривает концепцию псевдобилдеров. Псевдобилдер — это по сути объект-"обёртка" вокруг настоящего билдера, осуществляющая все необходимые вспомогательные действия перед вызовом собственно билдера. Технически псевдобилдер представляет собой функцию языка Python, содержащую два и более аргумента:

def pseudo_builder(env, src [,...]):
    ...

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

Сценарии сборки рассматриваемой сборочной системы используют преимущественно псевдобилдеры.

Tools

Для реализации сборочных сценариев в системе сборки для работы над проектами с FPGA требуются специализированные билдеры (и псевдобилдеры), обеспечивающие создание и обновление промежуточных и финальных целей проекта на FPGA, таких как создание IP ядер, out-of-context компиляция IP ядер, компиляция симуляционных моделей IP ядер и компиляция рабочей библиотеки симулятора, создание проектов блочных дизайнов и их синтез, компиляция IP ядер из HLS описаний, создание проекта САПР FPGA, запуск моделирования и синтеза и т.д. Все эти действия выполняются по зависимостям и реализуются с помощью соответствующих билдеров.

Для того, чтобы не загромождать файл сборочного сценария текущего варианта, весь код билдеров, псевдобилдеров, сканеров, переменных СО и ряда вспомогательных функций вынесен в специальные файлы или пакеты языка Python, называемые в терминологии SCons инструментами — tools. Рассматриваемая система сборки в настоящий момент поддерживает два инструмента: vivado и questa, для синтеза и моделирования соответственно. Перечень доступных средств упомянутых инструментов кратко описан в разделе Reference.

Подключение инструмента к сборочному сценарию осуществляется с помощью объекта 'Tool':

#
#  <variant-name>.scons
#
Import('envx')

envx.Tool('vivado')
envx.Tool('questa')

после чего можно пользоваться всеми средствами инструментов, начиная от изменения значений переменных СО, заданных инструментами по умолчанию, и заканчивая построением непосредственно сценария сборки, устанавливая связь "цель→зависимость" с помощью билдеров (псевдобилдеров).

Реализация сценария сборки

Общий план

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

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

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

Подготовка параметров

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

cfg  = import_config('main.yml')
dirs = import_config('dirpath.yml')

Технически объекты cfg, dirs являются классами Python, что позволяет работать с ними в удобной нотации <object>.<name>.

Настройка сборочного окружения

Здесь выполняется основная работа по подготовке и настройке внутреннего инструментария сборки:

Import('envx')        

#  tools
envx.Tool('vivado')   
envx.Tool('questa') 

# some project parameters
envx['VIVADO_PROJECT_NAME'] = cfg.PROJECT_NAME   
envx['TOP_NAME']            = cfg.TOP_NAME       
envx['TESTBENCH_NAME']      = cfg.TESTBENCH_NAME  
envx['DEVICE']              = cfg.DEVICE         

# path
envx['SETTINGS_SEARCH_PATH'] = [dirs.SETTINGS]       # dirs for setting files (typically *.yml) need for import scanners
envx['INC_PATH']             = envx['BUILD_SRC_PATH']
envx['SIM_INC_PATH']         = envx['INC_PATH']

# simulator invocation flags
envx.Append(VLOG_FLAGS = ' -O5 -timescale=1ns/1ps')
envx.Append(VOPT_FLAGS = ' -O5 -L wlib -L unifast_ver -L unisims_ver -L unimacro_ver -L secureip -L xpmlib -L ipsimlib')

# user-defined parameters
envx.Append(USER_DEFINED_PARAMS = {'ROOT_DIR'      : envx['ROOT_PATH']})
envx.Append(USER_DEFINED_PARAMS = {'CFG_DIR'       : envx['CFG_PATH']})
envx.Append(USER_DEFINED_PARAMS = {'BUILD_SRC_DIR' : envx['BUILD_SRC_PATH']})
...

Подготовка списков исходных файлов

Подготовка списков исходных файлов — это чтение конфигурационных файлов со списками и формирование списков языка Python, составляющих зависимости для различных целей, например:

src     = read_sources('src_syn.yml')
src_sim = read_sources('src_sim.yml')
ip      = read_sources('ip.yml')

src_par = 'main.yml clk.yml'
src_xpr = ['src_syn.yml', 'xdc.yml', 'xpr_hook.tcl']

В этом примере создаются разные списки исходных файлов для описания различных целей. Списки создаются как с помощью чтения конфигурационных файлов (src, src_sim, ip), так и вручную (src_par, src_xpr). Поддерживается строковое описание (src_par) и в виде списка ЯП Python (src_xpr).

Описание целей

Описание целей технически является простой задачей — просто указать зависимости с помощью билдеров. Но в действительности это самая сложная часть, она требует хорошего понимания, что из чего строится и от чего зависит. Например, цель "Создание проекта Vivado" зависит от src_xpr (см. пример выше) и от IP ядер, т.е. если поменялся один из файлов в списке src_xpr или обновилось хотя бы одно IP ядро, то проект Vivado будет пересоздан. Но помимо явных зависимостей есть ещё неявные, изменение которых тоже влияет на цель. К таким неявным зависимостям относятся, в частности, файлы, создаваемые из параметров — в данном примере, это заголовочный файл Verilog/SystemVerilog для HDL и Tcl для скриптов, запускаемых во время создания проекта Vivado.

...
#   scripts
IP_Create_Scripts  = envx.IpCreateScripts(ip)
IP_Cores           = envx.CreateIps(IP_Create_Scripts)

...
#   generated sources
CfgParamsHeader    = envx.CreateCfgParamsHeader(os.path.join(envx['BUILD_SRC_PATH'], 'cfg_params.svh'), src_par)
CfgParamsTcl       = envx.CreateCfgParamsTcl(os.path.join(envx['BUILD_SRC_PATH'], 'cfg_params.tcl'), 'params.yml')
...
VivadoProject      = envx.CreateVivadoProject(src_xpr , IP_Cores)
...
Depends(VivadoProject, [CfgParamsHeader, CfgParamsTcl])
...

В этом примере показаны все зависимости, необходимые для создания цели VivadoProject. Указание явной зависимости через Depends() вынудит SCons проверить зависимость промежуточных целей CfgParamsHeader и CfgParamsTcl от их источников, которыми являются конфигурационные файлы с параметрами проекта. Таким образом, если, например, изменён какой-то файл с параметром, влияющий на любой из этих генерируемых файлов, то при запуске сборки цели VivadoProject эти промежуточные файлы будут обновлены перед запуском обновления основной цели.

Это один из вариантов решения. Цель примера — проиллюстрировать приблизительный путь работы с целями и зависимостями.

Для удобства использования при запуске SCons предусмотрена возможность создавать псевдонимы целей:

envx.Alias('cparam',     CfgParamsHeader)
envx.Alias('cparam-tcl', [CfgParamsTcl])
envx.Alias('xpr',        VivadoProject)

Теперь достаточно указать имя псевдонима в командной строке запуска SCons.

Рабочий пример сборочного сценария можно найти в тестовом проекте.

⚠️ **GitHub.com Fallback** ⚠️