Разработка программного кода для цифровых сигнальных процессоров TMS320C6000
Статья открывает новый цикл, в котором будут показаны примеры реализации алгоритмов цифровой обработки сигналов (ЦОС) на базе цифровых сигнальных процессоров (ЦСП) фирмы TI. Цель предлагаемых материалов — показать технологию программирования ЦСП фирмы TI от постановки задачи на разработку программной реализации алгоритма ЦОС до получения бинарного кода для ЦСП, его отладки и оптимизации с помощью аппаратных и программных средств, предоставляемых фирмой TI. Предлагаемая программная модель реализации алгоритмов ЦОС учитывает требования стандарта eXpresDSP, внедряемого фирмой TI. Кроме того, модель предполагает максимально возможную переносимость разработанного программного кода между различными семействами ЦСП фирмы TI.
В ближайших выпусках цикла будут рассмотрены общие положения ЦОС, рекомендации по написанию программного кода алгоритмов ЦОС на примере фильтра с конечной импульсной характеристикой (в дальнейшем — КИХ-фильтр), использование интегральной среды разработки (ИСР) CCS для реализации КИХ-фильтра на ЦСП TMS320C6000, тестирование и отладка полученного кода с помощью CCS и отладочного модуля на основе ЦСП TMS320C6211. Использование младшего процессора из серии TMS320C6000 обеспечивает полную совместимость программного кода со всеми старшими ЦСП данного семейства.
Цифровая обработка сигналов (ЦОС) — это дисциплина, изучающая дискретные и цифровые сигналы, системы их обработки, а также цифровые процессоры, реализующие данные системы.
Сигнал — это изменение параметров среды распространения в зависимости от передаваемого сообщения, описываемое функцией времени. В качестве среды распространения могут выступать: электромагнитное поле (радиосигнал), воздух (звуковой сигнал), вода (гидроакустический сигнал), почва (сейсмический сигнал) и т. д.
В ЦОС различают четыре типа сигналов:
-
Аналоговый (рис. 1, а) — это сигнал, непрерывный во времени и по значению. Описывается непрерывной (или кусочно-непрерывной) функцией времени x(t). Аргумент и функция могут принимать любые значения из некоторых произвольных интервалов:
Рис. 1. Основные типы сигналов
-
Дискретный (рис. 1, б) — это сигнал, дискретный во времени и непрерывный по значению. Представляет собой последовательность чисел, называемых отсчетами. Описывается решетчатой функцией x(nT), где n = 0, 1, 2, 3… — номер отсчета, а Т — интервал между отсчетами, называемый периодом дискретизации. Обратную величину 1/Т называют частотой дискретизации
Решетчатая функция определена только в моменты времени t = nT и может принимать произвольное значение из некоторого произвольного интервала X
minx(nT)
X
max. - Цифровой (рис. 1, в) — это сигнал, дискретный во времени и квантованный по значению. Описывается решетчатой функцией, которая может принимать только конечное число значений из некоторого конечного интервала. Эти значения называются уровнями квантования, а соответствующая функция — квантованной.
-
Цифро-аналоговый (рис. 1, г) — это сигнал, непрерывный во времени и квантованный по значению. Описывается непрерывной (или кусочно-непрерывной) функцией времени x
ц(t), причем аргумент может принимать любые значения из некоторого интервала t’t
t», а сама функция — только конечное число значений из некоторого конечного интервала x’
x
x», то есть является квантованной.
В частотной области для описания сигналов используется преобразование Фурье. Оно представляет собой пару соотношений (функции прямого и обратного преобразования), которые устанавливают взаимнооднозначное соответствие между сигналом и спектром. Причем, под спектром понимается функция прямого преобразования Фурье, а под сигналом — функция обратного преобразования Фурье.
Обобщенная процедура ЦОС включает три этапа:
- Преобразование входного аналогового сигнала в дискретный сигнал.
- Обработка дискретного сигнала по заданному алгоритму цифровым сигнальным процессором (ЦСП) и формирование выходного дискретного сигнала.
- Преобразование дискретного сигнала в выходной аналоговый.

