Подробное объяснение принципа гетерогенной многоядерной связи Allwinner R128 RTOS.
Подробное объяснение принципа гетерогенной многоядерной связи Allwinner R128 RTOS.

Гетерогенная многоядерная связь RTOS

Введение в гетерогенную многоядерную связь

Основное ядро ​​M33 R128 полностью отличается от ядра C906 и HIFI5 DSP. Чтобы максимизировать их производительность и сотрудничать для выполнения определенной задачи, системы, работающие на разных ядрах, также различаются. Ядра этих различных архитектур и работающее на них программное обеспечение объединяются в систему AMP (асимметричная многопроцессорная система, гетерогенная многопроцессорная система).

Чтобы многоядерные устройства могли работать вместе, гетерогенная многоядерная коммуникационная структура должна выполнять следующие функции:

  1. Изолирование межъядерных разногласий,Развернуть часть Служить на ядре,Другая часть Служить развертывания существуют дополнительно из ядерных,Доступ к коду прикладного уровня требуется только через стандартный интерфейс.,Он осознает, что никто не осознает, какое ядро ​​лежащего в основе Служить конкретное существует.
  2. то же ядро,Может использоваться как удаленный терминал,Также можно использовать в качестве клиента.

Учитывая особенности гетерогенных многоядерных систем, при совершении удаленных сервисных вызовов необходимо решить следующие вопросы:

  1. Проблемы с согласованностью кэша. Согласованность кэша является очень важной проблемой в гетерогенных многоядерных системах, и вызывающие программы и провайдеры, использующие многоядерные процессоры, должны знать об их использовании. buffer Будет ли он изменен другими ядрами и будет ли он прочитан другими ядрами. Если оно изменено другими ядрами, оно должно вступить в силу до использования текущего ядра. buffer Переписка dcache, если он будет читаться другими ядрами, после того, как текущее ядро ​​запишет данные, ему необходимо это сделать; buffer Переписка dcache Обновиться до следующего уровня Память. В результате, если удаленный параметр Служитьиз или возврат цен усложняется, пользователю и поставщику Служить придется потратить много усилий, чтобы разобраться в каждом из них. buffer Использование связей значительно увеличивает их бремя. И, чтобы избежать влияния на другие данные, очистите dcache Также необходимо убедиться, что его данные Эксклюзивный cacheline, иначе другие файлы будут сброшены по ошибке. Это также затруднит решение проблем согласованности кэша.
  2. CPU Проблема несоответствия разрядности. существовать R128 В проекте HIFI5 из CPU Разрядность равна 32bit,C906 из CPU Разрядность равна64bit,M33 из CPU Разрядность равна 32бит. Для трех разных ядер переменные-указатели программного обеспечения, длинные Тип переменных, изданные размеры не согласованы,Тогда это приведет к одной и той же структуресуществовать разной разрядности ядер.,Его макет «Память» не соответствует действительности.,Если три ядра будут читать напрямую, возникнут ошибки.
  3. Сложные проблемы с обработкой сцены. существовать между несколькими ядрами,Необходимо учитывать возможности параллельной обработки удаленных терминалов Служить, взаимосвязь вложенных вызовов между ядрами, Функцию сна терминала Служить, как уменьшить накладные расходы на использование Память и т. д. Все эти сценарии необходимо оптимизировать.,Возможность параллельной обработки терминала Служить будет влиять на эффективность межъядерных удаленных вызовов между ядрами; ИСлужитьфункцияизвпадать в спячку,оказывать воздействиеудаленный вызов Служитьизстабильность。
существование Вставьте сюда описание изображения
существование Вставьте сюда описание изображения

Чтобы решить эти проблемы, платформа Sunxi-AMP предоставляет интерфейс для гетерогенной связи. Он также предоставляет rpdata для реализации гетерогенной связи нижнего уровня.

Введение в Sunxi‑AMP

Схема рабочего процесса Sunxi‑AMP
существование Вставьте сюда описание изображения
существование Вставьте сюда описание изображения
пул потоков

amp_threadpool.c В файле реализован простой пул потоков,существующаясистема стартап создатель укажите количество испул тредов потоков, к повышает эффективность удаленной обработки сообщений. Если необходимо обработать много удаленных сообщений и они не могут быть обработаны вовремя, количество сообщений будет динамически увеличиваться. Количество тредов в потоках, если сообщений мало, пул будет удален динамически; потоковая ветка. Во время инициализации количество потоков определяется выражением amp_threadpool.h в AMP_THD_POOL_MIN_NUM Макрос решает, это тоже выражатьпул потоков Сохраните хотя бысуществоватьизминимальное количествоизпоток обработки。AMP_THD_POOL_MAX_NUM выражатьпул Максимальное количество потоков обработки, которые могут одновременно храниться в потоках. Условием динамического добавления потока обработки является наличие двух сообщений в очереди приема сообщений или сообщение не обработано, а текущее сообщение Число выживших потоков в потоках меньше AMP_THD_POOL_MAX_NUM。динамичныйудалитьпоток обработкииз Условие (Количество потоков, обрабатывающих сообщения * 2) Меньше количества выживших потоков в системе и текущем пуле Число выживших потоков в потоках больше, чем AMP_THD_POOL_MAX_NUM

Способ реализации удаленного звонка

Sunxi‑AMP в настоящее время поддерживает 1 метод реализации удаленного межъядерного вызова:

  • Передача указателя параметра: параметр изуказательилиданныеценить при удаленном межъядерном вызове Служить передается напрямую. Если параметр имеет тип указатель,Затем передайте указатель Должену напрямую, если параметр является числом ценить;,Затем передайте номер Должена ценить напрямую. Передача указателя параметра больше подходит для сценариев, где структура параметра проста, а объем передачи между ядрами велик. Недостатком является то, что сложно обеспечить согласованность кэша.

Передача параметров и возвращаемых значений напрямую через указатели позволяет избежать копирования при передаче данных, потребляет меньше памяти и имеет более высокую производительность.

к setConfig(struct config *data) Взяв функцию в качестве примера, при запуске setConfig удаленный вызовчас,встречасоздаватьодинsunxi_amp_msg_args структура, будет setConfig параметры установлены на sunxi_amp_msg_args из args массив, затем добавьте sunxi_amp_msg из data В поле установлено значение sunxi_amp_msg_args Адрес структуры. проходить msgbox Воляsunxi_amp_msg После отправки одного ядра другому ядру второе ядро ​​собирается заново. sunxi_map_msg,Вы можете получить данные параметров.

существование Вставьте сюда описание изображения
существование Вставьте сюда описание изображения
Структуры данных Sunxi‑AMP
Структура сообщения

sunxi_amp_msg Структура представляет собой msgbox Передача содержимого сообщения, Sunxi‑AMP Платформа удаленных межъядерных вызовов основана наsunxi_amp_msg сообщения для выполнения различных функций. Полный из sunxi_amp_msg,Содержит следующие поля,Размер 12 байт.

Язык кода:javascript
копировать
typedef struct _sunxi_amp_msg_t
{
    uint32_t rpcid: 16; // удаленный вызов ID ценить,высокий 8 Кусочеквыражать service идентификатор, низкий 8 Кусочеквыражать function id
    uint32_t prio: 5;   // Приоритет задачи отправителя
    uint32_t type: 5;   // Тип сообщения
    uint32_t src: 3;    // Адрес источника, то есть ядро, с которого выражение отправляет сообщение.
    uint32_t dst: 3;    // Адрес назначения, который является ядром, на которое отправляется сообщение.
    uint32_t data;      // информацияданные    uint32_t flags;     // информациялоготип,В настоящее время установлено дескриптор потока,используется длясуществоватьудаленный вызовзабитыйждатьчасбудить Долженнить
} sunxi_amp_msg;

вудаленный вызовиз ID Разделен на две части, высокий. 8 бит указывает группу удаленного обслуживания ID, низкий 8 Местоположение выражать Некоторые удаленные Служитьвыражать function ID,То есть наиболее поддерживаемый 256 группы удаленного обслуживания, каждая группа удаленного обслуживания поддерживает до 256 индивидуальныйудаленный вызов。подк FSYS Служба файловой системы взята в качестве примера для ознакомления. rpcid состава и метода его расчета.

Язык кода:javascript
копировать
#define RPCCALL_FSYS(y) (RPCNO(RPCNO_FSYS, RPCHandler_FSYS_t, y) | (RPCSERVICE_FSYS_DIR << 29) | SELF_DIRECTION << 26)
  • RPCCALL_FSYS(y) Макросы рассчитываются автоматически rpcid к И src, dst Три поля ценить.
  • RPCSERVICE_FSYS_DIR выражать FSYS Служить существующим ядром, которое используется для установки dst поле.
  • SELF_DIRECTION выразить на данный момент все существующееизядро для установки src поле.
  • RPCNO(RPCNO_FSYS, RPCHandler_FSYS_t, y) используется для расчета rpcid。в,RPCNO_FSYS Рассчитать service ID,помещается в старшие 8 бит,RPCNO Рассчитать function ID,Разместите на нижнем уровне 8 Кусочек.
Тип сообщения
Язык кода:javascript
копировать
MSG_SERIAL_FUNC_CALL   // Сериализация удаленных вызовов
MSG_SERIAL_FUNC_RET    // Сериализовать удаленный Служить возвращает ценить
MSG_SERIAL_FREE_BUFFER // Сериализация удаленного Служитьвыпускать Память
MSG_DIRECT_FUNC_CALL   // Параметры указатель переданы на удаленный вызов Служить
MSG_DIRECT_FUNC_RET    // Параметры указатель передать удаленно Служить вернуть ценить
Структура параметров при передаче указателя на удаленный вызов

sunxi_amp_msg_args используется длявыражатьиспользоватьуказательпередачаудаленный вызовчасиз Информация о параметрах,sunxi_amp_msg_args Адрес Структураиз будет установлен на sunxi_amp_msg из data поля пройдены.

Язык кода:javascript
копировать
typedef struct _sunxi_amp_msg_args_t
{
    uint32_t args_num: 8;
    uint32_t reserved;
    uint32_t args[SUNXI_AMP_MAX_ARGS_NUM];
} sunxi_amp_msg_args;
Таблица сервисных функций для служб удаленных вызовов

sunxi_amp_func_table используется длявыражать Таблица сервисных функций для служб удаленных вызовов。args_num выражать Должен Служитьиз Количество параметров,return_type выражать Должен Служитьизвозвращатьсяценитьтип,func Представляет указатель на функцию службы.

Язык кода:javascript
копировать
typedef struct _sunxi_amp_func_table
{
    uint32_t args_num: 8;
    uint32_t return_type: 8;
    sunxi_amp_service_func func;
} sunxi_amp_func_table;

Структура исходного кода Sunxi-AMP

