Основное ядро M33 R128 полностью отличается от ядра C906 и HIFI5 DSP. Чтобы максимизировать их производительность и сотрудничать для выполнения определенной задачи, системы, работающие на разных ядрах, также различаются. Ядра этих различных архитектур и работающее на них программное обеспечение объединяются в систему AMP (асимметричная многопроцессорная система, гетерогенная многопроцессорная система).
Чтобы многоядерные устройства могли работать вместе, гетерогенная многоядерная коммуникационная структура должна выполнять следующие функции:
Учитывая особенности гетерогенных многоядерных систем, при совершении удаленных сервисных вызовов необходимо решить следующие вопросы:
Чтобы решить эти проблемы, платформа Sunxi-AMP предоставляет интерфейс для гетерогенной связи. Он также предоставляет rpdata для реализации гетерогенной связи нижнего уровня.
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_msg
Структура представляет собой msgbox
Передача содержимого сообщения, Sunxi‑AMP Платформа удаленных межъядерных вызовов основана наsunxi_amp_msg
сообщения для выполнения различных функций. Полный из sunxi_amp_msg
,Содержит следующие поля,Размер 12 байт.
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
состава и метода его расчета.
#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 Кусочек.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
поля пройдены.
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
Представляет указатель на функцию службы.
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_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
System components ‑‑‑>
aw components ‑‑‑>
AMP Components Support ‑‑‑>
[*] Tina RTOS AMP # давать возможность Sunxi‑AMP компоненты
[*] AMP Funcall Thread # давать Возможность, вызываемая функцией обработки задачи
[*] AMP Funcall ThreadPool # давать возможностьпул потоков
[*] AMP Change Service Task Priority # давать возможностьприоритетпередача
заголовочный файл
#include <sunxi_amp.h>
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;
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;
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;
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;
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,
};
enum FUNC_RETURN_TYPE
{
RET_NULL = 0,
RET_POINTER,
RET_NUMBER_32,
RET_NUMBER_64,
};
enum RPC_MSG_DIRECTION
{
RPC_MSG_DIR_UNKNOWN = 0,
RPC_MSG_DIR_CM33 = 1,
RPC_MSG_DIR_RV,
RPC_MSG_DIR_DSP,
};
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;
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_info *get_amp_info(void);
параметр:
Возвращаемое значение:
прототип функции
int hal_amp_msg_send(sunxi_amp_msg *msg);
параметр:
Возвращаемое значение:
прототип функции
sunxi_amp_msg_ops *get_msg_ops(void);
параметр:
Возвращаемое значение:
прототип функции
int hal_amp_msg_recv(sunxi_amp_msg *msg);
параметр:
Возвращаемое значение:
прототип функции
unsigned long func_stub(uint32_t id, int haveRet, int stub_args_num, void *stub_args[]);
параметр:
Возвращаемое значение:
прототип функции
void *amp_align_malloc(int size);
параметр:
Возвращаемое значение:
прототип функции
void amp_align_free(void *ptr);
параметр:
Возвращаемое значение:
прототип функции
void *amp_malloc(int size);
параметр:
Возвращаемое значение:
прототип функции
void amp_free(void *ptr);
параметр:
Возвращаемое значение:
добавить вуказательпередачаудаленный вызов Служитьиз Процесс заключается в следующем:
lichee/rtos‑components/aw/amp/service/fsys/
удаленныйдокументсистема Служить。существовать lichee/rtos-components/aw/amp/service/
Следующее создание соответствует папке Служитьиз. fsys
к ИПереписка service
и stub
Конечный исходный файл, удаленный файл система Служить fsys_ser.c
и fsys_stub.c
。Можно ссылаться Makefile
и Kconfig
добавить вкомпилировать、настроить новыйизудаленный вызов Служить
fsys_ser.c
Создано в sunxi_amp_func_table
множество fsys_table
amp_service.c
генерал-лейтенант fsys_table
добавить в func_table
множествосередина sunxi_amp_table.h
Добавитьдокументсистемаудаленный Служить Группаиз Структура RPCHandler_FSYS_t
,Необходимость Уведомления есть,RPCHandler_FSYS_t
Для структуризации переменной-члена требуется и sunxi_amp
sunxi_amp.h
из RPCHandler_RPCT_t
Добавлено в определение структуры RPCHandler_FSYS
указатель
sunxi_amp.h
Определить макрос в
#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
#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
#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);
}
rpcconsole_stub.c
#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
#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},
};
Buffer
начальный адрес и длина, требования к начальному адресу основаны на максимальном значении в системе. CacheLine(64B)
Выравнивание,Требования к длине Buffer
Эксклюзивный CacheLine
,Не допускается совместное использование другими данными CacheLine
。
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
функция。
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 Запускаем на основе обычных данных.
sunxi_amp.h
Добавить #define AMP_DEBUG
,новстречасуществоватьключевой узелсерединадавать возможностькомпонентыиз Вывод отладочной информации,Используется для анализа таких проблем, как ошибки удаленного вызова функций.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;
Для взаимодействия между различными ядрами на основе аппаратной функции msgbox, предоставляемой программным обеспечением механизма AMP. связи, по этому мы предоставляем интерфейс rpdata, позволяющий пользователям более легко реализовать межъядерную связь.
rpdata_t *rpdata_create(int dir, const char *type, const char *name, size_t buf_len)
параметр:
Возвращаемое значение:
type и name Используется для идентификации уникального канала передачи, Можно установить самостоятельно, длина не превышает 16 Байтов достаточно. Сносно rpd ‑l Команда для просмотра текущей Призамениз rpdata к Статус ИПереписка. rpdata_create Функция не блокируется. существоватьсоздаватьиз, вы можете назвать это напрямую.
rpdata_t *rpdata_connect(int dir, const char *type, const char *name)
параметр:
Возвращаемое значение:
rpdata_create и rpdata_connect Функция аналогична из, но первая будет использоваться для передачи данныхиз. буфер, последнего не будет. Следовательно, для связи между двумя ядрами эти два интерфейса должны использоваться отдельно и не могут использоваться вместе. rpdata_create или rpdata_connect。rpdata_connect Функция заблокируется. Если удаленное ядро не вызывает rpdata_create, то поток всегда будет заблокирован. Обратите внимание на использование rpdata_connect, если Если вам нужен удаленный поток, подтвердите, можно ли его заблокировать. Если нет, вам нужно использовать отдельный поток.
void *rpdata_buffer_addr(rpdata_t *rpd)
параметр:
Возвращаемое значение:
Должен buffer Адрес можно использовать для сохранения взаимодействия, которое будет передано изданным.
int rpdata_is_connect(rpdata_t *rpd)
параметр:
Возвращаемое значение:
int rpdata_wait_connect(rpdata_t *rpd)
параметр:
Возвращаемое значение:
Нужно судить и ждать rpdata Только после достижения состояния соединения можно начать передачу данных.
Используется для отправки данных на удаленное ядро, а удаленное ядро заполняет обработанные данные в буфер.
int rpdata_process(rpdata_t *rpd, unsigned int offset, unsigned int data_len)
параметр:
Возвращаемое значение:
После обработки результаты сохраняются в buffer в, то есть rpdata_buffer_addr получатьизадрес。
Используется только для отправки данных на удаленное ядро.
int rpdata_send(rpdata_t *rpd, unsigned int offset, unsigned int data_len)
параметр:
Возвращаемое значение:
rpdata_send просто отправляет данные из буфера на удаленное ядро, а rpdata_process получает обработанные данные.
int rpdata_recv(rpdata_t *rpd, unsigned int *offset, unsigned int *data_len, int timeout_ms)
параметр:
Возвращаемое значение:
Для использования интерфейса Должен необходимо добавить ringbuffer режиме, в противном случае может возникнуть явление перезаписи данных.
int rpdata_set_recv_cb(rpdata_t *rpd, struct rpdata_cbs *cbs)
параметр:
Возвращаемое значение:
int (recv_cb_t)(rpdata_t rpd, void *data, int data_len)
Обратный вызов Уведомлениесуществовать Должен должен обработать данные и скопировать их, в противном случае они могут быть перезаписаны при следующей передаче данных.
int rpdata_destroy(rpdata_t *rpd)
параметр:
Возвращаемое значение:
Только оба ядра вызывают rpdata_destroy Только тогда это будет по-настоящему изразрушать, но приказ о вызове не требуется.
#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
#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);
Тестовый пример:
РВ принимает, ДСП отправляет
rpdata_demo ‑m 1 ‑d 3 ‑t DSPtoRV ‑n RVrecvDSPsend
rpccli dsp rpdata_demo ‑m 0 ‑d 2 ‑t DSPtoRV ‑n RVrecvDSPsend
РВ отправляет, ДСП получает
rpdata_demo ‑m 0 ‑d 3 ‑t RVtoDSP ‑n RVsendDSPrecv
rpccli dsp rpdata_demo ‑m 1 ‑d 2 ‑t RVtoDSP ‑n RVsendDSPrecv
фургон принимает, м33 отправляет
rpdata_demo ‑m 1 ‑d 1 ‑t M33toRV ‑n RVrecvM33send
rpccli arm rpdata_demo ‑m 0 ‑d 2 ‑t M33toRV ‑n RVrecvM33send
РВ отправляет, М33 получает
rpdata_demo ‑m 0 ‑d 1 ‑t RVtoM33 ‑n RVsendM33recv
rpccli arm rpdata_demo ‑m 1 ‑d 2 ‑t RVtoM33 ‑n RVsendM33recv
m33 принять, dsp отправить
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 отправить
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 из Статус операции:
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