Система ЦОС, реализующая процедуру ЦОС, включает (рис. 2):
- Аналоговый антиэлайсинговый фильтр низкой частоты (АФНЧ). Обеспечивает корректность преобразования аналогового сигнала в дискретный.
- Аналого-цифровой преобразователь (АЦП). Формирует из аналогового сигнала ~x(nT) цифровой x(nT) и выполняет две функции: дискретизацию во времени и квантование по уровням.
- Цифровой сигнальный процессор (ЦСП). Вычисляет по заданному алгоритму выходной отсчет y(nT) в зависимости от входного отсчета x(nT).
- Цифро-аналоговый преобразователь (ЦАП). Формирует цифро-аналоговый сигнал ~y(t).
- Аналоговый сглаживающий фильтр низкой частоты (СФНЧ). С его помощью сигнал ~y(t) преобразуется в аналоговый сигнал y(t).
При анализе дискретных сигналов удобно пользоваться нормированным временем:

Таким образом, номер отсчета n интерпретируется как нормированное время. Переход к нормированному времени позволяет рассматривать дискретный сигнал как функцию целочисленной переменной. Представления дискретного сигнала x(n) и x(nT) являются равнозначными. Используется то, которое наиболее удобно в каждом конкретном случае.
Основной математической моделью для описания систем ЦОС служит разностное уравнение (уравнение вход-выход), связывающее входное воздействие x(n) и реакцию системы на него y(n):

где bi и ai — весовые постоянные коэффициенты. Конкретный набор коэффициентов определяет алгоритм обработки дискретного сигнала. Описываемая соотношением (1.2) цифровая система является линейной и называется линейной дискретной системой (ЛДС). Реализация ЛДС требует только трех операций:
- умножения на постоянный весовой коэффициент;
- суммирования;
- задержки на один период частоты дискретизации (сдвиг).

Поставив в соответствие каждой операции графический символ (рис. 3) и объединив их в соответствии с разностным уравнением, получим еще одну модель представления ЛДС — структурную схему. Пример реализации структурной схемы для разностного уравнения (РУ) вида

показан на рис. 4.

При изучении цифровых систем в качестве испытательных воздействий используются дискретные сигналы, называемые типовыми. Одним из примеров может служить цифровой единичный импульс u
0(n–n
0), равный единице при n = n
0 и равный нулю при остальных значениях n:

где n = 0, ±1, ±2, ±3…, n
0 — целочисленная константа.
Если ЛДС до момента поступления на вход единичного цифрового импульса u
0(n–n
0) находилась в нулевом состоянии, то есть x(n) = 0 и y(n) = 0 для всех n 0(n)) называется импульсной характеристикой h(n) системы ЦОС (ИХ):

Функционирование ЛДС происходит по тактам, равным по длительности периоду дискретизации. Каждый такт включает несколько этапов:
1 этап — поступление текущего отсчета x(n) на вход системы ЦОС;
2 этап — вычисление текущего выходного отсчета y(n) в соответствии с выражением (1.2);
3 этап — поступление выходного текущего отсчета y(n) на выход системы ЦОС;
4 этап — формирование новой задержанной последовательности отсчетов x(n–i) и y(n–i) в соответствии с правилом:

На следующем такте процесс повторяется.
На рис. 5 показан процесс функционирования ЛДС, описываемой РУ (1.3), в случае поступления на вход единичного цифрового импульса, при условии n
0 = 0 (см. (1.2)).

Блок-схема алгоритма функционирования ЛДС показана на рис. 6.

Для программного моделирования алгоритмов ЦОС может быть использована файловая модель. Суть данного подхода заключается в том, что входное воздействие и реакция на него находятся в файлах, которые называются тестовыми векторами. При разработке конкретного алгоритма ЦОС (например, цифровой фильтрации) выбирают воздействие, реакция на которое заранее известна. Таких воздействий может быть несколько.
Обрабатывая входной файл при помощи программной реализации алгоритма, получают выходной файл и убеждаются в соответствии расчетного и полученного результатов. Основные требования файловой модели просты и заключаются в следующем:
- Наличие только одного заголовочного файла в программном коде. В данном файле производится подключение всех необходимых внешних библиотек, объявление констант, массивов, функций, макросов, пользовательских типов и т. д.
- Создание контекстной структуры, включающей все необходимые для функционирования программы глобальные указатели, константы, переменные и т. д.
- Отсутствие статических и динамических переменных и констант. Если их наличие необходимо, они инициализируются как глобальные и включаются в контекстную структуру.
- Создание глобальных переменных, констант, структур и массивов производится в одном отдельном файле. Все глобальные переменные, константы и т. п. должны быть объявлены в заголовочном файле.
- Каждая функция располагается в отдельном файле и должна быть объявлена в заголовочном файле.
- Обмен данными между функциями осуществляется через указатель на контекстную структуру.
- Программный код функций основывается на базовых операторах языка С.
- Функции, реализующие алгоритм ЦОС, не должны быть привязаны к конкретному типу ЦСП. Обмен данными с периферийными устройствами ввода-вывода организуется через входной и выходной буфер.
- Обработка массивов осуществляется через указатели на эти массивы, включенные в контекстную структуру.
- Функция main() не должна включать программный код, реализующий алгоритм цифровой обработки сигналов. Задача данной функции — заполнить входной буфер данными из файла, вызвать основную функцию алгоритма ЦОС и записать результат из выходного буфера в выходной файл.
Структура файловой модели показана на рис. 7.

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

где x(n)— входной цифровой сигнал, y(n) — выходной цифровой сигнал, M — масштабирующий коэффициент, bi — коэффициенты фильтра.
Для генерации программного кода воспользуемся интегрированной средой разработки (ИСР) Visual Studio (VS) версии 5 и выше. Необходимо запустить VS (рис. 8) и создать консольное приложение. Для этого в меню «File» нужно выбрать команду «New» (рис. 9).


Появится окно, показанное на рис. 10.

На вкладке «Projects» данного окна определяется тип приложения, его расположение на диске и задается имя.
Необходимо выбрать тип «Win32 Console Application», указать в окошке «Location» место размещения консольного приложения (в дальнейшем проекта), в окошке «Project name» ввести его имя и нажать кнопку «OK» (подтвердить ввод). В результате появится окно параметров создаваемого приложения.
Все настройки необходимо выставить, как показано на рис. 11, и нажать кнопку «Finish».

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

В результате будет создано консольное приложение, и вид ИСР VS изменится (рис. 13).

Станет активным окно «WorcSpace» и в нем две вкладки: «ClassView» и «FileView». Необходимо перейти на вкладку «FileView», где показана структура проекта.
В директории, указанной в окошке «Location» при создании проекта, появится папка, в которой находятся служебные файлы проекта. Эти файлы определяют тип и структуру создаваемого программного кода (рис. 14). Данные файлы можно редактировать, только имея достаточный опыт в создании подобных проектов. На начальном этапе лучше оставить их без изменений.

После создания проекта к нему подключаются заголовочный файл (с расширением *.h) и файлы с функциями, реализующими алгоритм ЦОС (с расширением *.с или *.cpp). Эти файлы являются текстовыми и содержат исходный программный код алгоритма на языке С. Создать файлы можно в произвольном текстовом редакторе, а затем скопировать в папку с проектом и подключить. Или же файлы можно создать непосредственно в ИСР VS при помощи встроенного текстового редактора. В данной статье рассматривается второй вариант.
Для создания файлов с исходным текстом программного кода необходимо в меню «File» выбрать опцию «New» (рис. 9) и на вкладке «Files» указать тип файла (рис. 15).

Для заголовочного файла выбирают тип «С/С++ Header File», а для файлов с функциями — тип «С++ Source File». Затем необходимо указать место размещения файла в окошке «Location» (обычно это папка проекта) и имя файла в окошке «File name». Необходимо также установить флажок в позиции «Add to project» и убедиться, что в окошке, расположенном ниже флажка, имя проекта указано верно. Определив тип, место и имя файла, подтверждают ввод.