Язык кода:javascript
копировать
├── amp_core.c            # Sunxi‑AMP Основной код обработки, включая анализ сообщений и т. д.
├── amp_msgbox.c          # Sunxi‑AMP msgbox стыковочный пакет
├── amp_service.c         # удаленный Служитьмножество
├── amp_stub.c            # курокудаленный Служитьизкрюкфункция
├── amp_test.c            # Sunxi‑AMP Одноядерный тестовый файл
├── amp_threadpool.c      # Sunxi‑AMP пул потоков
├── amp_threadpool.h      # пул потоковзаголовочный файл
├── Kconfig               # Конфигурационный файл
├── Makefile
├── msgbuffer.c           # стыковка erpc Реализация исходного кода заброшена
├── msgbuffer.h
├── service               # Уже поддержан изудаленный вызов Служить
│   ├── audio             # Аудиоудаленный вызов Служить
│   ├── bt                # Bluetoothудаленный вызов Служить
│   ├── demo              # erpc тестовый пример
│   ├── flashc            # flashcводить машинуудаленный вызов Служить
│   ├── fsys              # документсистемаудаленный вызов Служить
│   ├── misc              # Разнообразныйудаленный вызов Служить,основнойиспользуется длядействовать Заказпередачаи тому подобноеизсцена
│   ├── net               # wifi сетьудаленный вызов Служить
│   ├── pm                # впадать в спячкубудитьудаленный вызов Служить
│   ├── rpcconsole        # консольудаленный вызов Служить
│   ├── rpdata            # Удаленная обработка данных вызывает вызов Служить, который используется для защиты комплекса операций, поэтому разработчики заботятся только о получении и отправке данных.
│   └── tfm               # Безопасностьсистемаудаленный вызов Служить
├── sunxi_amp.h
├── sunxi_amp_msg.h
├── sunxi_amp_status.h
├── sunxi_amp_table.h
└── tests                 # Стресс-тест многоядерной связи
	└── test_stress.c
Конфигурация модуля

М33 и С906

Язык кода:javascript
копировать
System components ‑‑‑>
    aw components ‑‑‑>
        AMP Components Support ‑‑‑>
            [*] Tina RTOS AMP                    # давать возможность Sunxi‑AMP компоненты
            [*] AMP Funcall Thread               # давать Возможность, вызываемая функцией обработки задачи
            [*] AMP Funcall ThreadPool           # давать возможностьпул потоков
            [*] AMP Change Service Task Priority # давать возможностьприоритетпередача

Описание интерфейса Sunxi-AMP

заголовочный файл

Язык кода:javascript
копировать
#include <sunxi_amp.h>
Структура функции обслуживания удаленных вызовов
Язык кода:javascript
копировать
typedef struct
{
    sunxi_amp_func_table *RPCHandler_FSYS;
    sunxi_amp_func_table *RPCHandler_NET;
    sunxi_amp_func_table *RPCHandler_BT;
    sunxi_amp_func_table *RPCHandler_DEMO;
    sunxi_amp_func_table *RPCHandler_ARM_CONSOLE;
    sunxi_amp_func_table *RPCHandler_DSP_CONSOLE;
    sunxi_amp_func_table *RPCHandler_RV_CONSOLE;
    sunxi_amp_func_table *RPCHandler_PMOFM33;
    sunxi_amp_func_table *RPCHandler_PMOFRV;
    sunxi_amp_func_table *RPCHandler_PMOFDSP;
    sunxi_amp_func_table *RPCHandler_FLASHC;
    sunxi_amp_func_table *RPCHandler_M33_MISC;
    sunxi_amp_func_table *RPCHandler_RV_MISC;
    sunxi_amp_func_table *RPCHandler_DSP_MISC;
    sunxi_amp_func_table *RPCHandler_AUDIO;
    sunxi_amp_func_table *RPCHandler_RPDATA;
    sunxi_amp_func_table *RPCHandler_TFM;
} RPCHandler_RPCT_t;
Информационная структура AMP
Язык кода:javascript
копировать
typedef struct _sunxi_amp_info_t
{
    QueueHandle_t send_queue; /*send to remote processor */
    QueueHandle_t recv_queue; /*receive from remote processor */
    TaskHandle_t  sendTask;   /*send to remote processor */
    TaskHandle_t  recvTask;   /*receive from remote processor */
    struct msg_endpoint sedp_arm;
    struct msg_endpoint sedp_rv;
    struct msg_endpoint sedp_dsp;
    sunxi_amp_wait wait;
    QueueHandle_t amp_msg_heap_mutex;
} sunxi_amp_info;
Структура сообщения AMP
Язык кода:javascript
копировать
typedef struct _sunxi_amp_msg_t
{
    uint32_t rpcid: 16;
    uint32_t prio: 5;
    uint32_t type: 5;
    uint32_t src: 3;
    uint32_t dst: 3;
    uint32_t data;
    uint32_t flags;
} __attribute__((packed)) sunxi_amp_msg;
Структура работы AMP
Язык кода:javascript
копировать
typedef struct _sunxi_amp_msg_ops
{
    sunxi_amp_msg_func send_to_queue;
    sunxi_amp_msg_func send_to_dev;
    sunxi_amp_msg_func receive_from_dev;
    sunxi_amp_dev_init init;
} sunxi_amp_msg_ops;
Перечисление типов сообщений AMP
Язык кода:javascript
копировать
enum MSG_TYPE
{
    MSG_SERIAL_FUNC_CALL = 0,
    MSG_SERIAL_FUNC_RET,
    MSG_SERIAL_FREE_BUFFER,
    MSG_DIRECT_FUNC_CALL,
    MSG_DIRECT_FUNC_RET,
    MSG_TYPE_NUM,
};
Перечисление возвращаемого значения сообщения AMP
Язык кода:javascript
копировать
enum FUNC_RETURN_TYPE
{
    RET_NULL = 0,
    RET_POINTER,
    RET_NUMBER_32,
    RET_NUMBER_64,
};
Перечисление направлений отправки сообщений AMP
Язык кода:javascript
копировать
enum RPC_MSG_DIRECTION
{
    RPC_MSG_DIR_UNKNOWN = 0,
    RPC_MSG_DIR_CM33 = 1,
    RPC_MSG_DIR_RV,
    RPC_MSG_DIR_DSP,
};
Параметры сообщения AMP
Язык кода:javascript
копировать
typedef struct _sunxi_amp_msg_args_t
{
    uint32_t args_num: 8;
    uint32_t reserved;
    uint32_t args[SUNXI_AMP_MAX_ARGS_NUM];
} __attribute__((packed)) sunxi_amp_msg_args;
Структура таблицы функций AMP
Язык кода:javascript
копировать
typedef struct _sunxi_amp_func_table
{
    uint32_t args_num: 8;
    uint32_t return_type: 8;
    sunxi_amp_service_func func;
} sunxi_amp_func_table;
Получить информацию о системе AMP

прототип функции

Язык кода:javascript
копировать
sunxi_amp_info *get_amp_info(void);

параметр:

  • никто

Возвращаемое значение:

  • структура sunxi_amp_info
AMP отправляет сообщение

прототип функции

Язык кода:javascript
копировать
int hal_amp_msg_send(sunxi_amp_msg *msg);

параметр:

  • msg:Структура сообщениятело

Возвращаемое значение:

  • 0: Успех
  • -1: не удалось
Получите действие AMP

прототип функции

Язык кода:javascript
копировать
sunxi_amp_msg_ops *get_msg_ops(void);

параметр:

  • никто

Возвращаемое значение:

  • sunxi_amp_msg_ops: структура операции
AMP получает сообщение

прототип функции

Язык кода:javascript
копировать
int hal_amp_msg_recv(sunxi_amp_msg *msg);

параметр:

  • msg:Структура сообщениятело

Возвращаемое значение:

  • 0: Успех
  • -1: не удалось
Инициировать удаленный вызов функции

прототип функции

Язык кода:javascript
копировать
unsigned long func_stub(uint32_t id, int haveRet, int stub_args_num, void *stub_args[]);

параметр:

  • id : удаленный Служитьфункцияиз ID ценить
  • haveRet : Чтобы сохранить существование и вернуть цену, фактически используется существование. Чтобы обеспечить порядок вызова функции, необходимо использовать Долженценить. 1
  • stub_args_num : Удаленное Служитьфункциииз параметровчисло
  • stub_args : Удаленное Служитьфункциииз параметров

Возвращаемое значение:

  • удаленный Служитьфункцияизвозвращатьсяценить
Подать заявку на выравнивание памяти по кэш-линии

прототип функции

Язык кода:javascript
копировать
void *amp_align_malloc(int size);

параметр:

  • size : нуждаться Применить Памятьразмер

Возвращаемое значение:

  • Применятьиз Памятьадрес
Освободить память, выровненную по кэш-линии

прототип функции

Язык кода:javascript
копировать
void amp_align_free(void *ptr);

параметр:

  • ptr:нуждатьсявыпускатьиз Памятьадрес

Возвращаемое значение:

  • никто
Подать заявку на память AMP

прототип функции

Язык кода:javascript
копировать
void *amp_malloc(int size);

параметр:

  • size : нуждаться Применить Памятьразмер

Возвращаемое значение:

  • Применятьиз Памятьадрес
Освободите память AMP

прототип функции

Язык кода:javascript
копировать
void amp_free(void *ptr);

параметр:

  • ptr:нуждатьсявыпускатьиз Памятьадрес

Возвращаемое значение:

  • никто

Примеры использования Sunxi-AMP

Добавить службу передачи вызовов указателя

добавить вуказательпередачаудаленный вызов Служитьиз Процесс заключается в следующем:

  1. Можно ссылаться lichee/rtos‑components/aw/amp/service/fsys/ удаленныйдокументсистема Служить。существовать lichee/rtos-components/aw/amp/service/ Следующее создание соответствует папке Служитьиз. fsys к ИПереписка service и stub Конечный исходный файл, удаленный файл система Служить fsys_ser.c и fsys_stub.c。Можно ссылаться Makefile и Kconfig добавить вкомпилировать、настроить новыйизудаленный вызов Служить
  2. существовать fsys_ser.c Создано в sunxi_amp_func_table множество fsys_table
  3. существовать amp_service.c генерал-лейтенант fsys_table добавить в func_table множествосередина
  4. существовать sunxi_amp_table.h Добавитьдокументсистемаудаленный Служить Группаиз Структура RPCHandler_FSYS_t,Необходимость Уведомления есть,RPCHandler_FSYS_t Для структуризации переменной-члена требуется и sunxi_amp
  5. существовать sunxi_amp.h из RPCHandler_RPCT_t Добавлено в определение структуры RPCHandler_FSYS указатель
  6. существовать sunxi_amp.h Определить макрос в
Язык кода:javascript
копировать
#define RPCNO_FSYS RPCCALL_RPCNO(RPCHandler_FSYS)
#define RPCSERVICE_FSYS_DIR (RPC_MSG_DIR_RV) // Это необходимо изменить в зависимости от фактического развертывания удаленной файловой системы Служитьиз ядра
#define RPCCALL_FSYS(y) (RPCNO(RPCNO_FSYS, RPCHandler_FSYS_t, y) | (RPCSERVICE_FSYS_DIR << 28) |
SELF_DIRECTION << 24)

fsys_ser.c

Язык кода:javascript
копировать
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <statfs.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>

#include "sunxi_amp.h"
#include <hal_cache.h>

extern int truncate (const char *__file, __off_t __length);
extern int fstatfs (int __fildes, struct statfs *__buf);

static int _open(const char *name, int nameSize, int flag)
{
    int ret;
    hal_dcache_invalidate((unsigned long)name, nameSize);
    ret = open(name, flag);
    hal_dcache_invalidate((unsigned long)name, nameSize);
    return ret;
}

static int _close(int fd)
{
    return close(fd);
}

static ssize_t _read(int fd, void *buffer, size_t size)
{
    ssize_t ret = -1;
    ret = read(fd, buffer, size);
    hal_dcache_clean((unsigned long)buffer, size);
    return ret;
}

static ssize_t _write(int fd, void *buffer, size_t size)
{
    ssize_t ret = -1;
    hal_dcache_invalidate((unsigned long)buffer, size);
    ret = write(fd, buffer, size);
    hal_dcache_clean((unsigned long)buffer, size);
    return ret;
}

static off_t _lseek(int fd, off_t offset, int whence)
{
    return lseek(fd, offset, whence);
}

static int _stat(const char *path, int pathSize, struct stat *st)
{
    int ret = -1;
    hal_dcache_invalidate((unsigned long)st, sizeof(*st));
    hal_dcache_invalidate((unsigned long)path, pathSize);
    ret = stat(path, st);
    hal_dcache_clean((unsigned long)st, sizeof(*st));
    return ret;
}

static int _fstat(int fd, struct stat *st)
{
    int ret = -1;
    hal_dcache_invalidate((unsigned long)st, sizeof(*st));
    ret = fstat(fd, st);
    hal_dcache_clean((unsigned long)st, sizeof(*st));
    return ret;
}

static int _unlink(const char *path, int pathSize)
{
    int ret = -1;
    hal_dcache_invalidate((unsigned long)path, pathSize);
    ret = unlink(path);
    hal_dcache_invalidate((unsigned long)path, pathSize);
    return ret;
}

static int _rename(const char *old, int oldSize, const char *new, int newSize)
{
    int ret = -1;
    hal_dcache_invalidate((unsigned long)new, newSize);
    hal_dcache_invalidate((unsigned long)old, oldSize);
    ret = rename(old, new);
    hal_dcache_invalidate((unsigned long)old, oldSize);
    hal_dcache_invalidate((unsigned long)new, newSize);
    return ret;
}

static DIR *_opendir(const char *path, int pathSize)
{
    DIR *ret = NULL;
    hal_dcache_invalidate((unsigned long)path, pathSize);
    ret = opendir(path);
    if (ret)
        hal_dcache_clean((unsigned long)ret, sizeof(*ret));
    hal_dcache_invalidate((unsigned long)path, pathSize);
    return ret;
}

static struct dirent *_readdir(DIR *pdir)
{
    struct dirent *ret = NULL;
    ret = readdir(pdir);
    if (ret)
        hal_dcache_clean((unsigned long)ret, sizeof(*ret));
    return ret;
}

static int _closedir(DIR *pdir)
{
    return closedir(pdir);
}

static int _mkdir(const char *name, int nameSize, mode_t mode)
{
    int ret;
    hal_dcache_invalidate((unsigned long)name, nameSize);
    ret = mkdir(name, mode);
    hal_dcache_invalidate((unsigned long)name, nameSize);
    return ret;
}

static int _rmdir(const char *name, int nameSize)
{
    int ret;
    hal_dcache_invalidate((unsigned long)name, nameSize);
    ret = rmdir(name);
    hal_dcache_invalidate((unsigned long)name, nameSize);
    return ret;
}

static int _access(const char *name, int nameSize, int amode)
{
    int ret;
    hal_dcache_invalidate((unsigned long)name, nameSize);
    ret = access(name, amode);
    hal_dcache_invalidate((unsigned long)name, nameSize);
    return ret;
}

static int _truncate(const char *name, int nameSize, off_t length)
{
    int ret;
    hal_dcache_invalidate((unsigned long)name, nameSize);
    ret = truncate(name, length);
    hal_dcache_invalidate((unsigned long)name, nameSize);
    return ret;
}

static int _statfs(const char *name, int nameSize, struct statfs *buf)
{
    int ret = -1;
    hal_dcache_invalidate((unsigned long)name, nameSize);
    ret = statfs(name, buf);
    hal_dcache_clean((unsigned long)buf, sizeof(*buf));
    return ret;
}

static int _fstatfs(int fd, struct statfs *buf)
{
    int ret = -1;
    ret = fstatfs(fd, buf);
    hal_dcache_clean((unsigned long)buf, sizeof(*buf));
    return ret;
}

static int _fsync(int fd)
{
    return fsync(fd);
}

sunxi_amp_func_table fsys_table[] =
{
    {.func = (void *)&_open, .args_num = 3, .return_type = RET_POINTER},
    {.func = (void *)&_close, .args_num = 1, .return_type = RET_POINTER},
    {.func = (void *)&_read, .args_num = 3, .return_type = RET_POINTER},
    {.func = (void *)&_write, .args_num = 3, .return_type = RET_POINTER},
    {.func = (void *)&_lseek, .args_num = 3, .return_type = RET_POINTER},
    {.func = (void *)&_fstat, .args_num = 2, .return_type = RET_POINTER},
    {.func = (void *)&_stat, .args_num = 3, .return_type = RET_POINTER},
    {.func = (void *)&_unlink, .args_num = 2, .return_type = RET_POINTER},
    {.func = (void *)&_rename, .args_num = 4, .return_type = RET_POINTER},
    {.func = (void *)&_opendir, .args_num = 2, .return_type = RET_POINTER},
    {.func = (void *)&_readdir, .args_num = 1, .return_type = RET_POINTER},
    {.func = (void *)&_closedir, .args_num = 1, .return_type = RET_POINTER},
    {.func = (void *)&_mkdir, .args_num = 3, .return_type = RET_POINTER},
    {.func = (void *)&_rmdir, .args_num = 2, .return_type = RET_POINTER},
    {.func = (void *)&_access, .args_num = 3, .return_type = RET_POINTER},
    {.func = (void *)&_truncate, .args_num = 3, .return_type = RET_POINTER},
    {.func = (void *)&_statfs, .args_num = 3, .return_type = RET_POINTER},
    {.func = (void *)&_fstatfs, .args_num = 2, .return_type = RET_POINTER},
    {.func = (void *)&_fsync, .args_num = 1, .return_type = RET_POINTER},
};

fsys_stub.c

Язык кода:javascript
копировать
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <reent.h>

#include "sunxi_amp.h"

#include <hal_cache.h>

#ifndef _MAYBE_STATIC
#include <statfs.h>
#include <dirent.h>

#else
struct dirent
{
    int d_ino;          /*!< file number */
    uint8_t d_type;     /*!< not defined in POSIX, but present in BSD and Linux */
#define DT_UNKNOWN  0
#define DT_REG      1
#define DT_DIR      2
    char d_name[256];   /*!< zero-terminated file name */
};

typedef __uint64_t __fsblkcnt_t;

struct statfs
{
    uint32_t f_type;
    uint32_t f_bsize;
    __fsblkcnt_t f_blocks;
    __fsblkcnt_t f_bfree;
    __fsblkcnt_t f_bavail;
    __fsfilcnt_t f_files;
    __fsfilcnt_t f_ffree;
};

typedef struct
{
    uint16_t dd_vfs_idx; /*!< VFS index, not to be used by applications */
    uint16_t dd_rsv;     /*!< field reserved for future extension */
    /* remaining fields are defined by VFS implementation */
} DIR;
#endif


MAYBE_STATIC int open(const char *name, int flag, ...)
{
    void *args[3] = {0};
    int ret = -1;
    int nameSize = strlen(name) + 1;
    char *align_name = amp_align_malloc(nameSize);
    if (!align_name)
    {
        return -1;
    }
    memset(align_name, 0, nameSize);
    memcpy(align_name, name, nameSize);
    args[0] = align_name;
    args[1] = (void *)(unsigned long)nameSize;
    args[2] = (void *)(unsigned long)flag;
    hal_dcache_clean((unsigned long)align_name, nameSize);
    ret = func_stub(RPCCALL_FSYS(open), 1, ARRAY_SIZE(args), args);
    amp_align_free(align_name);
    return ret;
}

MAYBE_STATIC int close(int fd)
{
    void *args[1] = {0};
    args[0] = (void *)(unsigned long)fd;
    return func_stub(RPCCALL_FSYS(close), 1, ARRAY_SIZE(args), args);
}

MAYBE_STATIC ssize_t read(int fd, void *buffer, size_t size)
{
    void *args[3] = {0};
    ssize_t ret = -1;
    args[0] = (void *)(unsigned long)fd;
    args[1] = buffer;
    args[2] = (void *)(unsigned long)size;
    if (fd < 3)
    {
        return _read_r(_REENT, fd, buffer, size);
    }
    hal_dcache_invalidate((unsigned long)buffer, size);
    ret = func_stub(RPCCALL_FSYS(read), 1, ARRAY_SIZE(args), args);
    hal_dcache_invalidate((unsigned long)buffer, size);
    return ret;
}

MAYBE_STATIC ssize_t write(int fd, void *buffer, size_t size)
{
    void *args[3] = {0};
    args[0] = (void *)(unsigned long)fd;
    args[1] = buffer;
    args[2] = (void *)(unsigned long)size;
    if (fd < 3)
    {
        return _write_r(_REENT, fd, buffer, size);
    }
    hal_dcache_clean((unsigned long)buffer, size);
    return func_stub(RPCCALL_FSYS(write), 1, ARRAY_SIZE(args), args);
}

MAYBE_STATIC off_t lseek(int fd, off_t offset, int whence)
{
    void *args[3] = {0};
    args[0] = (void *)(unsigned long)fd;
    args[1] = (void *)(unsigned long)offset;
    args[2] = (void *)(unsigned long)whence;
    return func_stub(RPCCALL_FSYS(lseek), 1, ARRAY_SIZE(args), args);
}

#ifndef _MAYBE_STATIC
MAYBE_STATIC int fstat(int fd, struct stat *st)
{
    void *args[2] = {0};
    int ret = -1;
    struct stat *st_align = amp_align_malloc(sizeof(*st));
    if (!st_align)
    {
        return -1;
    }
    memset(st_align, 0, sizeof(*st));
    args[0] = (void *)(unsigned long)fd;
    args[1] = st_align;
    hal_dcache_clean((unsigned long)st_align, sizeof(struct stat));
    ret = func_stub(RPCCALL_FSYS(fstat), 1, ARRAY_SIZE(args), args);
    hal_dcache_invalidate((unsigned long)st_align, sizeof(struct stat));
    memcpy(st, st_align, sizeof(struct stat));
    amp_align_free(st_align);
    return ret;
}

MAYBE_STATIC int stat(const char *path, struct stat *st)
{
    void *args[3] = {0};
    int ret = -1;
    int pathSize = strlen(path) + 1;
    char *align_path = amp_align_malloc(pathSize);
    if (!align_path)
    {
        return -1;
    }
    memset(align_path, 0, pathSize);
    memcpy(align_path, path, pathSize);
    struct stat *st_align = amp_align_malloc(sizeof(*st));
    if (!st_align)
    {
        amp_align_free(align_path);
        return -1;
    }
    memset(st_align, 0, sizeof(*st));

    args[0] = align_path;
    args[1] = (void *)(unsigned long)pathSize;
    args[2] = st_align;

    hal_dcache_clean((unsigned long)align_path, pathSize);
    hal_dcache_clean((unsigned long)st_align, sizeof(struct stat));

    ret = func_stub(RPCCALL_FSYS(stat), 1, ARRAY_SIZE(args), args);

    hal_dcache_invalidate((unsigned long)st_align, sizeof(struct stat));
    memcpy(st, st_align, sizeof(struct stat));
    amp_align_free(st_align);
    amp_align_free(align_path);
    return ret;
}
#endif

MAYBE_STATIC int unlink(const char *path)
{
    void *args[2] = {0};
    int ret = -1;
    int pathSize = strlen(path) + 1;
    char *align_path = amp_align_malloc(pathSize);
    if (!align_path)
    {
        return -1;
    }
    memset(align_path, 0, pathSize);
    memcpy(align_path, path, pathSize);
    args[0] = align_path;
    args[1] = (void *)(unsigned long)pathSize;
    hal_dcache_clean((unsigned long)align_path, pathSize);
    ret = func_stub(RPCCALL_FSYS(unlink), 1, ARRAY_SIZE(args), args);
    amp_align_free(align_path);
    return ret;
}

#ifndef _MAYBE_STATIC
MAYBE_STATIC int rename(const char *old, const char *new)
{
    void *args[4] = {0};
    int ret = -1;
    int oldSize = strlen(old) + 1;
    int newSize = strlen(new) + 1;
    char *align_old = amp_align_malloc(oldSize);
    if (!align_old)
    {
        return -1;
    }
    char *align_new = amp_align_malloc(newSize);
    if (!align_new)
    {
        amp_align_free(align_old);
        return -1;
    }
    memset(align_new, 0, newSize);
    memcpy(align_new, new, newSize);
    memset(align_old, 0, oldSize);
    memcpy(align_old, old, oldSize);

    args[0] = align_old;
    args[1] = (void *)(unsigned long)oldSize;
    args[2] = align_new;
    args[3] = (void *)(unsigned long)newSize;
    hal_dcache_clean((unsigned long)align_old, oldSize);
    hal_dcache_clean((unsigned long)align_new, newSize);
    ret = func_stub(RPCCALL_FSYS(rename), 1, ARRAY_SIZE(args), args);
    amp_align_free(align_old);
    amp_align_free(align_new);
    return ret;
}
#endif

MAYBE_STATIC DIR *opendir(const char *path)
{
    DIR *ret = NULL;
    void *args[2] = {0};
    int pathSize = strlen(path) + 1;
    char *align_path = amp_align_malloc(pathSize);
    if (!align_path)
    {
        return NULL;
    }
    memset(align_path, 0, pathSize);
    memcpy(align_path, path, pathSize);
    args[0] = align_path;
    args[1] = (void *)(unsigned long)pathSize;
    hal_dcache_clean((unsigned long)align_path, pathSize);
    ret = (void *)func_stub(RPCCALL_FSYS(opendir), 1, ARRAY_SIZE(args), args);
    if (ret)
    {
        hal_dcache_invalidate((unsigned long)ret, sizeof(*ret));
    }
    amp_align_free(align_path);
    return ret;
}

MAYBE_STATIC struct dirent *readdir(DIR *pdir)
{
    struct dirent *ent = NULL;
    void *args[1] = {0};
    args[0] = pdir;
    hal_dcache_clean((unsigned long)pdir, sizeof(*pdir));
    ent = (void *)func_stub(RPCCALL_FSYS(readdir), 1, ARRAY_SIZE(args), args);
    if (ent)
    {
        hal_dcache_invalidate((unsigned long)ent, sizeof(*ent));
    }
    return ent;
}

MAYBE_STATIC int closedir(DIR *pdir)
{
    void *args[1] = {0};
    args[0] = pdir;
    hal_dcache_clean((unsigned long)pdir, sizeof(*pdir));
    return func_stub(RPCCALL_FSYS(closedir), 1, ARRAY_SIZE(args), args);
}

#ifndef _MAYBE_STATIC
MAYBE_STATIC int mkdir(const char *name, mode_t mode)
{
    void *args[3] = {0};
    int ret = -1;
    int nameSize = strlen(name) + 1;
    char *align_name = amp_align_malloc(nameSize);
    if (!align_name)
    {
        return -1;
    }
    memset(align_name, 0, nameSize);
    memcpy(align_name, name, nameSize);
    args[0] = align_name;
    args[1] = (void *)(unsigned long)nameSize;
    args[2] = (void *)(unsigned long)mode;
    hal_dcache_clean((unsigned long)align_name, nameSize);
    ret = func_stub(RPCCALL_FSYS(mkdir), 1, ARRAY_SIZE(args), args);
    amp_align_free(align_name);
    return ret;
}
#endif

MAYBE_STATIC int rmdir(const char *name)
{
    void *args[2] = {0};
    int ret = -1;
    int nameSize = strlen(name) + 1;
    char *align_name = amp_align_malloc(nameSize);
    if (!align_name)
    {
        return -1;
    }
    memset(align_name, 0, nameSize);
    memcpy(align_name, name, nameSize);
    args[0] = align_name;
    args[1] = (void *)(unsigned long)nameSize;
    hal_dcache_clean((unsigned long)align_name, nameSize);
    ret = func_stub(RPCCALL_FSYS(rmdir), 1, ARRAY_SIZE(args), args);
    amp_align_free(align_name);
    return ret;
}

MAYBE_STATIC int access(const char *name, int amode)
{
    void *args[3] = {0};
    int ret = -1;
    int nameSize = strlen(name) + 1;
    char *align_name = amp_align_malloc(nameSize);
    if (!align_name)
    {
        return -1;
    }
    memset(align_name, 0, nameSize);
    memcpy(align_name, name, nameSize);
    args[0] = align_name;
    args[1] = (void *)(unsigned long)nameSize;
    args[2] = (void *)(unsigned long)amode;
    hal_dcache_clean((unsigned long)align_name, nameSize);
    ret = func_stub(RPCCALL_FSYS(access), 1, ARRAY_SIZE(args), args);
    amp_align_free(align_name);
    return ret;
}

MAYBE_STATIC int truncate(const char *name, off_t length)
{
    void *args[3] = {0};
    int ret = -1;
    int nameSize = strlen(name) + 1;
    char *align_name = amp_align_malloc(nameSize);
    if (!align_name)
    {
        return -1;
    }
    memset(align_name, 0, nameSize);
    memcpy(align_name, name, nameSize);
    args[0] = align_name;
    args[1] = (void *)(unsigned long)nameSize;
    args[2] = (void *)(unsigned long)length;
    hal_dcache_clean((unsigned long)align_name, nameSize);
    ret = func_stub(RPCCALL_FSYS(truncate), 1, ARRAY_SIZE(args), args);
    amp_align_free(align_name);
    return ret;
}

MAYBE_STATIC int statfs(const char *path, struct statfs *buf)
{
    void *args[3] = {0};
    int ret = -1;
    int pathSize = strlen(path) + 1;
    char *align_path = amp_align_malloc(pathSize);
    if (!align_path)
    {
        return -1;
    }
    char *align_statfs = amp_align_malloc(sizeof(struct statfs));
    if (!align_statfs)
    {
        amp_align_free(align_statfs);
        return -1;
    }
    memset(align_path, 0, pathSize);
    memcpy(align_path, path, pathSize);
    memset(align_statfs, 0, sizeof(struct statfs));

    args[0] = align_path;
    args[1] = (void *)(unsigned long)pathSize;
    args[2] = align_statfs;

    hal_dcache_clean((unsigned long)align_path, pathSize);
    hal_dcache_clean((unsigned long)align_statfs, sizeof(struct statfs));
    ret = func_stub(RPCCALL_FSYS(statfs), 1, ARRAY_SIZE(args), args);

    hal_dcache_invalidate((unsigned long)align_statfs, sizeof(struct statfs));
    memcpy(buf, align_statfs, sizeof(struct statfs));

    amp_align_free(align_path);
    amp_align_free(align_statfs);
    return ret;
}

MAYBE_STATIC int fstatfs(int fd, struct statfs *buf)
{
    void *args[2] = {0};
    int ret = -1;
    char *align_statfs = amp_align_malloc(sizeof(struct statfs));
    if (!align_statfs)
    {
        return -1;
    }
    args[0] = (void *)(unsigned long)fd;
    args[1] = align_statfs;

    hal_dcache_clean((unsigned long)align_statfs, sizeof(struct statfs));
    ret = func_stub(RPCCALL_FSYS(fstatfs), 1, ARRAY_SIZE(args), args);

    hal_dcache_invalidate((unsigned long)align_statfs, sizeof(struct statfs));
    memcpy(buf, align_statfs, sizeof(struct statfs));

    amp_align_free(align_statfs);
    return ret;
}

MAYBE_STATIC int fsync(int fd)
{
    void *args[1] = {0};
    args[0] = (void *)(unsigned long)fd;

    return func_stub(RPCCALL_FSYS(fsync), 1, ARRAY_SIZE(args), args);
}
Реализация межъядерного терминала RPCLI

rpcconsole_stub.c

Язык кода:javascript
копировать
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <console.h>

#include <sunxi_amp.h>
#include <hal_cache.h>

#define SEND_TO_32BIT (1)
#define SEND_TO_64BIT (2)

struct rpcconsole_arg_t
{
    uint32_t argc;
    uint32_t flag;
    uint64_t argv_64[SUNXI_AMP_SH_MAX_CMD_ARGS];
    uint32_t argv_32[SUNXI_AMP_SH_MAX_CMD_ARGS];
    union
    {
        char *argv_cmd;
        uint64_t argv_cmd_data;
    };
};

static int cmd_rpcconsole(int argc, char **argv)
{
    int ret = -1;
    int i;

    if (argc <= 2)
    {
        printf("Usage: rpccli [arm|dsp|rv] commandname [arg0 ...] \n");
        return -1;
    }
    if (argc > SUNXI_AMP_SH_MAX_CMD_ARGS)
    {
        printf("maximum number of arg:%d\n", SUNXI_AMP_SH_MAX_CMD_ARGS);
        return -1;
    }

    struct rpcconsole_arg_t *rpc_arg = amp_align_malloc(sizeof(struct rpcconsole_arg_t));
    if (rpc_arg == NULL)
    {
        printf("Alloc memory failed!\n");
        return -1;
    }
    memset(rpc_arg, 0, sizeof(struct rpcconsole_arg_t));

    char *rpc_args_cmd = amp_align_malloc(SH_MAX_CMD_LEN);
    if (rpc_args_cmd == NULL)
    {
        printf("Alloc memory failed!\n");
        amp_align_free(rpc_arg);
        return -1;
    }
    memset(rpc_args_cmd, 0, SH_MAX_CMD_LEN);

    rpc_arg->argv_cmd = rpc_args_cmd;
    rpc_arg->argc = argc - 2;

    for (i = 2; i < argc; i++)
    {
        rpc_arg->argv_32[i - 2] = (uint32_t)(unsigned long)rpc_args_cmd;
        rpc_arg->argv_64[i - 2] = (uint32_t)(unsigned long)rpc_args_cmd;
        memcpy(rpc_args_cmd, argv[i], strlen(argv[i]));
        rpc_args_cmd += ALIGN_UP((strlen(argv[i]) + 1), 4);
    }

    void *args[1] = {0};
    args[0] = rpc_arg;

    if (!strcmp("arm", argv[1]))
    {
        rpc_arg->flag = SEND_TO_32BIT;
        hal_dcache_clean((unsigned long)rpc_arg, sizeof(*rpc_arg));
        hal_dcache_clean((unsigned long)rpc_arg->argv_cmd, SH_MAX_CMD_LEN);

        ret = func_stub(RPCCALL_ARM_CONSOLE(exe_cmd), 1, 1, (void *)&args);
    }
    else if (!strcmp("dsp", argv[1]))
    {
        rpc_arg->flag = SEND_TO_32BIT;
        hal_dcache_clean((unsigned long)rpc_arg, sizeof(*rpc_arg));
        hal_dcache_clean((unsigned long)rpc_arg->argv_cmd, SH_MAX_CMD_LEN);

        ret = func_stub(RPCCALL_DSP_CONSOLE(exe_cmd), 1, 1, (void *)&args);
    }
    else if (!strcmp("rv", argv[1]))
    {
        rpc_arg->flag = SEND_TO_64BIT;
        hal_dcache_clean((unsigned long)rpc_arg, sizeof(*rpc_arg));
        hal_dcache_clean((unsigned long)rpc_arg->argv_cmd, SH_MAX_CMD_LEN);

        ret = func_stub(RPCCALL_RV_CONSOLE(exe_cmd), 1, 1, (void *)&args);
    }

    amp_align_free(rpc_arg->argv_cmd);
    amp_align_free(rpc_arg);

    return ret;
}
FINSH_FUNCTION_EXPORT_CMD(cmd_rpcconsole, rpccli, exe);

rpcconsole_ser.c

Язык кода:javascript
копировать
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <console.h>
#include <barrier.h>

#include <sunxi_amp.h>
#include <hal_cache.h>

#define SEND_TO_32BIT (1)
#define SEND_TO_64BIT (2)

struct rpcconsole_arg_t
{
    uint32_t argc;
    uint32_t flag;
    uint64_t argv_64[SUNXI_AMP_SH_MAX_CMD_ARGS];
    uint32_t argv_32[SUNXI_AMP_SH_MAX_CMD_ARGS];
    union
    {
        char *argv_cmd;
        uint64_t argv_cmd_data;
    };
};

static int _execute_cmd(struct rpcconsole_arg_t *rpc_arg)
{
    char *command_name;
    struct finsh_syscall *call;
    int ret = -1;

    hal_dcache_invalidate((unsigned long)rpc_arg, (unsigned long)sizeof(*rpc_arg));
#ifndef CONFIG_ARCH_DSP
    dsb();
    isb();
#endif
    hal_dcache_invalidate((unsigned long)rpc_arg->argv_cmd, SH_MAX_CMD_LEN);

    if (rpc_arg->flag == SEND_TO_32BIT) {
        command_name = (char *)(unsigned long)(rpc_arg->argv_32[0]);
    } else {
        command_name = (char *)(unsigned long)(rpc_arg->argv_64[0]);
    }

    call = finsh_syscall_lookup(command_name);
    if (call == NULL || call->func == NULL)
    {
        printf("The command(%s) no exist !\n", command_name);
        return -1;
    }

    if (rpc_arg->flag == SEND_TO_32BIT) {
        ret = call->func(rpc_arg->argc, (char **)(unsigned long)rpc_arg->argv_32);
    } else {
        ret = call->func(rpc_arg->argc, (char **)(unsigned long)rpc_arg->argv_64);
    }
    hal_dcache_invalidate((unsigned long)rpc_arg, (unsigned long)sizeof(*rpc_arg));
    hal_dcache_invalidate((unsigned long)rpc_arg->argv_cmd, SH_MAX_CMD_LEN);
    return ret;
}

sunxi_amp_func_table console_table[] =
{
    {.func = (void *)&_execute_cmd, .args_num = 1, .return_type = RET_POINTER},
};

Примечания к Sunxi‑AMP

  1. Уведомлениемногоядерныйкоммуникациячасизданные Buffer начальный адрес и длина, требования к начальному адресу основаны на максимальном значении в системе. CacheLine(64B)Выравнивание,Требования к длине Buffer Эксклюзивный CacheLine,Не допускается совместное использование другими данными CacheLine
  2. еслисуществоватьудаленный вызовпроцесссередина,нуждаться Воля Памятьданныепередачадругомуиз CPU Процессор, существующий, необходимо вызывать перед выполнением удаленной функции. hal_dcache_clean позвоню в ядро CPU Dcache данные flash back Память медиа, см. lichee/rtos‑components/aw/amp/service/flashc/flashc_stub.c в nor_write Функция Если вам нужно выполнить удаленный вызов функции из другого существующего процессора, вам необходимо выполнить; hal_dcache_invalidate будет местный CPU Dcacheникто Вступить в силу,кизбегатьполучатьстарыйизисторияданные,Можеткссылкаlichee/rtos‑components/aw/amp/service/flashc/flashc_stub.c в nor_read функция。
  3. Уведомление Cache из Dirty Кусочек.кипарисфункция (stub конец) Сначала нужно перейти к Служитьфункция (service конец) из buffer Место Переписка cacheline никтоэффективный, сосредоточься на очистке dirty Кусочек,в противном случаесуществовать cachelien Сотрясения во время выгрузки могут привести к срабатыванию ядра. buffer Перепискагрязный cacheline Замените и запишите обратно, перезаписав другое ядро, только что записанное изданное. Служитьфункция (service конец) Need больше не будет использоваться из buffer Переписка cacheline никто не воздействует и не отмахивается Память, сосредоточься на существовании на очистке dirty Кусочек,кпрофилактикасуществовать cahcline ухабистый процесс замены генерал-лейтенант buffer Перепискагрязный cacheline вданные отписываются, в это время очень возможно подделать другое ядро ​​изданные. Пример анализа:существовать lichee/rtos‑components/aw/amp/service/flashc/flashc_stub.c в nor_read функциясередина,ядерныйсуществоватьвызов func_stub(RPCCALL_FLASHC(nor_read), 1, ARRAY_SIZE(args), args); Прежде всего, вам нужно позвонить hal_dcache_clean_invalidate((unsigned long)buffer, size); интерфейс понятен buffer переписыватьсяcacheline из dirty бит, иначе существование программы может привести к M33 ядерныйиз Служитьфункция Только Воляданные Обновить Память,C906 Ядерный и потому cacheline из Заменить ручку buffer изданные были изменены. Точно так же существуют lichee/rtos‑components/aw/amp/service/flashc/flashc_ser.c из _nor_write функциясередина,M33 ядерныйсуществовать Звонок завершен nor_write После этого вам нужно позвонить еще раз hal_dcache_invalidate((unsigned long)buf, size);,потому что nor_write Изменения могут быть сохранены в существующем buffer возможность передачи данных, поэтому из buffer Переписка cacheline существовать Сердечник М33 грязный, существует. M33 ядерныйбегатьизпроцесссередина,Существует высокая вероятность того, что в Память будут записаны грязные данные. И в это время,buffer существоватьC906 могли быть использованы для других целей, подав заявку на выпуск, M33 Nuclear записала грязные данные обратно в Память и напрямую вмешалась в них. C906 Запускаем на основе обычных данных.
  4. Максимальное количество поддерживаемых в настоящее время параметров: 8 индивидуальный.
  5. 64bit данныесуществовать всегда будут усекаться во время передачи. 32bit данные。
  6. Нет Можетсуществоватьсерединаперерывфункциясерединаиспользоватьудаленный вызов。
  7. существовать sunxi_amp.h Добавить #define AMP_DEBUG,новстречасуществоватьключевой узелсерединадавать возможностькомпонентыиз Вывод отладочной информации,Используется для анализа таких проблем, как ошибки удаленного вызова функций.
  8. При передаче одной и той же структуры между несколькими ядрами старайтесь не использовать тип указатель внутри структуры Должен. Основных причин три. Разрядность ядра непостоянна, и существуют различия в понимании типа указателя и длины z, что приведет к существованию различий в структуре изданных структур. Когда две разные разрядности битов CPU При получении цен через тот же индекс структуры, указывающий на адрес, это приведет к ошибке получения цен. Например, следующий пример:
Язык кода:javascript
копировать
struct test_t {
    char *ptr;
    int offset;
};

struct test_t a;
ptr = &a;                // ptr указать на struct test_t Структуризация показателя при условии, что его адрес 0x4000000;

int offset = ptr‑>offset // существовать 32bit система посетит 0x4000004;
int offset = ptr‑>offset // существовать 64bit система посетит 0x4000008;

Межъядерная связь RPDATA

Для взаимодействия между различными ядрами на основе аппаратной функции msgbox, предоставляемой программным обеспечением механизма AMP. связи, по этому мы предоставляем интерфейс rpdata, позволяющий пользователям более легко реализовать межъядерную связь.

введение в интерфейс rpdata
Создать рпданные
Язык кода:javascript
копировать
rpdata_t *rpdata_create(int dir, const char *type, const char *name, size_t buf_len)

параметр:

  • dir: указывает удаленное ядро. 1:М33 2:РВ 3:ДСП;
  • тип: тип,Тип данных передачи,нить Может Настроить,Тип и имя вместе образуют уникальный идентификатор.
  • имя: имя,Имя передаваемых данных,нить Может Настроить,Тип и имя вместе образуют уникальный идентификатор.
  • buf_len: Размер буфера, Долженбуфер используется для временного сохранения данных межъядерной передачи.

Возвращаемое значение:

  • Дескриптор rpdata возвращается в случае успеха, а в случае неудачи возвращается NULL.

type и name Используется для идентификации уникального канала передачи, Можно установить самостоятельно, длина не превышает 16 Байтов достаточно. Сносно rpd ‑l Команда для просмотра текущей Призамениз rpdata к Статус ИПереписка. rpdata_create Функция не блокируется. существоватьсоздаватьиз, вы можете назвать это напрямую.

Подключить rpdata
Язык кода:javascript
копировать
rpdata_t *rpdata_connect(int dir, const char *type, const char *name)

параметр:

  • dir: указывает удаленное ядро. 1:М33 2:РВ 3:ДСП;
  • тип: тип,Тип данных передачи,нить Может Настроить,Тип и имя вместе образуют уникальный идентификатор.
  • имя: имя,Имя передаваемых данных,нить Может Настроить,Тип и имя вместе образуют уникальный идентификатор.

Возвращаемое значение:

  • Дескриптор rpdata возвращается в случае успеха, а в случае неудачи возвращается NULL.

rpdata_create и rpdata_connect Функция аналогична из, но первая будет использоваться для передачи данныхиз. буфер, последнего не будет. Следовательно, для связи между двумя ядрами эти два интерфейса должны использоваться отдельно и не могут использоваться вместе. rpdata_create или rpdata_connect。rpdata_connect Функция заблокируется. Если удаленное ядро ​​не вызывает rpdata_create, то поток всегда будет заблокирован. Обратите внимание на использование rpdata_connect, если Если вам нужен удаленный поток, подтвердите, можно ли его заблокировать. Если нет, вам нужно использовать отдельный поток.

Получить значение буфера
Язык кода:javascript
копировать
void *rpdata_buffer_addr(rpdata_t *rpd)

параметр:

  • rpd: дескриптор rpdata

Возвращаемое значение:

  • buffизценить

Должен buffer Адрес можно использовать для сохранения взаимодействия, которое будет передано изданным.

Определите, подключен ли rpdata
Язык кода:javascript
копировать
int rpdata_is_connect(rpdata_t *rpd)

параметр:

  • rpd: дескриптор rpdata

Возвращаемое значение:

  • Возврат 0выражать для статуса соединения, не-0выражать статус несоединять
Подождите, пока rpdata подключится
Язык кода:javascript
копировать
int rpdata_wait_connect(rpdata_t *rpd)

параметр:

  • rpd: дескриптор rpdata

Возвращаемое значение:

  • Возвращает 0 в случае успеха и не-0 в случае неудачи.

Нужно судить и ждать rpdata Только после достижения состояния соединения можно начать передачу данных.

Межъядерная обработка данных

Используется для отправки данных на удаленное ядро, а удаленное ядро ​​заполняет обработанные данные в буфер.

Язык кода:javascript
копировать
int rpdata_process(rpdata_t *rpd, unsigned int offset, unsigned int data_len)

параметр:

  • rpd: дескриптор rpdata
  • offset: Смещение относительно буфера
  • data_len: данныеколичество,байт

Возвращаемое значение:

  • Возвращает 0 в случае успеха и не-0 в случае неудачи.

После обработки результаты сохраняются в buffer в, то есть rpdata_buffer_addr получатьизадрес。

Межъядерная отправка данных

Используется только для отправки данных на удаленное ядро.

Язык кода:javascript
копировать
int rpdata_send(rpdata_t *rpd, unsigned int offset, unsigned int data_len)

параметр:

  • rpd: дескриптор rpdata
  • offset: Смещение относительно буфера
  • data_len: данныеколичество,байт

Возвращаемое значение:

  • Возвращает 0 в случае успеха и не-0 в случае неудачи.

rpdata_send просто отправляет данные из буфера на удаленное ядро, а rpdata_process получает обработанные данные.

Принимать данные, передаваемые между ядрами
Язык кода:javascript
копировать
int rpdata_recv(rpdata_t *rpd, unsigned int *offset, unsigned int *data_len, int timeout_ms)

параметр:

  • rpd: дескриптор rpdata
  • offset: получать Смещение относительно буфера
  • data_len: получатьданныеколичество,байт
  • timeout_ms: время ожидания ожидания

Возвращаемое значение:

  • Возвращает 0 в случае успеха и не-0 в случае неудачи.

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

Установить обратный вызов для принятия данных
Язык кода:javascript
копировать
int rpdata_set_recv_cb(rpdata_t *rpd, struct rpdata_cbs *cbs)

параметр:

  • rpd: дескриптор rpdata
  • cbs: вернуться к функциональному эпизоду,В настоящее время поддерживается только Recv_cb.,то есть принять обратный вызов

Возвращаемое значение:

  • Возвращает 0 в случае успеха и не-0 в случае неудачи.
Функция обратного вызова приема данных
Язык кода:javascript
копировать
int (recv_cb_t)(rpdata_t rpd, void *data, int data_len)

Обратный вызов Уведомлениесуществовать Должен должен обработать данные и скопировать их, в противном случае они могут быть перезаписаны при следующей передаче данных.

Уничтожить rpdata
Язык кода:javascript
копировать
int rpdata_destroy(rpdata_t *rpd)

параметр:

  • rpd: дескриптор rpdata

Возвращаемое значение:

  • Возвращает 0 в случае успеха и не-0 в случае неудачи.

Только оба ядра вызывают rpdata_destroy Только тогда это будет по-настоящему изразрушать, но приказ о вызове не требуется.

процесс использования rpdata

#mermaid-svg-jRGud5Kzbo0jw8Co {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-jRGud5Kzbo0jw8Co .error-icon{fill:#552222;}#mermaid-svg-jRGud5Kzbo0jw8Co .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jRGud5Kzbo0jw8Co .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-jRGud5Kzbo0jw8Co .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jRGud5Kzbo0jw8Co .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jRGud5Kzbo0jw8Co .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jRGud5Kzbo0jw8Co .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jRGud5Kzbo0jw8Co .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jRGud5Kzbo0jw8Co .marker.cross{stroke:#333333;}#mermaid-svg-jRGud5Kzbo0jw8Co svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jRGud5Kzbo0jw8Co .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jRGud5Kzbo0jw8Co text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-jRGud5Kzbo0jw8Co .actor-line{stroke:grey;}#mermaid-svg-jRGud5Kzbo0jw8Co .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-jRGud5Kzbo0jw8Co .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-jRGud5Kzbo0jw8Co #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-jRGud5Kzbo0jw8Co .sequenceNumber{fill:white;}#mermaid-svg-jRGud5Kzbo0jw8Co #sequencenumber{fill:#333;}#mermaid-svg-jRGud5Kzbo0jw8Co #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-jRGud5Kzbo0jw8Co .messageText{fill:#333;stroke:#333;}#mermaid-svg-jRGud5Kzbo0jw8Co .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jRGud5Kzbo0jw8Co .labelText,#mermaid-svg-jRGud5Kzbo0jw8Co .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-jRGud5Kzbo0jw8Co .loopText,#mermaid-svg-jRGud5Kzbo0jw8Co .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-jRGud5Kzbo0jw8Co .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-jRGud5Kzbo0jw8Co .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-jRGud5Kzbo0jw8Co .noteText,#mermaid-svg-jRGud5Kzbo0jw8Co .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-jRGud5Kzbo0jw8Co .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jRGud5Kzbo0jw8Co .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jRGud5Kzbo0jw8Co .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jRGud5Kzbo0jw8Co .actorPopupMenu{position:absolute;}#mermaid-svg-jRGud5Kzbo0jw8Co .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-jRGud5Kzbo0jw8Co .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jRGud5Kzbo0jw8Co .actor-man circle,#mermaid-svg-jRGud5Kzbo0jw8Co line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-jRGud5Kzbo0jw8Co :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} M33 RV rpdata_create notify rpdata_create ack rpdata_set_recv_cb rpdata_send send data курок callback loop [Отправить несколько раз] notify rpdata_destory wait destory M33 RV

  1. rpdata_create и rpdate_connect из Требования к вызывающему приказу нет, однако тот, кто его выполнит первым, может это сделать; rpdata_data Да, будет создано для взаимодействия с данными из buffer
  2. Примите окончание данных, и вы сможете использовать их. rpdata_set_recv_cb Настройте обратные вызовы для обработки данных или вызовов rpdata_recv Блокировать ожидание принятия взаимных данных.
  3. Два ядра rpdata_destroy Приказ о вызове не требуется.
пример rpdata
Язык кода:javascript
копировать
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>

#include <rpdata.h>
#include <console.h>

#include <hal_time.h>
#include <hal_mem.h>
#include <hal_thread.h>
#include <md5.h>

//#define RECV_CALLBACK_TEST

/* print md5 check result */
static int g_rpd_verbose = 0;

#define configAPPLICATION_RPDATA_DEMO_PRIORITY	\
	(configMAX_PRIORITIES > 20 ? configMAX_PRIORITIES - 8 : configMAX_PRIORITIES - 3)

static void rpdata_demo_usage(void)
{
	printf("Usgae: rpdata_demo [option]\n");
	printf("-h,          rpdata help\n");
	printf("-m,          mode, 0-send; 1-recv; 2-recv+send\n");
	printf("-t,          type, type name\n");
	printf("-n,          name, id name\n");
	printf("             (type + name) specify unique data xfer\n");
	printf("-c,          string, send string\n");
	printf("-i,          single test, do send or recv test once\n");
	printf("-d,          dir, remote processor, 1-cm33;2-c906;3-dsp\n");
	printf("-s,          src dir, valid in mode 2\n");
	printf("\n");
	printf("RV -> DSP\n");
	printf("rpdata_demo -m 0 -d 3 -t RVtoDSP -n RVsendDSPrecv\n");
	printf("rpccli dsp rpdata_demo -m 1 -d 2 -t RVtoDSP -n RVsendDSPrecv\n");
	printf("\n");
	printf("RV -> M33\n");
	printf("rpdata_demo -m 0 -d 1 -t RVtoM33 -n RVsendM33recv\n");
	printf("rpccli arm rpdata_demo -m 1 -d 2 -t RVtoM33 -n RVsendM33recv\n");
	printf("\n");
	printf("RV <- M33\n");
	printf("rpdata_demo -m 1 -d 1 -t M33toRV -n RVrecvM33send\n");
	printf("rpccli arm rpdata_demo -m 0 -d 2 -t M33toRV -n RVrecvM33send\n");
	printf("\n");
	printf("RV -> DSP -> M33\n");
	printf("rpccli dsp rpdata_demo -d 2 -t RVtoDSP -n DSPrecvRVsend -s 1 -t DSPtoM33 -n M33recvDSPsend\n");
	printf("rpccli arm rpdata_demo -m 1 -d 3 -t DSPtoM33 -n M33recvDSPsend\n");
	printf("rpdata_demo -m 0 -d 3 -t RVtoDSP -n DSPrecvRVsend\n");
	printf("\n");
	printf("RV -> DSP -> M33 -> DSP -> RV\n");
	printf("rpccli dsp rpdata_demo -d 2 -t RD -n rs -s 1 -t DM -n ds\n");
	printf("rpccli arm rpdata_demo -d 3 -t DM -n ds -s 3 -t MD -n ms\n");
	printf("rpccli dsp rpdata_demo -d 1 -t MD -n ms -s 2 -t DR -n ds\n");
	printf("rpdata_demo -m 1 -d 3 -t DR -n ds\n");
	printf("rpdata_demo -m 0 -d 3 -t RD -n rs\n");
	printf("\n");
	printf("RV->M33->RV process(M33 do aec process: input mic+ref, and output aec data to RV)\n");
	printf("rpccli arm rpdata_demo -p -m 1 -d 2 -t ALGO -n AEC\n");
	printf("rpdata_demo -p -m 0 -d 1 -t ALGO -n AEC\n");
	printf("\n");
}

struct rpdata_arg_test {
	char type[32];
	char name[32];
	int dir;
	char stype[32];
	char sname[32];
	int sdir;
};

static int do_rpdata_send_test(struct rpdata_arg_test *targ, void *data, uint32_t len)
{
	rpdata_t *rpd;
	void *buffer;
	int ret = -1;

	printf("Cteate %s:%s.\n", targ->type, targ->name);
	rpd = rpdata_create(targ->dir, targ->type, targ->name, len);
	if (!rpd) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		return -1;
	}
#ifndef RECV_CALLBACK_TEST
	rpdata_set_recv_ringbuffer(rpd, 2 * rpdata_buffer_len(rpd));
#endif

	buffer = rpdata_buffer_addr(rpd);
	if (!buffer) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}

	memcpy(buffer, data, len);
	rpdata_wait_connect(rpd);
	ret = rpdata_send(rpd, 0, len);
	if (ret != 0) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}

exit:
	if (rpd)
		rpdata_destroy(rpd);
	return ret;
}

static int do_rpdata_recv_test(struct rpdata_arg_test *targ, void *data, uint32_t len)
{
	rpdata_t *rpd;
	char *rx_buf;
	int ret = -1;

	rx_buf = hal_malloc(len + 1);
	if (rx_buf == NULL) {
		printf("[%s] line:%d alloc rx buffer fialed.\n", __func__, __LINE__);
		return -1;
	}

	printf("connect to %s:%s.\n", targ->type, targ->name);
	rpd = rpdata_connect(targ->dir, targ->type, targ->name);
	if (!rpd) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		return -1;
	}
#ifndef RECV_CALLBACK_TEST
	rpdata_set_recv_ringbuffer(rpd, 2 * rpdata_buffer_len(rpd));
#endif

	while (1) {
		ret = rpdata_recv(rpd, rx_buf, len, 10000);
		if (ret <= 0) {
			printf("rpdata recv timeout \n");
			goto exit;
		}
		rx_buf[ret] = 0;
		printf("%s, len:%d\n", rx_buf, ret);
		if (!memcmp("quit", rx_buf, 4))
			break;
	}

exit:
	if (rx_buf)
		hal_free(rx_buf);
	if (rpd)
		rpdata_destroy(rpd);
	return ret;
}

static void data_fill(void *buffer, uint32_t len)
{
	int i;
	int data_len = len - 16;

	memset(buffer, 0, len);
	for (i = 0; i < len;) {
		*(int *)(buffer + i) = rand();
		i += sizeof(int);
	}
	md5(buffer, data_len,  buffer + data_len);
}

static int data_check(void *buffer, uint32_t len)
{
	unsigned char res[16];
	int data_len = len - 16;

	md5(buffer, data_len, res);
	if (!memcmp(buffer + data_len, res, 16))
		return 0;
	return -1;
}

static void rpdata_auto_send(void *arg)
{
	struct rpdata_arg_test targ;
	rpdata_t *rpd;
	void *buffer;
	int ret = -1;
	uint32_t len = 512;


	memcpy(&targ, arg, sizeof(struct rpdata_arg_test));
	printf("dir:%d, type:%s, name:%s\n",
		targ.dir, targ.type, targ.name);

	rpd = rpdata_create(targ.dir, targ.type, targ.name, 512);
	if (!rpd) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}
#ifndef RECV_CALLBACK_TEST
	rpdata_set_recv_ringbuffer(rpd, 2 * rpdata_buffer_len(rpd));
#endif

	buffer = rpdata_buffer_addr(rpd);
	if (!buffer) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}

	rpdata_wait_connect(rpd);
	while (1) {
		data_fill(buffer, len);
		ret = rpdata_send(rpd, 0, len);
		if (ret != 0) {
			printf("[%s] line:%d \n", __func__, __LINE__);
			goto exit;
		}
		hal_msleep(10);
	}

exit:
	if (rpd)
		rpdata_destroy(rpd);
	printf("rpdata auto send test finish\n");
	vTaskDelete(NULL);
}

static int rpd_demo_recv_cb(rpdata_t *rpd, void *data, uint32_t data_len)
{
	static int count = 0;
	if (data_check(data, data_len) < 0) {
		printf("check md5 failed\n");
	}
	if (!g_rpd_verbose)
		return 0;
	if (count++ % g_rpd_verbose == 0)
		printf("check md5 ok(print interval %d)\n", g_rpd_verbose);
	return 0;
}

struct rpdata_cbs rpd_demo_cbs = {
	.recv_cb = rpd_demo_recv_cb,
};

static int rpd_demo_process_cb(rpdata_t *rpd, void *buffer, uint32_t data_len)
{
	void *mic_data, *ref_data, *aec_data;
	uint32_t mic_offset = 128;
	uint32_t ref_offset = 128 + 640;
	uint32_t aec_offset = 128 + 640 + 320;
	uint32_t total_len = 1408;

	/* aec process
	 * input:
	 * params:
	 * simulator: 128bytes
	 * mic data: 2ch+16K+16bit, 10ms, 160*4=640
	 * ref data: 1ch+16K+16bit, 10ms, 160*2=320
	 * outout:
	 * aec data: 1ch+16K+16bit, 10ms, 160*2=320
	 *
	 * total= 128 + 640 + 320 + 320 = 1408
	 * 1408 is cacheline lenght
	 */

	mic_data = buffer + mic_offset;
	ref_data = buffer + ref_offset;
	aec_data = buffer + aec_offset;

	if (data_len != total_len) {
		printf("expected len:%d but:%d\n", total_len, data_len);
		return -1;
	}
	if (data_check(mic_data, ref_data - mic_data) < 0) {
		printf("check mic data md5 failed\n");
	}
	if (data_check(ref_data, aec_data - ref_data) < 0) {
		printf("check ref data md5 failed\n");
	}
	data_fill(aec_data, total_len - aec_offset);
	return 0;
}

struct rpdata_cbs rpd_demo_process_cbs = {
	.recv_cb = rpd_demo_process_cb,
};

static int rpd_demo_recv_and_send_cb(rpdata_t *rpd, void *data, uint32_t data_len)
{
	rpdata_t *rpd_send = NULL;
	void *buffer;
	int ret;

	if (data_check(data, data_len) < 0) {
		printf("[%s] line:%d check md5 failed\n", __func__, __LINE__);
	}
	rpd_send = rpdata_get_private_data(rpd);
	if (!rpd_send)
		return -1;

	buffer = rpdata_buffer_addr(rpd_send);
	memcpy(buffer, data, data_len);
	ret = rpdata_send(rpd_send, 0, data_len);
	if (ret != 0) {
		printf("rpdata_send failed\n");
		return ret;
	}

	return 0;
}

struct rpdata_cbs rpd_demo_rs_cbs = {
	.recv_cb = rpd_demo_recv_and_send_cb,
};

static void rpdata_auto_recv(void *arg)
{
	struct rpdata_arg_test targ;
	rpdata_t *rpd;
	void *rx_buf = NULL;
	int len, ret;

	memcpy(&targ, arg, sizeof(struct rpdata_arg_test));
	printf("dir:%d, type:%s, name:%s\n",
		targ.dir, targ.type, targ.name);

	rpd = rpdata_connect(targ.dir, targ.type, targ.name);
	if (!rpd) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}
#ifndef RECV_CALLBACK_TEST
	rpdata_set_recv_ringbuffer(rpd, 2 * rpdata_buffer_len(rpd));
#endif

	len = rpdata_buffer_len(rpd);
	if (len <= 0) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}

	rx_buf = hal_malloc(len);
	if (!rx_buf) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}

#ifdef RECV_CALLBACK_TEST
	rpdata_set_recv_cb(rpd, &rpd_demo_cbs);
	while (1) {
		hal_msleep(10);
	}
#else
	while (1) {
		ret = rpdata_recv(rpd, rx_buf, len, 10000);
		if (ret <= 0) {
			printf("rpdata recv timeout \n");
			goto exit;
		}
		if (data_check(rx_buf, ret) < 0) {
			printf("check md5 failed\n");
		}
	}
#endif
exit:
	if (rx_buf)
		hal_free(rx_buf);
	if (rpd)
		rpdata_destroy(rpd);
	printf("rpdata auto recv test finish\n");
	vTaskDelete(NULL);
}

static void rpdata_auto_recv_and_send(void *arg)
{
	struct rpdata_arg_test targ;
	rpdata_t *rpd_recv = NULL, *rpd_send = NULL;
	void *buf_recv = NULL, *buf_send = NULL;
	uint32_t len = 512;
	int ret;

	memcpy(&targ, arg, sizeof(struct rpdata_arg_test));
	printf("recv dir:%d, type:%s, name:%s\n",
		targ.dir, targ.type, targ.name);
	printf("send dir:%d, type:%s, name:%s\n",
		targ.sdir, targ.stype, targ.sname);

	rpd_send = rpdata_create(targ.sdir, targ.stype, targ.sname, len);
	if (!rpd_send) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}

	buf_send = rpdata_buffer_addr(rpd_send);
	if (!buf_send) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}

	rpd_recv = rpdata_connect(targ.dir, targ.type, targ.name);
	if (!rpd_recv) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}
#ifndef RECV_CALLBACK_TEST
	rpdata_set_recv_ringbuffer(rpd_recv, 2 * rpdata_buffer_len(rpd_recv));
	rpdata_set_recv_ringbuffer(rpd_send, 2 * rpdata_buffer_len(rpd_send));
#endif

	buf_recv = hal_malloc(len);
	if (!buf_recv) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}

	rpdata_wait_connect(rpd_send);
#ifdef RECV_CALLBACK_TEST
	rpdata_set_private_data(rpd_recv, rpd_send);
	rpdata_set_recv_cb(rpd_recv, &rpd_demo_rs_cbs);
	while (1) {
		hal_msleep(10);
	}
#else
	while (1) {
		ret = rpdata_recv(rpd_recv, buf_recv, len, 10000);
		if (ret <= 0) {
			printf("rpdata recv timeout \n");
			goto exit;
		}
		if (data_check(buf_recv, ret) < 0) {
			printf("check md5 failed\n");
		}
		memcpy(buf_send, buf_recv, ret);
		ret = rpdata_send(rpd_send, 0, ret);
		if (ret != 0) {
			printf("rpdata send failed\n");
			goto exit;
		}
	}
