Главная » Статьи » Программирование в CodeWarrior Development Studio 1.0.0 |
Работа с TVPE
TVPE - это аппаратный турбо и витерби декодер. TVPE - это модуль MAPLE-B. Последний включает в себя так же FFT, iFFT, DFT, iDFT, CRC. Мы рассмотрим только турбо декодер. Открытие MAPLE MAPLE-B получает данные по средством DMA (рис 1). Т.е. перед работой непосредственно с TVPE необходимо сначала настроить общение с MAPLE-B.
// Open MAPLE device
Стоит описать что же тут настраивается. error_callback - Это ссылка на функцию, вызываемую если происходит какая-либо ошибка в MAPLE-B. mbus_base, sbus_base - адреса шин. Стандартные. config_mbus_mmu - разрешает использование MBUS. config_sbus_mmu - при наших настройках включать не надо. dev_init_params.initialization_func, dev_init_params.ucode - Эти 2 параметра определяют стандарт декодера, который будет использовать система. MAPLE-B поддерживает 4 стандарта. Это 3GLTE, WIMAX, 3GPP, 3GPP2. Мы будем использовать WiMax. dispatch_callback - возвращаемая функция по завершении работы MAPLE-B. В нашем случае мы пока поставили заглущку. О настройке всё. Теперь это всё надо записать в MAPLE с помощью следующей строки: maple_dev_handle = osCopDeviceOpen(MAPLE_DEV_NAME_0, &cop_dev_open_params); В итоге у нас есть номер нашего устройства - maple_dev_handle, если всё прошло хорошо и 0, если что то пошло не так. Открытие TVPE
// Open TVPE device
В остальных настройках, если необходимо, можете поразбираться сами. Когда всё заполнено, посылаем настройки командой: TVPE_Device = osCopDeviceOpen(TVPE_DEV_NAME_0, &cop_dev_open_params); Открытие канала TVPE Теперь для работы с TVPE осталось открыть канал по которому мы будем посылать данные.
// Open TVPE channel
Теперь нам надо передать и эти настройки. osCopChannelOpen(TVPE_Device, &TVPE_channel, &ch_open_params); Эта функция возвращает OS_SUCCESS если прошла успешно и NULL если это не так. Если было получено OS_SUCCESS, следуем дальше. Когда вся настройка завершена, нам нужно, что бы вызывалась какая-то функция, говорящая, что преобразование завершено. Её можно приписать к TVPE следующим образом: osCopChannelCtrl(&TVPE_channel, COP_CHANNEL_CALLBACK_SET, appTvpeDispatchCb) Она так же возвращает OS_SUCCESS, если выполнена успешно. Теперь, по окончании преобразования будет вызвана функция appTvpeDispatchCb. На этом настройку можно считать завершённой. Формат входных данных. Когда DMA настроено, нужно подготовить данные и отправить их в MAPLE.На этом этапе нам надо разобраться в каком формате данные посылать в турбо декодер. Все данные представляют собой 8-ми битные мягкие решения, т.е. в одном байте у нас 1 бит (Почитайте про мягкие решения в интернете). Что бы разобраться с порядком следования данных нужно обратиться к тому как работает кодер. На входе у кодера сплошной битовый поток. Кодер захватывает данные по 2 бита, условно называемые А и В. Создаёт для них проверочные биты условно названные Y1,Y2,W1W2. Т.е. на выходе данных в 3 раза больше чем на входе. Т.е. скорость потока становится 1/3. Именно эту скорость поддерживает для WIMAX наш декодер. Для выходных данных кодера определяется размер блока, на основе этой цифры и кодируются и в дальнейшем собираются полученные данные. Сборка данных обратно в один поток производится следующим образом: Сначала идут биты А в количестве, равном размеру блока. Потом соответствующие им биты В. За ними идут чередующиеся Y1и Y2. Последними следуют чередующиеся W1 и W2. В итоге А представляет 1/6 от всего блока данных. Размер В тоже равен 1/6. Размер каждого из блоков Y1Y2 и W1W2 равен 1/3. Зная это можно настраивать. По стандарту данные можно поделить на 4 части: A, B, Y1Y2, W1W2. Подготовка данных. Теперь подготовим данные для записи их в работу. Предположим, что у нас есть описанный выше поток входных данных.#define DATA_SIZE 480 unsigned char DataIn[DATA_SIZE*3]; Для того, чтобы заполнить работу данными, нам надо разбить их на 4 части, упорядоченные по 256 байт. Определим массив, в котором будут располагаться наши данные: #define BLOCK_SIZE 15000//Можно взять и поменьше. uint8_t Data_in_mem[BLOCK_SIZE]; И указатели на данные в нём: int8_t* TVPE_A; int8_t* TVPE_B; int8_t* TVPE_Y1Y2; int8_t* TVPE_W1W2; Теперь надо в нашем массиве получить адреса, кратные 256. Это можно сделать по следующему алгоритму: uint32_t addr = (uint32_t)Data_in_mem; addr += 255; TVPE_A &= ~(((uint32_t)256) - 1); В переменной TVPE_A будет уже адрес, кратный 256. Аналогично находятся адреса для других данных. Копируем данные из входного массива в соответствующие области: memcpy(TVPE_A, DataIn, DATA_SIZE >> 1); Т.о. необходимо заполнить все 4 массива. После копирования необходимо обновить кэш следующими строками: uint32_t cache_hi, cache_lo; os_status status; cache_hi = CACHE_OP_HIGH_ADDR(TVPE_A, DATA_SIZE >> 1, ARCH_CACHE_LINE_SIZE); cache_lo = CACHE_OP_LOW_ADDR(TVPE_A, ARCH_CACHE_LINE_SIZE); status = osCacheL1L2DataFlush((const void*)cache_lo, (const void*)cache_hi, MMU_SHARED_PID); Чтобы MAPLE работал с новыми данными. Настройка работы. Прежде всего необходимо выделить память до настройки работы. Это делается следующим образом:
Здесь стоит остановиться на термине упорядочивание массивов данных в памяти. Это значит, что первый элемент массива имеет адрес, кратный размеру по которому упорядочивают. Т.е. если производится упорядочивание по 256 байт, то последние 8 бит адреса должны быть нулевыми. Обычно это используется для того, чтобы программа могла сама находить начало новых данных. Как можно видеть в настройке работы, она должна быть упорядочена по 4 байта, т.е. первые 2 бита должны быть нулевыми. Что входит в настройки: Первый параметр - это размер размер настроек нашей работы, причём уже учитывается упорядоченный вариант. Второй - максимально возможное число одновременных запросов. Третий - Адрес, с которого начинается расположение данных. Четвёртый - упорядочивание. Пятый - смещение. Шестой - Адрес пространства менеджера памяти для инициализации. Седьмой - Флаг, показывающий, что данные распределены между несколькими ядрами. Для правильной работы надо определить следующие значения:
После того, как выделена память для работы надо определить на неё указатель. typedef struct { cop_job_handle cop_job; maple_tvpe_job_t tvpe_job; } app_tvpe_job; app_tvpe_job *job=NULL;//Job for TVPE В коде пишем: job = (app_tvpe_job*)osMemBlockGet(jobs_pool); Инициализация работы производится следующим образом: //Инициализируем работу.
Прокомментирую настройки: job_id - нужен, если вы одновременно записываете больше одного задания в MAPLE. next - сюда записывается ссылка на следующую работу. Собственно выглядит это так: Вы посылаете в MAPLE самую первую работу. MAPLE же смотрит есть ли продолжение, считывая этот параметр. Если параметр не NULL, то MAPLE считывает следующую работу по указанному адресу. И т.д. пока next не будет равен NULL. hard_output_addr - переменная куда будут записаны наши выходные данные. Так же, как и входные данные она должна быть упорядочена по 256 байт. inputs - указатель на входные данные. Данные могут читаться либо с INPUT_BASE_ADDR если не определены данные с ADDR_OFFSET. Либо с ADDR_OFFSET. Самая важная часть - флаги: first_flags - тут на данный мамент определены следующие параметры: TURBO_JOB - определяет то, что работа для турбо декодера. STRUCT_SUB_BLK_INTRLV - определяет способ которым передаются данные в декодер. В данном случае у нас данные с перемежёнными сабблоками и именно такой способ подачи данных используется. При нём данные как раз и разбиваются на 4 части и подаются в ADDR_OFFSET. Если у вас данные без сабблокового перемежителя, то вам надо будет читать техническую документацию и разбираться как подавать данные в вашем случае (точнее в каком виде). MAX_ITER_16 - Максимальное число итераций. MIN_ITER_3 - Минимальное число итераций. RATE_1_3 - Скорость данных. В данном случае можено только RATE_1_3 - 1/3. Это именно та скорость, что описана в разделе формат данных. TVPE_HOE - включает аппаратные прерывания. block_size - размер данных. Сюда записывается количество мягких информационных бит. Т.е. размер сабблока *2 Ожидаем пока данные полностью будут переданы в физическую память. osCacheDataWaitSweep(); Посылаем данные в MAPLE while (osCopChannelDispatch(&TVPE_channel, &job->cop_job, &num_jobs) != OS_SUCCESS ){} Отправляем запросы о готовности. Иначе MAPLE может прерывание и не выдать. while (DecDataComp == 0) { osHwiSwiftDisable(); osCopChannelCtrl(&TVPE_channel, MAPLE_TVPE_CMD_RX_POLL, NULL); osHwiSwiftEnable(); } DecDataComp - эту переменную мы сделаем равной 1 в функции прерывания, чтобы программа продолжила своё выполнение. Ну что ж. Данные посланы, обработаны, прерывание сработало. Опишем функцию, которая будет вызвана: static void appTvpeDispatchCb(void *cop_job, void * error_status) {
OS_ASSERT_COND(!((uint32_t)error_status & 0x0F000000)); tvpe_job = (maple_tvpe_job_t *)job->device_specific; job_id = (uint32_t)job->job_id; maple_read(output_data, tvpe_job->hard_output_addr, Length_Subblock>>2); osMemBlockFree(jobs_pool, job); DecDataComp = 1; } Собственно на этом всё. Наши данные лежат в переменной out. Она же tvpe_job->hard_output_addr. Но для полной честности рассмотрим подробнее функцию maple_read. она копирует данные из tvpe_job->hard_output_addr в наш буфер. разумеется буфер должен быть размером больше, чем количество считываемых данных. Причём данные на выходе представлены уже в байтах (биты А и В объединены по алгоритму: А0 В0 А1 В1 А2 В2 и т.д.) и размер считываемых данных вычисляется как размер сабблока/4. maple_read выглядит следующим образом: static inline void maple_read(void *dst, const void *src, int size) { OS_ASSERT_COMPILER_COND(IS_ALIGNED(src, ARCH_CACHE_LINE_SIZE)); sweep_cache((uint32_t)src, size, CACHE_INVALIDATE); memcpy(dst, src, (size_t)size); } static inline void sweep_cache(uint32_t addr, int size, uint32_t mode) { uint32_t cache_hi, cache_lo; os_status status; cache_hi = CACHE_OP_HIGH_ADDR(addr, size, ARCH_CACHE_LINE_SIZE); cache_lo = CACHE_OP_LOW_ADDR(addr, ARCH_CACHE_LINE_SIZE); status = osCacheL1L2DataSweep((const void*)cache_lo, (const void*)cache_hi, MMU_SHARED_PID, mode); } В данной функции данные копируются в наш массив, а затем сбрасывают кэш. На этом работа TVPE закончена. Статья получилась большая и может содержать множество опечаток, поэтому может использоваться только в ознакомительных целях. Копировать куски кода из неё настоятельно не рекомендуется. Для этого есть готовый пример работы, который можно найти тут. В данном архиве содержится пример работы с декодером и технические документации на процессор. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Просмотров: 1219 | |
Всего комментариев: 0 | |