VDK: менеджер отложенных функций обратного вызова |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
В этой статье приведен перевод раздела "Deferred Callback Manager" из документации "VisualDSP++ 5.0 Device Drivers and System Services Manual for Blackfin® Processors" [1]. Описывается менеджер отложенных функций обратного вызова, deferred callback (DCB) manager, используемый разработчиками приложения для управления отложенным выполнением вызовов функции. Имеется также подробное описание интерфейса программирования (API), предоставляемое Менеджером DCB. Рассматриваются следующие вопросы: • Использование Менеджера DCB Функции обратного вызова (callback functions) обычно используются в приложениях, управляемых событиями, где клиент приложения запрашивает от менеджера службы (такой как Менеджер DMA из библиотеки Системных Служб, SSL) оповещение о том, что запрошенная задача выполнена, например завершена передача DMA, через вызов клиентской функции обратного вызова, указанной клиентом приложения в момент инициализации требуемой службы. Необходимость вызова клиентской callback-функции обычно возникает, когда выполняется обработчик прерывания ISR с относительно высоким приоритетом. Основное правило таких ISR - как можно меньше занимать времени на свою обработку, и сохранить свое время выполнения максимально определенным и минимально возможным. С другой стороны, callback-функции могут быть длительными, и с неопределенным заранее временем выполнения. В большинстве случаев пользователю было бы предпочтительнее отложить выполнение таких callback-ов на планировщик, работающий с приоритетом ниже, который мог бы быть вытесненным более высокоприоритетными прерываниями. Если делать так, то запрошенная обработка ISR может завершиться с минимальной задержкой. Менеджер отложенных функций обратного вызова (deferred callback, DCB) из состава Системных Служб предоставляет такой сервис путем обслуживания одной или большего количества очередей отложенных функций обратного вызова, так что их обработка их вызова обычно происходит из функции диспетчера, работающей с приоритетом ниже, чем остальные обработчики прерываний приложения. Такие точки входа в функции обратного вызова ставятся в очередь, с предоставлением адреса требуемой callback-функции вместе с тремя значениями (2 указателя и одно 32-битное целое число без знака), которые передаются в callback-функцию при её (отложенном) запуске. Менеджер DCB разработан как модуль, работающий отдельно, или совместно с операционной системой реального времени (real-time operating system, RTOS). Реализация модуля имеется для систем Express Logic ThreadX, Green Hills Software INTEGRITY, а так же для VDK компании Analog Devices. Количество доступных очередей и их длина определяется клиентским приложением в момент инициализации модуля и очереди. От того, как реализован Менеджер DCB (как отдельный модуль или совместно с RTOS), также зависит количество и размер очередей. При реализации совместно с VDK Менеджер DCB может поддерживать только одну очередь с фиксированным уровнем приоритета IVG 14. Хотя разрешена только одна очередь на один уровень IVG, инженеры могут установить приоритеты для отдельных callback-записей в момент их постановки в очередь. Нет ограничений на количество уровней программного приоритета, которые можно использовать (за исключением практического ограничения на значения числа типа unsigned short). Функция диспетчера пытается выполнить высокоприоритетные callback-и перед теми, у кого программный приоритет ниже, и все они будут выполняться на на одном уровне IVG. Подробное описание работы Менеджера DCB предоставлено в разделе "Использование Менеджера DCB" вместе с кусками кода, иллюстрирующими его использование в отдельном режиме (без RTOS). Как Менеджер DCB использовать совместно с RTOS, показано в разделе "Взаимодействие с RTOS". Менеджер DCB использует однозначное соглашение об именовании функций, типов и макросов API, чтобы избежать конфликтов в с другими программными библиотеками, предоставленными ADI (это компания Analog Devices) или другими компаниями. В результате все значения перечисления (enum) и операторы определения типа (typedef) используют префикс ADI_DCB_, и соответственно функции и глобальные переменные используют adi_dcb_, эквивалентный префикс в нижнем регистре. [Использование Менеджера DCB] Работа Менеджера DCB состоит в выполнении следующих функций. • Настройка Менеджера DCB: Инициализация Менеджера DCB • Обслуживание очереди: Постановка callback-ов в требуемую очередь • Выполнение вспомогательных функций: Закрытие очереди Как все это реализовано - зависит от условий работы Менеджера DCB, т. е. в отдельном приложении (без RTOS), или под управлением механизма отложенных вызовов, предоставляемых RTOS. В любом случае API-вызовы к Менеджеру DCB одинаковые: очередь инициализируется вызовом adi_dcb_Open, и callback-и ставятся в очередь вызовом adi_dcb_Post. Отложенное выполнение callback-ов планируется по программному приоритету с помощью функции adi_dcb_Dispatch_Callbacks. В рабочем окружении обычного приложения (не RTOS) Менеджер DCB регистрирует (в момент инициализации очереди) подпрограмму обработчика прерывания на желаемом уровне IVG, используя модуль Менеджера Прерываний [2] из библиотеки Системных Служб, и прерывание вызывается каждый раз при постановке в очередь callback-а. Поскольку версия без RTOS использует Менеджер Прерываний, то Менеджер Прерываний должен быть инициализирован перед инициализацией Менеджера DCB. Следующий код демонстрирует использование без RTOS на одной очереди, инициализированной на уровне IVG 14, с самым низким уровнем IVG, доступным на уровне приложения. Как уже упоминалось, работа без RTOS требует инициализации Менеджера Прерываний перед инициализацией Менеджера DCB. Если предположить, что пример приложения требует только один обработчик прерывания на один уровень IVG, то Менеджер Прерываний инициализируется следующим кодом: u32 ne; adi_int_Init(NULL,0,&ne,NULL); Инициализация Менеджера DCB памятью, достаточной для одной очереди, следующая: static char mjk_dcb_Data[ADI_DCB_QUEUE_SIZE]; :
u32 ns;
: adi_dcb_Init( (void*)mjk_dcb_Data, // Адрес используемой памяти. ADI_DCB_QUEUE_SIZE, // Количество байт, требуемых для // требуемого количества серверов очереди. &ns // На выходе это должно быть таким же, // как требуемое количество очередей. NULL // Не требуется специальной области данных // для критического региона. ); Далее для использования открывается сервер очереди путем передачи достаточной памяти для требуемой длины очереди (в этом примере очередь на 5 записей) и желаемого уровня IVG, на котором работает очередь. Этот уровень игнорируется приложениями, основанными на VDK. Будет возвращен хендл к серверу очереди p_DCB_handle: static char mjk_dcb_QueueData[5*ADI_DCB_ENTRY_SIZE]; ADI_DCB_HANDLE p_DCB_handle; :
u32 nqe;
: adi_dcb_Open( 14, // Требуемый уровень IVG. (void*) mjk_dcb_QueueData, // Адрес памяти, используемой для 5*ADI_DCB_ENTRY_SIZE, // очереди глубиной в 5 callback-ов. &nqe; // На выходе это должно быть таким же, // как требуемое количество записей // в очереди (5 для нашего примера). &p_DCB_handle // Возвращаемый handle к серверу очереди. ); Теперь Менеджер DCB готов принять постановки в очередь callback-ов для сервера очереди. Обратите внимание, что эта функция обычно выполняется в ISR другой службы. Менеджер DCB передает серверу (идентифицированному по полученному handle) очереди адрес клиентской callback-функции и связанные с ней значения аргументов: adi_dcb_Post( p_DCB_handle, // Хендл к требуемому серверу очереди. 0, // Уровень приоритета. ClientCallback, // Адрес callback-функции. pService, // Адрес экземпляра службы, которая // поставила этот callback в очередь. event, // Флаг, идентифицирующий событие, которое // вызвало прерывание. (void*)data // Адрес данных, относящихся к // callback-функции. ); В примере, приведенном выше, event обычно определяет событие (например, завершение передачи DMA) и data обычно указывают на соответствующее место в памяти, которое имеет значение в контексте callback-функции. В контексте Менеджера DMA, этот аргумент - адрес подходящего дескриптора или буфера данных. По любой причине сброс записей очереди может произойти одним из 2 способов: прямым вызовом функции adi_dcb_Remove, или косвенно вызовом функции adi_dcb_Control. См. описание функции adi_dcb_Terminate для получения подробной информации и примера использования совместно с другими запросами. Следующий код описывает метод прямого вызова: adi_dcb_Remove( p_DCB_handle, // Хендл для требуемого сервера очереди. ClientCallback // Адрес сбрасываемой callback-функции. ); И наконец, если требуется, очередь может быть закрыта и Менеджер DCB остановлен: adi_dcb_Close(
p_DCB_handle, // Хендл к требуемому серверу очереди.
);
adi_dcb_Terminate();
[Взаимодействие с RTOS] Менеджер DCB реализует 2 функции, adi_dcb_RegisterISR и adi_dcb_Forward, для интерфейса с различными рабочими окружениями RTOS, включая выделенный режим (standalone mode, работа без RTOS). Эти функции предоставляются в отдельном файле исходного кода adi_dcb_xxxx.c для каждой реализации рабочего окружения, где xxxx идентифицирует требуемую RTOS (например, threadx для Express Logic ThreadX, и integrity для Green Hill Software INTEGRITY), либо standalone для использования без RTOS. Поддержка для VDK реализована так, что эти вышеописанные функции встроены напрямую в VDK. В результате для случая использования рабочего окружения VDK нет эквивалентного файла adi_dcb_vdk.c. Соответствующий файл adi_dcb_xxxx.c встраивается (или не встраивается) в основной файл adi_dcb.c через операторы условной компиляции, управляемые макросом ADI_SSL_XXXX, где вместо XXXX может быть STANDALONE, THREADX, INTEGRITY или VDK. В нестоящее время реализации Менеджера DCB предоставлены только для ранее описанных рабочих окружений. Чтобы реализовать эти функции для альтернативных операционных систем (например Linux, FreeRTOS и т. п.), разработчики должны предоставить заменяющие определения в эквивалентных файлах. В этой секции функции API описаны более подробно. adi_dcb_Forward. Функция adi_dcb_Forward принимает 2 аргумента. Первый это указатель на структуру ADI_DCB_ENTRY_HDR заголовка записи DCB, и второй это уровень IVG соответствующей очереди. Функция adi_dcb_Forward вызывается из тела функции adi_dcb_Post, и имеет следующий прототип: void adi_dcb_Forward( ADI_DCB_ENTRY_HDR *Entry, u16 IvgLevel); Ниже приведено описание аргументов.
Структура ADI_DCB_ENTRY_HDR используется для передачи информации в нижележащее рабочее окружение RTOS, и она описана так: typedef struct ADI_DCB_ENTRY_HDR { struct ADI_DCB_ENTRY_HDR *pNext; ADI_DCB_DEFERRED_FNpDeferredFunction; } ADI_DCB_ENTRY_HDR; Первое поле в этой структуре это pNext, которое будет NULL на входе в функцию adi_dcb_Forward. В то время как это значение обычно используется для указания на следующий элемент в очереди, его требуемая интерпретация в функции adi_dcb_Forward зависит от специфики реализации RTOS. Второе поле pDeferredFunction устанавливается для указания на функцию adi_dcb_DispatchCallbacks, когда инициализируется очередь. Сервер вызовов отложенных процедур в соответствующей RTOS должен передать указатель в функцию adi_dcb_DispatchCallbacks при выполнении отложенной функции. adi_dcb_RegisterISR. Функция adi_dcb_RegisterISR вызывается из тела функции adi_dcb_Open, и имеет следующий прототип: void adi_dcb_RegisterISR( u16 IvgLevel, ADI_INT_HANDLER_FN Dispatcher, ADI_DCB_HANDLE *hServer); Типы данных определены в заголовочном файле < services/services.h >, и аргументы функции следующие.
В выделенной реализации (без RTOS) эта функция регистрирует функцию adi_dcb_DispatchCallbacks с Менеджером Прерываний на указанном уровне прерываний. В реализации VDK она делает возврат без всякого эффекта. Обработка критических регионов в callback-функции. Если требуется наличие критического региона в теле функции обратного вызова, Вы должны избегать любых ограничений, которые накладывает применение нижележащей RTOS. Например, для VDK-приложения запрещено вызывать PushCriticalRegion/PopCriticalRegion из уровня прерываний. Если используется VDK-версия Менеджера DCB, вызовы такого плана можно использовать, поскольку callback-функция выполняется на уровне кода ядра. Однако если используется выделенная (без RTOS) версия библиотеки для запуска очереди DCB с приоритетом выше, чем очередь отложенных вызовов VDK, то такие вызовы недопустимы, поскольку callback выполняется на уровне прерывания. В этих случаях эффект критического региона может быть получен прямым использованием встроенных функций cli() и sti(). [Документация API Менеджера DCB] В этой секции предоставлено описание API-функций Менеджера DCB. Функция adi_dcb_Close() закрывает очередь DCB для сервера, идентифицируемого по единственному аргументу (хендл сервера), с освобождением слота для последующего использования. В выделенном режиме (без RTOS) функция Менеджера DCB adi_dcb_DispatchCallbacks отцепляется от цепочки обработчиков прерывания на указанном уровне IVG. ADI_DCB_RESULT adi_dcb_Close(ADI_DCB_HANDLE hServer);
Аргумент:
Возвращаемые значения:
Функция adi_dcb_Control() используется для конфигурирования/управления сервером очереди callback-функций в соответствии с парами команда-значение (параметры Command и Value). Для дополнительной информации см. описание ADI_DCB_COMMAND_PAIR. Прототип функции: ADI_DCB_RESULT adi_dcb_Control( ADI_DCB_HANDLE hServer, ADI_DCB_COMMAND Command, void *Value); В настоящий момент имеет значение только одна команда, ADI_DCB_CMD_FLUSH_QUEUE, хотя в будущем могут быть добавлены и другие. Пары команда-значение можно указать тремя способами: • Передается одна пара command-value. adi_dcb_Control( hServer, ADI_DCB_CMD_FLUSH_QUEUE, (void*)ClientCallback); • Передача одной пары command-value с использованием структуры. ADI_DCB_COMMAND_PAIR cmd = { ADI_DCB_CMD_FLUSH_QUEUE, (void *)ClientCallback }; adi_dcb_Control( hServer, ADI_DCB_CMD_PAIR, (void*)&cmd); • Передается таблица (массив) структур ADI_DCB_COMMAND_PAIR. Последней записью в таблице должна быть ADI_DCB_CMD_END. ADI_DCB_COMMAND_PAIR table[2] = { { ADI_DCB_CMD_FLUSH_QUEUE, (void*)ClientCallback }, { ADI_DCB_CMD_END, 0 } }; adi_dcb_Control( hServer, ADI_DCB_CMD_TABLE, (void*)table); Полный список команд и связанные значения см. в описании ADI_DCB_COMMAND. Аргументы:
Возвращаемое значение:
Функция adi_dcb_Init инициализирует Менеджер DCB с предоставлением достаточного объема памяти для очереди из требуемого количества отложенных вызовов функций. Эта функция может быть вызвана один раз на процессорное ядро. ADI_DCB_RESULT adi_dcb_Init( void *ServerMemData, size_t szServer, u32 *NumServers void *hCriticalRegionData); Аргументы:
Возвращаемое значение:
Функция adi_dcb_Open открывает сервер очереди для использования назначенной памяти для её callback-очереди. Дополнительно, в выделенном режиме (без RTOS) очередь назначается на запрашиваемый уровень IVG и функция adi_dcb_DispatchCallbacks Менеджера DCB подцепляется к цепочке обработчиков прерываний с помощью Менеджера Прерываний, для обработки на имеющемся уровне IVG. ! Менеджер Прерываний должен быть инициализирован перед открытием сервера очереди. ADI_DCB_RESULT adi_dcb_Open( u32 IvgLevel, void *QueueMemData, size_t szQueue, u32 *NumEntries, ADI_DCB_HANDLE *hServer); Аргументы:
Возвращаемое значение:
Функция adi_dcb_Post() ставит в очередь (для последующей обработки) callback-функцию со связанными аргументами. Сервер очереди идентифицируется по аргументу хендла hServer. Функция обратного вызова (callback) привязывается к уровню приоритета, так что callback-и с более высоким уровнем приоритета отработают раньше, чем callback-и с низким уровнем приоритета. Чтобы запускать callback-и с одинаковым уровнем приоритета, назначайте параметр Priority одинаковым для каждой постановки в очередь. ADI_DCB_RESULT adi_dcb_Post( ADI_DCB_HANDLE *hServer, u32 Priority; ADI_DCB_CALLBACK_FN Callback, void *pHandle, u32 u32Arg, void *pArg); Аргументы (таблица 5-1):
Возвращаемое значение:
Функция adi_dcb_Remove() удаляет записи в указанной очереди, которые совпадают по адресу с указанной callback-функцией. Альтернативно можно передать значение NULL для адреса callback-функции, что задаст полную очистку очереди (удаление из неё всех функций обратного вызова). ADI_DCB_RESULT adi_dcb_Remove(
ADI_DCB_HANDLE hServer,
ADI_DCB_CALLBACK_FN Callback);
Аргументы:
Возвращаемое значение:
Функция adi_dcb_Terminate() завершает работу Менеджера DCB путем освобождения предоставленной для него памяти (см. описание функции adi_dcb_Init) и данных критического региона. ADI_DCB_RESULT adi_dcb_Terminate (void); Возвращаемое значение:
[Публичные типы данных и макросы] Тип данных ADI_DCB_CALLBACK_FN определяет прототип для добавляемых в очередь callback-функций: typedef void (*ADI_DCB_CALLBACK_FN) (void* pHandle, u32 u32Arg, void* pArg); Здесь значения аргументов будут переданы функции adi_dcb_Post, когда callback ставится в очередь для отложенного выполнения. Тип данных ADI_DCB_COMMAND_PAIR используется, чтобы разрешить генерацию таблицы управляющих команд для отправки в Менеджер DCB через функцию adi_dcb_Control. typedef struct ADI_DCB_COMMAND_PAIR { ADI_DCB_COMMAND kind; void *value; } ADI_DCB_COMMAND_PAIR; Для допустимых значений полей см. описание ADI_DCB_COMMAND. Например, следующая команда должна быть отправлена Менеджеру DCB, чтобы сбросить все callback-и в очереди: ADI_DCB_COMMAND_PAIR CMD = { ADI_DCB_CMD_FLUSH_QUEUE, NULL }; Команда ADI_DCB_COMMAND используется для управления сервером очереди Менеджера DCB. Этот тип данных используется в ADI_DCB_COMMAND_PAIR, чтобы поменять значение конфигурации с помощью вызова adi_dcb_Control.
Структура ADI_DCB_ENTRY_HDR предоставляет интерфейс с нижележащей RTOS через функцию adi_dcb_Forward (см. описание функции adi_dcb_Forward): typedef struct ADI_DCB_ENTRY_HDR ( struct ADI_DCB_ENTRY *pNext; // Следующий элемент в очереди. ADI_DCB_DEFERRED_FN pDeferredFunction; // Указывает на процедуру отложенного // вызова (Deferred Callback), здесь // находится указатель на функцию. } ADI_DCB_ENTRY_HDR; Здесь pNext указывает на следующий элемент в очереди, pDeferredFunction это адрес отложенной функции, которая всегда указывает на adi_dcb_DispatchCallbacks. Определение типа ADI_DCB_DEFERRED_FN задает прототип для этой функции: typedef void (*ADI_DCB_DEFERRED_FN) (ADI_DCB_ENTRY *); Все публичные функции Менеджера DCB возвращают код типа ADI_DCB_RESULT. Ниже перечислены его возможные значения.
[Ссылки] 1. VisualDSP++ 5.0 Device Drivers and System Services Manual for Blackfin® Processors site:analog.com. |