Для реализации файловой модели алгоритма (рис. 16) необходимо создать следующие файлы:
- заголовочный файл «filter.h», содержащий объявления функций, массивов, пользовательских типов и т. д.;
- файл «main.cpp» с функцией main() (соответствует блокам 0, 2, 3, 9 и 10 в блок-схеме на рис. 6);
- файл «init.cpp» с функцией начальной инициализации контекстной структуры (блок 1 на рис. 6);
- файл «run.cpp» с функцией запуска алгоритма обработки входного буфера (блоки 4, 5, 7 на рис. 6);
- файл «convolution.cpp» с функцией, выполняющей операции свертки и формирования новой задержанной последовательности (блоки 6, 8 на рис. 6);
- файл «const.cpp», со всеми необходимыми глобальными константами, переменными, массивами и т. д.
Данная структура проекта удовлетворяет требованиям файловой модели и, с точки зрения автора, является наиболее рациональной, но не единственно возможной.
После создания всех необходимых файлов ИСР VS примет вид, показанный на рис. 16. Можно выбрать мышкой любой файл и в клиентской области ИСР VS откроется область его редактирования, где необходимо набрать программный код (рис. 17). Вначале поле редактирования пусто. Необходимо набрать текст программного кода, как в обычном текстовом редакторе.

Для программной реализации алгоритма (1.7) необходимо определить тип контекстной структуры со следующими полями:
- указатель на входной буфер;
- указатель на выходной буфер;
- длина входного и выходного буферов;
- указатель на буфер линии задержки;
- указатель на буфер с коэффициентами фильтра;
- длина буферов линии задержки и коэффициентов.
Тип контекстной структуры определяется в файле «filter.h» следующим образом:
|
Затем в файле «const.cpp» необходимо создать контекстную структуру данного типа и указатель на нее:
|
Кроме того, в этом же файле создаются массивы:
- для входных данных;
- для выходных данных;
- для линии задержки;
- для буфера коэффициентов.
Создание массива происходит следующим образом:
|
Размеры массивов для входных и выходных данных, а также для линии задержки указываются через макросы, которые определяются в файле «filter.h»:
|
Кроме того, в этом же файле вводятся новые имена стандартных типов:
|
Этот шаг обусловлен тем, что разрядность стандартных типов часто зависит от используемого компилятора, а при разработке алгоритма для ЦСП необходимо строго контролировать разрядность переменных. При использовании новых имен для стандартных типов всегда точно известна разрядность переменных. Если необходимо использовать другой компилятор и там изменилась разрядность стандартных типов, то это можно легко компенсировать изменением нескольких строк программного кода в заголовочном файле.
Буфер коэффициентов непосредственно инициализируется при создании значениями коэффициентов:
|
Полный листинг файла «const.cpp»:
|
Инициализацию контекстной структуры выполняет функция инициализации initFilter():
|
Обработку буфера осуществляет функция runFilter():
|
Функции инициализации контекстной структуры и обработки входного буфера должны быть объявлены в заголовочном файле следующим образом:
|

Вычисления выходного отсчета осуществляет функция свертки convolution() (см. рис. 18):
|
Данная функция также должна быть объявлена в заголовочном файле:
|
Окончательный вид заголовочного файла «filter.h» будет иметь следующий вид:
|
Завершает разработку программного кода фильтра функция main(). Ее главная задача — провести тестирование функций, реализующих разрабатываемый алгоритм, путем имитации функционирования разработанного кода. Это достигается за счет поступления отсчетов входного сигнала из входного файлаво входной буфер, в вызове функции обработки этого буфера и сохранении результата работы программы (выходной буфер) в выходном файле. Листинг функции main():
|
В рассматриваемом примере фильтр соответствует требованиям:
- частота дискретизации — 8 000 Гц;
- полоса пропускания — 1 600 Гц;
- полоса задерживания — 2 400 Гц;
- отклонение в полосе пропускания — 1 дБ;
- отклонение в полосе задерживания — –20 дБ;
- число коэффициентов — 10;
- импульсная характеристика — симметричная, с четным количеством коэффициентов.
Конкретные значения коэффициентов фильтра могут быть рассчитаны, например, в программе MatLab или FD-3. Результат расчета в программе FD-3 показан в таблице.
№ п/п | Коэффициент с фиксированной точкой | Целочисленное представление коэффициентов х(2 15–1) |
0 | 0,034449903800000 | 1128 |
1 | –0,072134019699999 | -2364 |
2 | –0,090314822399999 | -2960 |
3 | 0,139855699899999 | 4582 |
4 | 0,447167894399999 | 14652 |
5 | 0,447167894399999 | 14652 |
6 | 0,139855699899999 | 4582 |
7 | –0,090314822399999 | -2960 |
8 | –0,072134019699999 | -2364 |
9 | 0,34449903800000 | 1128 |
M | 0,857374999999999 | 28093 |
На рис. 19 и рис. 20 показаны расчетные амплитудно-частотная и импульсная характеристики фильтра.


В рассмотренной программной реализации используется целочисленное представление коэффициентов. Для этого необходимо все коэффициенты умножить на нормирующий множитель и округлить результат до целого значения. Чем большая точность представления коэффициентов требуется, тем большее значение нормирующего множителя необходимо устанавливать. В данном примере точность ограничивается 16 разрядами, поэтому нормирующий множитель равен 2
15–1, так как один разряд знаковый.
Применение целочисленного представления коэффициентов приводит к тому, что результат умножения больше истинного. Поэтому результат умножения необходимо разделить на нормирующий множитель (нормировать). Так как нормирующий множитель выбран равным степени двойки, то деление можно заменить сдвигом вправо. Это и сделано в примере программного кода.
Все набранные в текстовом редакторе VS файлы должны быть сохранены нажатием одной из двух (

) клавиш на кнопочной панели.
Программный код, набранный в текстовом виде (файлы с расширением «.h» и «.cpp»), необходимо преобразовать в исполняемый модуль (файл с расширением «.exe»). Эта операция называется компиляцией, выполняется компилятором (в данном случае VS) и включает два этапа:
- трансляция каждого файла с кодом программы (транслируются только файлы с расширением «.cpp», заголовочные файлы подключаются автоматически за счет директивы «#include») в объектные модули (файлы с расширением «.obj»);
- компоновка (или линковка) объектных файлов в исполняемый модуль.
На первом этапе проверяется соответствие программного кода каждого файла в отдельности требованиям языка С. На втором — взаимосвязь между частями программы (функциями, константами, массивами, подключаемыми внешними библиотеками и т. д.).
В VS определены три режима компиляции:
- трансляция отдельного файла и создание его объектного модуля;
- трансляция только тех файлов, которые были изменены, в объектные модули, линковка всего проекта и получение нового исполняемого модуля;
- трансляция и линковка всех файлов, построение нового исполняемого модуля.


Первый режим (рис. 21a) используется для проверки синтаксиса отдельного файла. Третий режим (рис. 21c) обычно используется при начальной компиляции проекта и периодического контроля правильности выполнения компиляции во втором режиме. Это обусловлено тем, что время компиляции для третьего режима самое большое и для реальных проектов может достигать десятка минут. Наиболее часто при отладке программы используется второй режим (рис. 21b). Он позволяет значительно экономить время компиляции, но иногда могут возникнуть сбои, поэтому периодически необходимо проводить компиляцию в третьем режиме.
В результате компиляции создается исполняемый модуль (файл с расширением .exe), который находится в папке проекта «debug». Запустить файл на выполнение можно непосредственно из операционной системы. Но лучше всего сделать это в среде VS, нажав клавишу

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

На этом построение файловой модели можно считать завершенным. Однако немаловажную роль в разработке программного кода играет процесс отладки. Во-первых, необходимо постараться выявить все ошибки в коде. Во-вторых, необходимо оптимизировать программный код для выбранного ЦСП.
Вторая задача будет подробно рассмотрена в третьей статье данного цикла. А сейчас ознакомимся с некоторыми способами поиска ошибок в программном коде.
В процессе создания программного кода могут возникнуть ошибки трех типов:
- синтаксические ошибки;
- ошибки линковки;
- ошибки выполнения.
Синтаксические ошибки вызваны несоответствием требованиям языка программирования. Определяются они на первом этапе компиляции. Если ошибка данного типа произошла, появляется информационное сообщение об ошибке и ее типе в одном из окон ИСР VS (рис. 23). Необходимо навести курсор на описание выявленной ошибки и щелкнуть мышкой. Курсор переместится к месту выявленной ошибки в тексте программного кода. Однако нужно помнить, что компилятор иногда выставляет курсор не на ошибку, а на строку ниже. Поэтому необходимо проверить не только выделенную строку, но и предыдущие.

Ошибки линковки обусловлены несоответствием связей между отдельными компонентами программы. Например, была объявлена некая функция в заголовочном файле проекта, в одной из функций она была использована (вызвана), а файл с описанием самой функции отсутствует или не подключен к проекту (рис. 24). Такие ошибки выявляются на втором этапе компиляции.

Ошибки выполнения связаны, в первую очередь, с нарушением логики работы программы. Они выявляются в процессе работы программы на ПК. К таким ошибкам можно отнести деление на ноль, использование созданной, но не инициализированной переменной, выход за пределы массива. Перечисленные ошибки обычно приводят к аварийному завершению выполнения программы (рис. 25). Для выявления таких ошибок используют запуск программы в режиме отладки. Этот режим задается нажатием клавиши

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

В качестве метода поиска ошибки в данном случае можно предложить следующий алгоритм:
- В проверяемой функции исключить из процесса выполнения все внешние функции. Для этого вызываемые функции могут быть закомментированы, или вместо них вставляют код, имитирующий их работу. Например, если результатом работы вызываемой функции является заполнение некоторого буфера, тогда вставляем код, который просто заполняет данный буфер произвольными числами (или одним и тем же числом).
- Устранить все выявленные ошибки в проверяемой функции.
- Подключить (раскомментировать) по очереди все вызываемые функции и убедиться в работоспособности программы. Если появилась ошибка, тогда перейти к функции, вызвавшей ошибку, и повторить алгоритм проверки для нее заново.
По этому алгоритму необходимо проверить все функции разрабатываемого проекта, начав с функции main(). Еще существуют ошибки выполнения, обусловленные некорректной реализацией самого алгоритма. При этом программа работает без сбоев, но выдает неправильный результат. Выявляются такие ошибки путем подачи на вход цифрового устройства типового воздействия с заранее известной реакцией. Сравнивая полученную реакцию с требуемой, делают вывод о степени соответствия реализованного алгоритма поставленной задаче.
В рассматриваемом примере реализован цифровой фильтр на базе нерекурсивного разностного уравнения (2.1). Одним из его свойств является то, что его импульсная характеристика (ИХ) конечна и равна (с точностью до нормирующего множителя M) коэффициентам фильтра. Кроме этого, преобразование Фурье от ИХ для любого фильтра есть частотная характеристика (ЧХ). Модуль ЧХ— это амплитудно-частотная характеристика (АЧХ). Поэтому для контроля корректной работы фильтра можно в качестве тестового сигнала использовать единичный цифровой импульс, описываемый выражением (1.4), где n0 = 0. Данный сигнал равен единице при n = 0 и равен нулю при остальных значениях n. Реакция фильтра на него при нулевых начальных условиях (y(n) и x(n)=0, для n
В начале необходимо сформировать входной сигнал, соответствующий (1.4). Это можно сделать самостоятельно, написав программу генерации файла бинарного типа, содержащего 16-разрядные (для рассматриваемого примера) целые числа. Программа не очень сложная даже для программиста невысокой квалификации. Но лучше воспользоваться специализированным ПО. В частности, автор пользуется программой EDSW. Скачать демо-версию этой программы можно с сайта www.dsp-sut.spb.ru. Программа не только позволяет сгенерировать файл с нужным сигналом, но и предоставляет инструменты для анализа результата. Пример работы программы показан на рис. 26. На левом верхнем графике изображен входной сигнал (единичный цифровой импульс), на левом нижнем — реакция (ИХ), на правом — модуль преобразования Фурье (АЧХ).

Сравнивая результат работы фильтра (рис. 26) с предъявленными требованиями (рис. 19 и рис. 20) видим, что отклонение в полосе пропускания несколько больше заданных требований. Это вызвано квантованием коэффициентов фильтра. Для устранения погрешности можно повысить точность представления коэффициентов фильтра, увеличить его порядок, или изменить структуру фильтра. В каждом конкретном случае решение принимает разработчик, исходя из условий конкретной задачи.