#endif
exit:
	if (buf_recv)
		hal_free(buf_recv);
	if (rpd_send)
		rpdata_destroy(rpd_send);
	if (rpd_recv)
		rpdata_destroy(rpd_recv);
	printf("rpdata auto recv_and_send test finish\n");
	vTaskDelete(NULL);
}

static void rpdata_process_send(void *arg)
{
	struct rpdata_arg_test targ;
	rpdata_t *rpd;
	void *buffer;
	int ret = -1;
	uint32_t len;
	void *params, *mic_data, *ref_data, *aec_data;
	uint32_t params_offset = 0;
	uint32_t mic_offset = 128;
	uint32_t ref_offset = 128 + 640;
	uint32_t aec_offset = 128 + 640 + 320;

	/* aec process
	 * input:
	 * params:
	 * simulator: 128bytes
	 * mic data: 2ch+16K+16bit, 10ms, 160*4=640
	 * ref data: 1ch+16K+16bit, 10ms, 160*2=320
	 * outout:
	 * aec data: 1ch+16K+16bit, 10ms, 160*2=320
	 *
	 * total= 128 + 640 + 320 + 320 = 1408
	 * 1408 is cacheline lenght
	 * */
	len = 1408;

	memcpy(&targ, arg, sizeof(struct rpdata_arg_test));
	printf("dir:%d, type:%s, name:%s\n",
		targ.dir, targ.type, targ.name);

	rpd = rpdata_connect(targ.dir, targ.type, targ.name);
	if (!rpd) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}
#ifndef RECV_CALLBACK_TEST
	rpdata_set_recv_ringbuffer(rpd, 2 * rpdata_buffer_len(rpd));
#endif

	buffer = rpdata_buffer_addr(rpd);
	if (!buffer) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}
	params = buffer + params_offset;
	mic_data = buffer + mic_offset;
	ref_data = buffer + ref_offset;
	aec_data = buffer + aec_offset;

	rpdata_wait_connect(rpd);

	data_fill(params, mic_offset - params_offset);
	data_fill(mic_data, ref_offset - mic_offset);
	data_fill(ref_data, aec_offset - ref_offset);
	memset(aec_data, 0, len - aec_offset);

	ret = rpdata_process(rpd, 0, len);
	if (ret != 0) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}

	if (data_check(aec_data, len - aec_offset) < 0) {
		printf("aec data check failed\n");
	} else {
		printf("aec data check ok\n");
	}

exit:
	if (rpd)
		rpdata_destroy(rpd);
	vTaskDelete(NULL);
}

static void rpdata_process_recv(void *arg)
{
	struct rpdata_arg_test targ;
	rpdata_t *rpd;
	uint32_t len;
	void *rx_buf = NULL;
	int ret;

	/* aec process
	 * input:
	 * params:
	 * simulator: 128bytes
	 * mic data: 2ch+16K+16bit, 10ms, 160*4=640
	 * ref data: 1ch+16K+16bit, 10ms, 160*2=320
	 * outout:
	 * aec data: 1ch+16K+16bit, 10ms, 160*2=320
	 *
	 * total= 128 + 640 + 320 + 320 = 1408
	 * 1408 is cacheline lenght
	 * */
	len = 1408;

	memcpy(&targ, arg, sizeof(struct rpdata_arg_test));
	printf("dir:%d, type:%s, name:%s\n",
		targ.dir, targ.type, targ.name);

	rpd = rpdata_create(targ.dir, targ.type, targ.name, len);
	if (!rpd) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}
#ifndef RECV_CALLBACK_TEST
	rpdata_set_recv_ringbuffer(rpd, 2 * rpdata_buffer_len(rpd));
#endif

#ifndef RECV_CALLBACK_TEST
	rx_buf = hal_malloc(rpdata_buffer_len(rpd));
	if (!rx_buf) {
		printf("[%s] line:%d \n", __func__, __LINE__);
		goto exit;
	}

	ret = rpdata_recv(rpd, rx_buf, len, 30*1000);
	if (ret <= 0) {
		printf("[%s] : Timeout!\n", __func__);
		goto exit;
	}
	rpd_demo_process_cb(rpd, rx_buf, ret);

#else
	rpdata_set_recv_cb(rpd , &rpd_demo_process_cbs);
	hal_sleep(30);
#endif
exit:
	if (rx_buf)
		hal_free(rx_buf);
	if (rpd)
		rpdata_destroy(rpd);
	printf("rpdata recv process finish\n");
	vTaskDelete(NULL);
}

static int do_rpdata_process_test(struct rpdata_arg_test *targ, int mode)
{
	hal_thread_t handle;

	if (mode == 0)
		handle = hal_thread_create(rpdata_process_send, targ,
				"rpd_send_process", 512, HAL_THREAD_PRIORITY_APP);
	else if (mode == 1)
		handle = hal_thread_create(rpdata_process_recv, targ,
				"rpd_recv_process", 512, HAL_THREAD_PRIORITY_APP);
	return 0;
}

static int do_rpdata_auto_test(struct rpdata_arg_test *targ, int mode)
{
	hal_thread_t handle;

	if (mode == 0)
		handle = hal_thread_create(rpdata_auto_send, targ, "rpd_send_test",
				512, HAL_THREAD_PRIORITY_APP);
	else if (mode == 1)
		handle = hal_thread_create(rpdata_auto_recv, targ, "rpd_recv_test",
				512, HAL_THREAD_PRIORITY_APP);
	else if (mode == 2)
		handle = hal_thread_create(rpdata_auto_recv_and_send, targ, "rpd_rs_test",
				512, HAL_THREAD_PRIORITY_APP);
	return 0;
}

static int check_dir(int dir)
{
	switch (dir) {
	case RPDATA_DIR_CM33:
	case RPDATA_DIR_RV:
	case RPDATA_DIR_DSP:
		return 0;
	default:
		return -1;
	}
}

static int cmd_rpdata_demo(int argc, char *argv[])
{
	int c, mode = 2;
	int single_test = 0, process_test = 0;
	int get_sdir_arg = 0;
	/* string/data must be cache_line align */
	char string[128] = "rpdata test string";
	static struct rpdata_arg_test targ = {
		.type = "RVtoDSP",
		.name = "DSPrecvRVsend",
		.dir  = RPDATA_DIR_DSP,
		.stype = "DSPtoM33",
		.sname = "M33recvDSPsend",
		.sdir  = RPDATA_DIR_CM33,
	};

	optind = 0;
	while ((c = getopt(argc, argv, "hm:t:n:c:s:d:iv:p")) != -1) {
		switch (c) {
		case 'm':
			mode = atoi(optarg);
			break;
		case 't':
			if (!get_sdir_arg)
				strncpy(targ.type, optarg, sizeof(targ.type));
			else
				strncpy(targ.stype, optarg, sizeof(targ.stype));
			break;
		case 'n':
			if (!get_sdir_arg)
				strncpy(targ.name, optarg, sizeof(targ.name));
			else
				strncpy(targ.sname, optarg, sizeof(targ.sname));
			break;
		case 'c':
			strncpy(string, optarg, sizeof(string));
			break;
		case 's':
			targ.sdir = atoi(optarg);
			get_sdir_arg = 1;
			break;
		case 'd':
			targ.dir = atoi(optarg);
			get_sdir_arg = 0;
			break;
		case 'i':
			single_test = 1;
			break;
		case 'v':
			g_rpd_verbose = atoi(optarg);
			return 0;
		case 'p':
			process_test = 1;
			break;
		case 'h':
		default:
			goto usage;
		}
	}

	if (mode != 0 && mode != 1 && mode != 2)
		goto usage;

	if (check_dir(targ.dir) < 0 || check_dir(targ.sdir) < 0)
		goto usage;

	if (process_test) {
		do_rpdata_process_test(&targ, mode);
		return 0;
	}

	if (!single_test) {
		do_rpdata_auto_test(&targ, mode);
		return 0;
	}

	if (mode == 0)
		do_rpdata_send_test(&targ, string, sizeof(string));
	else if (mode == 1)
		do_rpdata_recv_test(&targ, string, sizeof(string));

	return 0;
usage:
	rpdata_demo_usage();
	return -1;
}
FINSH_FUNCTION_EXPORT_CMD(cmd_rpdata_demo, rpdata_demo, rpdata test demo);

Тестовый пример:

  • Передача и прием между RV/DSP

РВ принимает, ДСП отправляет

Язык кода:javascript
копировать
rpdata_demo ‑m 1 ‑d 3 ‑t DSPtoRV ‑n RVrecvDSPsend
rpccli dsp rpdata_demo ‑m 0 ‑d 2 ‑t DSPtoRV ‑n RVrecvDSPsend

РВ отправляет, ДСП получает

Язык кода:javascript
копировать
rpdata_demo ‑m 0 ‑d 3 ‑t RVtoDSP ‑n RVsendDSPrecv
rpccli dsp rpdata_demo ‑m 1 ‑d 2 ‑t RVtoDSP ‑n RVsendDSPrecv
  • Отправка и получение между рв/м33

фургон принимает, м33 отправляет

Язык кода:javascript
копировать
rpdata_demo ‑m 1 ‑d 1 ‑t M33toRV ‑n RVrecvM33send
rpccli arm rpdata_demo ‑m 0 ‑d 2 ‑t M33toRV ‑n RVrecvM33send

РВ отправляет, М33 получает

Язык кода:javascript
копировать
rpdata_demo ‑m 0 ‑d 1 ‑t RVtoM33 ‑n RVsendM33recv
rpccli arm rpdata_demo ‑m 1 ‑d 2 ‑t RVtoM33 ‑n RVsendM33recv
  • Передача и прием между m33/dsp

m33 принять, dsp отправить

Язык кода:javascript
копировать
rpccli arm rpdata_demo ‑m 1 ‑d 3 ‑t DSPtoM33 ‑n M33recvDSPsend
rpccli dsp rpdata_demo ‑m 0 ‑d 1 ‑t DSPtoM33 ‑n M33recvDSPsend

DSP принять, m33 отправить

Язык кода:javascript
копировать
rpccli dsp rpdata_demo ‑m 1 ‑d 1 ‑t M33toDSP ‑n DSPrecvM33send
rpccli arm rpdata_demo ‑m 0 ‑d 3 ‑t M33toDSP ‑n DSPrecvM33send

Кроме того, он обеспечивает rpd команда для просмотра rpdata из Статус операции:

Язык кода:javascript
копировать
c906>rpd ‑l
‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑
RPdata RV <‑‑> CM33
id type+name state
‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑
RPdata RV <‑‑> DSP
id type+name state
0x01 DSPtoRV
0x01 └─── RVrecvDSPsend CNXN


c906>rpccli dsp rpd ‑l
‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑
RPdata DSP <‑‑> CM33
id type+name state
‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑
RPdata DSP <‑‑> RV
id type+name state
0x01 DSPtoRV
0x01 └─── RVrecvDSPsend CNXN
boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода