Министерство образования и науки РФ
РГРТУ
Кафедра ТОР
Курсовая работа
на тему: “Реализация демодулятора сигнала на многоядерном сигнальном процессоре TMS320C6678”
Выполнил: студент группы 519м
Субботин И.В.
Проверил: кандидат технических наук
Рязань 2016 г.
Оглавление
ВВЕДЕНИЕ.. 3
1 ТЕОРЕТИЧЕСКАЯ ЧАСТЬ. 4
1.1 Математическое описание алгоритма обработки. 4
1.2 Расчет характеристик фильтра. 5
2 ЭКСПЕРЕМЕНТАЛЬНАЯ ЧАСТЬ. 7
2.1 Реализация алгоритма обработки на одном ядре ЦСП TMS320C6678. 7
2.2 Оптимизация ПО обработки на оном ядре. 7
2.3 Реализации обработки на 8 ядрах ЦСП TMS320C6678 с применением OpenMP. 7
2.4 Реализации обработки на 8 ядрах ЦСП TMS320C6678 с применением IPC.. 8
3 ОЦЕНКА ЭФФЕКТИВНОСТИ РАЗРАБОТАННОЙ СИСТЕМЫ... 11
3.1 Сравнение одноядерной и 8-ядерной реализаций. 11
3.2 Сравнение OpenMP- и IPC –реализаций. 11
4 ЗАКЛЮЧЕНИЕ.. 13
5 СПИСОК ИСПОЛЬЗУЕМОЙ ЛИТЕРАТУРЫ... 14
ПРИЛОЖЕНИЕ 1. 15
ПРИЛОЖЕНИЕ 2. 17
ПРИЛОЖЕНИЕ 3. 19
ВВЕДЕНИЕ
В данной курсовой работе будет разрабатываться программное обеспечение алгоритма демодуляции для реализации на 8-ядерном сигнальном процессоре с распределением задач по ядрам с помощью инструментариев OpenMP и IPC, а также осуществляться сравнение одноядерной и многоядерной реализации.
1 ТЕОРЕТИЧЕСКАЯ ЧАСТЬ
1.1 Математическое описание алгоритма обработки
Для решения поставленной задачи были предоставлены следующие исходные данные[1]:
- входной сигнал, записанный в памяти процессора;
- T0 – длительность сигнала;
- F0 – несущая частота, на которую был перенесен входной сигнал, который представляет из себя запись речевого сигнала;
- Fd – частота дискретизации;
В Таблице 1 определены значения перечисленных величин.
Таблица 1 – Исходные данные
Вариант № |
F0, кГц |
T0, с |
Fd, кГц |
19 |
18 |
8 |
84 |
Обработка сигнала будет проходить в два этапа:
- на первом этапе будет осуществляться обработка записи сигнала с целью переноса ее на нулевую центральную частоту, обработка будет происходить по следующему математическому описанию:
где sвх- входной сигнал; n-размер входного сигнала; sдм- выходной сигнал после переноса спектра на нулевую центральную частоту.
- на втором этапе будет осуществляться фильтрация сигнала, после демодуляции. При этом должна быть восстановлена исходная запись звукового сигнала. Фильтрация сигнала будет происходить по следующему математическому описанию:
где h-низкочастотный фильтр речевого сигнала; sф- восстановленный исходный сигнал.
Спектр записи записанного входного сигнала представлен на Рисунке 1.
Рисунок 1 – спектр записи записанного входного сигнала
1.2 Расчет характеристик фильтра
Расчет характеристик фильтра был произведен в программе Matlab с использованием встроенной возможности fdatool. На Рисунке 1 представлена характеристика низкочастного фильтра для речевого сигнала.
Рисунок 1 – характеристики фильтра
Частота дискретизация была взята из исходных данных и составила 84000 Гц, так как речевой сигнал имеет полосу 0.3-3.4 кГц, по рекомендациям для полосы пропускания была взята частота 3000 Гц, а для полосы среза использовалась частота 3700 Гц. Остальные параметры оставили неизменными. При построение видна частотная характеристика фильтра, порядок фильтра составил 304, следовательно, значений фильтра будет составлять 305. Данные значения фильтра были переданы в программу CCS, в которой будут разрабатываться программное обеспечение алгоритмов демодуляции на языке Си.
2 ЭКСПЕРЕМЕНТАЛЬНАЯ ЧАСТЬ
2.1 Реализация алгоритма обработки на одном ядре ЦСП TMS320C6678
Поставленная задача была реализована на стандартном языке Си на одном ядре код программы может выглядеть следующем образом:
for(n=0; n< Fd*T0; n++)
{
Sdm[n]=Svx[n]*cos(2*PI*F0*n/Fd);
}
for(i=0; i<Fd*T0; i++)
{
in_ptr=&Sdm[i];
Sf[i]=dotpt (in_ptr, h, fir);
}
Время реализации программы на одном ядре составило 21 573 517 831 тактов. Попробуем оптимизировать работу данного кода программы. Код программы находится в Приложение 1.
Спектр сигнала на выходе демодулятора выглядит следующем образом:
Рисунок 3 – спектр сигнала после демодуляции
После фильтрации можем наблюдать спектр восстановленной исходной записи звукового сигнала
Рисунок 4 – спектр восстановленной исходной записи звукового сигнала
2.2 Оптимизация ПО обработки на одном ядре
В качестве оптимизации ПО обработки на одном ядре, была использована автоматическая оптимизация. Чтобы включить автоматическую оптимизацию нужно открыть свойства проекта, пройти по пути Build -> C6000 Compiler -> Optimization и установить значение параметра Optimization level равным 3. После это был повторно произведен эксперемент[2].
Время реализации программы на одном ядре с оптимизацией составило 923 260 275 тактов.
2.3 Реализации обработки на 8 ядрах ЦСП TMS320C6678 с применением OpenMP
Для реализации обработки кода программы на 8 ядрах с применением OpenMP, был изменен код программы следующем образом[3]:
#pragma omp parallel private(in_ptr, i) shared(Sdm, Svx,Sf)
{
#pragma omp for
for(i=0; i< Fd*T0; i++)
{
Sdm[i]=Svx[i]*cos(2*PI*F0*i/Fd);
}
#pragma omp for
for(i=0; i<Fd*T0; i++)
{
in_ptr=&Sdm[i];
Sf[i]=dotpt (in_ptr, h, fir);
}
}
Время реализации программы на 8 ядрах с использованием OpenMP составило 3 889 659 854 тактов, что составило выигрыш в 5.55 раза.
Время реализации программы на 8 ядрах с использованием OpenMP и оптимизацией составило 161 158 121 тактов, что составило выигрыш в 5.78 раза. Код программы находится в Приложение 2.
2.4 Реализации обработки на 8 ядрах ЦСП TMS320C6678 с применением IPC
Одним из отличий IPC от OpenMP является необходимость писать разный код для разных ядер. В данной курсовой работе мы будем считать, что одно ядро является главным. Оно подготавливает входные данные, обеспечивает распределение вычислений между ядрами и синхронизацию по завершении обработки. Остальные ядра являются ведомыми. Их основная задача – производить необходимые вычисления по команде от главного ядра. Таким образом, необходимо разработать две программы: одну для главного ядра, а вторую общую для всех ведомых ядер. Существует два подхода к решению этой задачи[4]:
- создается один проект, в котором с помощью условных операторов по номеру ядра выбирается какой код должен выполняться;
- создается два независимых проекта: один для главного ядра, второй – для ведомых.
Каждый из способов имеет свои достоинства и недостатки. В данной курсовой работе будет использоваться второй подход. Достоинством данного подхода является уменьшение размера программы за счет того, что каждый проект содержит только тот код, который необходим для работы определенного ядра. К недостаткам можно отнести необходимость очень внимательно следить за тем, чтобы не было конфликтов по использованию памяти между двумя проектами. Приложение будет работать по следующему принципу: главное ядро заполняет массив входных данных; главное ядро передает ведомым информацию о том, где располагаются входные данные для каждого из ядер, где располагается массив для сохранения результатов обработки для каждого из ядер, размер входных данных, которые необходимо обработать; ведомые ядра, получив информацию от главного ядра, сохраняют ее и сообщают о своей готовности; главное ядро, дождавшись сообщений о готовности от каждого из ядер, переходит непосредственно к обработке данных; главное ядро сообщает ведомым ядрам о том, что они могут начать обработку своих частей данных; главное ядро и ведомые ядра обрабатывают свои части данных; ведомые ядра, завершив вычисления, сообщают об этом главному ядру; главное ядро ожидает уведомления от каждого из ведомых ядер об успешном выполнении вычислений; получив уведомления от всех ведомых ядер, главное ядро завершает выполнение программы путем отправки ведомым ядрам сообщения о завершении работы, после чего также завершает работу[4].
В рамках данного приложения можно выделить два вида взаимодействия между ядрами: передача некоторых данных (сообщений) и передача уведомления о каком-то событии, не требующего передачи данных. К первому виду можно отнести передачу информации о массивах главным ядром ведомым, ко второму – сообщения о начале работы и о завершении вычислений. Два эти вида взаимодействия будут реализованы с помощью разных модулей, входящих в состав пакета IPC. Для передачи сообщений используется модуль MessageQ, а для передачи уведомлений – модуль Notify. Код программы находится в Приложение 3[4].
3 ОЦЕНКА ЭФФЕКТИВНОСТИ РАЗРАБОТАННОЙ СИСТЕМЫ
3.1 Сравнение одноядерной и 8-ядерной реализаций
Для сравнения одноядерной и многоядерной реализаций были составлены таблицы.
Таблица 1 – Сравнение Одноядерной реализацией с OpenMP
Способ реализации |
Одноядерная |
OpenMP |
Выигрыш |
Время реализации |
21 573 517 831 |
3 889 659 854 |
в 5.55 раза |
Время реализации с оптимизацией |
923 260 275 |
161 158 121 |
в 5.78 раза |
Таблица 2 – Сравнение Одноядерной реализацией с IPC
Способ реализации |
Одноядерная |
IPC |
Выигрыш |
Время реализации |
810 036 371 |
552 802 631 |
в 1.5 раза |
3.2 Сравнение OpenMP- и IPC –реализаций
Как было уже сказано ранее одним из отличий IPC от OpenMP является необходимость писать разный код для разных ядер, т.е. необходимо сильно изменять исходный код программы, или даже создавать несколько проектов, как было сделано в данной курсовой работе. Данный подход сложнее реализовать, чем OpenMP, однако время на реализации такого подхода дает выигрыш перед OpenMP. Результаты сравнения представлены в Таблицы 3. OpenMP простой в реализации и позволяет при незначительном изменение исходного кода программа перейти от одноядерной к многоядерной реализации. За простоту реализации по сравнению с IPC мы платим временем на реализацию[4].
Таблица 2 – Сравнение Одноядерной реализацией с IPC
Способ реализации |
OpenMP |
IPC |
Выигрыш |
Время реализации |
3 889 659 854 |
552 802 631 |
в 7 раза IPC |
4 ЗАКЛЮЧЕНИЕ
В ходе курсовой работы было разработано программное обеспечение алгоритма демодуляции реализованное на 8-ядерном сигнальном процессоре с распределением задач по ядрам с помощью инструментариев OpenMP и IPC. Были составлены таблица способов реализаций от времени на реализацию. Производились сравнения между одноядерной реализацией и многоядерной реализацией с использованием OpenMP и IPC, как без оптимизации, так и с ней. А так же проводилось сравнение между инструментариями OpenMP и IPC. По сравнениям были сделаны выводы о достоинствах и недостатках данных способов реализации.
5 СПИСОК ИСПОЛЬЗУЕМОЙ ЛИТЕРАТУРЫ
1 Лекции Витязев С.В. по курсу: «Проектирование систем цифровой обработки сигналов в ТКС».
2 Методические указания к Лабораторной работе 2:Оптимизация.
3 Методические указания к Лабораторной работе 3:OpenMP.
4 Методические указания к Лабораторной работе 4:IPC.
ПРИЛОЖЕНИЕ 1
#include <ti/omp/omp.h>
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include "xdc/runtime/Timestamp.h"
#define fir 305
#define Fd 84000
#define T0 8
#define F0 18000
#define PI 3.14159265359
float Svx[Fd*T0]={
#include "sig_v19.dat"
};
float Sdm[Fd*T0+fir]={0};
float Sf[Fd*T0]={0};
float h[fir]={
#include "h.h"
};
float dotpt (float *arr1, float *arr2, int size);
void main(void)
{
int n,i;
float *in_ptr;
uint32_t stamp1, stamp2;
stamp1 = Timestamp_get32();
for(n=0; n< Fd*T0; n++)
{
Sdm[n]=Svx[n]*cos(2*PI*F0*n/Fd);
}
for(i=0; i<Fd*T0; i++)
{
in_ptr=&Sdm[i];
Sf[i]=dotpt (in_ptr, h, fir);
}
stamp2 = Timestamp_get32();
printf("FIR finished in %u cycles\n", stamp2 - stamp1);
}
float dotpt (float *arr1, float *arr2, int size)
{
int l;
float result=0;
for(l=0;l<fir;l++)
{
result+=arr1[l]*arr2[l];
}
return result;
}
ПРИЛОЖЕНИЕ 2
#include <ti/omp/omp.h>
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include "xdc/runtime/Timestamp.h"
#define fir 305
#define Fd 84000
#define T0 8
#define F0 18000
#define PI 3.14159265359
float Svx[Fd*T0]={
#include "sig_v19.dat"
};
float Sdm[Fd*T0+fir]={0};
float Sf[Fd*T0]={0};
float h[fir]={
#include "h.h"
};
float dotpt (float *arr1, float *arr2, int size);
void main(void)
{
int n,i;
float *in_ptr;
uint32_t stamp1, stamp2;
omp_set_num_threads(8);
stamp1 = Timestamp_get32();
#pragma omp parallel private(in_ptr, i) shared(Sdm, Svx,Sf)
{
#pragma omp for
for(i=0; i< Fd*T0; i++)
{
Sdm[i]=Svx[i]*cos(2*PI*F0*i/Fd);
}
#pragma omp for
for(i=0; i<Fd*T0; i++)
{
in_ptr=&Sdm[i];
Sf[i]=dotpt (in_ptr, h, fir);
}
}
stamp2 = Timestamp_get32();
printf("FIR finished in %u cycles\n", stamp2 - stamp1);
}
float dotpt (float *arr1, float *arr2, int size)
{
int l;
float result=0;
for(l=0;l<fir;l++)
{
result+=arr1[l]*arr2[l];
}
return result;
}
ПРИЛОЖЕНИЕ 3
IPC_MASTER
/* ---- Стандартные заголовочные файлы */
#include <stdio.h>
#include <stdint.h>
#include <math.h>
/* ---- Заголовочные файлы модуля XDC.RUNTIME */
#include <xdc/runtime/System.h>
#include <xdc/runtime/IHeap.h>
#include <xdc/runtime/Timestamp.h>
#include <xdc/runtime/Types.h>
/* ---- Заголовочные файлы модули модуля IPC */
#include <ti/ipc/MessageQ.h>
#include <ti/ipc/Notify.h>
#include <ti/ipc/HeapMemMP.h>
#include <ti/ipc/MultiProc.h>
#include <ti/ipc/Ipc.h>
/* ----- Заголовочные файлы модуля BIOS6 */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Semaphore.h>
/* ---- Заголовочные файл для получения глобальных переменных из .cfg */
#include <xdc/cfg/global.h>
// Заголовочный файл, содержащий описание структур передаваемых сообщений
#include "structs.h"
#define HEAPID 0 // идентификатор Heap-a
#define CORES 8 // количество используемых ядер
#define PROCEVENTID 10 // идентификатор события обработки
#define RPLYEVENTID 11 // идентификатор ответного события
#define fir 305 // порядок фильтра
#define Fd 84000 // частота дискретизации записи
#define T0 8 // длительность сигнала
#define F0 18000 // несущая частота
#define PI 3.14159265359 // число ПИ
// Прототипы функций
void data_init(int);
Void postLocSem(UInt16, UInt16, UInt32, UArg, UInt32);
float dotp(float*, float*, int);
// Имена локальной и удаленных очередей
char locQueueName[10] = "queue0";
char remoteQueuesNames[CORES][10];
// Входной сигнал в общей памяти
#pragma DATA_SECTION(Svx, ".ddr_data")
#pragma DATA_ALIGN(Svx, 8)
//float input[SIG_SIZE + FIR_SIZE] = {0};
float Svx[Fd*T0]={
#include "sig_v19.dat"
};
// Выходной сигнал при обработке одним ядром в общей памяти
#pragma DATA_SECTION(Sdm, ".ddr_data")
#pragma DATA_ALIGN(Sdm, 8)
//float multi_out[SIG_SIZE];
float Sdm[Fd*T0+fir]={0};
#pragma DATA_SECTION(Sf, ".ddr_data")
#pragma DATA_ALIGN(Sf, 8)
float Sf[Fd*T0]={0};
// Выходной сигнал при обработке несколькими ядрами в общей памяти
#pragma DATA_SECTION(Sf1, ".ddr_data")
#pragma DATA_ALIGN(Sf1, 8)
float Sf1[Fd*T0]={0};
// Коэффициенты фильтра в локальной памяти
#pragma DATA_SECTION(h, ".local_data")
#pragma DATA_ALIGN(h, 8)
float h[fir] = {
#include "h.h"
};
// Семафор для синхронизации ядер
Semaphore_Handle syncSem;
void main()
{
int status;
// Запускаем IPC
status = Ipc_start();
if (status < 0)
System_abort("Failed to start IPC\n");
// Запускаем ОС
BIOS_start();
}
/* Задача, выполняемая на главном ядре */
Void master_task(UArg arg1, UArg arg2)
{
// Переменные для работы с Heap-ом
HeapMemMP_Params heapParams;
HeapMemMP_Handle heapHandle;
//GateMP_Params gateParams;
//GateMP_Handle gateHandle;
// Переменные для работы с очередями
MessageQ_Params msgParams;
MessageQ_Handle locMsgHdl;
MessageQ_QueueId remoteQueuesIds[CORES];
// Переменная для работы с семафором
Semaphore_Params semParams;
// Указатели на сообщения
dataInfoMsg_t* msg2send;
responseMsg_t* msg2rcv;
MessageQ_Msg emptyMsg;
// Вспомогательные переменные
float *in_ptr;
int i, status,n;
const int part_size = (Fd*T0) / CORES;
uint32_t stamp1, stamp2;
// ---- Одноядерная реализация ----
stamp1 = Timestamp_get32();
for(n=0; n< Fd*T0; n++)
{
Sdm[n]=Svx[n]*cos(2*PI*F0*n/Fd);
}
for(i=0; i<Fd*T0; i++)
{
in_ptr=&Sdm[i];
Sf[i]=dotp (in_ptr, h, fir);
}
stamp2 = Timestamp_get32();
printf("Single core FIR is done in %d cycles\n", stamp2 - stamp1);
// ---- Подготовка к многоядерной реализации ----
stamp1 = Timestamp_get32();
// Задаем имена удаленных очередей
for (i = 1; i < CORES; i++)
{
System_sprintf(&remoteQueuesNames[i][0], "queue%d", i);
}
// Создаем семафор для синхронизации
Semaphore_Params_init(&semParams);
semParams.mode = Semaphore_Mode_COUNTING;
syncSem = Semaphore_create(0, &semParams, NULL);
// Регистрируем входящие события
for (i = 1; i < CORES; i++)
{
Notify_registerEventSingle(i, 0, RPLYEVENTID, (Notify_FnNotifyCbck)postLocSem, 0);
}
//экземпляр модуля Gate
//GateMP_Params_init(&gateParams);
//gateHandle = GateMP_create(&gateParams);
// Задаем параметры Heap-а
HeapMemMP_Params_init(&heapParams);
heapParams.regionId = 0;
heapParams.sharedBufSize = 0x8000;
heapParams.gate = NULL;
heapParams.name = "msgHeap";
// Создаем Heap
heapHandle = HeapMemMP_create(&heapParams);
if (heapHandle == NULL)
System_abort("Failed to create heap\n");
// Регистрируем созданный Heap
status = MessageQ_registerHeap(heapHandle, HEAPID);
if (status < 0)
System_abort("Error occurred on heap registration\n");
// Создаем локальную очередь
MessageQ_Params_init(&msgParams);
locMsgHdl = MessageQ_create(locQueueName, &msgParams);
if (locMsgHdl == NULL)
System_abort("Failed to create local queue");
// Открываем удаленные очереди
for (i = 1; i < CORES; i++)
{
do
{
status = MessageQ_open(&remoteQueuesNames[i][0], &remoteQueuesIds[i]);
} while (status < 0);
}
// Подготавливаем и передаем сообщения
for (i = 1; i < CORES; i++)
{
msg2send = (dataInfoMsg_t*)MessageQ_alloc(HEAPID, sizeof(dataInfoMsg_t));
if (msg2send == NULL)
System_abort("Failed to allocate memory for message\n");
msg2send->inData = &Svx[i * part_size];
// msg2send->iData = &Sdm[i*part_size];
msg2send->outData = &Sf1[i * part_size];
msg2send->size = part_size;
status = MessageQ_put(remoteQueuesIds[i], (MessageQ_Msg)msg2send);
}
// Ожидаем ответные сообщения о готовности
for (i = 1; i < CORES; i++)
{
status = MessageQ_get(locMsgHdl, (MessageQ_Msg*)&msg2rcv, MessageQ_FOREVER);
if (status < 0)
System_abort("Failed to get message. This should not be happen\n");
if (msg2rcv->ready != 1)
System_abort("Slave core sent message it's not ready\n");
MessageQ_free((MessageQ_Msg)msg2rcv);
}
stamp2 = Timestamp_get32();
printf("Prepare multi core is done in %d cylces\n", stamp2 - stamp1);
// ---- Многоядерная реализация ----
stamp1 = Timestamp_get32();
// Отправляем ведомым ядрам события о начале обработки
for (i = 1; i < CORES; i++)
{
Notify_sendEvent(i, 0, PROCEVENTID, NULL, FALSE);
}
// Выполняем часть вычислений
for(n=0; n< Fd*T0; n++)
{
Sdm[n]=Svx[n]*cos(2*PI*F0*n/Fd);
}
for(i=0; i<Fd*T0; i++)
{
in_ptr=&Sdm[i];
Sf1[i]=dotp (in_ptr, h, fir);
}
// Ожидаем ведомые ядра
for (i = 1; i < CORES; i++)
{
Semaphore_pend(syncSem, BIOS_WAIT_FOREVER);
}
stamp2 = Timestamp_get32();
printf("Multi core FIR is done in %d cycles\n", stamp2 - stamp1);
// Сообщаем ведомым ядрам о заверешении работы
for (i = 1; i < CORES; i++)
{
emptyMsg = MessageQ_alloc(HEAPID, sizeof(MessageQ_Msg));
MessageQ_put(remoteQueuesIds[i], emptyMsg);
}
// Заверашаем работу программы
BIOS_exit(0);
}
/* Функция поднимающая семафор при получении уведомления от ведомого ядра */
Void postLocSem(UInt16 procId, UInt16 lineId, UInt32 eventId, UArg arg, UInt32 payload)
{
Semaphore_post(syncSem);
}
/* Функция расчета скалярного произведения двух векторов */
float dotp(float *arr1, float *arr2, int arr_size)
{
int i;
float result = 0;
for (i = 0; i < arr_size; i++)
result += arr1[i] * arr2[i];
return result;
}
IPS_SLAVE
/* ---- Стандартные заголовочные файлы */
#include <stdio.h>
#include <stdint.h>
#include <math.h>
/* ---- Заголовочные файлы модуля XDC.RUNTIME */
#include <xdc/runtime/System.h>
#include <xdc/runtime/IHeap.h>
#include <xdc/runtime/Timestamp.h>
#include <xdc/runtime/Types.h>
/* ---- Заголовочные файлы модули модуля IPC */
#include <ti/ipc/MessageQ.h>
#include <ti/ipc/Notify.h>
#include <ti/ipc/HeapMemMP.h>
#include <ti/ipc/MultiProc.h>
#include <ti/ipc/Ipc.h>
/* ----- Заголовочные файлы модуля BIOS6 */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Semaphore.h>
/* ---- Заголовочные файл для получения глобальных переменных из .cfg */
#include <xdc/cfg/global.h>
// Заголовочный файл, содержащий описание структур передаваемых сообщений
#include "structs.h"
#define HEAPID 0 // идентификатор Heap-a
#define CORES 8 // количество используемых ядер
#define PROCEVENTID 10 // идентификатор события обработки
#define RPLYEVENTID 11 // идентификатор ответного события
//#define FIR_SIZE 128 // порядок фильтра
#define fir1 305 // порядок фильтра
#define Fd1 84000 // частота дискретизации записи
#define T01 8 // длительность сигнала
#define F01 18000 // несущая частота
#define PI1 3.14159265359 // число ПИ
// Прототипы функций
Void procData(UInt16, UInt16, UInt32, UArg, UInt32);
float dotp(float*, float*, int);
// Имена локальной и удаленной очереди
char locQueueName[10];
char remoteQueueName[10] = "queue0";
// Коэффициенты фильтра в локальной памяти
#pragma DATA_SECTION(h, ".local_data")
#pragma DATA_ALIGN(h, 8)
float h[fir1] = {
#include "h.h"
};
// Вспомогательные переменные
uint32_t myId;
uint32_t dataSize;
float *locInData;
float *locOutData;
float Sdm1[Fd1*T01/8];
void main()
{
int status;
// Определяем имя локальной очереди
myId = MultiProc_self();
System_sprintf(locQueueName, "queue%d", myId);
// Запускаем IPC
status = Ipc_start();
if (status < 0)
System_abort("Failed to start IPC\n");
// Запускаем ОС
BIOS_start();
}
/* Задача, выполняемая на ведомых ядрах */
Void slave_task(UArg arg1, UArg arg2)
{
// Переменная для работы с Heap-ом
HeapMemMP_Handle heapHandle;
// Переменные для работы с очередями
MessageQ_Params msgParams;
MessageQ_Handle locMsgHdl;
MessageQ_QueueId remoteQueueId;
// Указатели на сообщения
dataInfoMsg_t* msg2rcv;
responseMsg_t* msg2send;
MessageQ_Msg emptyMsg;
// Вспомогательные переменные
int status;
// Региструем событие обработки данных
Notify_registerEventSingle(0, 0, PROCEVENTID,(Notify_FnNotifyCbck)procData,0);
// Открываем Heap, созданный главным ядром
do
{
status = HeapMemMP_open("msgHeap", &heapHandle);
} while (status < 0);
// Регистрируем открытый Heap
status = MessageQ_registerHeap(heapHandle, HEAPID);
if (status < 0)
System_abort("Failed to register Heap\n");
// Создаем локальную очередь
MessageQ_Params_init(&msgParams);
locMsgHdl = MessageQ_create(locQueueName, &msgParams);
if (locMsgHdl == NULL)
System_abort("Failed to create local queue\n");
// Открываем удаленную очередь на главном ядре
do
{
status = MessageQ_open(remoteQueueName, &remoteQueueId);
} while (status < 0);
// Получаем сообщение с параметрами от главного ядра
status = MessageQ_get(locMsgHdl, (MessageQ_Msg *)&msg2rcv, MessageQ_FOREVER);
if (status < 0)
System_abort("Failed to get massage. This should not be happen\n");
locInData = msg2rcv->inData;
locOutData = msg2rcv->outData;
dataSize = msg2rcv->size;
MessageQ_free((MessageQ_Msg)msg2rcv);
// Подготавливаем ответное сообщение о готовности к обработке
msg2send = (responseMsg_t*)MessageQ_alloc(HEAPID, sizeof(responseMsg_t));
if (msg2send == NULL)
System_abort("Failed to allocate memory for message\n");
msg2send->ready = 1;
// Отправляем сообщении о готовности
status = MessageQ_put(remoteQueueId, (MessageQ_Msg)msg2send);
// Ожидаем сообщение о завершении работы
status = MessageQ_get(locMsgHdl, &emptyMsg, MessageQ_FOREVER);
if (status < 0)
System_abort("Failed to get message. This should not be happen\n");
MessageQ_free(emptyMsg);
// Завершаем работу программы
BIOS_exit(0);
}
/* Функция обработки события */
Void procData(UInt16 procId, UInt16 lineId, UInt32 eventId, UArg arg, UInt32 payload)
{
int i,n;
float *in_ptr;
// Выполняем обработку
for(n=0; n< dataSize; n++)
{
Sdm1[n]=locInData[n]*cos(2*PI1*F01*n/Fd1);
}
for (i = 0; i < dataSize; i++)
{
in_ptr = &Sdm1[i];
locOutData[i] = dotp(in_ptr, h, fir1);
}
// Отправляем событие о завершении обработки
Notify_sendEvent(0, 0, RPLYEVENTID, 0, TRUE);
}
/* Функция расчета скалярного произведения двух векторов */
float dotp(float *arr1, float *arr2, int size)
{
int i;
float result = 0;
for (i = 0; i < size; i++)
{
result += arr1[i] * arr2[i];
}
return result;
}
Скачать: