diff --git a/doc/icom.en.md b/doc/icom.en.md new file mode 100644 index 0000000..6ca9e75 --- /dev/null +++ b/doc/icom.en.md @@ -0,0 +1,86 @@ +# ICOM module documentation + +## Overview + +ICOM (Internal Communication) is a simple C language internal communication module, which is used to realize efficient bidirectional data communication in embedded systems. It provides a communication mechanism based on shared memory to support multi-channel data transmission and state management. + +## Core features + +- **Bidirectional communication** : Support for separate send and receive channels +- **State management** : Provides four channel states: initialized, idle, busy, and error +- **Data length control** : Supports dynamic data length setting and validation +- **Live detection** : Automatically detects the online state of the communication peer +- **Cache control** : Supports cache invalidation and writeback operations +- **Multi-channel support** : Multiple send and receive channels can be configured + +## Architectural Design + +### Communication protocol format +| offset | field | size | description | +| :----: | :--: | :--: | :--------- | +| 0 | Flag | 4 | Channel status flag | +| 4 | Tcnt | 4 | Send channel count | +| 8 | Rcnt | 4 | Receive channel count | +| 12 | Dlen | 4 | data length | +| 16 | Data | N | actual data | + +### State Machine design + +- **INIT** : The initial state +- **BUSY** : Busy (in data transfer) +- **IDLE** : Idle state (transferable) +- **ERROR** : An error state + +## API explained in detail + +### Initialization functions +```c +int icom_init(ICOM *icom); +``` +Initialize the ICOM module and configure the sending and receiving channels. + +### Task handlers +```c +int icom_task(ICOM *icom); +``` +Handle the periodic tasks of the ICOM module, including online state detection and data reception processing. + +### Data sending function +```c +int icom_transmit(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t length); +``` +Send data over the specified channel. + +### Data receiving functions +```c +int icom_receive(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t *length); +``` +Handle the periodic tasks of the ICOM module, including online state detection and data reception processing. Receives data from the specified channel. + +### Online presence checks +```c +int icom_txchannel_online(ICOM *icom, uint16_t channel, uint8_t *online); +int icom_rxchannel_online(ICOM *icom, uint16_t channel, uint8_t *online); +``` +Check the online status of the send/receive channel. + +## Instructions + +1. Initialize the ICOM struct and configure the channel +2. Call icom_init() for initialization +3. Periodic calls to icom_task() for state management and data reception processing +4. Use icom_transmit() to send data +5. Use icom_receive() to receive data + +## Advanced features + +* Manual receive mode: You can manually check the receive status without using callbacks +* Cache control: Cache control via 'inv' and 'wbinv' callbacks +* Private data: Each channel maintains 4 private data words for extended functionality + +## Use cases + +* Inter-Processor Communication (IPC) +* Data exchange between multiple cores +* Modular system component communication +* Embedded systems that require shared memory communication \ No newline at end of file diff --git a/doc/icom.md b/doc/icom.md new file mode 100644 index 0000000..2295789 --- /dev/null +++ b/doc/icom.md @@ -0,0 +1,86 @@ +# ICOM 模块文档 + +## 概述 + +ICOM (Internal Communication) 是一个简单的C语言内部通信模块,用于在嵌入式系统中实现高效的双向数据通信。它提供了基于共享内存的通信机制,支持多通道数据传输和状态管理。 + +## 核心特性 + +- **双向通信**:支持独立的发送和接收通道 +- **状态管理**:提供初始化、空闲、忙碌、错误四种通道状态 +- **数据长度控制**:支持动态数据长度设置和校验 +- **在线检测**:自动监测通信对端在线状态 +- **缓存控制**:支持缓存无效化和回写操作 +- **多通道支持**:可配置多个发送和接收通道 + +## 架构设计 + +### 通信协议格式 +| 偏移量 | 字段 | 大小 | 描述 | +| :----: | :---: | :--: | :--------- | +| 0 | Flag | 4 | 通道状态标志 | +| 4 | Tcnt | 4 | 发送通道计数 | +| 8 | Rcnt | 4 | 接收通道计数 | +| 12 | Dlen | 4 | 数据长度 | +| 16 | Data | N | 实际数据 | + +### 状态机设计 + +- **INIT** : 初始化状态 +- **BUSY** : 忙碌状态(数据传输中) +- **IDLE** : 空闲状态(可传输) +- **ERROR** : 错误状态 + +## API接口详解 + +### 初始化函数 +```c +int icom_init(ICOM *icom); +``` +初始化ICOM模块,配置发送和接收通道。 + +### 任务处理函数 +```c +int icom_task(ICOM *icom); +``` +处理ICOM模块的周期性任务,包括在线状态检测和数据接收处理。 + +### 数据发送函数 +```c +int icom_transmit(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t length); +``` +通过指定通道发送数据。 + +### 数据接收函数 +```c +int icom_receive(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t *length); +``` +处理ICOM模块的周期性任务,包括在线状态检测和数据接收处理。从指定通道接收数据。 + +### 在线状态检查 +```c +int icom_txchannel_online(ICOM *icom, uint16_t channel, uint8_t *online); +int icom_rxchannel_online(ICOM *icom, uint16_t channel, uint8_t *online); +``` +检查发送/接收通道的在线状态。 + +## 使用指南 + +1. 初始化ICOM结构体并配置通道 +2. 调用icom_init()进行初始化 +3. 周期性调用icom_task() +4. 使用icom_transmit()发送数据 +5. 使用icom_receive()接收数据 + +## 高级功能 + +* 手动接收模式:可以不使用回调函数,手动检查接收状态 +* 缓存控制:通过 `inv` 和 `wbinv` 回调函数实现缓存控制 +* 私有数据:每个通道维护4个私有数据字,用于扩展功能 + +## 应用场景 + +* 处理器间通信(IPC) +* 多核间数据交换 +* 模块化系统组件通信 +* 需要共享内存通信的嵌入式系统 \ No newline at end of file diff --git a/source/06_performance/icom.c b/source/06_performance/icom.c new file mode 100644 index 0000000..2950bee --- /dev/null +++ b/source/06_performance/icom.c @@ -0,0 +1,631 @@ +/********************************************************************************************************* + * ------------------------------------------------------------------------------------------------------ + * file description + * ------------------------------------------------------------------------------------------------------ + * \file icom.c + * \unit icom + * \brief This is a simple internal communication module for C language + * \author Lamdonn + * \version v0.1.0 + * \license GPL-2.0 + * \copyright Copyright (C) 2026 Lamdonn. + ********************************************************************************************************/ +#include "icom.h" + +/** + * \brief: ICOM header field + * \note: This size is used to store the flag, transmit channel count, receive channel count, and data length of the channel + */ + +#define ICOM_FLAG_SIZE (4) /**< ICOM flag size */ +#define ICOM_TCNT_SIZE (4) /**< ICOM transmit channel count size */ +#define ICOM_RCNT_SIZE (4) /**< ICOM receive channel count size */ +#define ICOM_DLEN_SIZE (4) /**< ICOM data length size */ + +/** + * \brief: ICOM header field index + * \note: This index is used to access the flag, transmit channel count, receive channel count, and data length of the channel + */ + +#define ICOM_FLAG_INDEX (0) +#define ICOM_TCNT_INDEX (ICOM_FLAG_INDEX + ICOM_FLAG_SIZE) +#define ICOM_RCNT_INDEX (ICOM_TCNT_INDEX + ICOM_TCNT_SIZE) +#define ICOM_DLEN_INDEX (ICOM_RCNT_INDEX + ICOM_RCNT_SIZE) +#define ICOM_DATA_INDEX (ICOM_DLEN_INDEX + ICOM_DLEN_SIZE) + +/** + * \brief: ICOM header size + * \note: + * | Offset | Field | Size | Description | + * | :----: | :---: | :--: | :--------- | + * | 0 | Flag | 4 | Channel flag | + * | 4 | Tcnt | 4 | Transmit channel count | + * | 8 | Rcnt | 4 | Receive channel count | + * | 12 | Dlen | 4 | Data length | + */ +#define ICOM_HEAD_SIZE (ICOM_FLAG_SIZE + ICOM_TCNT_SIZE + ICOM_RCNT_SIZE + ICOM_DLEN_SIZE) + +/** + * \brief: ICOM channel flag + * \note: This flag is used to indicate the status of the channel + */ + +#define ICOM_F_INIT ((uint8_t)0x12) /**< ICOM channel flag: initialized */ +#define ICOM_F_BUSY ((uint8_t)0x34) /**< ICOM channel flag: busy */ +#define ICOM_F_IDLE ((uint8_t)0x56) /**< ICOM channel flag: idle */ +#define ICOM_F_ERROR ((uint8_t)0x78) /**< ICOM channel flag: error */ + +#define getTxBase(i) (icom->txconfig[i].base) /**< ICOM transmit channel base address */ +#define getTxSize(i) (icom->txconfig[i].size) /**< ICOM transmit channel size */ +#define getTxFlag(i) (getTxBase(i)[ICOM_FLAG_INDEX]) /**< ICOM transmit channel flag */ +#define getTxDlen(i) (getTxBase(i)[ICOM_DLEN_INDEX]) /**< ICOM transmit channel data length */ +#define getTxData(i) (getTxBase(i)[ICOM_DATA_INDEX]) /**< ICOM transmit channel data */ +#define getTxTcnt(i) (getTxBase(i)[ICOM_TCNT_INDEX]) /**< ICOM transmit channel count */ +#define getTxRcnt(i) (getTxBase(i)[ICOM_RCNT_INDEX]) /**< ICOM transmit channel receive count */ + +#define getRxBase(i) (icom->rxconfig[i].base) /**< ICOM receive channel base address */ +#define getRxSize(i) (icom->rxconfig[i].size) /**< ICOM receive channel size */ +#define getRxFlag(i) (getRxBase(i)[ICOM_FLAG_INDEX]) /**< ICOM receive channel flag */ +#define getRxDlen(i) (getRxBase(i)[ICOM_DLEN_INDEX]) /**< ICOM receive channel data length */ +#define getRxData(i) (getRxBase(i)[ICOM_DATA_INDEX]) /**< ICOM receive channel data */ +#define getRxTcnt(i) (getRxBase(i)[ICOM_TCNT_INDEX]) /**< ICOM receive channel count */ +#define getRxRcnt(i) (getRxBase(i)[ICOM_RCNT_INDEX]) /**< ICOM receive channel receive count */ + +#define getTxPrivate(i) (icom->txconfig[i].private) /**< ICOM transmit channel private data */ +#define getRxPrivate(i) (icom->rxconfig[i].private) /**< ICOM receive channel private data */ +#define getPrivateOnline(p) ((p)[0]) /**< ICOM private data: online */ +#define getPrivateOffCnt(p) ((p)[1]) /**< ICOM private data: offline count */ +#define getPrivatePCount(p) ((p)[2]) /**< ICOM private data: process count */ +#define getPrivateCCount(p) ((p)[3]) /**< ICOM private data: complete count */ + +/** + * \brief: ICOM cache invalidate function + * \param addr: Cache address + * \param size: Cache size + */ +#define Cache_Inv(addr, size) do { if (icom->inv) (icom->inv)((addr), (size)); } while (0) + +/** + * \brief: ICOM cache write-back invalidate function + * \param addr: Cache address + * \param size: Cache size + */ +#define Cache_wbInv(addr, size) do { if (icom->wbinv) (icom->wbinv)((addr), (size)); } while (0) + +/** + * \brief: ICOM transmitter ready function + * \param icom: ICOM handle + * \param channel: Channel number + * \return: 1 means ready, 0 means not ready + */ +static int icom_transmitter_ready(ICOM *icom, uint16_t channel) +{ + int ret = 0; + Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE); + switch (getRxFlag(channel)) + { + case ICOM_F_INIT: + { + getRxFlag(channel) = ICOM_F_IDLE; + Cache_wbInv(&getRxFlag(channel), ICOM_FLAG_SIZE); + ret = 1; + } break; + case ICOM_F_IDLE: + case ICOM_F_BUSY: + { + ret = 1; + } break; + default: + { + ret = 0; + } break; + } + return ret; +} + +/** + * \brief: ICOM set data length function + * \param icom: ICOM handle + * \param channel: Channel number + * \param length: Data length + */ +static void icom_set_data_length(ICOM *icom, uint16_t channel, uint32_t length) +{ + /* Data length is stored in little-endian format */ + getTxBase(channel)[ICOM_DLEN_INDEX] = length & 0xFF; + getTxBase(channel)[ICOM_DLEN_INDEX + 1] = (length >> 8) & 0xFF; + getTxBase(channel)[ICOM_DLEN_INDEX + 2] = (length >> 16) & 0xFF; + getTxBase(channel)[ICOM_DLEN_INDEX + 3] = (length >> 24) & 0xFF; + Cache_wbInv(&(getTxBase(channel)[ICOM_DLEN_INDEX]), ICOM_DLEN_SIZE); +} + +/** + * \brief: ICOM get data length function + * \param icom: ICOM handle + * \param channel: Channel number + * \return: Data length + */ +static uint32_t icom_get_data_length(ICOM *icom, uint16_t channel) +{ + uint32_t length = 0; + Cache_Inv(&(getRxBase(channel)[ICOM_DLEN_INDEX]), ICOM_DLEN_SIZE); + /* Data length is stored in little-endian format */ + length = (uint32_t)((getRxBase(channel)[ICOM_DLEN_INDEX]) | + (getRxBase(channel)[ICOM_DLEN_INDEX + 1] << 8) | + (getRxBase(channel)[ICOM_DLEN_INDEX + 2] << 16) | + (getRxBase(channel)[ICOM_DLEN_INDEX + 3] << 24)); + return length; +} + +/** + * \brief: ICOM control online function + * \param icom: ICOM handle + * \param private: Private data + * \param count: Process count + * \note: If process count is equal to complete count, then set online to 1. + * If offline count is less than 5, then set offline count to 1. + * Otherwise, set online to 0. + * If online is 1, then set offline count to 0. + * If offline count is greater than 5, then set online to 0. + */ +static void icom_control_online(ICOM *icom, uint32_t *private, uint32_t count) +{ + getPrivateCCount(private) = count; + if (getPrivateCCount(private) == getPrivatePCount(private)) + { + if (getPrivateOffCnt(private) <= 5) + { + getPrivateOffCnt(private)++; + } + /* If offline count is greater than 5, then set online to 0 */ + else + { + getPrivateOnline(private) = 0; + } + } + else + { + getPrivateOnline(private) = 1; + getPrivateOffCnt(private) = 0; + } + getPrivatePCount(private) = getPrivateCCount(private); +} + +/** + * \brief: ICOM online handle function + * \param icom: ICOM handle + * \note: If online is 1, then increment transmitter count. + * If offline count is greater than 5, then set online to 0. + */ +static void icom_online_handle(ICOM *icom) +{ + uint16_t channel; + + /* Handle tx channal */ + if (icom->txconfig && icom->txcount > 0) + { + for (channel = 0; channel < icom->txcount; channel++) + { + Cache_Inv(&getTxTcnt(channel), ICOM_TCNT_SIZE); + /* Increase tx count to indicate tx channal is online */ + (*((uint32_t *)(&getTxTcnt(channel))))++; + Cache_wbInv(&getTxTcnt(channel), ICOM_TCNT_SIZE); + Cache_Inv(&getTxRcnt(channel), ICOM_RCNT_SIZE); + /* Control online status */ + icom_control_online(icom, getTxPrivate(channel), (*((uint32_t *)&getTxRcnt(channel)))); + } + } + + /* Handle rx channal */ + if (icom->rxconfig && icom->rxcount > 0) + { + for (channel = 0; channel < icom->rxcount; channel++) + { + Cache_Inv(&getRxRcnt(channel), ICOM_RCNT_SIZE); + /* Increase rx count to indicate rx channal is online */ + (*((uint32_t *)(&getRxRcnt(channel))))++; + Cache_wbInv(&getRxRcnt(channel), ICOM_RCNT_SIZE); + Cache_Inv(&getRxTcnt(channel), ICOM_TCNT_SIZE); + /* Control online status */ + icom_control_online(icom, getRxPrivate(channel), (*((uint32_t *)&getRxTcnt(channel)))); + } + } +} + +/** + * \brief: ICOM rxdata handle function + * \param icom: ICOM handle + * \note: If rx channal is ready, then check data length. + * If data length is valid, then call callback function. + * Otherwise, set rx channal to idle. + */ +static void icom_rxdata_handle(ICOM *icom) +{ + uint16_t channel; + uint32_t length = 0; + + /* Handle Rx */ + if (icom->rxconfig && icom->rxcount > 0) + { + for (channel = 0; channel < icom->rxcount; channel++) + { + /* Check rx channal is ready */ + if (icom_transmitter_ready(icom, channel)) + { + Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE); + /* Check rx channal is busy. + * Indicate that rx channal is ready to receive data. + */ + if (getRxFlag(channel) == ICOM_F_BUSY) + { + /* Get data length and check data length is valid */ + length = icom_get_data_length(icom, channel); + if (length <= getRxSize(channel) - ICOM_HEAD_SIZE) + { + /* Call callback function */ + if (icom->rxconfig[channel].callback) + { + Cache_Inv(&getRxData(channel), length); + icom->rxconfig[channel].callback(channel, &getRxData(channel), length); + /* Set rx channal to idle */ + getRxFlag(channel) = ICOM_F_IDLE; + Cache_wbInv(&getRxFlag(channel), ICOM_FLAG_SIZE); + } + else /* Receive manual */ + { + + } + } + } + } + } + } +} + +/** + * \brief: ICOM initialize function + * \param icom: ICOM handle + * \return: ICOM error code + */ +int icom_init(ICOM *icom) +{ + uint16_t channel; + + /* Input value validity check */ + if (!icom) return ICOM_E_IHANDLE; + if (icom->init) return ICOM_E_OK; + + /* Initialize tx channal */ + if (icom->txconfig && icom->txcount > 0) + { + for (channel = 0; channel < icom->txcount; channel++) + { + if (getTxBase(channel) != NULL) + { + /* Initialize tx channal flag */ + getTxFlag(channel) = ICOM_F_INIT; + Cache_wbInv(&getTxFlag(channel), ICOM_FLAG_SIZE); + } + + /* Initialize tx channal private data */ + memset(getTxPrivate(channel), 0, sizeof(getTxPrivate(channel))); + + /* Initialize tx channal callback function, default is NULL */ + icom->txconfig[channel].callback = NULL; + } + } + + /* Initialize rx channal */ + if (icom->rxconfig && icom->rxcount > 0) + { + for (channel = 0; channel < icom->rxcount; channel++) + { + if (getRxBase(channel) != NULL) + { + /* Initialize rx channal flag */ + icom_transmitter_ready(icom, channel); + } + + /* Initialize rx channal private data */ + memset(getRxPrivate(channel), 0, sizeof(getRxPrivate(channel))); + } + } + + /* ICOM controller hs been initialized. */ + icom->init = 1; + + return ICOM_E_OK; +} + +/** + * \brief: ICOM task function + * \param icom: ICOM handle + * \return: ICOM error code + */ +int icom_task(ICOM *icom) +{ + /* Input value validity check */ + if (!icom) return ICOM_E_IHANDLE; + if (!icom->init) return ICOM_E_NINIT; + + /* Handle online status */ + icom_online_handle(icom); + /* Handle rxdata */ + icom_rxdata_handle(icom); + + return ICOM_E_OK; +} + +/** + * \brief: ICOM transmit getbuffer function + * \param icom: ICOM handle + * \param channel: Transmit channal index + * \param buffer: Pointer to store data buffer + * \param length: Pointer to store data length + * \return: ICOM error code + */ +int icom_transmit_getbuffer(ICOM *icom, uint16_t channel, uint8_t **buffer, uint32_t *length) +{ + /* Input value validity check */ + if (!icom) return ICOM_E_IHANDLE; + if (!icom->init) return ICOM_E_NINIT; + if (channel >= icom->txcount) return ICOM_E_IOUT; + if (!getTxBase(channel)) return ICOM_E_NBASE; + if (!buffer) return ICOM_E_IBUFFER; + + /* Get transmit buffer */ + *buffer = &getTxData(channel); + + /* Get data length */ + if (length) *length = getTxSize(channel) - ICOM_HEAD_SIZE; + + return ICOM_E_OK; +} + +/** + * \brief: ICOM transmit trigger function + * \param icom: ICOM handle + * \param channel: Transmit channal index + * \param length: Data length + * \note: Data length must be less than or equal to the transmit buffer size minus ICOM_HEAD_SIZE + * \return: ICOM error code + */ +int icom_transmit_trigger(ICOM *icom, uint16_t channel, uint32_t length) +{ + /* Input value validity check */ + if (!icom) return ICOM_E_IHANDLE; + if (!icom->init) return ICOM_E_NINIT; + if (channel >= icom->txcount) return ICOM_E_IOUT; + if (!getTxBase(channel)) return ICOM_E_NBASE; + if (length + ICOM_HEAD_SIZE > getTxSize(channel)) return ICOM_E_LOUT; + if (getTxFlag(channel) != ICOM_F_IDLE) return ICOM_E_BUSY; + + /* Set data length */ + icom_set_data_length(icom, channel, length); + /* Set tx channal to busy, need rx object receive data and set to ICOM_F_IDLE */ + getTxFlag(channel) = ICOM_F_BUSY; + Cache_wbInv(&getTxFlag(channel), ICOM_FLAG_SIZE); + + return ICOM_E_OK; +} + +/** + * \brief: ICOM transmit function + * \param icom: ICOM handle + * \param channel: Transmit channal index + * \param data: Pointer to data buffer + * \param length: Data length + * \note: Data length must be less than or equal to the transmit buffer size minus ICOM_HEAD_SIZE + * \return: ICOM error code + */ +int icom_transmit(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t length) +{ + int ret = ICOM_E_OK; + uint8_t *buffer = NULL; + uint32_t blength = 0; + uint32_t i = 0; + + /* Get transmit buffer */ + ret = icom_transmit_getbuffer(icom, channel, &buffer, &blength); + if (ICOM_E_OK != ret) return ret; + if (length > blength) return ICOM_E_LOUT; + + /* Copy data to transmit buffer */ + for (i = 0; i < length; i++) + { + buffer[i] = data[i]; + } + Cache_wbInv(buffer, length); + + /* Trigger transmit */ + ret = icom_transmit_trigger(icom, channel, length); + + return ret; +} + +/** + * \brief: ICOM receive getbuffer function + * \param icom: ICOM handle + * \param channel: Receive channal index + * \param buffer: Pointer to store data buffer + * \param length: Pointer to store data length + * \return: ICOM error code + */ +int icom_receive_getbuffer(ICOM *icom, uint16_t channel, uint8_t **buffer, uint32_t *length) +{ + if (!icom) return ICOM_E_IHANDLE; + if (!icom->init) return ICOM_E_NINIT; + if (channel >= icom->txcount) return ICOM_E_IOUT; + if (!getRxBase(channel)) return ICOM_E_NBASE; + if (!buffer) return ICOM_E_IBUFFER; + + *buffer = &getRxData(channel); + + if (length) *length = getRxSize(channel) - ICOM_HEAD_SIZE; + + return ICOM_E_OK; +} + +/** + * \brief: ICOM receive indicate function + * \param icom: ICOM handle + * \param channel: Receive channal index + * \param length: Pointer to store data length + * \return: ICOM error code + */ +int icom_receive_indicate(ICOM *icom, uint16_t channel, uint32_t *length) +{ + /* Input value validity check */ + if (!icom) return ICOM_E_IHANDLE; + if (!icom->init) return ICOM_E_NINIT; + if (channel >= icom->rxcount) return ICOM_E_IOUT; + if (!getRxBase(channel)) return ICOM_E_NBASE; + + /* Check if the rx channal is ready */ + if (icom_transmitter_ready(icom, channel)) + { + Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE); + if (getRxFlag(channel) == ICOM_F_BUSY) + { + /* Get data length */ + if (length) *length = icom_get_data_length(icom, channel); + } + else + { + return ICOM_E_NRECEIVE; + } + } + else + { + return ICOM_E_NRECEIVE; + } + + return ICOM_E_OK; +} + +/** + * \brief: ICOM receive end function + * \param icom: ICOM handle + * \param channel: Receive channal index + * \return: ICOM error code + */ +int icom_receive_end(ICOM *icom, uint16_t channel) +{ + /* Input value validity check */ + if (!icom) return ICOM_E_IHANDLE; + if (!icom->init) return ICOM_E_NINIT; + if (channel >= icom->rxcount) return ICOM_E_IOUT; + + /* Check if the rx channal is ready */ + if (icom_transmitter_ready(icom, channel)) + { + Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE); + if (getRxFlag(channel) == ICOM_F_BUSY) + { + /* Set rx channal to idle */ + getRxFlag(channel) = ICOM_F_IDLE; + } + else + { + return ICOM_E_NRECEIVE; + } + } + else + { + return ICOM_E_NRECEIVE; + } + + return ICOM_E_OK; +} + +/** + * \brief: ICOM receive function + * \param icom: ICOM handle + * \param channel: Receive channal index + * \param data: Pointer to data buffer + * \param length: Pointer to store data length + * \note: Data length must be less than or equal to the receive buffer size minus ICOM_HEAD_SIZE + * \return: ICOM error code + */ +int icom_receive(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t *length) +{ + int ret = ICOM_E_OK; + uint8_t *buffer = NULL; + uint32_t rxlength = 0; + uint32_t blength = 0; + uint32_t ilength = 0; + uint32_t i = 0; + + /* Input value validity check */ + if (!icom) return ICOM_E_IHANDLE; + if (!icom->init) return ICOM_E_NINIT; + if (channel >= icom->rxcount) return ICOM_E_IOUT; + if (!getRxBase(channel)) return ICOM_E_NBASE; + if (!data) return ICOM_E_IBUFFER; + + if (!length) return ICOM_E_ILENGTH; + ilength = *length; + + /* Determine if there is any data to receive */ + ret = icom_receive_indicate(icom, channel, &rxlength); + if (ICOM_E_OK != ret) return ret; + + /* Get receive buffer */ + ret = icom_receive_getbuffer(icom, channel, &buffer, &blength); + if (ICOM_E_OK != ret) return ret; + + if (rxlength > blength) return ICOM_E_LOUT; + + /* Copy data from receive buffer */ + for (i = 0; i < rxlength; i++) + { + data[i] = buffer[i]; + } + + /* End receive */ + icom_receive_end(icom, channel); + + *length = rxlength; + + return ICOM_E_OK; +} + +/** + * \brief: ICOM transmit channal online check function + * \param icom: ICOM handle + * \param channel: Transmit channal index + * \param online: Pointer to store online status + * \return: ICOM error code + */ +int icom_txchannel_online(ICOM *icom, uint16_t channel, uint8_t *online) +{ + if (!icom) return ICOM_E_IHANDLE; + if (!icom->init) return ICOM_E_NINIT; + if (channel >= icom->txcount) return ICOM_E_IOUT; + if (!online) return ICOM_E_IBUFFER; + + *online = getPrivateOnline(getTxPrivate(channel)); + + return ICOM_E_OK; +} + +/** + * \brief: ICOM receive channal online check function + * \param icom: ICOM handle + * \param channel: Receive channal index + * \param online: Pointer to store online status + * \return: ICOM error code + */ +int icom_rxchannel_online(ICOM *icom, uint16_t channel, uint8_t *online) +{ + if (!icom) return ICOM_E_IHANDLE; + if (!icom->init) return ICOM_E_NINIT; + if (channel >= icom->rxcount) return ICOM_E_IOUT; + if (!online) return ICOM_E_IBUFFER; + + *online = getPrivateOnline(getRxPrivate(channel)); + + return ICOM_E_OK; +} + + diff --git a/source/06_performance/icom.h b/source/06_performance/icom.h new file mode 100644 index 0000000..01959b0 --- /dev/null +++ b/source/06_performance/icom.h @@ -0,0 +1,193 @@ +/********************************************************************************************************* + * ------------------------------------------------------------------------------------------------------ + * file description + * ------------------------------------------------------------------------------------------------------ + * \file icom.h + * \unit icom + * \brief This is a simple internal communication module for C language + * \author Lamdonn + * \version v0.1.0 + * \license GPL-2.0 + * \copyright Copyright (C) 2026 Lamdonn. + ********************************************************************************************************/ +#ifndef __icom_H +#define __icom_H + +#include +#include +#include + +/* Version infomation */ + +#define ICOM_V_MAJOR 0 +#define ICOM_V_MINOR 1 +#define ICOM_V_PATCH 0 + +/** + * \brief: ICOM error code + * \note: 0 means success, negative value means error + */ + +#define ICOM_E_OK (0) /* OK, no error */ +#define ICOM_E_IHANDLE (-1) /* Invalid icom handle */ +#define ICOM_E_NINIT (-2) /* Not initialized */ +#define ICOM_E_IOUT (-3) /* Iterator out of range */ +#define ICOM_E_NBASE (-4) /* NULL base address */ +#define ICOM_E_LOUT (-5) /* Length out of range */ +#define ICOM_E_BUSY (-6) /* Current channel is busy */ +#define ICOM_E_IBUFFER (-7) /* Invalid buffer */ +#define ICOM_E_ILENGTH (-8) /* Invalid length */ +#define ICOM_E_NRECEIVE (-9) /* No data received */ + +/** + * \brief: ICOM receive callback function + * \param channel: Channel number + * \param data: Data buffer + * \param length: Data length + * \return: ICOM error code + */ +typedef int (*icom_callback_t)(uint16_t channel, uint8_t *data, uint32_t length); + +/** + * \brief: ICOM cache invalidate function + * \param address: Cache address + * \param size: Cache size + */ +typedef void (*icom_cache_inv_t)(uint8_t *address, uint32_t size); + +/** + * \brief: ICOM cache write-back invalidate function + * \param address: Cache address + * \param size: Cache size + */ +typedef void (*icom_cache_wbinv_t)(uint8_t *address, uint32_t size); + +/** + * \brief: ICOM channel configuration structure + * \note: This structure is used to configure the ICOM channel + */ +typedef struct +{ + uint8_t *base; /**< Base address of the channel */ + uint32_t size; /**< Size of the channel */ + icom_callback_t callback; /**< Callback function */ + uint32_t private[4]; /**< Private data */ +} ICOM_CHNCFG; + +/** + * \brief: ICOM structure + * \note: This structure is used to manage the ICOM module + */ +typedef struct +{ + uint8_t init; /**< Initialize flag */ + ICOM_CHNCFG *txconfig; /**< Transmit channel configuration */ + ICOM_CHNCFG *rxconfig; /**< Receive channel configuration */ + uint16_t txcount; /**< Transmit channel count */ + uint16_t rxcount; /**< Receive channel count */ + icom_cache_inv_t inv; /**< Cache invalidate function */ + icom_cache_wbinv_t wbinv; /**< Cache write-back invalidate function */ +} ICOM; + +/** + * \brief: ICOM initialize function + * \param icom: ICOM handle + * \return: ICOM error code + */ +int icom_init(ICOM *icom); + +/** + * \brief: ICOM task function + * \param icom: ICOM handle + * \return: ICOM error code + */ +int icom_task(ICOM *icom); + +/** + * \brief: ICOM transmit getbuffer function + * \param icom: ICOM handle + * \param channel: Transmit channal index + * \param buffer: Pointer to store data buffer + * \param length: Pointer to store data length + * \return: ICOM error code + */ +int icom_transmit_getbuffer(ICOM *icom, uint16_t channel, uint8_t **buffer, uint32_t *length); + +/** + * \brief: ICOM transmit trigger function + * \param icom: ICOM handle + * \param channel: Transmit channal index + * \param length: Data length + * \note: Data length must be less than or equal to the transmit buffer size minus ICOM_HEAD_SIZE + * \return: ICOM error code + */ +int icom_transmit_trigger(ICOM *icom, uint16_t channel, uint32_t length); + +/** + * \brief: ICOM transmit function + * \param icom: ICOM handle + * \param channel: Transmit channal index + * \param data: Pointer to data buffer + * \param length: Data length + * \note: Data length must be less than or equal to the transmit buffer size minus ICOM_HEAD_SIZE + * \return: ICOM error code + */ +int icom_transmit(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t length); + +/** + * \brief: ICOM receive getbuffer function + * \param icom: ICOM handle + * \param channel: Receive channal index + * \param buffer: Pointer to store data buffer + * \param length: Pointer to store data length + * \return: ICOM error code + */ +int icom_receive_getbuffer(ICOM *icom, uint16_t channel, uint8_t **buffer, uint32_t *length); + +/** + * \brief: ICOM receive indicate function + * \param icom: ICOM handle + * \param channel: Receive channal index + * \param length: Pointer to store data length + * \return: ICOM error code + */ +int icom_receive_indicate(ICOM *icom, uint16_t channel, uint32_t *length); + +/** + * \brief: ICOM receive end function + * \param icom: ICOM handle + * \param channel: Receive channal index + * \return: ICOM error code + */ +int icom_receive_end(ICOM *icom, uint16_t channel); + +/** + * \brief: ICOM receive function + * \param icom: ICOM handle + * \param channel: Receive channal index + * \param data: Pointer to data buffer + * \param length: Pointer to store data length + * \note: Data length must be less than or equal to the receive buffer size minus ICOM_HEAD_SIZE + * \return: ICOM error code + */ +int icom_receive(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t *length); + +/** + * \brief: ICOM transmit channal online check function + * \param icom: ICOM handle + * \param channel: Transmit channal index + * \param online: Pointer to store online status + * \return: ICOM error code + */ +int icom_txchannel_online(ICOM *icom, uint16_t channel, uint8_t *online); + +/** + * \brief: ICOM receive channal online check function + * \param icom: ICOM handle + * \param channel: Receive channal index + * \param online: Pointer to store online status + * \return: ICOM error code + */ +int icom_rxchannel_online(ICOM *icom, uint16_t channel, uint8_t *online); + +#endif diff --git a/test/test.mk b/test/test.mk index f0e51c4..ed286eb 100644 --- a/test/test.mk +++ b/test/test.mk @@ -45,6 +45,7 @@ TEST_LIST += romt TEST_LIST += cant TEST_LIST += slup # TEST_LIST += cpul +TEST_LIST += icom TEST_LIST += date TEST_LIST += unitt TEST_LIST += coroutine diff --git a/test/test_icom.c b/test/test_icom.c new file mode 100644 index 0000000..9160421 --- /dev/null +++ b/test/test_icom.c @@ -0,0 +1,327 @@ +#include +#include +#include +#if defined(TEST_TARGET_icom) +#include +#include +#include +#else +#include "init.h" +#include "command.h" +#include "unitt.h" +#include "kern.h" +#include "icom.h" +#endif + +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "icom suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static uint8_t icomBuffer0[1024] = {0}; +static uint8_t icomBuffer1[1024] = {0}; + +static int dev0_rx_callback(uint16_t channel, uint8_t *data, uint32_t length); +static int dev1_rx_callback(uint16_t channel, uint8_t *data, uint32_t length); + +static ICOM dev0ICOM = { + .init = 0, + .txconfig = (ICOM_CHNCFG[1]){ + { + .base = icomBuffer0, + .size = sizeof(icomBuffer0), + .callback = NULL, + }, + }, + .rxconfig = (ICOM_CHNCFG[1]){ + { + .base = icomBuffer1, + .size = sizeof(icomBuffer1), + .callback = dev0_rx_callback, + }, + }, + .txcount = 1, + .rxcount = 1, + .inv = NULL, + .wbinv = NULL, +}; + +static ICOM dev1ICOM = { + .init = 0, + .txconfig = (ICOM_CHNCFG[1]){ + { + .base = icomBuffer1, + .size = sizeof(icomBuffer1), + .callback = NULL, + }, + }, + .rxconfig = (ICOM_CHNCFG[1]){ + { + .base = icomBuffer0, + .size = sizeof(icomBuffer0), + // .callback = dev1_rx_callback, + .callback = NULL, + }, + }, + .txcount = 1, + .rxcount = 1, + .inv = NULL, + .wbinv = NULL, +}; + +static int dev0_rx_callback(uint16_t channel, uint8_t *data, uint32_t length) +{ + printf("[%d]: %s\r\n", length, data); + return 0; +} + +static int dev1_rx_callback(uint16_t channel, uint8_t *data, uint32_t length) +{ + printf("[%d]: %s\r\n", length, data); + // printf("[%d]\r\n", length); + // for (int i = 0; i < length; i++) + // { + // printf("%02x ", data[i]); + // } + // printf("\r\n"); + icom_transmit(&dev1ICOM, 0, "Copy that!", 11); + return 0; +} + +static void dev0_task(void); +static void dev1_task(void); + +static void dev0_task(void) +{ + static uint8_t init = 0; + static uint32_t count = 0; + + if (!init) + { + icom_init(&dev0ICOM); + init = 1; + } + + count += 10; + if (count >= 25200000) count = 0; + + if (count % 10 == 0) + { + icom_task(&dev0ICOM); + } + + if (count % 1000 == 0) + { + uint8_t txonline = 0, rxonline = 0; + icom_txchannel_online(&dev0ICOM, 0, &txonline); + icom_rxchannel_online(&dev0ICOM, 0, &rxonline); + printf("online %d,%d\r\n", txonline, rxonline); + icom_transmit(&dev0ICOM, 0, "Hello dev1", 11); + + static int time = 0; + if (time >= 3) + { + task_create(10, dev1_task); + } + else + { + time++; + } + } +} + +static void dev1_task(void) +{ + static uint8_t init = 0; + static uint32_t count = 0; + + if (!init) + { + icom_init(&dev1ICOM); + init = 1; + } + + count += 10; + if (count >= 25200000) count = 0; + + char rxBuffer[1024]; + uint32_t length = sizeof(rxBuffer); + if (ICOM_E_OK == icom_receive(&dev1ICOM, 0, rxBuffer, &length)) + { + printf("[%d]: %s\r\n", length, rxBuffer); + } + + if (count % 10 == 0) + { + icom_task(&dev1ICOM); + } + + if (count % 1000 == 0) + { + + } +} + +static void test_base(void) +{ + task_create(10, dev0_task); + // task_create(10, dev1_task); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: icom [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" ...\n" +" -h Print help\n" +" -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" +"\n" + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + int ut_period = 1000; + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvu::"); + if (opt == -1) break; + + switch (opt) + { + // Add others opt here + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("icom version %d.%d.%d\r\n", ICOM_V_MAJOR, ICOM_V_MINOR, ICOM_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_icom) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } + +#if 0 + if (1 == command_optind) // no opt + { + + } + + for (int index = command_optind; index < argc; index++) + { + if (!strcmp(argv[index], "base")) + { + test_base(); + } + } + + if (1 == argc) + { + test_base(); + } +#endif + + return 0; +} + +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + +#if defined(TEST_TARGET_icom) +int main(int argc, char *argv[]) +{ + return test(argc, argv); +} +#else +void test_icom(void) +{ + command_export("icom", test); +} +init_export_app(test_icom); +#endif