diff --git a/CI/build/conf/cores_config.json b/CI/build/conf/cores_config.json index 3b57a8331e..8e152b82eb 100644 --- a/CI/build/conf/cores_config.json +++ b/CI/build/conf/cores_config.json @@ -11,6 +11,14 @@ { "name": "STEVAL_MKSBOX1V1", "options": "usb=CDCgen" + }, + { + "name": "STM32MP157A_DK1", + "options": "virtio=enabled" + }, + { + "name": "STM32MP157C_DK2", + "options": "virtio=generic" } ], "sketches": [ diff --git a/CI/build/conf/cores_config_ci.json b/CI/build/conf/cores_config_ci.json index fadf93e7d6..23a436db43 100644 --- a/CI/build/conf/cores_config_ci.json +++ b/CI/build/conf/cores_config_ci.json @@ -11,6 +11,14 @@ { "name": "STEVAL_MKSBOX1V1", "options": "usb=CDCgen" + }, + { + "name": "STM32MP157A_DK1", + "options": "virtio=enabled" + }, + { + "name": "STM32MP157C_DK2", + "options": "virtio=generic" } ], "sketches": [ diff --git a/CI/build/conf/cores_config_travis.json b/CI/build/conf/cores_config_travis.json index be1cad29a3..ae87b824af 100644 --- a/CI/build/conf/cores_config_travis.json +++ b/CI/build/conf/cores_config_travis.json @@ -7,6 +7,14 @@ { "name": "NUCLEO_L031K6", "options": "opt=oslto" + }, + { + "name": "STM32MP157A_DK1", + "options": "virtio=enabled" + }, + { + "name": "STM32MP157C_DK2", + "options": "virtio=generic" } ], "sketches": [ diff --git a/CI/build/examples/BareMinimum/BareMinimum.ino b/CI/build/examples/BareMinimum/BareMinimum.ino index 9fdfe0df4e..1e39c262d8 100644 --- a/CI/build/examples/BareMinimum/BareMinimum.ino +++ b/CI/build/examples/BareMinimum/BareMinimum.ino @@ -42,7 +42,8 @@ SoftwareSerial swSerial(10, 11); void setup() { // Serial HW & SW -#if !defined(USBD_USE_CDC) && !defined(DISABLE_GENERIC_SERIALUSB) +#if (!defined(USBD_USE_CDC) && !defined(DISABLE_GENERIC_SERIALUSB)) &&\ + (!defined(VIRTIOCON) && !defined(DISABLE_GENERIC_SERIALVIRTIO)) Serial.setRx(PIN_SERIAL_RX); Serial.setTx(digitalPinToPinName(PIN_SERIAL_TX)); #endif diff --git a/boards.txt b/boards.txt index 5d3d3bc785..60396186ba 100644 --- a/boards.txt +++ b/boards.txt @@ -5,6 +5,7 @@ menu.pnum=Board part number menu.xserial=U(S)ART support menu.usb=USB support (if available) menu.xusb=USB speed (if available) +menu.virtio=Virtual serial support menu.opt=Optimize menu.rtlib=C Runtime Library @@ -707,7 +708,7 @@ STM32MP1.build.mcu=cortex-m4 STM32MP1.build.flags.fp=-mfpu=fpv4-sp-d16 -mfloat-abi=hard STM32MP1.build.series=STM32MP1xx STM32MP1.build.cmsis_lib_gcc=arm_cortexM4l_math -STM32MP1.build.extra_flags=-DCORE_CM4 -DUSE_FULL_LL_DRIVER -D{build.product_line} {build.xSerial} +STM32MP1.build.extra_flags=-DCORE_CM4 -DUSE_FULL_LL_DRIVER -D{build.product_line} {build.enable_virtio} {build.xSerial} # STM32MP157A-DK1 board STM32MP1.menu.pnum.STM32MP157A_DK1=STM32MP157A-DK1 @@ -1886,6 +1887,13 @@ Eval.menu.xserial.none.build.xSerial=-DHAL_UART_MODULE_ENABLED -DHWSERIAL_NONE Eval.menu.xserial.disabled=Disabled (no Serial support) Eval.menu.xserial.disabled.build.xSerial= +STM32MP1.menu.virtio.disable=Disabled (no SerialVirtIO nor /dev/ttyRPMSG0 available) +STM32MP1.menu.virtio.disable.build.enable_virtio= +STM32MP1.menu.virtio.generic=SerialVirtIO (= generic 'Serial') <=> /dev/ttyRPMSG0 +STM32MP1.menu.virtio.generic.build.enable_virtio={build.virtio_flags} +STM32MP1.menu.virtio.enabled=SerialVirtIO <=> /dev/ttyRPMSG0 +STM32MP1.menu.virtio.enabled.build.enable_virtio={build.virtio_flags} -DDISABLE_GENERIC_SERIALVIRTIO + STM32MP1.menu.xserial.generic=UART only (generic 'Serial') STM32MP1.menu.xserial.generic.build.xSerial=-DHAL_UART_MODULE_ENABLED STM32MP1.menu.xserial.none=UART only (no generic 'Serial') diff --git a/cores/arduino/Print.cpp b/cores/arduino/Print.cpp index da2b7c6f5d..b1dd901bca 100644 --- a/cores/arduino/Print.cpp +++ b/cores/arduino/Print.cpp @@ -28,6 +28,10 @@ #include "Print.h" +#if defined (VIRTIO_LOG) +#include "virtio_log.h" +#endif + // Public Methods ////////////////////////////////////////////////////////////// /* default implementation: may be overridden */ @@ -203,10 +207,13 @@ extern "C" { switch (file) { case STDOUT_FILENO: case STDERR_FILENO: -#if defined(HAL_UART_MODULE_ENABLED) && !defined(HAL_UART_MODULE_ONLY) /* Used for core_debug() */ +#if defined (VIRTIO_LOG) + virtio_log((uint8_t *)ptr, (uint32_t)len); +#elif defined(HAL_UART_MODULE_ENABLED) && !defined(HAL_UART_MODULE_ONLY) uart_debug_write((uint8_t *)ptr, (uint32_t)len); #endif + break; case STDIN_FILENO: break; default: diff --git a/cores/arduino/VirtIOSerial.cpp b/cores/arduino/VirtIOSerial.cpp new file mode 100644 index 0000000000..cfee3298a9 --- /dev/null +++ b/cores/arduino/VirtIOSerial.cpp @@ -0,0 +1,217 @@ +/** + * MIT License: + * Copyright (c) 2019 Bumsik kim + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if defined (VIRTIOCON) + +#include "VirtIOSerial.h" +#include "core_debug.h" + +#if !defined(VIRTIOSERIAL_NUM) +#define VIRTIOSERIAL_NUM 1 +#endif + +VirtIOSerialObj_t *VirtIOSerial_Handle[VIRTIOSERIAL_NUM] = {NULL}; + +uint32_t VirtIOSerial::VirtIOSerial_index = 0; + +// Default instance +VirtIOSerial SerialVirtIO; + +void serialEventVirtIO() __attribute__((weak)); +VirtIOSerialObj_t *get_VirtIOSerial_obj(VIRT_UART_HandleTypeDef *huart); + +void VirtIOSerial::begin(void) +{ + virtio_buffer_init(&_VirtIOSerialObj.ring); + if (_VirtIOSerialObj.initialized) { + return; + } + if (OPENAMP_Init() != 0) { + Error_Handler(); + } + if (VIRT_UART_Init(&_VirtIOSerialObj.handle) != VIRT_UART_OK) { + Error_Handler(); + } + + VirtIOSerial_index ++; + _VirtIOSerialObj.__this = (void *)this; + + /* Need to register callback for message reception by channels */ + if (VIRT_UART_RegisterCallback(&_VirtIOSerialObj.handle, VIRT_UART_RXCPLT_CB_ID, rxGenericCallback) != VIRT_UART_OK) { + Error_Handler(); + } + + VirtIOSerial_Handle[VirtIOSerial_index] = &_VirtIOSerialObj; + _VirtIOSerialObj.initialized = true; + _VirtIOSerialObj.first_message_discarded = false; + + // This will wait for the first message "DUMMY", see rxCallback(). + OPENAMP_Wait_EndPointready(&_VirtIOSerialObj.handle.ept); +} + +void VirtIOSerial::begin(uint32_t /* baud_count */) +{ + // uart config is ignored in OpenAMP + begin(); +} + +void VirtIOSerial::begin(uint32_t /* baud_count */, uint8_t /* config */) +{ + // uart config is ignored in OpenAMP + begin(); +} + +void VirtIOSerial::end() +{ + VIRT_UART_DeInit(&_VirtIOSerialObj.handle); + OPENAMP_DeInit(); + virtio_buffer_init(&_VirtIOSerialObj.ring); + _VirtIOSerialObj.initialized = false; +} + +int VirtIOSerial::available(void) +{ + checkMessageFromISR(); + return virtio_buffer_read_available(&_VirtIOSerialObj.ring); +} + +int VirtIOSerial::availableForWrite() +{ + checkMessageFromISR(); + // Just return max length of VIRT_UART_Transmit() can transmit. + // See VIRT_UART_Transmit(). + return RPMSG_VRING_PAYLOAD_SIZE; +} + +int VirtIOSerial::peek(void) +{ + checkMessageFromISR(); + if (virtio_buffer_read_available(&_VirtIOSerialObj.ring) > 0) { + uint8_t tmp; + virtio_buffer_peek(&_VirtIOSerialObj.ring, &tmp, 1); + return tmp; + } else { + return -1; + } +} + +int VirtIOSerial::read(void) +{ + if (available() > 0) { + char ch; + readBytes(&ch, 1); + return ch; + } else { + return -1; + } +} + +size_t VirtIOSerial::readBytes(char *buffer, size_t length) +{ + checkMessageFromISR(); + const size_t size = virtio_buffer_read(&_VirtIOSerialObj.ring, reinterpret_cast(buffer), length); + // The ring buffer might be available enough to write after reading + checkMessageFromISR(); + return size; +} + +size_t VirtIOSerial::write(uint8_t ch) +{ + // Just write single-byte buffer. + return write(&ch, 1); +} + +// Warning: Currently VirtIOSerial implementation is synchronous, blocking +// until all bytes are sent. But it will be fast enough. +size_t VirtIOSerial::write(const uint8_t *buffer, size_t size) +{ + checkMessageFromISR(); + if (VIRT_UART_Transmit(&_VirtIOSerialObj.handle, const_cast(buffer), size) != VIRT_UART_OK) { + // This error usually happens when size > 496. See VirtIOSerial::availableForWrite() + core_debug("ERROR: VirtIOSerial::write() failed. Check availableForWrite().\n"); + return 0; + } + // It is likely receive "buf free" from the Linux host right after + // VIRT_UART_Transmit(). So check it here too. + checkMessageFromISR(); + return size; +} + +void VirtIOSerial::flush(void) +{ + checkMessageFromISR(); + // write() is blocked until all bytes are sent. So flush() doesn't need to do + // anything. See rpmsg_send(). + return; +} + +/** + * @brief Check if RPMsg message arrived from IPCC ISR + * @note This should called as mush as possible to consume RPMsg ring buffers, + * so that the host processor can send more messages quickly. + */ +void VirtIOSerial::checkMessageFromISR(void) +{ + OPENAMP_check_for_tx_message(); + if (virtio_buffer_write_available(&_VirtIOSerialObj.ring) >= RPMSG_VRING_TOTAL_PAYLOAD_SIZE) { + // This calls rxCallback() VRING_NUM_BUFFS times at maximum + OPENAMP_check_for_rx_message(); + } +} + +void VirtIOSerial::rxGenericCallback(VIRT_UART_HandleTypeDef *huart) +{ + VirtIOSerialObj_t *obj = get_VirtIOSerial_obj(huart); + VirtIOSerial *VIOS = (VirtIOSerial *)(obj->__this); + + VIOS->rxCallback(huart); +} + +void VirtIOSerial::rxCallback(VIRT_UART_HandleTypeDef *huart) +{ + // Linux host must send a dummy data first to finish initialization of rpmsg + // on the coprocessor side. This message should be discarded. + // run_arduino_gen.sh script will send dummy data: "DUMMY". + // See: https://github.com/OpenAMP/open-amp/issues/182 + // See: run_arduino_gen.sh + if (!_VirtIOSerialObj.first_message_discarded) { + huart->RxXferSize = 0; + _VirtIOSerialObj.first_message_discarded = true; + } + + size_t size = min(huart->RxXferSize, virtio_buffer_write_available(&_VirtIOSerialObj.ring)); + while (size > 0) { + size -= virtio_buffer_write(&_VirtIOSerialObj.ring, huart->pRxBuffPtr, size); + } +} + +/* Aim of the function is to get _VirtIOSerialObj pointer using huart pointer */ +/* Highly inspired from magical linux kernel's "container_of" */ +VirtIOSerialObj_t *get_VirtIOSerial_obj(VIRT_UART_HandleTypeDef *huart) +{ + VirtIOSerialObj_t *obj; + obj = (VirtIOSerialObj_t *)((char *)huart - offsetof(VirtIOSerialObj_t, handle)); + return (obj); +} + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/VirtIOSerial.h b/cores/arduino/VirtIOSerial.h new file mode 100644 index 0000000000..416ad3dc24 --- /dev/null +++ b/cores/arduino/VirtIOSerial.h @@ -0,0 +1,82 @@ +/** + * MIT License: + * Copyright (c) 2019 Bumsik kim + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _VIRTIOSERIAL_H_ +#define _VIRTIOSERIAL_H_ + +#if defined (VIRTIOCON) +#include "Stream.h" +#include "openamp.h" +#include "wiring.h" +#include "virtio_buffer.h" + +//================================================================================ +// Serial over OpenAMP + +// This structure is used to be able to get VirtIOSerial instance (C++ class) +// from handler (C structure) specially for rpmsg message management +typedef struct { + // Those 2 first fields must remain in this order at the beginning of the structure + void *__this; + VIRT_UART_HandleTypeDef handle; + bool initialized; + bool first_message_discarded; + virtio_buffer_t ring; +} VirtIOSerialObj_t; + + +class VirtIOSerial : public Stream { + public: + void begin(void); + void begin(uint32_t); + void begin(uint32_t, uint8_t); + void end(void); + + virtual int available(void); + virtual int availableForWrite(void); + virtual int peek(void); + virtual int read(void); + virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buffer, size_t size); + virtual void flush(void); + + static void rxGenericCallback(VIRT_UART_HandleTypeDef *huart); + void rxCallback(VIRT_UART_HandleTypeDef *huart); + + using Print::write; // pull in write(str) from Print + operator bool(void) + { + return true; + } + + private: + static uint32_t VirtIOSerial_index; + VirtIOSerialObj_t _VirtIOSerialObj; + void checkMessageFromISR(void); +}; + +extern VirtIOSerial SerialVirtIO; + +#endif /* VIRTIOCON */ +#endif /* _VIRTIOSERIAL_H_ */ diff --git a/cores/arduino/WSerial.cpp b/cores/arduino/WSerial.cpp index 3d866cae2f..dfbda4320d 100644 --- a/cores/arduino/WSerial.cpp +++ b/cores/arduino/WSerial.cpp @@ -62,5 +62,10 @@ WEAK void serialEventRun(void) serialEventUSB(); } #endif +#if defined(HAVE_SERIALVIRTIO) + if (serialEventVirtIO && SerialVirtIO.available()) { + serialEventVirtIO(); + } +#endif } diff --git a/cores/arduino/WSerial.h b/cores/arduino/WSerial.h index ffdc98ad52..6132593314 100644 --- a/cores/arduino/WSerial.h +++ b/cores/arduino/WSerial.h @@ -4,6 +4,7 @@ #include "variant.h" #include "HardwareSerial.h" #include "USBSerial.h" +#include "VirtIOSerial.h" #if defined (USBCON) && defined(USBD_USE_CDC) #ifndef DISABLE_GENERIC_SERIALUSB @@ -21,6 +22,22 @@ extern void serialEventUSB(void) __attribute__((weak)); #endif /* USBCON && USBD_USE_CDC */ +#if defined(VIRTIOCON) +#ifndef DISABLE_GENERIC_SERIALVIRTIO +#define ENABLE_SERIALVIRTIO +#if !defined(Serial) +#define Serial SerialVirtIO +#define serialEvent serialEventVirtIO +#endif +#endif + +#if defined(ENABLE_SERIALVIRTIO) +#define HAVE_SERIALVIRTIO +#endif + +extern void serialEventVirtIO(void) __attribute__((weak)); +#endif /* VIRTIOCON */ + #if defined(HAL_UART_MODULE_ENABLED) && !defined(HAL_UART_MODULE_ONLY) #if !defined(HWSERIAL_NONE) && defined(SERIAL_UART_INSTANCE) #if SERIAL_UART_INSTANCE == 0 diff --git a/cores/arduino/stm32/OpenAMP/libmetal/device.c b/cores/arduino/stm32/OpenAMP/libmetal/device.c new file mode 100644 index 0000000000..ad62849d0f --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/device.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/device.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/generic/condition.c b/cores/arduino/stm32/OpenAMP/libmetal/generic/condition.c new file mode 100644 index 0000000000..a8f7a7427d --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/generic/condition.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/system/generic/condition.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/generic/cortexm/sys.c b/cores/arduino/stm32/OpenAMP/libmetal/generic/cortexm/sys.c new file mode 100644 index 0000000000..83f98dd29f --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/generic/cortexm/sys.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/system/generic/cortexm/sys.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_device.c b/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_device.c new file mode 100644 index 0000000000..85a97eef94 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_device.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/system/generic/generic_device.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_init.c b/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_init.c new file mode 100644 index 0000000000..abdbe5cf12 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_init.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/system/generic/generic_init.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_io.c b/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_io.c new file mode 100644 index 0000000000..d1fcb36feb --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_io.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/system/generic/generic_io.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_shmem.c b/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_shmem.c new file mode 100644 index 0000000000..75be24b343 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/generic/generic_shmem.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/system/generic/generic_shmem.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/generic/time.c b/cores/arduino/stm32/OpenAMP/libmetal/generic/time.c new file mode 100644 index 0000000000..917e7b88bc --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/generic/time.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/system/generic/time.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/init.c b/cores/arduino/stm32/OpenAMP/libmetal/init.c new file mode 100644 index 0000000000..e7ddd67304 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/init.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/init.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/io.c b/cores/arduino/stm32/OpenAMP/libmetal/io.c new file mode 100644 index 0000000000..902aca060e --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/io.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/io.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/log.c b/cores/arduino/stm32/OpenAMP/libmetal/log.c new file mode 100644 index 0000000000..03eff42dc6 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/log.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/log.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/libmetal/shmem.c b/cores/arduino/stm32/OpenAMP/libmetal/shmem.c new file mode 100644 index 0000000000..83b2e508a1 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/libmetal/shmem.c @@ -0,0 +1,5 @@ +#ifdef VIRTIOCON + +#include "libmetal/lib/shmem.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/mbox_ipcc.c b/cores/arduino/stm32/OpenAMP/mbox_ipcc.c new file mode 100644 index 0000000000..cb55eaea86 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/mbox_ipcc.c @@ -0,0 +1,197 @@ +/** + ****************************************************************************** + * @file mbox_ipcc.c + * @author MCD Application Team + * @brief This file provides code for the configuration + * of the mailbox_ipcc_if.c MiddleWare. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ +#ifdef VIRTIOCON + +/* + * Channel direction and usage: +virtio_rpmsg_bus.c virtqueue vring rpmsg_virtio.c + ======== <-- new msg ---=============--------<------ ========== +|| || rvq (rx) || IPCC CHANNEL 1 || svq (tx_vq) -> vring0 || || +|| A7 || ------->-------=============--- buf free--> || M4 || +|| || || || +||master|| <-- buf free---=============--------<------ || slave || +|| || svq (tx) || IPCC CHANNEL 2 || rvq (rx_vq) -> vring1 ||(remote)|| + ======== ------->-------=============----new msg --> ========== + */ + +/* Includes ------------------------------------------------------------------*/ +#include "virtio_config.h" +#include +#include "stm32_def.h" +#include "openamp_conf.h" +#include "mbox_ipcc.h" + +/* Private define ------------------------------------------------------------*/ +#define MASTER_CPU_ID 0 +#define REMOTE_CPU_ID 1 +#define IPCC_CPU_A7 MASTER_CPU_ID +#define IPCC_CPU_M4 REMOTE_CPU_ID + +typedef enum { + MBOX_NO_MSG = 0, + MBOX_NEW_MSG = 1, + MBOX_BUF_FREE = 2 +} mbox_status_t; + +/* Private variables ---------------------------------------------------------*/ +IPCC_HandleTypeDef hipcc; +mbox_status_t msg_received_ch1 = MBOX_NO_MSG; +mbox_status_t msg_received_ch2 = MBOX_NO_MSG; + +/* Private function prototypes -----------------------------------------------*/ +void IPCC_channel1_callback(IPCC_HandleTypeDef *hipcc, uint32_t ChannelIndex, IPCC_CHANNELDirTypeDef ChannelDir); +void IPCC_channel2_callback(IPCC_HandleTypeDef *hipcc, uint32_t ChannelIndex, IPCC_CHANNELDirTypeDef ChannelDir); + +/** + * @brief Initialize MAILBOX with IPCC peripheral + * @param None + * @retval : Operation result + */ +int MAILBOX_Init(void) +{ + __HAL_RCC_IPCC_CLK_ENABLE(); + HAL_NVIC_SetPriority(IPCC_RX1_IRQn, IPCC_IRQ_PRIO, IPCC_IRQ_SUBPRIO); + HAL_NVIC_EnableIRQ(IPCC_RX1_IRQn); + + hipcc.Instance = IPCC; + if (HAL_IPCC_Init(&hipcc) != HAL_OK) { + Error_Handler(); + } + + if (HAL_IPCC_ActivateNotification(&hipcc, IPCC_CHANNEL_1, IPCC_CHANNEL_DIR_RX, + IPCC_channel1_callback) != HAL_OK) { + Error_Handler(); + return -1; + } + + if (HAL_IPCC_ActivateNotification(&hipcc, IPCC_CHANNEL_2, IPCC_CHANNEL_DIR_RX, + IPCC_channel2_callback) != HAL_OK) { + Error_Handler(); + return -1; + } + + return 0; +} + +/** + * @brief Process vring messages received from IPCC ISR + * @param vdev: virtio device + * @param vring_id: Vring ID + * @retval : Operation result + */ +int MAILBOX_Poll(struct virtio_device *vdev, uint32_t vring_id) +{ + int ret = -1; + + switch (vring_id) { + case VRING0_ID: + if (msg_received_ch1 == MBOX_BUF_FREE) { + /* This calls rpmsg_virtio_tx_callback(), which actually does nothing. */ + rproc_virtio_notified(vdev, VRING0_ID); + msg_received_ch1 = MBOX_NO_MSG; + ret = 0; + } + break; + case VRING1_ID: + if (msg_received_ch2 == MBOX_NEW_MSG) { + /** + * This calls rpmsg_virtio_rx_callback(), which calls virt_uart rx callback + * RING_NUM_BUFFS times at maximum. + */ + rproc_virtio_notified(vdev, VRING1_ID); + msg_received_ch2 = MBOX_NO_MSG; + ret = 0; + } + break; + default: + break; + } + + return ret; +} + +/** + * @brief Callback function called by OpenAMP MW to notify message processing (aka. kick) + * @note This callback is called by virtqueue_kick() in rpmsg_virtio_send_offchannel_raw(). + * Therefore, it is only called while tx, but not rx. + * @param Vring id + * @retval Operation result + */ +int MAILBOX_Notify(void *priv, uint32_t vring_id) +{ + (void)priv; + uint32_t channel; + + /* Called after virtqueue processing: time to inform the remote */ + if (vring_id == VRING0_ID) { + channel = IPCC_CHANNEL_1; + } else if (vring_id == VRING1_ID) { + /* Note: the OpenAMP framework never notifies this */ + channel = IPCC_CHANNEL_2; + return -1; + } else { + return -1; + } + + /* Check that the channel is free (otherwise wait until it is) */ + if (HAL_IPCC_GetChannelStatus(&hipcc, channel, IPCC_CHANNEL_DIR_TX) == IPCC_CHANNEL_STATUS_OCCUPIED) { + /* Wait for channel to be freed */ + while (HAL_IPCC_GetChannelStatus(&hipcc, channel, IPCC_CHANNEL_DIR_TX) == IPCC_CHANNEL_STATUS_OCCUPIED) + ; + } + + /* Inform the host processor (either new message, or buf free) */ + HAL_IPCC_NotifyCPU(&hipcc, channel, IPCC_CHANNEL_DIR_TX); + return 0; +} + +/* Private function ---------------------------------------------------------*/ +/* Callback from IPCC Interrupt Handler: Master Processor informs that there are some free buffers */ +void IPCC_channel1_callback(IPCC_HandleTypeDef *hipcc, + uint32_t ChannelIndex, IPCC_CHANNELDirTypeDef ChannelDir) +{ + (void) ChannelDir; + msg_received_ch1 = MBOX_BUF_FREE; + + /* Inform the host processor that we have received the 'buff free' msg */ + HAL_IPCC_NotifyCPU(hipcc, ChannelIndex, IPCC_CHANNEL_DIR_RX); +} + +/* Callback from IPCC Interrupt Handler: new message received from Master Processor */ +void IPCC_channel2_callback(IPCC_HandleTypeDef *hipcc, + uint32_t ChannelIndex, IPCC_CHANNELDirTypeDef ChannelDir) +{ + (void) ChannelDir; + msg_received_ch2 = MBOX_NEW_MSG; + + /* Inform the host processor that we have received the msg */ + HAL_IPCC_NotifyCPU(hipcc, ChannelIndex, IPCC_CHANNEL_DIR_RX); +} + +/** + * @brief This function handles IPCC RX1 occupied interrupt. + */ +void IPCC_RX1_IRQHandler(void) +{ + HAL_IPCC_RX_IRQHandler(&hipcc); +} + +#endif /* VIRTIOCON */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/cores/arduino/stm32/OpenAMP/mbox_ipcc.h b/cores/arduino/stm32/OpenAMP/mbox_ipcc.h new file mode 100644 index 0000000000..34dbb7af84 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/mbox_ipcc.h @@ -0,0 +1,38 @@ +/** + ****************************************************************************** + * @file mbox_ipcc.h + * @author MCD Application Team + * @brief Header for mbox_ipcc.c module + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +#ifndef __OPENAMP_MBOX_IPCC_H +#define __OPENAMP_MBOX_IPCC_H + +#ifdef VIRTIOCON + +/* Interrupt priority */ +#ifndef IPCC_IRQ_PRIO +#define IPCC_IRQ_PRIO 1 +#endif +#ifndef IPCC_IRQ_SUBPRIO +#define IPCC_IRQ_SUBPRIO 0 +#endif + +int MAILBOX_Init(void); +int MAILBOX_Poll(struct virtio_device *vdev, uint32_t vring_id); +int MAILBOX_Notify(void *priv, uint32_t id); + +#endif /* VIRTIOCON */ +#endif /* __OPENAMP_MBOX_IPCC_H */ diff --git a/cores/arduino/stm32/OpenAMP/open-amp/remoteproc/remoteproc_virtio.c b/cores/arduino/stm32/OpenAMP/open-amp/remoteproc/remoteproc_virtio.c new file mode 100644 index 0000000000..c829226b93 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/open-amp/remoteproc/remoteproc_virtio.c @@ -0,0 +1,6 @@ +#ifdef VIRTIOCON + +#include "virtio_config.h" +#include "open-amp/lib/remoteproc/remoteproc_virtio.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/open-amp/rpmsg/rpmsg.c b/cores/arduino/stm32/OpenAMP/open-amp/rpmsg/rpmsg.c new file mode 100644 index 0000000000..11b66787ff --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/open-amp/rpmsg/rpmsg.c @@ -0,0 +1,6 @@ +#ifdef VIRTIOCON + +#include "virtio_config.h" +#include "open-amp/lib/rpmsg/rpmsg.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/open-amp/rpmsg/rpmsg_virtio.c b/cores/arduino/stm32/OpenAMP/open-amp/rpmsg/rpmsg_virtio.c new file mode 100644 index 0000000000..8a89668a05 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/open-amp/rpmsg/rpmsg_virtio.c @@ -0,0 +1,6 @@ +#ifdef VIRTIOCON + +#include "virtio_config.h" +#include "open-amp/lib/rpmsg/rpmsg_virtio.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/openamp.c b/cores/arduino/stm32/OpenAMP/openamp.c new file mode 100644 index 0000000000..095785eff1 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/openamp.c @@ -0,0 +1,232 @@ +/** + ****************************************************************************** + * @file openamp.c + * @author MCD Application Team + * @brief Code for openamp applications + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ +#ifdef VIRTIOCON + +#include "virtio_config.h" +#include "openamp/open_amp.h" +#include "openamp.h" +#include "rsc_table.h" +#include "metal/sys.h" +#include "metal/device.h" +#include "mbox_ipcc.h" +/* Private define ------------------------------------------------------------*/ + +#define SHM_DEVICE_NAME "STM32_SHM" + +/* Globals */ + +static struct metal_io_region *shm_io; +static struct metal_io_region *rsc_io; +static struct shared_resource_table *rsc_table; +static struct rpmsg_virtio_shm_pool shpool; +static struct rpmsg_virtio_device rvdev; +static metal_phys_addr_t shm_physmap; + +/** + * @brief OpenAMP libmetal device structure + */ +struct metal_device shm_device = { + .name = SHM_DEVICE_NAME, + .num_regions = 2, + .regions = { + {.virt = NULL}, /* shared memory */ + {.virt = NULL}, /* rsc_table memory */ + }, + .node = { NULL }, + .irq_num = 0, + .irq_info = NULL +}; + +/** + * @brief Initialize OpenAMP shared memory (libmetal and resource table) + * + * @param RPMsgRole: RPMSG_REMOTE - Device is remote (slave) + * @retval 0 on success + */ +static int OPENAMP_shmem_init(int RPMsgRole) +{ + int status = 0; + struct metal_device *device; + struct metal_init_params metal_params = METAL_INIT_DEFAULTS; + void *rsc_tab_addr; + int rsc_size; + + metal_init(&metal_params); + + status = metal_register_generic_device(&shm_device); + if (status != 0) { + return status; + } + + status = metal_device_open("generic", SHM_DEVICE_NAME, &device); + if (status != 0) { + return status; + } + + shm_physmap = SHM_START_ADDRESS; + metal_io_init(&device->regions[0], (void *)SHM_START_ADDRESS, &shm_physmap, + SHM_SIZE, -1, 0, NULL); + + shm_io = metal_device_io_region(device, 0); + if (shm_io == NULL) { + return -1; + } + + /* Initialize resources table variables */ + resource_table_init(RPMsgRole, &rsc_tab_addr, &rsc_size); + rsc_table = (struct shared_resource_table *)rsc_tab_addr; + if (!rsc_table) { + return -1; + } + + metal_io_init(&device->regions[1], rsc_table, + (metal_phys_addr_t *)rsc_table, rsc_size, -1U, 0, NULL); + + rsc_io = metal_device_io_region(device, 1); + if (rsc_io == NULL) { + return -1; + } + + return 0; +} + +/** + * @brief Initialize the openamp framework + * + * @retval 0 when success + */ +int OPENAMP_Init() +{ + struct fw_rsc_vdev_vring *vring_rsc; + struct virtio_device *vdev; + int status = 0; + + MAILBOX_Init(); + + /* Libmetal Initilalization */ + status = OPENAMP_shmem_init(RPMSG_REMOTE); + if (status) { + return status; + } + + vdev = rproc_virtio_create_vdev(RPMSG_REMOTE, VDEV_ID, &rsc_table->vdev, + rsc_io, NULL, MAILBOX_Notify, NULL); + if (vdev == NULL) { + return -1; + } + + rproc_virtio_wait_remote_ready(vdev); + vring_rsc = &rsc_table->vring0; + status = rproc_virtio_init_vring(vdev, 0, vring_rsc->notifyid, + (void *)vring_rsc->da, shm_io, + vring_rsc->num, vring_rsc->align); + if (status != 0) { + return status; + } + vring_rsc = &rsc_table->vring1; + status = rproc_virtio_init_vring(vdev, 1, vring_rsc->notifyid, + (void *)vring_rsc->da, shm_io, + vring_rsc->num, vring_rsc->align); + if (status != 0) { + return status; + } + + rpmsg_virtio_init_shm_pool(&shpool, (void *)VRING_BUFF_ADDRESS, + (size_t)SHM_SIZE); + rpmsg_init_vdev(&rvdev, vdev, NULL, shm_io, &shpool); + + return 0; +} + +/** + * @brief Deinitialize the openamp framework + */ +void OPENAMP_DeInit() +{ + rpmsg_deinit_vdev(&rvdev); + + metal_finish(); +} + +/** + * @brief Initialize the endpoint struct + * + * @param ept: virtio rpmsg endpoint + */ +void OPENAMP_init_ept(struct rpmsg_endpoint *ept) +{ + rpmsg_init_ept(ept, "", RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, NULL, NULL); +} + +/** + * @brief Create and register the name service endpoint + * + * @param ept: virtio rpmsg endpoint + * @param name: virtio rpmsg name service name + * @param dest: message destination address. Set RPMSG_ADDR_ANY if + * the host processor will decide this + * @param cb: virtio rpmsg endpoint callback (receive) + * @param unbind_cb: virtio rpmsg endpoint destroy callback + * @retval 0 when success + */ +int OPENAMP_create_endpoint(struct rpmsg_endpoint *ept, const char *name, + uint32_t dest, rpmsg_ept_cb cb, + rpmsg_ns_unbind_cb unbind_cb) +{ + return rpmsg_create_ept(ept, &rvdev.rdev, name, RPMSG_ADDR_ANY, dest, cb, + unbind_cb); +} + +/** + * @brief Check and process any received messages from tx channel + * @note Don't call this in an ISR. + */ +void OPENAMP_check_for_tx_message(void) +{ + MAILBOX_Poll(rvdev.vdev, VRING0_ID); +} + +/** + * @brief Check and process any received messages from rx channel + * @note Don't call this in an ISR. + */ +void OPENAMP_check_for_rx_message(void) +{ + MAILBOX_Poll(rvdev.vdev, VRING1_ID); +} + +/** + * @brief Wait loop on rpmsg endpoint (VirtIOSerial) ready to send a message. + * (until message dest address is known) + * @note This will wait until the first message arrives from the Linux host. + * If we send a rpmsg message before this it will fail. + * @note Don't call this in an ISR. + * @see VirtIOSerial::rxCallback + * @param rp_ept: virtio rpmsg endpoint + */ +void OPENAMP_Wait_EndPointready(struct rpmsg_endpoint *rp_ept) +{ + while (!is_rpmsg_ept_ready(rp_ept)) { + MAILBOX_Poll(rvdev.vdev, VRING0_ID); + MAILBOX_Poll(rvdev.vdev, VRING1_ID); + } +} + +#endif /* VIRTIOCON */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/cores/arduino/stm32/OpenAMP/openamp.h b/cores/arduino/stm32/OpenAMP/openamp.h new file mode 100644 index 0000000000..09b684edd7 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/openamp.h @@ -0,0 +1,53 @@ +/** + ****************************************************************************** + * @file openamp.h + * @brief Header for openamp applications + * @author MCD Application Team + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __OPENAMP_OPENAMP_H +#define __OPENAMP_OPENAMP_H + +#ifdef VIRTIOCON + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "openamp_conf.h" + +#define OPENAMP_send rpmsg_send +#define OPENAMP_destroy_ept rpmsg_destroy_ept + +int OPENAMP_Init(void); +void OPENAMP_DeInit(void); +void OPENAMP_init_ept(struct rpmsg_endpoint *ept); + +int OPENAMP_create_endpoint(struct rpmsg_endpoint *ept, const char *name, + uint32_t dest, rpmsg_ept_cb cb, + rpmsg_ns_unbind_cb unbind_cb); +void OPENAMP_check_for_tx_message(void); +void OPENAMP_check_for_rx_message(void); +void OPENAMP_Wait_EndPointready(struct rpmsg_endpoint *rp_ept); + +#ifdef __cplusplus +} +#endif +#endif /* VIRTIOCON */ +#endif /*__OPENAMP_OPENAMP_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/cores/arduino/stm32/OpenAMP/openamp_conf.h b/cores/arduino/stm32/OpenAMP/openamp_conf.h new file mode 100644 index 0000000000..0d1fd35954 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/openamp_conf.h @@ -0,0 +1,83 @@ +/** + ****************************************************************************** + * @file openamp_conf.h + * @author MCD Application Team + * @brief Configuration file for OpenAMP MW + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __OPENAMP_OPENAMP_CONF_H +#define __OPENAMP_OPENAMP_CONF_H + +#ifdef VIRTIOCON + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Note: Do not remove this. Removing this might not trigger compile error but + * the configuration can be significantly different. + */ +#include "virtio_config.h" +#include "virt_uart.h" + +/* + * for GCC add the following content to the .ld file: + * MEMORY + * { + * ... + * OPEN_AMP_SHMEM (xrw) : ORIGIN = 0x38000400, LENGTH = 63K + * } + * __OPENAMP_region_start__ = ORIGIN(OPEN_AMP_SHMEM); + * __OPENAMP_region_end__ = ORIGIN(OPEN_AMP_SHMEM) + LENGTH(OPEN_AMP_SHMEM); + * + * using the LENGTH(OPEN_AMP_SHMEM) to set the SHM_SIZE lead to a crash thus we + * use the start and end address. + */ +extern int __OPENAMP_region_start__[]; /* defined by linker script */ +extern int __OPENAMP_region_end__[]; /* defined by linker script */ + +#define SHM_START_ADDRESS ((metal_phys_addr_t)__OPENAMP_region_start__) +#define SHM_SIZE (size_t)((void *)__OPENAMP_region_end__ - (void *) __OPENAMP_region_start__) + +#if defined STM32MP1xx +#define VRING_RX_ADDRESS -1 /* allocated by Master processor: CA7 */ +#define VRING_TX_ADDRESS -1 /* allocated by Master processor: CA7 */ +#define VRING_BUFF_ADDRESS -1 /* allocated by Master processor: CA7 */ +#define VRING_ALIGNMENT 16 /* fixed to match with linux constraint */ +#ifndef VRING_NUM_BUFFS +#define VRING_NUM_BUFFS 16 /* number of rpmsg buffer */ +#endif +#else +#error "VRING configuration for the device missing" +#endif + +/* Fixed parameters */ +#define NUM_RESOURCE_ENTRIES 2 +#define VRING_COUNT 2 + +#define VDEV_ID 0xFF +#define VRING0_ID 0 /* VRING0 ID (master to remote) fixed to 0 for linux compatibility*/ +#define VRING1_ID 1 /* VRING1 ID (remote to master) fixed to 1 for linux compatibility */ + +#ifdef __cplusplus +} +#endif + +#endif /* VIRTIOCON */ +#endif /* __OPENAMP_OPENAMP_CONF_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/cores/arduino/stm32/OpenAMP/rsc_table.c b/cores/arduino/stm32/OpenAMP/rsc_table.c new file mode 100644 index 0000000000..ed1078661b --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/rsc_table.c @@ -0,0 +1,95 @@ +/** + ****************************************************************************** + * @file rsc_table.c + * @author MCD Application Team + * @brief Ressource table + * + * This file provides a default resource table requested by remote proc to + * load the elf file. It also allows to add debug trace using a shared buffer. + * + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ +#ifdef VIRTIOCON + +#include "rsc_table.h" +#include "openamp/open_amp.h" + +#define RPMSG_IPU_C0_FEATURES 1 +#define VRING_COUNT 2 + +/* VirtIO rpmsg device id */ +#define VIRTIO_ID_RPMSG 7 + +#if defined (VIRTIO_LOG) +extern char virtio_log_buffer[]; +#endif + +const struct shared_resource_table __attribute__((__section__(".resource_table"))) __attribute__((used)) resource_table = { + .version = 1, +#if defined (VIRTIO_LOG) + .num = 2, +#else + .num = 1, +#endif + .reserved = {0, 0}, + .offset = { + offsetof(struct shared_resource_table, vdev), + offsetof(struct shared_resource_table, cm_trace), + }, + /* Virtio device entry */ + .vdev = { + .type = RSC_VDEV, + .id = VIRTIO_ID_RPMSG, + .notifyid = 0, + .dfeatures = RPMSG_IPU_C0_FEATURES, + .gfeatures = 0, + .config_len = 0, + .status = 0, + .num_of_vrings = VRING_COUNT, + .reserved = {0, 0}, + }, + /* Vring rsc entry - part of vdev rsc entry */ + .vring0 = { + .da = VRING_TX_ADDRESS, + .align = VRING_ALIGNMENT, + .num = VRING_NUM_BUFFS, + .notifyid = VRING0_ID, + .reserved = 0 + }, + .vring1 = { + .da = VRING_RX_ADDRESS, + .align = VRING_ALIGNMENT, + .num = VRING_NUM_BUFFS, + .notifyid = VRING1_ID, + .reserved = 0 + }, +#if defined (VIRTIO_LOG) + .cm_trace = { + .type = RSC_TRACE, + .da = (uint32_t)virtio_log_buffer, + .len = VIRTIO_LOG_BUFFER_SIZE, + .reserved = 0, + .name = "arduino_log", + }, +#endif +} ; + +void resource_table_init(int RPMsgRole, void **table_ptr, int *length) +{ + (void)RPMsgRole; + *length = sizeof(resource_table); + *table_ptr = (void *)&resource_table; +} + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/rsc_table.h b/cores/arduino/stm32/OpenAMP/rsc_table.h new file mode 100644 index 0000000000..3979dc15f3 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/rsc_table.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 STMicroelectronics. + * All rights reserved. + * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#ifndef __OPENAMP_RSC_TABLE_H +#define __OPENAMP_RSC_TABLE_H + +#ifdef VIRTIOCON + +#include "virtio_config.h" +#include "openamp/open_amp.h" +#include "openamp_conf.h" + +/* Resource table for the given remote */ +struct shared_resource_table { + unsigned int version; + unsigned int num; + unsigned int reserved[2]; + unsigned int offset[NUM_RESOURCE_ENTRIES]; + /* text carveout entry */ + + /* rpmsg vdev entry */ + struct fw_rsc_vdev vdev; + struct fw_rsc_vdev_vring vring0; + struct fw_rsc_vdev_vring vring1; + struct fw_rsc_trace cm_trace; +}; + +void resource_table_init(int RPMsgRole, void **table_ptr, int *length); + +#endif /* VIRTIOCON */ +#endif /* __OPENAMP_RSC_TABLE_H */ diff --git a/cores/arduino/stm32/OpenAMP/virt_uart.c b/cores/arduino/stm32/OpenAMP/virt_uart.c new file mode 100644 index 0000000000..3b86748c66 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/virt_uart.c @@ -0,0 +1,6 @@ +#ifdef VIRTIOCON + +#include "virtio_config.h" +#include "virtual_driver/virt_uart.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/virtio/virtio.c b/cores/arduino/stm32/OpenAMP/virtio/virtio.c new file mode 100644 index 0000000000..2e7ec33eae --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/virtio/virtio.c @@ -0,0 +1,6 @@ +#ifdef VIRTIOCON + +#include "virtio_config.h" +#include "open-amp/lib/virtio/virtio.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/virtio/virtqueue.c b/cores/arduino/stm32/OpenAMP/virtio/virtqueue.c new file mode 100644 index 0000000000..150a7d06d3 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/virtio/virtqueue.c @@ -0,0 +1,6 @@ +#ifdef VIRTIOCON + +#include "virtio_config.h" +#include "open-amp/lib/virtio/virtqueue.c" + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/virtio_buffer.c b/cores/arduino/stm32/OpenAMP/virtio_buffer.c new file mode 100644 index 0000000000..b6643f2688 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/virtio_buffer.c @@ -0,0 +1,130 @@ +/** + * MIT License: + * Copyright (c) 2019 Bumsik kim + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifdef VIRTIOCON + +#include "virtio_config.h" +#include "virtio_buffer.h" +#include +#include "wiring.h" + +void virtio_buffer_init(virtio_buffer_t *ring) +{ + ring->write_index = 0; + ring->read_index = 0; +} + +uint16_t virtio_buffer_read_available(virtio_buffer_t *ring) +{ + int32_t delta = ring->write_index - ring->read_index; + + if (delta < 0) { + return (VIRTIO_BUFFER_SIZE + delta); + } + return delta; +} + +/* WARNING no protection against race codition (on ring->read_index) when used in interruption */ +static uint16_t read(virtio_buffer_t *ring, uint8_t *dst, uint16_t size, bool peek) +{ + uint16_t read_index = ring->read_index; + int32_t delta = ring->write_index - read_index; + if (delta < 0) { + delta += VIRTIO_BUFFER_SIZE; + } + + size = min(size, delta); + + if ((size + read_index) > VIRTIO_BUFFER_SIZE) { + // Manage ring buffer rollover + // First, copy ring buffer from read index to end of buffer + memcpy(dst, ring->buffer + read_index, VIRTIO_BUFFER_SIZE - read_index); + // then, copy ring buffer from begining of buffer to end of read + memcpy(dst + VIRTIO_BUFFER_SIZE - read_index, ring->buffer, size - (VIRTIO_BUFFER_SIZE - read_index)); + } else { + memcpy(dst, ring->buffer + read_index, size); + } + + // Update read index if not peeked + if (!peek) { + ring->read_index += size; + + // Manage ring buffer rollover + if (ring->read_index >= VIRTIO_BUFFER_SIZE) { + ring->read_index -= VIRTIO_BUFFER_SIZE; + } + } + return size; +} + +uint16_t virtio_buffer_read(virtio_buffer_t *ring, uint8_t *dst, uint16_t size) +{ + return read(ring, dst, size, false); +} + + +uint16_t virtio_buffer_peek(virtio_buffer_t *ring, uint8_t *dst, uint16_t size) +{ + return read(ring, dst, size, true); +} + + + +uint16_t virtio_buffer_write_available(virtio_buffer_t *ring) +{ + int32_t delta = ring->read_index - ring->write_index - 1; + + if (delta < 0) { + return (VIRTIO_BUFFER_SIZE + delta); + } + return delta; +} + +/* WARNING no protection against race codition (on ring->write_index) when used in interruption */ +uint16_t virtio_buffer_write(virtio_buffer_t *ring, uint8_t *src, uint16_t size) +{ + uint16_t write_index = ring->write_index; + int32_t delta = ring->read_index - write_index - 1; + if (delta < 0) { + delta += VIRTIO_BUFFER_SIZE; + } + + size = min(size, delta); + + if ((size + write_index) > VIRTIO_BUFFER_SIZE) { + // Manage ring buffer rollover + // First, write ring buffer from write index to end of buffer + memcpy(ring->buffer + write_index, src, VIRTIO_BUFFER_SIZE - write_index); + // then, write ring buffer from beginning of buffer to end of read + memcpy(ring->buffer, src + VIRTIO_BUFFER_SIZE - write_index, size - (VIRTIO_BUFFER_SIZE - write_index)); + } else { + memcpy(ring->buffer + write_index, src, size); + } + + ring->write_index += size; + if (ring->write_index >= VIRTIO_BUFFER_SIZE) { + ring->write_index -= VIRTIO_BUFFER_SIZE; + } + return size; +} + +#endif /* VIRTIOCON */ diff --git a/cores/arduino/stm32/OpenAMP/virtio_buffer.h b/cores/arduino/stm32/OpenAMP/virtio_buffer.h new file mode 100644 index 0000000000..d88f83810c --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/virtio_buffer.h @@ -0,0 +1,66 @@ +/** + * MIT License: + * Copyright (c) 2019 Bumsik kim + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __OPENAMP_VIRTIO_BUFFER_H +#define __OPENAMP_VIRTIO_BUFFER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Size of virtio ring buffer + * @note See virtio_config.h for the size decision. (7937 bytes by default) + * The multiplier should be at least 1. As a result, the minimum + * required size of the initial virtio_buffer_write_available() + * must be RPMSG_VRING_TOTAL_PAYLOAD_SIZE. + * @note If VIRTIO_BUFFER_SIZE is still too big, RPMSG_VRING_TOTAL_PAYLOAD_SIZE + * can be reduced by reducing the number of VRING_NUM_BUFFS in + * virtio_config.h. + */ +#ifndef VIRTIO_BUFFER_SIZE +#define VIRTIO_BUFFER_SIZE (RPMSG_VRING_TOTAL_PAYLOAD_SIZE * 1 + 1) +#endif + +typedef struct { + uint8_t buffer[VIRTIO_BUFFER_SIZE]; + volatile uint16_t write_index; + volatile uint16_t read_index; +} virtio_buffer_t; + +void virtio_buffer_init(virtio_buffer_t *ring); + +uint16_t virtio_buffer_read_available(virtio_buffer_t *ring); +uint16_t virtio_buffer_read(virtio_buffer_t *ring, uint8_t *dst, uint16_t size); +uint16_t virtio_buffer_peek(virtio_buffer_t *ring, uint8_t *dst, uint16_t size); + +uint16_t virtio_buffer_write_available(virtio_buffer_t *ring); +uint16_t virtio_buffer_write(virtio_buffer_t *ring, uint8_t *src, uint16_t size); + +#ifdef __cplusplus +} +#endif + +#endif // __OPENAMP_VIRTIO_BUFFER_H diff --git a/cores/arduino/stm32/OpenAMP/virtio_config.h b/cores/arduino/stm32/OpenAMP/virtio_config.h new file mode 100644 index 0000000000..4ca823ea36 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/virtio_config.h @@ -0,0 +1,42 @@ +#ifndef __OPENAMP_VIRTIO_CONFIG_H +#define __OPENAMP_VIRTIO_CONFIG_H + +#ifndef RPMSG_BUFFER_SIZE +/** + * @brief Size of buffer of each vring buffers + * @note If the host processor is Linux, RPMSG_BUFFER_SIZE must be aligned + * with the kernel definition. + * Reference: https://elixir.bootlin.com/linux/v5.5.2/source/drivers/rpmsg/virtio_rpmsg_bus.c#L137 + */ +#define RPMSG_BUFFER_SIZE (512) +#endif + +/** Size of header of a vqueue message */ +#define RPMSG_VRING_HEADER_SIZE (16) +/** Available size of payload of a vqueue message */ +#define RPMSG_VRING_PAYLOAD_SIZE (RPMSG_BUFFER_SIZE - RPMSG_VRING_HEADER_SIZE) + +/** + * number of rpmsg buffer + * This means that a single call of rproc_virtio_notified(rvdev.vdev, VRING1_ID) + * will trigger VirtIOSerial::rxCallback() VRING_NUM_BUFFS times at maximum. + * A buffer handles VirtIOSerial::rxCallback() requires to be + * (RPMSG_VRING_PAYLOAD_SIZE * VRING_NUM_BUFFS) at minimum to prevent overflow. + */ +#ifndef VRING_NUM_BUFFS +#define VRING_NUM_BUFFS (16) +#endif +#define RPMSG_VRING_TOTAL_PAYLOAD_SIZE (RPMSG_VRING_PAYLOAD_SIZE * VRING_NUM_BUFFS) + +#if defined (VIRTIO_LOG) +/** + * OpenAMP trace (log) buffer configuration. + * Users are free to redefine the size if needed. + */ +#ifndef VIRTIO_LOG_BUFFER_SIZE +#define VIRTIO_LOG_BUFFER_SIZE (2048) +#endif + +#endif + +#endif // __OPENAMP_VIRTIO_CONFIG_H diff --git a/cores/arduino/stm32/OpenAMP/virtio_log.c b/cores/arduino/stm32/OpenAMP/virtio_log.c new file mode 100644 index 0000000000..c20c21ae21 --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/virtio_log.c @@ -0,0 +1,43 @@ +/** + * MIT License: + * Copyright (c) 2020 Bumsik kim + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#if defined(VIRTIOCON) && defined(VIRTIO_LOG) + +#include "virtio_config.h" +#include "virtio_log.h" + +char virtio_log_buffer[VIRTIO_LOG_BUFFER_SIZE]; + +void virtio_log(uint8_t *data, uint32_t size) +{ + static int offset = 0; + + for (uint32_t i = 0; i < size; i++) { + virtio_log_buffer[offset++] = *(data + i); + if ((offset + 1) >= VIRTIO_LOG_BUFFER_SIZE) { + offset = 0; + } + } + virtio_log_buffer[offset] = '\0'; +} + +#endif diff --git a/cores/arduino/stm32/OpenAMP/virtio_log.h b/cores/arduino/stm32/OpenAMP/virtio_log.h new file mode 100644 index 0000000000..6c29c1ae6a --- /dev/null +++ b/cores/arduino/stm32/OpenAMP/virtio_log.h @@ -0,0 +1,49 @@ +/** + * MIT License: + * Copyright (c) 2020 Bumsik kim + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef __OPENAMP_VIRTIO_LOG_H +#define __OPENAMP_VIRTIO_LOG_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Write data to the OpenAMP trace buffer + * @note By writing OpenAMP trace (log) buffer here, the Linux host can access + * to the content by the following command: + * $ cat /sys/kernel/debug/remoteproc/remoteproc0/trace0 + * @note When the buffer overflows (2kb by default, configurable.), + * the buffer is cleaned by a null character. Consider increasing + * VIRTIO_LOG_BUFFER_SIZE if a larger log buffer is needed. + * @param data: Data to send + * @param size: size of data + */ +void virtio_log(uint8_t *data, uint32_t size); + +#ifdef __cplusplus +} +#endif + +#endif // __OPENAMP_VIRTIO_LOG_H diff --git a/platform.txt b/platform.txt index 7e359536ca..f9109293f1 100644 --- a/platform.txt +++ b/platform.txt @@ -9,7 +9,7 @@ version=1.0.0 # STM compile variables # ---------------------- -compiler.stm.extra_include="-I{build.source.path}" "-I{build.core.path}/avr" "-I{build.core.path}/stm32" "-I{build.core.path}/stm32/LL" "-I{build.core.path}/stm32/usb" "-I{build.core.path}/stm32/usb/hid" "-I{build.core.path}/stm32/usb/cdc" "-I{build.system.path}/Drivers/{build.series}_HAL_Driver/Inc" "-I{build.system.path}/Drivers/{build.series}_HAL_Driver/Src" "-I{build.system.path}/{build.series}" "-I{build.system.path}/Middlewares/ST/STM32_USB_Device_Library/Core/Inc" "-I{build.system.path}/Middlewares/ST/STM32_USB_Device_Library/Core/Src" +compiler.stm.extra_include="-I{build.source.path}" "-I{build.core.path}/avr" "-I{build.core.path}/stm32" "-I{build.core.path}/stm32/LL" "-I{build.core.path}/stm32/usb" "-I{build.core.path}/stm32/OpenAMP" "-I{build.core.path}/stm32/usb/hid" "-I{build.core.path}/stm32/usb/cdc" "-I{build.system.path}/Drivers/{build.series}_HAL_Driver/Inc" "-I{build.system.path}/Drivers/{build.series}_HAL_Driver/Src" "-I{build.system.path}/{build.series}" "-I{build.system.path}/Middlewares/ST/STM32_USB_Device_Library/Core/Inc" "-I{build.system.path}/Middlewares/ST/STM32_USB_Device_Library/Core/Src" {build.virtio_extra_include} compiler.warning_flags=-w compiler.warning_flags.none=-w @@ -84,6 +84,10 @@ build.pid=0 # options here, since then the default would be a defined, but empty macro # that the preprocessor cannot detect. +# VirtIO RPMsg Serial Flags +build.virtio_flags=-DVIRTIOCON -DHAL_IPCC_MODULE_ENABLED -DNO_ATOMIC_64_SUPPORT -DMETAL_INTERNAL -DMETAL_MAX_DEVICE_REGIONS=2 -DVIRTIO_SLAVE_ONLY -DVIRTIO_LOG +build.virtio_extra_include="-I{build.system.path}/Middlewares/OpenAMP" "-I{build.system.path}/Middlewares/OpenAMP/open-amp/lib/include" "-I{build.system.path}/Middlewares/OpenAMP/libmetal/lib/include" "-I{build.system.path}/Middlewares/OpenAMP/virtual_driver" + # Build information's build.info.flags=-D{build.series} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DBOARD_NAME="{build.board}" @@ -91,6 +95,7 @@ build.info.flags=-D{build.series} -DARDUINO={runtime.ide.version} -DARDUINO_{bui build.xSerial=-DHAL_UART_MODULE_ENABLED build.enable_usb= build.usb_speed= +build.enable_virtio= build.startup_file= build.flags.fp= build.flags.optimize=-Os diff --git a/variants/STM32MP157_DK/README.md b/variants/STM32MP157_DK/README.md index ac6c453883..c5b0f79c25 100644 --- a/variants/STM32MP157_DK/README.md +++ b/variants/STM32MP157_DK/README.md @@ -43,7 +43,8 @@ In this example, the user **must** upload `/run_ardui After uploading the user can use `sh run_arduino_.sh start` in the console of host Linux via either SSH or Serial Console, to run the Arduino firmware. #### Note - * `sh run_arduino_.sh start` is a one-shot command, the Arduino firmware only runs for the current boot. If you want to make it run after reboot, you need to use `sh run_arduino_.sh install` command. + +* `sh run_arduino_.sh start` is a one-shot command, the Arduino firmware only runs for the current boot. If you want to make it run after reboot, you need to use `sh run_arduino_.sh install` command. `run_arduino_.sh` help page summary: @@ -63,6 +64,21 @@ After uploading the user can use `sh run_arduino_.sh start` in the sh run_arduino_.sh uninstall Uninstall the autostart service. + sh run_arduino_.sh monitor + Monitor data received from the coprocessor via the virtual serial. + + sh run_arduino_.sh send-msg + Send a message to the coprocessor via the virtual serial. + + sh run_arduino_.sh send-file + Send a file content to the coprocessor via the virtual serial. + + sh run_arduino_.sh minicom + Launch minicom interactive serial communication program. + + sh run_arduino_.sh log + Print debugging log in OpenAMP trace buffer. + sh run_arduino_.sh stop Stop the coprocessor. @@ -73,6 +89,122 @@ See the source code [run_arduino_gen.sh] for the full help page and the more det [run_arduino_gen.sh] is the shell script that produces a copy of the script called `run_arduino_.sh` but with the sketch binary self-contained. +## Virtual Serial + +With Virtual Serial, you can easily implement inter-core communication between the Linux host and Arduino coprocessor. Virtual Serial uses OpenAMP rpmsg framework. This is available as `SerialVirtIO` object and you can use it as a standard Arduino Serial object. + +Enable `SerialVirtIO` in Arduino IDE->Tools->Virtual serial support. You can optionally alias generic `Serial` object with `SerialVirtIO` as well. + +When enabled, `/dev/ttyRPMSG0` is available to the Linux host. You can use it as a normal serial tty. `sh run_arduino_.sh` provides `monitor`, `minicom`, `send-msg`, `send-file` as a convenience. See above command descriptions. + +See [OpenAMP] and [Linux RPMsg] to learn more. + +### Configuration + +To increase the performance of SerialVirtIO you can resize the related buffer configurations. There are three definitions you can use: + +* [`VRING_NUM_BUFFS`](/cores/arduino/stm32/OpenAMP/virtio_config.h) +* [`RPMSG_BUFFER_SIZE`](/cores/arduino/stm32/OpenAMP/virtio_config.h) +* [`VIRTIO_BUFFER_SIZE`](/cores/arduino/stm32/OpenAMP/virtio_buffer.h) + +The recommended option is to resize `VRING_NUM_BUFFS`. Be very cautious when resizing `RPMSG_BUFFER_SIZE`, which must be matched with the Linux kernel definition. Also `VIRTIO_BUFFER_SIZE` has the minimum required size depending on the other two. See their links above for further descriptions. + +To redefine these definitions, see how to create `build_opt.h` described in Debugging section below. + +### Virtual Serial Example + +Here is a basic echo example: +```cpp +int available; +char buffer[1024]; + +unsigned long time = 0; + +void setup() { + // You can SerialVirtIO.begin() and use SerialVirtIO later instead. + Serial.begin(); // You don't need to configure speed, it is ignored. + pinMode(LED_BUILTIN, OUTPUT); +} + +void loop() { + available = Serial.available(); + while (available > 0) { + int size = min(available, Serial.availableForWrite()); + Serial.readBytes(buffer, size); + Serial.write(buffer, size); + available -= size; + } + + // Heartbeat. If Arduino stops the LED won't flash anymore. + if ((millis() - time) > 1000) { + time = millis(); + digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); + } +} +``` + +Note the use of `Serial.availableForWrite()`. SerialVirtIO has [a hard restriction of the write size], so it is important to `Serial.write()` less than the value of `Serial.availableForWrite()`. + +After loading Arduino, You can use SerialVirtIO in two ways in this example: + +* Run `sh run_arduino_.sh minicom` and type anything in the minicom console. The console will print out what you type immediately. + +* Open two Linux consoles (using SSH) + 1. In the first console, run `sh run_arduino_.sh monitor` + 2. In the second console, run `sh run_arduino_.sh send-msg ` or `sh run_arduino_.sh send-file `, the first console will print the content of the message. + +## Debugging + +For printf-style debugging, `core_debug()` is highly recommended instead of using Arduino Serial. In STM32MP1, `core_debug()` utilizes OpenAMP trace buffer and it has a minimal real-time impact (other than the overhead of printf) because it is not bound to the speed of a hardware IO peripheral while printing it. + +Create [build_opt.h] in the sketch directory and simply put `-DCORE_DEBUG`. Additionally you can resize the buffer size of logging by redefining `VIRTIO_LOG_BUFFER_SIZE` (2kb by default). As an example you can create a file like the following: + +``` +build_opt.h (in the same directory of your Sketch) +----------------------------- + +-DCORE_DEBUG +-DVIRTIO_LOG_BUFFER_SIZE=4086 +``` + +Don't forget to change any of Arduino IDE option to reflect this as in the warning section in [build_opt.h description in wiki]. This is important because if `-DCORE_DEBUG` is not configured correctly `core_debug()` silently becomes an empty function without triggering any build error. Don't forget to add `#include "core_debug.h"` in your code in order to use `core_debug()`. + +Also, you must enable the Virtual Serial (described in the above section) and include `SerialVirtIO.begin();` in your Arduino sketch, because this logging feature is tightly coupled to OpenAMP virtio. + +You can use `sh run_arduino_.sh log` command or `cat /sys/kernel/debug/remoteproc/remoteproc0/trace0` command to print out the debug log in the Linux host. + +Note that when overflow occurs the trace buffer is re-written from the beginning, removing existing logs. Consider increasing `VIRTIO_LOG_BUFFER_SIZE` in this case, as mentioned above. + +See [virtio_log.h] for more information. + +### Debugging Example + +Here is a basic blink example with `core_debug()`: +```cpp +#include "core_debug.h" + +unsigned long time = 0; +unsigned long count = 1; + +void setup() { + // You must enable SerialVirtIO to use core_debug(), even if you don't use SerialVirtIO. + SerialVirtIO.begin(); + pinMode(LED_BUILTIN, OUTPUT); +} + +void loop() { + if ((millis() - time) > 1000) { + time = millis(); + core_debug("%u seconds\n", count++); + digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); + } +} +``` + +Don't forget to add [build_opt.h] described above. + +After loading Arduino, you can simply `sh run_arduino_.sh log` to print the current `core_debug()` logs. + ## Pin mapping The boards have two pin headers: Raspberry Pi HAT headers and Arduino shield headers. This project currently supports Arduino Shield headers only, leaving RPi HAT headers for the Linux applications. @@ -108,7 +240,7 @@ There are additional pins for LEDs and buttons. | PA_13 | 17 / LED_RED | USER2_BTN | Active Low, LED LD6, also connected to B4 button | | PH_7 | 18 / LED_ORANGE / LED_BUILTIN | | Active High, LED LD7 | -[`variant.h` of the board](https://github.com/stm32duino/Arduino_Core_STM32/tree/master/variants/STM32MP157_DK/variant.h) has the complete information about the pinouts. +[`variant.h` of the board] has the complete information about the pinouts. ## Uploading @@ -152,9 +284,8 @@ And then the Device Tree should enable TIM1 for the coprocessor, although this d ## Limitations * Ethernet and USB are not supported. Use them in the Linux host. -* Currently there is no easy way for communication between the Linux host and Arduino coprocessor. There is ongoing work for virtual serial communications using OpenAMP rpmsg framework. Currently one possible way is to wire between UART7 (Arduino SCL/SDA pins) and USART3 (Linux RPi HAT GPIO14/GPIO15 pins), however, users should manually modify [Linux Device tree to enable `usart3` and recompile it](usart3). * I2C pins on Raspberry Pi HAT header (GPIO2 and GPIO3) are not available in Linux host. This is because the Discovery board shares I2C pins on Arduino header and those on the HAT header. -* [Early firmware loading from U-Boot stage] is not supported. Only firmware loading on Linux boot stage by systemd supported. The binary itself may be loaded by U-Boot without any problems, but there is no out-of-box tool to configure U-Boot to load the firmware using Arduino IDE yet. +* [Early firmware loading from U-Boot stage] is not supported. Only firmware loading on Linux boot stage by systemd (aka. `sh run_arduino_.sh install`) supported. The binary itself may be loaded by U-Boot without any problems, but there is no out-of-box tool to configure U-Boot to load the firmware using Arduino IDE yet. * EEPROM library: Those devices do not have non-volatile memory. The emulation is done using RETRAM. Therefore data will be preserved *only* when VBAT is supplied (e.g. A coin battery is connected to CN3 on STM32MP157A_DK1) and the coprocessor is waken up from sleep. This implies that cold boot the board may cause data loss, even if VBAT is supplied. See [discussions on RETRAM] for more detail. @@ -170,11 +301,20 @@ And then the Device Tree should enable TIM1 for the coprocessor, although this d [run_arduino_gen.sh]: https://github.com/stm32duino/Arduino_Tools/blob/master/run_arduino_gen.sh +[OpenAMP]: https://github.com/OpenAMP/open-amp/wiki/OpenAMP-Overview +[Linux RPMsg]: https://wiki.st.com/stm32mpu/wiki/Linux_RPMsg_framework_overview +[a hard restriction of the write size]: /cores/arduino/VirtIOSerial.cpp#L148 + +[build_opt.h]: https://github.com/stm32duino/wiki/wiki/Customize-build-options-using-build_opt.h +[build_opt.h description in wiki]: https://github.com/stm32duino/wiki/wiki/Customize-build-options-using-build_opt.h +[virtio_log.h]: /cores/arduino/stm32/OpenAMP/virtio_log.h + +[`variant.h` of the board]: /variants/STM32MP157_DK/variant.h + [The ST Wiki page on C-Kermit]: https://wiki.st.com/stm32mpu/wiki/How_to_transfer_a_file_over_serial_console [a bug in OpenSTLinux]: https://community.st.com/s/question/0D50X0000B9vHa4/cannot-get-download-a-file-using-kermit [stm32mp157c-dk2-m4-examples.dts]: https://github.com/STMicroelectronics/meta-st-stm32mp/blob/d8cbac759e1275b1a27d4ba38b64a0d83d0e8c9f/recipes-kernel/linux/linux-stm32mp/4.19/4.19.49/0029-ARM-stm32mp1-r2-DEVICETREE.patch#L4334 -[usart3]: https://github.com/STMicroelectronics/meta-st-stm32mp/blob/d8cbac759e1275b1a27d4ba38b64a0d83d0e8c9f/recipes-kernel/linux/linux-stm32mp/4.19/4.19.49/0029-ARM-stm32mp1-r2-DEVICETREE.patch#L4274 [Early firmware loading from U-Boot stage]: https://wiki.st.com/stm32mpu/wiki/How_to_start_the_coprocessor_from_the_bootloader [discussions on RETRAM]: https://community.st.com/s/question/0D50X0000B44pHUSQY/doesnt-the-mcu-coprocessor-have-nonvolatile-memory diff --git a/variants/STM32MP157_DK/variant.h b/variants/STM32MP157_DK/variant.h index 8acc778e07..d94a9c0936 100644 --- a/variants/STM32MP157_DK/variant.h +++ b/variants/STM32MP157_DK/variant.h @@ -178,6 +178,7 @@ extern "C" { // pins are NOT connected to anything by default. #define SERIAL_PORT_MONITOR Serial #define SERIAL_PORT_HARDWARE Serial +#define SERIAL_PORT_LINUXBRIDGE SerialVirtIO #endif #endif /* _VARIANT_ARDUINO_STM32_ */