Язык Tconf для DSP/BIOS
Введение
Язык Tconf используется для конфигурации DSP/BIOS. Он основан на JavaScript. Более подробная информация о языке Tconf приведена в документе «DSP/BIOS Textual Configuration (Tconf) User’s Guide» (SPRU007).
Стандарт описывает соглашения по использованию скриптов Tconf при конфигурации проекта. Компания Texas Instruments с 1990 года разрабатывает и улучшает этот стандарт. Сторонним разработчикам программного обеспечения рекомендуется ему следовать. При этом исполнение требований стандарта дает следующие преимущества:
- Упрощается применение модулей. При использовании стандарта программисты знают, где найти определения символов, как создать и использовать различные объекты.
- Упрощается переход к новым ревизиям чипов и увеличивается производительность программного кода. Простая структура и понятные соглашения делают рассмотрение кода, созданного другими, более легким.
- Наличие инструментальной поддержки. Если программист придерживается стандарта, ему доступны простые инструменты для выполнения всех необходимых задач.
Модули
Система программного обеспечения состоит из иерархии модулей. Каждый модуль включает в себя набор взаимосвязанных констант, переменных и функций. И хотя DSP/BIOS предоставляет программисту широкий набор предопределенных модулей Tconf, имеется возможность создавать собственные. Так же как и в языке Cи, определение модуля Tconf должно содержать интерфейс и выполняемые им функции в одном файле.
Введем несколько терминологических определений, которые будут полезны для понимания модульности проекта:
- Модуль. Наименьшая логическая единица программного обеспечения. Модуль должен содержать все свои функции (а также их описание) и переменные в одном файле.
- Клиентское приложение (клиент). Программа, которая вызывает функции интерфейса для выполнения прикладной задачи. Это или *.tcf файл, или один из *.tci файлов.
- Интерфейс. Набор взаимосвязанных констант, типов, переменных и функций.
- Реализация. Исходный текст функций в модуле.
Лексика модуля
Каждый модуль определяется в своем файле, который должен иметь такое же имя, что и модуль. Например, если модуль называется myModule, то он должен быть определен в файле с именем myModule.tci. Все идентификаторы и функции, определенные в этом модуле, должны иметь префикс с названием модуля. Например, в файле myModule.tci могло бы быть примерно такое содержание:
myModule.myInteger = 1; myModule.myString = "foo"; myModule.myFunction = function (argument1, argument2) { ... }
Переменные, определенные в пределах модуля, должны иметь префикс с именем модуля для того, чтобы предотвратить коллизии имен. Переменные модуля нельзя создавать при помощи ключевого слова var.
Язык Tconf (и JavaScript) позволяет объявлять как локальные, так и глобальные переменные. Чтобы создать локальную переменную, необходимо использовать ключевое слово var, и, кроме того, переменная должна быть объявлена в теле функции. Например, в следующем кодексе только myModule.var_c является локальной переменной:
/* myModule.tci */ var myModule.var_a = 0; myModule.var_b = 0; myModule.myFunction = function (argument1, argument2) { var myModule.var_c = 0; myModule.var_d = 0; }
Переменные Tconf, определенные в пределах модуля и вне тела функции, всегда являются глобальными переменными. Если глобальная переменная foo определена в myModule.tci и позже еще раз определена в другом файле, тогда foo из myModule.tci будет переписана.
Локальные переменные, такие как myModule.var_c из предыдущего примера, можно определять без префикса с именем модуля.
Создание модуля
Файлы модуля Tconf должны включать следующее содержание (контент):
- Баннер. Каждый модуль содержит баннер с комментариями, которые описывают модуль.
-
Объект модуля (объект). Модуль должен начинаться с создания объекта модуля с таким же именем, что и у самого модуля. Объект модуля всегда нужно создавать вначале, его нельзя включать в алфавитный список переменных. Например, объект модуля в файле myModule.tci должен быть создан самым первым, до создания чего бы то ни было в этом файле:
var myModule = {};
Если у модуля есть какие-нибудь внутренние функции, «внутренний» объект, содержащий эти функции, должен быть объявлен сразу же после объекта модуля. Дополнительная информация о внутренних функциях приведена в п. 6.
- Объявление переменных. Все переменные модуля должны быть определены и перечислены в алфавитном порядке после создания объекта модуля. (Заметим, что локальные переменные, определенные в пределах функций, не включаются в этот список.)
- Объявление функций. Все функции должны быть определены в алфавитном порядке после последнего определения модульных переменных.
-
Функция init. У всех модулей должна быть функция init, которая явно вызывается в конце интерфейса модуля. Это требуется, даже если модуль не нуждается в функции init. В этом случае создается пустая функция init. Например, функция init в myModule.tci может быть примерно такой:
myModule.init = function() { }
В конце файла модуля функция init должна быть вызвана примерно так:
myModule.init();
-
Внутренние функции. Если в модуле определяется функция, которая является внутренней по отношению к модулю, то эта функция должна быть определена через «внутренний» объект. Таким образом, если необходимо создать внутреннюю функцию, сначала создается внутренний объект, который должен быть определен сразу после создания объекта модуля:
var myModule.internal = {};
Внутренние функции должны быть определены через «внутренний» объект следующим образом:
myModule.internal.myInternalFxn = function() { }
Функции, которые определены как внутренние, как правило, создает проектировщик модуля. Эти функции обычно используются для специальных операций. Внутренние функции не должны использоваться за пределами модуля, в котором они созданы.
Вот пример файла myModule.tci с определением модуля myModule:
/* * myModule.tci */ /* Определение объекта модуля myModule */ var myModule = {}; /* Определение переменных модуля в алфавитном порядке */ myModule.myInteger = 1; myModule.myString = "hello"; /* Определение функций модуля в алфавитном порядке */ myModule.foo = function() { } myModule.init = function() { } myModule.xyz = function() { /* * Эти переменные не должны объявляться выше * в алфавитном списке, потому что они являются * локальными в данной функции */ var x; var y; var z; x = y = z = 2; } /* Явный вызов функции init */ myModule.init();
Исходные файлы модуля
Приведем листинги одного *.tcf и двух *.tci файлов, взятые из стандартных примеров для DSP/BIOS. Изучите структуру этих файлов, ее мы объясним в следующем разделе, где показано, как создать собственный скрипт Tconf.
Файл TCF: hostio.tcf
В начале этого файла находятся строки, в которых импортируются файлы TCI:
/* * Авторские права с 2004 года принадлежат Texas Instruments Inc. * Все права защищены. Собственность Texas Instruments Inc. * Права на использование, дублирование или раскрытие этого * кода ограничены, код предоставляется только по контракту. * @(#) DSP/BIOS_Examples 5,0,0 05-03-2004 (biosEx-a16) */ /* * ======== hostio.tcf ======== * Пользовательский скрипт конфигурации является общим * конфигурационным файлом большинства примеров */ /* * Импортирование настроек dsk6713, которые являются * общими для большинства примеров. */ utils.importFile("dsk6713_common.tci"); /* * Импортирование конфигурационных настроек hostio, * которые относятся ко всем платформам. */ utils.importFile("hostio.tci"); if (config.hasReportedError == false) { prog.gen(); }
Файл TCI: dsk6713_common.tci
/* * Авторские права с 2004 года принадлежат Texas Instruments Inc. * Все права защищены. Собственность Texas Instruments Inc. * Права на использование, дублирование или раскрытие этого * кода ограничены, код предоставляется только по контракту. * @(#) DSP/BIOS_Examples 5,0,0 05-03-2004 (biosEx-a16) */ /* * ======== dsk6713_common.tci ======== * Специальные настройки только для платформы dsk6713. */ /* * Настройка платформозависимых карты памяти, частоты CLK и т. д. */ var mem_ext = []; mem_ext[0] = { name: "SDRAM", base: 0x80000000, len: 0x00800000, space: "code/data" }; var device_regs = new Object(); device_regs = { l2Mode: «SRAM» }; var params = new Object(); params = { clockRate: 225.0000, catalogName: "ti.catalog.c6000", deviceName: «6713», regs: device_regs, mem: mem_ext }; /* * Настройка выбранной платформы с параметрами, * определенными выше. */ utils.loadPlatform("ti.platforms.generic", params); /* * Разрешение общих для всех примеров настроек BIOS */ bios.enableRealTimeAnalysis(prog); bios.enableMemoryHeaps(prog); bios.enableRtdx(prog); bios.enableTskManager(prog); /* * Определение функции инициализации, которую вызывают * до вызова main() */ bios.GBL.CALLUSERINITFXN = true; bios.GBL.USERINITFXN = prog.extern("GBL_setPLLto225MHz"); /* * Включение heap в SDRAM и определение метки SEG0 * для использования * heap, если в программе определен useMemSettings. */ bios.SDRAM.createHeap = true; bios.SDRAM.heapSize = 0x8000; bios.SDRAM.enableHeapLabel = true; bios.SDRAM["heapLabel"] = prog.decl("SEG0");
Файл TCI: hostio.tci
/* * Авторские права с 2004 года принадлежат Texas Instruments Inc. * Все права защищены. Собственность Texas Instruments Inc. * Права на использование, дублирование или раскрытие этого * кода ограничены, код предоставляется только по контракту. * @(#) DSP/BIOS_Examples 5,0,0 05-03-2004 (biosEx-a16) */ /* * ======== hostio.tci ======== * Включаемый файл Tconf, импортируемый файлом hostio.tcf, * в котором настраиваются глобальные платформонезависимые * объекты BIOS, свойства и параметры. * */ /* * Создание и инициализация двух HST-объектов для входа * и одного для выхода */ var input; input = bios.HST.create("input"); input.frameSize = 64; input.numFrames = 2; input.bufAlign = 4; input.mode = "input"; input.notifyFxn = prog.decl("inputReady"); var output; output = bios.HST.create("output"); output.frameSize = 64; output.numFrames = 2; output.bufAlign = 4; output.mode = "output"; output.notifyFxn = prog.decl("outputReady"); /* * Разрешить все компоненты DSP/BIOS */ var copySwi; copySwi = bios.SWI.create("copySwi"); copySwi["fxn"] = prog.decl("copy"); copySwi.mailbox = 3; copySwi.arg0 = prog.decl("input"); copySwi.arg1 = prog.decl("output"); /* * Создание и инициализация LOG-объектов */ var trace; trace = bios.LOG.create("trace"); /* * Установить длину для буфера LOG_system */ bios.LOG_system.bufLen = 512;
Общая структура исходных файлов
В следующих подразделах описано, как должен быть сгруппирован и организован Tconf-код.
Группирование кода
Код Tconf делится на две категории независимый и зависимый от выбора конкретного типа процессора (платформы) код.
- Файлы с платформонезависимым кодом (сохраняемые в файлах с расширением *.tci) должны содержать код, который является (или может являться) общим для более чем одной стандартной архитектуры (ISA).
- Эти файлы могут импортироваться при помощи скриптов в файл *.tcf или в другой файл *.tci.
- Называть эти файлы рекомендуется в том же стиле, что и С-файлы проекта. Если С-файлы называются в соответствии с их фу нкциональными возможностями, то и файлы с кодом Tconf следует называть так же. Например, файл, который настраивает PIP-объекты DSP/BIOS, можно назвать myPipes.tci.
- Эти файлы должны быть доступны всем ISA, которым они нужны, и сохраняться в общей директории для того, чтобы избежать их дублирования.
- Платформозависимые файлы содержат код, который зависит от спецификации ISA, на котором ведется разработка программы. Многие файлы *.tci попадают в эту категорию. Скрипты в файлах *.tcf обычно так же платформозависимы, так как импортируют платформозависимые файлы (однако возможна разработка и платформонезависимых файлов *.tcf с использованием аргументов командной строки).
Замечание. Для создания конфигурации используются файлы *.tcf, которые импортируют как платформозависимые, так и платформонезависимые файлы *.tci. Таким образом, любая конфигурация производится через файл *.tcf и не может быть выполнена только файлом *.tci.
Организация кода
Код скрипта *.tcf должен поддерживать определенную структуру, описываемую далее. Если размер кода в группе достаточно большой, он должен быть сохранен в отдельном подключаемом файле Tconf (*.tci) и категоризирован либо как платформозависимый, либо как платформонезависимый (см. раздел «Группирование кода»).
- Определить все глобальные переменные. Если эти переменные находятся в отдельном файле *.tci, тогда этот файл должен быть импортирован при помощи функции utils.importFile( ).
- Загрузить как можно раньше аппаратные средства поддержки. Для этого используется функция utils.loadPlatform( ), которая может быть вызвана следующими способами:
-
Зависимый синтаксис (рекомендуемый). При этом способе используется файл *.tci, в котором сначала определяется объект params, а затем вызывается функция:
utils.loadPlatform("ti.platforms.generic", params);
Сам файл *.tci подключается в файле *.tcf. В качестве примера можно посмотреть файлы hostio.tcf и dsk6713_common.tci, листинги которых приведены в разделе «Исходные файлы модуля».
-
Прямой синтаксис. В этом случае вызов функции осуществляется непосредственно в файле *.tcf:
utils.loadPlatform("Dsk5510");
При этом вызов функции должен быть сделан как можно раньше. Эту форму вызова можно использовать и в файле *.tci, однако в этом случае необходимо файл *.tci импортировать в файл *.tcf как можно раньше. В документе «DSP/BIOS Textual Configuration (Tconf) User’s Guide (SPRU007)» описывается применение данной функции для различных платформ.
-
Зависимый синтаксис (рекомендуемый). При этом способе используется файл *.tci, в котором сначала определяется объект params, а затем вызывается функция:
- Определить все платформозависимые свойства, объекты DSP/BIOS, переменные и параметры. Если свойства, объекты, переменные и параметры находятся в отдельном файле *.tci, необходимо его импортировать.
- Определить все платформонезависимые свойства, объекты DSP/BIOS, переменные и параметры. Если свойства, объекты, переменные и параметры находятся в отдельном файле *.tci, необходимо его импортировать.
- Вызвать функцию prog.gen( ) в конце файла. Рекомендуется сначала провести проверку наличия ошибок и осуществить вызов функции следующим образом:
if (config.hasReportedError == false) { prog.gen(); } else { throw new Error("Error in config script: Generated files have not been created."); }
Хотя в приведенном примере у prog.gen( ) и нет никаких параметров, разрешается определять в качестве параметра директорию. Если директория указана, то в ней должны находиться файлы конфигурации. Если аргументов нет, файлы конфигурации размещаются в текущей директории. Важное замечание: если определяется произвольная директория для конфигурационных файлов, тогда ее местоположение должно быть указано в проекте или make-файле для корректного выполнения процесса компиляции.
Основные лексические соглашения
В данном разделе приводятся лексические соглашения, относящиеся ко всем исходным файлам Tconf. Вообще, лексические соглашения для Tconf аналогичны лексическим соглашениям языка C. Более подробно данный вопрос рассмотрен в руководстве «C Language Coding Standards for DSP/BIOS and XDAIS (SPRA788)».
Идентификаторы
В JavaScript для переменных тип явно не указывается, потому что используется динамическое определение типов. Это означает, что тип определяется непосредственно во времени выполнения инициализации переменной. Например:
var myInteger = 10; // целочисленная переменная var myString = «programming is fun»; // строковая переменная
- Имена переменных и функций состоят из ряда целых или сокращенных слов. Первое слово полностью состоит из строчных букв. Последующие слова в имени обычно начинаются с прописной буквы. Подчеркивание не используется между словами в имени, но может отделять суффикс в конце. Например:
var countNumberFrames;
- Языковые ключевые слова всегда сопровождаются знаком ‘;’:
for (i = 0; i < 10; i++) argument1 += argument2; }
Дополнительная информация об идентификаторах приведена в разделе «Объявления».
Выражения
Не должно быть свободного места в унитарных выражениях или между первичными операторами и их операндами.
- Пример унитарного выражения:
-17
- Пример первичных операторов:
a[i] s.member f(x, y)
Свободное пространство может существовать между другими типами операторов и их операндами. Например:
x = f(y) * (z + 2);
Круглые скобки используются для определения порядка вычислений, особенно в выражениях, имеющих несколько вложенных уровней.
Если в языке JavaScript нет явного определения, то порядок вычисления может быть произвольным.
Операторы
Все зависимые операторы закрываются скобками и выравниваются на четыре позиции.
Существует хотя бы один оператор в строке.
Протяженность строки восемь позиций.
Объявления
Каждая переменная или любой другой идентификатор объявляются на отдельной строке:
var x; var y;
Переменные можно объявлять и по-другому, так как JavaScript это язык с динамическим определением типов. Это значит, что тип переменной может быть определен во время выполнения программного кода, и более того, одна и та же переменная может быть использована для хранения данных различных типов. Например, следующий код является правильным:
var myInteger = 100; /* myInteger хранит целочисленное значение */ var myInteger = «hello world»; /* myInteger хранит уже строковую переменную, * перезаписав предыдущее целочисленное значение. */
Рекомендуется использовать при объявлении переменных ключевое слово var как можно чаще. Такое объявление переменных позволяет получить более эффективный и компактный программный код.
Объявление модулей должно происходить в следующем порядке:
- переменные,
- функции.
Определение функций
- Аргументы функции объявляются без указания типа, так как в JavaScript используется их динамическое определение.
- При создании функции необходимо учитывать ее принадлежность модулю.
-
Модульные функции. Для функций, которые принадлежат модулю, используется следующий аргумент:
myModule.myFunction = function (argument1, argument2) { ar i; for (i = 0; i < 10; i++) {/*перед оператором пропустить строку */ argument1 += argument2; } return (argument1); }
-
Внемодульные функции. При создании функций, которые не принадлежат модулю, необходимо придерживаться следующего формата:
function myFunction (argument1) { var x; x = argument1; return (x); }
-
Модульные функции. Для функций, которые принадлежат модулю, используется следующий аргумент:
- Функции должны быть определены в алфавитном порядке.
- Есть одна чистая линия между последней декларацией в теле функции и первым утверждением в функции.
- Тело функции заключается в скобки и выравнивается по четырем позициям.
Комментарии
Для обеспечения совместимости с современными средствами разработки комментарии должны быть отформатированы, как это показано в файле utils.tci. Общие правила следующие:
- Граничные комментарии могут начинаться с нулевой позиции в строке и описывают функции и основные секции. Текст таких комментариев начинается со второй строки и имеет два пробела в начале. Граничные комментарии возможны только между определениями функции, внутри тела функций их использовать нельзя:
/* * Это граничные комментарии. * Важное замечание. Необходимо два пробела от символа '*' */
- Файл с исходным кодом начинается с граничных комментариев, где приводится информация об авторском праве и идентифицируется файл. В некоторых случаях информация об авторском праве и версии добавляется автоматически менеджером исходных скриптов:
/* * Авторские права с 2004 года принадлежат Texas Instruments Inc. * Все права защищены. Собственность Texas Instruments Inc. * Права на использование, дублирование или раскрытие этого * кода ограничены, код предоставляется только по контракту. * */ /* * ======== filename.tci ======== * Комментарий с кратким описанием содержания * и предназначения файла */
- Все функций имеют следующий баннер:
/* * ======== foo ======== * Здесь размещается описание функции. * */
Небольшое замечание. Имя функции выделено двумя последовательностями из восьми знаков ‘=’ и размещено на отдельной строке. Такой стиль баннера помогает находить все функции, объявленные в модуле.
- Многострочные комментарии отформатированы следующим образом. Обратите внимание, что имеется два пробела между звездочкой и началом комментариев:
/* * Это многострочный выровненный * комментарий */
- Выровненные комментарии программных блоков:
while (expr) { /* Это простой выровненный комментарий */ if (expr) { 'statement(s)' } }
- Строчные комментарии описывают отдельные операторы или объявления. Все переменные должны иметь строчные или многострочные комментарии, описывающие их предназначение:
var frameSize; /* размер окна в словах */
- Файл с исходным кодом заканчивается секцией истории ревизий, оформленной в виде граничных комментариев, как это показано далее. Важно отметить, что перед продолжением длинного комментария на новой строке добавляется четыре пробела.
/*! *! История ревизий *! ================ *! 23-янв-2001 года: Все комментарии в секции истории ревизий *! должны начинаться с символа восклицательного знака, *! что позволит, в случае необходимости, их удалить. *! 24-дек-2000 года: Ревизии перечисляются в обратном *! хронологическом порядке; вначале описывается самая новая. *! При этом используется следующий формат даты: *! ДД-МММ-ГГГГ. *! */
Количество символов в строке и позиции табуляции
Стандартный размер сдвига четыре пробела. Символ табуляции (t) должен быть обработан как восемь пробелов. Исходный код должен содержать или только пробелы и никаких символов табуляции, или сочетания пробелов и символов табуляции, каждый из которых эквивалентен восьми пробелам. Окончательный вариант исходного кода («релиз») должен содержать только пробелы (никаких символов табуляции).
Длина строки не должна превышать восьмидесяти символов. И хотя новые редакторы поддерживают намного более длинные строки, большое количество печатных материалов имеют длину строки 80 символов или меньше. Поэтому для публикации в руководствах используются строки длиной 74 символа.
Обработка особых ситуаций
Код обработки особых ситуаций необходимо, по возможности, использовать максимально часто. Любой код, который может создать ошибку или исключение, должен быть написан с использованием кода обработки особых ситуаций.
Блоки try и catch
Для обработки особых ситуаций используются блоки try и catch. Программный код, который может создать исключение или ошибку, помещается внутрь блока try, а код обработки возникшего исключения помещается внутрь блока catch. Когда возникает исключение в блоке try, его «захватывает» блок catch, сохраняя его как локальную переменную.
Вот пример использования блоков try и catch :
try { var x = 20; var y = 0; var z = x/y; print(x + «/» + y + «= « + z); } catch (e) { //e — переменная, которая содержит исключение print(e); }
Существует еще один блок, называемый finally, включающий код, который гарантированно выполняется независимо от того, какое исключение произошло. Этот блок следует непосредственно за блоком catch, и код, который он включает, выполняется независимо от результата обработки блоков try/catch.
Утверждения
Утверждения используются, чтобы проверить правильность значений. Утверждения можно использовать для проверки корректности возвращаемого значения функции или того, что переменной присвоено правильное значение. Модуль с необходимым кодом для реализации функционала утверждений содержится в файле assert.tci, его желательно включить в файл Tconf с исходным кодом и использовать максимально часто.
Вот пример того, как можно использовать утверждения:
/* вызов функции извлечения квадратного корня из числа 25 */ var x = Math.sqrt(25); /* * добавить утверждения для проверки правильности результата. */ assert.add('x == 5'); /* проверить добавленное утверждение */ assert.check();
Литература
- DSP/BIOS Textual Configuration (Tconf) User’s Guide (SPRU007).
- C Language Coding Standards for DSP/BIOS and XDAIS (SPRA788).
- DSP/BIOS Tconf Language Coding Standards (SPRAA67).