建立工程,成功创建两个虚拟串口
This commit is contained in:
101
source/OpenAMP/libmetal/.travis.yml
Normal file
101
source/OpenAMP/libmetal/.travis.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
language: minimal # setting language to C will override cross-compiler and fail
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
env:
|
||||
global:
|
||||
- ZEPHYR_TOOLCHAIN_VARIANT=zephyr
|
||||
- ZEPHYR_SDK_INSTALL_DIR=/opt/zephyr-sdk
|
||||
- ZEPHYR_BASE=$TRAVIS_BUILD_DIR/deps/zephyr
|
||||
- ZEPHYR_SDK_VERSION=0.9.3
|
||||
- ZEPHYR_SDK_DOWNLOAD_FOLDER=https://github.com/zephyrproject-rtos/meta-zephyr-sdk/releases/download/$ZEPHYR_SDK_VERSION
|
||||
- ZEPHYR_SDK_SETUP_BINARY=zephyr-sdk-$ZEPHYR_SDK_VERSION-setup.run
|
||||
- ZEPHYR_SDK_DOWNLOAD_URL=$ZEPHYR_SDK_DOWNLOAD_FOLDER/$ZEPHYR_SDK_SETUP_BINARY
|
||||
- FREERTOS_ZIP_URL=https://cfhcable.dl.sourceforge.net/project/freertos/FreeRTOS/V10.0.1/FreeRTOSv10.0.1.zip
|
||||
- GCC_ARM_COMPILER_PACKAGE=gcc-arm-embedded_7-2018q2-1~trusty1_amd64.deb
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- os: linux
|
||||
env: TARGET="zephyr"
|
||||
- os: linux
|
||||
env: TARGET="linux"
|
||||
- os: linux
|
||||
env: TARGET="generic"
|
||||
- os: linux
|
||||
env: TARGET="freertos"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $ZEPHYR_SDK_INSTALL_DIR
|
||||
- /usr/local/bin
|
||||
|
||||
before_install:
|
||||
- if [[ "$TARGET" == "zephyr" ]]; then
|
||||
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test &&
|
||||
sudo apt-get update -qq &&
|
||||
sudo apt-get install libc6-dev-i386 make gperf gcc g++ python3-ply python3-yaml python3-pip device-tree-compiler ncurses-dev uglifyjs -qq &&
|
||||
sudo pip3 install pyelftools;
|
||||
fi
|
||||
- if [[ "$TARGET" == "linux" ]]; then
|
||||
sudo apt-get update -qq &&
|
||||
sudo apt-get install libsysfs-dev libhugetlbfs-dev make gcc;
|
||||
fi
|
||||
# This is to kick start CI on generic platform. Will need to have a proper way to get the required packages
|
||||
- if [[ "$TARGET" == "generic" || "$TARGET" == "freertos" ]]; then
|
||||
wget http://ppa.launchpad.net/team-gcc-arm-embedded/ppa/ubuntu/pool/main/g/gcc-arm-none-eabi/${GCC_ARM_COMPILER_PACKAGE} &&
|
||||
sudo dpkg -i ${GCC_ARM_COMPILER_PACKAGE};
|
||||
fi
|
||||
- if [[ "$TARGET" == "freertos" ]]; then
|
||||
wget $FREERTOS_ZIP_URL &&
|
||||
pwd && ls &&
|
||||
unzip FreeRTOSv10.0.1.zip > /dev/null;
|
||||
fi
|
||||
|
||||
install: >
|
||||
if [[ "$TARGET" == "zephyr" && "$(cat $ZEPHYR_SDK_INSTALL_DIR/sdk_version)" != "$ZEPHYR_SDK_VERSION" ]]; then
|
||||
wget $ZEPHYR_SDK_DOWNLOAD_URL &&
|
||||
chmod +x $ZEPHYR_SDK_SETUP_BINARY &&
|
||||
rm -rf $ZEPHYR_SDK_INSTALL_DIR &&
|
||||
./$ZEPHYR_SDK_SETUP_BINARY --quiet -- -y -d $ZEPHYR_SDK_INSTALL_DIR > /dev/null;
|
||||
fi
|
||||
|
||||
before_script: >
|
||||
if [[ "$TARGET" == "zephyr" ]]; then
|
||||
cd .. &&
|
||||
git clone --depth=1 https://github.com/zephyrproject-rtos/zephyr.git &&
|
||||
cd zephyr &&
|
||||
source zephyr-env.sh;
|
||||
fi
|
||||
|
||||
script:
|
||||
- if [[ "$TARGET" == "zephyr" ]]; then
|
||||
mkdir -p ../libmetal/build-zephyr &&
|
||||
cd ../libmetal/build-zephyr &&
|
||||
cmake .. -DWITH_ZEPHYR=on -DBOARD=qemu_cortex_m3 -DWITH_TESTS=on &&
|
||||
make VERBOSE=1;
|
||||
fi
|
||||
- if [[ "$TARGET" == "linux" ]]; then
|
||||
mkdir -p build-linux &&
|
||||
cd build-linux &&
|
||||
cmake .. -DWITH_TESTS_EXEC=on &&
|
||||
make VERBOSE=1 all test;
|
||||
fi
|
||||
- if [[ "$TARGET" == "generic" ]]; then
|
||||
mkdir -p build-generic &&
|
||||
cd build-generic &&
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=template-generic &&
|
||||
make VERBOSE=1;
|
||||
fi
|
||||
- if [[ "$TARGET" == "freertos" ]]; then
|
||||
mkdir -p build-freertos &&
|
||||
cd build-freertos &&
|
||||
export &&
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=template-freertos -DCMAKE_C_FLAGS="-I$PWD/../FreeRTOSv10.0.1/FreeRTOS/Source/include/ -I$PWD/../FreeRTOSv10.0.1/FreeRTOS/Demo/CORTEX_STM32F107_GCC_Rowley -I$PWD/../FreeRTOSv10.0.1/FreeRTOS/Source/portable/GCC/ARM_CM3" &&
|
||||
make VERBOSE=1;
|
||||
fi
|
40
source/OpenAMP/libmetal/CMakeLists.txt
Normal file
40
source/OpenAMP/libmetal/CMakeLists.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
cmake_minimum_required (VERSION 2.6)
|
||||
if (POLICY CMP0048)
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
endif()
|
||||
|
||||
list (APPEND CMAKE_MODULE_PATH
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/platforms")
|
||||
|
||||
include (syscheck)
|
||||
project (metal C)
|
||||
|
||||
include (CheckIncludeFiles)
|
||||
include (CheckCSourceCompiles)
|
||||
include (collect)
|
||||
include (options)
|
||||
include (depends)
|
||||
|
||||
foreach(_inc_path ${CMAKE_INCLUDE_PATH})
|
||||
collect (PROJECT_INC_DIRS "${_inc_path}")
|
||||
endforeach()
|
||||
|
||||
enable_testing ()
|
||||
|
||||
add_subdirectory (lib)
|
||||
|
||||
if (WITH_TESTS)
|
||||
add_subdirectory (test)
|
||||
endif (WITH_TESTS)
|
||||
|
||||
if (WITH_DOC)
|
||||
add_subdirectory (doc)
|
||||
endif (WITH_DOC)
|
||||
|
||||
if (WITH_EXAMPLES)
|
||||
add_subdirectory (examples)
|
||||
endif (WITH_EXAMPLES)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
41
source/OpenAMP/libmetal/LICENSE.md
Normal file
41
source/OpenAMP/libmetal/LICENSE.md
Normal file
@@ -0,0 +1,41 @@
|
||||
Software License Agreement (BSD License)
|
||||
========================================
|
||||
|
||||
Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of Xilinx nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Notes
|
||||
=========================================
|
||||
Use the following tag instead of the full license text in the individual files:
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
This enables machine processing of license information based on the SPDX
|
||||
License Identifiers that are here available: http://spdx.org/licenses/
|
||||
|
21
source/OpenAMP/libmetal/MAINTAINERS.md
Normal file
21
source/OpenAMP/libmetal/MAINTAINERS.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# libmetal Maintainers
|
||||
|
||||
libmetal project is maintained by the OpenAMP open source community.
|
||||
Everyone is encouraged to submit issues and changes to improve libmetal.
|
||||
|
||||
The intention of this file is to provide a set of names that developers can
|
||||
consult when they have a question about OpenAMP and to provide a a set of
|
||||
names to be CC'd when submitting a patch.
|
||||
|
||||
## Project Administration
|
||||
Wendy Liang <wendy.liang@xilinx.com>
|
||||
|
||||
### All patches CC here
|
||||
open-amp@googlegroups.com
|
||||
|
||||
## Machines
|
||||
### Xilinx Platform - Zynq-7000
|
||||
Wendy Liang <wendy.liang@xilinx.com>
|
||||
|
||||
### Xilinx Platform - Zynq UltraScale+ MPSoC
|
||||
Wendy Liang <wendy.liang@xilinx.com>
|
220
source/OpenAMP/libmetal/README.md
Normal file
220
source/OpenAMP/libmetal/README.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# libmetal
|
||||
|
||||
## Overview
|
||||
|
||||
Libmetal provides common user APIs to access devices, handle device interrupts
|
||||
and request memory across the following operating environments:
|
||||
* Linux user space (based on UIO and VFIO support in the kernel)
|
||||
* RTOS (with and without virtual memory)
|
||||
* Bare-metal environments
|
||||
|
||||
## Build Steps
|
||||
|
||||
### Building for Linux Host
|
||||
```
|
||||
$ git clone https://github.com/OpenAMP/libmetal.git
|
||||
$ mkdir -p libmetal/<build directory>
|
||||
$ cd libmetal/<build directory>
|
||||
$ cmake ..
|
||||
$ make VERBOSE=1 DESTDIR=<libmetal install location> install
|
||||
```
|
||||
|
||||
### Cross Compiling for Linux Target
|
||||
Use [meta-openamp](https://github.com/openamp/meta-openamp) to build
|
||||
libmetal library.
|
||||
Use package `libmetal` in your yocto config file.
|
||||
|
||||
### Building for Baremetal
|
||||
|
||||
To build on baremetal, you will need to provide a toolchain file. Here is an
|
||||
example toolchain file:
|
||||
```
|
||||
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
|
||||
set (MACHINE "zynqmp_r5" CACHE STRING "")
|
||||
|
||||
set (CROSS_PREFIX "armr5-none-eabi-" CACHE STRING "")
|
||||
set (CMAKE_C_FLAGS "-mfloat-abi=soft -mcpu=cortex-r5 -Wall -Werror -Wextra \
|
||||
-flto -Os -I/ws/xsdk/r5_0_bsp/psu_cortexr5_0/include" CACHE STRING "")
|
||||
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto")
|
||||
SET(CMAKE_AR "gcc-ar" CACHE STRING "")
|
||||
SET(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||
SET(CMAKE_C_ARCHIVE_FINISH true)
|
||||
|
||||
include (cross-generic-gcc)
|
||||
```
|
||||
* Note: other toolchain files can be found in the `cmake/platforms/` directory.
|
||||
* Compile with your toolchain file.
|
||||
```
|
||||
$ mkdir -p build-libmetal
|
||||
$ cd build-libmetal
|
||||
$ cmake <libmetal_source> -DCMAKE_TOOLCHAIN_FILE=<toolchain_file>
|
||||
$ make VERBOSE=1 DESTDIR=<libmetal_install> install
|
||||
```
|
||||
|
||||
### Building for Zephyr
|
||||
As Zephyr uses CMake, we build libmetal library and test application as
|
||||
targets of Zephyr CMake project. Here is how to build libmetal for Zephyr:
|
||||
```
|
||||
$ export ZEPHYR_GCC_VARIANT=zephyr
|
||||
$ export ZEPHYR_SDK_INSTALL_DIR=<where Zephyr SDK is installed>
|
||||
$ source <git_clone_zephyr_project_source_root>/zephyr-env.sh
|
||||
|
||||
$ cmake <libmetal_source_root> -DWITH_ZEPHYR=on -DBOARD=qemu_cortex_m3 \
|
||||
[-DWITH_TESTS=on]
|
||||
$ make VERBOSE=1 all
|
||||
# If we have turned on tests with "-DWITH_TESTS=on" when we run cmake,
|
||||
# we launch libmetal test on Zephyr QEMU platform as follows:
|
||||
$ make VERBOSE=1 run
|
||||
```
|
||||
|
||||
## Interfaces
|
||||
|
||||
The following subsections give an overview of interfaces provided by libmetal.
|
||||
|
||||
### Platform and OS Independent Utilities
|
||||
|
||||
These interfaces do not need to be ported across to new operating systems.
|
||||
|
||||
#### I/O
|
||||
|
||||
The libmetal I/O region abstraction provides access to memory mapped I/O and
|
||||
shared memory regions. This includes:
|
||||
* primitives to read and write memory with ordering constraints, and
|
||||
* ability to translate between physical and virtual addressing on systems
|
||||
that support virtual memory.
|
||||
|
||||
#### Log
|
||||
|
||||
The libmetal logging interface is used to plug log messages generated by
|
||||
libmetal into application specific logging mechanisms (e.g. syslog). This
|
||||
also provides basic message prioritization and filtering mechanisms.
|
||||
|
||||
#### List
|
||||
|
||||
This is a simple doubly linked list implementation used internally within
|
||||
libmetal, and also available for application use.
|
||||
|
||||
#### Other Utilities
|
||||
|
||||
The following utilities are provided in lib/utilities.h:
|
||||
* Min/max, round up/down, etc.
|
||||
* Bitmap operations
|
||||
* Helper to compute container structure pointers
|
||||
* ... and more ...
|
||||
|
||||
#### Version
|
||||
|
||||
The libmetal version interface allows user to get the version of the library.
|
||||
|
||||
### Top Level Interfaces
|
||||
|
||||
The users will need to call two top level interfaces to use libmetal APIs:
|
||||
* metal_init - initialize the libmetal resource
|
||||
* metal_finish - release libmetal resource
|
||||
|
||||
Each system needs to have their own implementation inside libmetal for these
|
||||
two APIs to call:
|
||||
* metal_sys_init
|
||||
* metal_sys_finish
|
||||
|
||||
For the current release, libmetal provides Linux userspace and bare-metal
|
||||
implementation for metal_sys_init and metal_sys_finish.
|
||||
|
||||
For Linux userspace, metal_sys_init sets up a table for available shared pages,
|
||||
checks whether UIO/VFIO drivers are avail, and starts interrupt handling
|
||||
thread.
|
||||
|
||||
For bare-metal, metal_sys_init and metal_sys_finish just returns.
|
||||
|
||||
### Atomics
|
||||
|
||||
The libmetal atomic operations API is consistent with the C11/C++11 stdatomics
|
||||
interface. The stdatomics interface is commonly provided by recent toolchains
|
||||
including GCC and LLVM/Clang. When porting to a different toolchain, it may be
|
||||
necessary to provide an stdatomic compatible implementation if the toolchain
|
||||
does not already provide one.
|
||||
|
||||
### Alloc
|
||||
|
||||
libmetal provides memory allocation and release APIs.
|
||||
|
||||
### Locking
|
||||
|
||||
libmetal provides the following locking APIs.
|
||||
|
||||
#### Mutex
|
||||
|
||||
libmetal has a generic mutex implementation which is a busy wait. It is
|
||||
recommended to have OS specific implementation for mutex.
|
||||
|
||||
The Linux userspace mutex implementation uses futex to wait for the lock
|
||||
and wakeup a waiter.
|
||||
|
||||
#### Condition Variable
|
||||
libmetal condition variable APIs provide "wait" for user applications to wait
|
||||
on some condition to be met, and "signal" to indicate a particular even occurs.
|
||||
|
||||
#### Spinlock
|
||||
libmetal spinlock APIs provides busy waiting mechanism to acquire a lock.
|
||||
|
||||
### Shmem
|
||||
|
||||
libmetal has a generic static shared memory implementation. If your OS has a
|
||||
global shared memory allocation, you will need to port it for the OS.
|
||||
|
||||
The Linux userspace shmem implementation uses libhugetlbfs to support huge page
|
||||
sizes.
|
||||
|
||||
### Bus and Device Abstraction
|
||||
|
||||
libmetal has a static generic implementation. If your OS has a driver model
|
||||
implementation, you will need to port it for the OS.
|
||||
|
||||
The Linux userspace abstraction binds the devices to UIO or VFIO driver.
|
||||
The user applications specify which device to use, e.g. bus "platform" bus,
|
||||
device "f8000000.slcr", and then the abstraction will check if platform UIO
|
||||
driver or platform VFIO driver is there. If platform VFIO driver exists,
|
||||
it will bind the device to the platform VFIO driver, otherwise, if UIO driver
|
||||
exists, it will bind the device to the platform UIO driver.
|
||||
|
||||
The VFIO support is not yet implemented.
|
||||
|
||||
### Interrupt
|
||||
|
||||
libmetal provides APIs to register an interrupt, disable interrupts and restore
|
||||
interrupts.
|
||||
|
||||
The Linux userspace implementation will use a thread to call select() function
|
||||
to listen to the file descriptors of the devices to see if there is an interrupt
|
||||
triggered. If there is an interrupt triggered, it will call the interrupt
|
||||
handler registered by the user application.
|
||||
|
||||
### Cache
|
||||
|
||||
libmetal provides APIs to flush and invalidate caches.
|
||||
|
||||
The cache APIs for Linux userspace are empty functions for now as cache
|
||||
operations system calls are not avaiable for all architectures.
|
||||
|
||||
### DMA
|
||||
|
||||
libmetal DMA APIs provide DMA map and unmap implementation.
|
||||
|
||||
After calling DMA map, the DMA device will own the memory.
|
||||
After calling DMA unmap, the cpu will own the memory.
|
||||
|
||||
For Linux userspace, it only supports to use UIO device memory as DMA
|
||||
memory for this release.
|
||||
|
||||
### Time
|
||||
libmetal time APIs provide getting timestamp implementation.
|
||||
|
||||
### Sleep
|
||||
libmetal sleep APIs provide getting delay execution implementation.
|
||||
|
||||
### Compiler
|
||||
|
||||
This API is for compiler dependent functions. For this release, there is only
|
||||
a GCC implementation, and compiler specific code is limited to atomic
|
||||
operations.
|
36
source/OpenAMP/libmetal/cmake/collect.cmake
Normal file
36
source/OpenAMP/libmetal/cmake/collect.cmake
Normal file
@@ -0,0 +1,36 @@
|
||||
function (collector_create name base)
|
||||
set_property (GLOBAL PROPERTY "COLLECT_${name}_LIST")
|
||||
set_property (GLOBAL PROPERTY "COLLECT_${name}_BASE" "${base}")
|
||||
endfunction (collector_create)
|
||||
|
||||
function (collector_list var name)
|
||||
get_property (_list GLOBAL PROPERTY "COLLECT_${name}_LIST")
|
||||
set (${var} "${_list}" PARENT_SCOPE)
|
||||
endfunction (collector_list)
|
||||
|
||||
function (collector_base var name)
|
||||
get_property (_base GLOBAL PROPERTY "COLLECT_${name}_BASE")
|
||||
set (${var} "${_base}" PARENT_SCOPE)
|
||||
endfunction (collector_base)
|
||||
|
||||
function (collect name)
|
||||
collector_base (_base ${name})
|
||||
string(COMPARE NOTEQUAL "${_base}" "" _is_rel)
|
||||
set (_list)
|
||||
foreach (s IN LISTS ARGN)
|
||||
if (_is_rel)
|
||||
get_filename_component (s "${s}" ABSOLUTE)
|
||||
file (RELATIVE_PATH s "${_base}" "${s}")
|
||||
endif (_is_rel)
|
||||
list (APPEND _list "${s}")
|
||||
endforeach ()
|
||||
set_property (GLOBAL APPEND PROPERTY "COLLECT_${name}_LIST" "${_list}")
|
||||
endfunction (collect)
|
||||
|
||||
# Create global collectors
|
||||
collector_create (PROJECT_INC_DIRS "")
|
||||
collector_create (PROJECT_LIB_DIRS "")
|
||||
collector_create (PROJECT_LIB_DEPS "")
|
||||
collector_create (PROJECT_HDR_TESTS "")
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
38
source/OpenAMP/libmetal/cmake/depends.cmake
Normal file
38
source/OpenAMP/libmetal/cmake/depends.cmake
Normal file
@@ -0,0 +1,38 @@
|
||||
if (WITH_DOC)
|
||||
find_package (Doxygen)
|
||||
endif (WITH_DOC)
|
||||
|
||||
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||
|
||||
check_include_files (stdatomic.h HAVE_STDATOMIC_H)
|
||||
check_include_files (linux/futex.h HAVE_FUTEX_H)
|
||||
|
||||
find_package (HugeTLBFS)
|
||||
if (HUGETLBFS_FOUND)
|
||||
collect (PROJECT_INC_DIRS "${HUGETLBFS_INCLUDE_DIR}")
|
||||
collect (PROJECT_LIB_DEPS "${HUGETLBFS_LIBRARIES}")
|
||||
add_definitions(-DHAVE_HUGETLBFS_H)
|
||||
endif(HUGETLBFS_FOUND)
|
||||
|
||||
find_package (LibSysFS REQUIRED)
|
||||
collect (PROJECT_INC_DIRS "${LIBSYSFS_INCLUDE_DIR}")
|
||||
collect (PROJECT_LIB_DEPS "${LIBSYSFS_LIBRARIES}")
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
collect (PROJECT_LIB_DEPS "${CMAKE_THREAD_LIBS_INIT}")
|
||||
|
||||
find_package(LibRt REQUIRED)
|
||||
collect (PROJECT_LIB_DEPS "${LIBRT_LIBRARIES}")
|
||||
|
||||
else ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||
|
||||
# TODO: fix for find_path() to detect stdatomic.h
|
||||
# find_path (HAVE_STDATOMIC_H stdatomic.h)
|
||||
set (_saved_cmake_required_flags ${CMAKE_REQUIRED_FLAGS})
|
||||
set (CMAKE_REQUIRED_FLAGS "-c" CACHE STRING "")
|
||||
check_include_files (stdatomic.h HAVE_STDATOMIC_H)
|
||||
set (CMAKE_REQUIRED_FLAGS ${_saved_cmake_required_flags})
|
||||
|
||||
endif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
34
source/OpenAMP/libmetal/cmake/modules/FindHugeTLBFS.cmake
Normal file
34
source/OpenAMP/libmetal/cmake/modules/FindHugeTLBFS.cmake
Normal file
@@ -0,0 +1,34 @@
|
||||
# FindHugeTLBFS
|
||||
# --------
|
||||
#
|
||||
# Find HugeTLBFS
|
||||
#
|
||||
# Find the native HugeTLBFS includes and library This module defines
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# HUGETLBFS_INCLUDE_DIR, where to find hugetlbfs.h, etc.
|
||||
# HUGETLBFS_LIBRARIES, the libraries needed to use HugeTLBFS.
|
||||
# HUGETLBFS_FOUND, If false, do not try to use HugeTLBFS.
|
||||
#
|
||||
# also defined, but not for general use are
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# HUGETLBFS_LIBRARY, where to find the HugeTLBFS library.
|
||||
|
||||
find_path (HUGETLBFS_INCLUDE_DIR hugetlbfs.h)
|
||||
|
||||
set (HUGETLBFS_NAMES ${HUGETLBFS_NAMES} hugetlbfs)
|
||||
find_library (HUGETLBFS_LIBRARY NAMES ${HUGETLBFS_NAMES})
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set HUGETLBFS_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
include (FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS (HUGETLBFS DEFAULT_MSG HUGETLBFS_LIBRARY HUGETLBFS_INCLUDE_DIR)
|
||||
|
||||
if (HUGETLBFS_FOUND)
|
||||
set (HUGETLBFS_LIBRARIES ${HUGETLBFS_LIBRARY})
|
||||
endif (HUGETLBFS_FOUND)
|
||||
|
||||
mark_as_advanced (HUGETLBFS_LIBRARY HUGETLBFS_INCLUDE_DIR)
|
46
source/OpenAMP/libmetal/cmake/modules/FindLibRt.cmake
Normal file
46
source/OpenAMP/libmetal/cmake/modules/FindLibRt.cmake
Normal file
@@ -0,0 +1,46 @@
|
||||
#.rst:
|
||||
# FindLibRt
|
||||
# --------
|
||||
#
|
||||
# Find the native realtime includes and library.
|
||||
#
|
||||
# IMPORTED Targets
|
||||
# ^^^^^^^^^^^^^^^^
|
||||
#
|
||||
# This module defines :prop_tgt:`IMPORTED` target ``LIBRT::LIBRT``, if
|
||||
# LIBRT has been found.
|
||||
#
|
||||
# Result Variables
|
||||
# ^^^^^^^^^^^^^^^^
|
||||
#
|
||||
# This module defines the following variables:
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# LIBRT_INCLUDE_DIRS - where to find time.h, etc.
|
||||
# LIBRT_LIBRARIES - List of libraries when using librt.
|
||||
# LIBRT_FOUND - True if realtime library found.
|
||||
#
|
||||
# Hints
|
||||
# ^^^^^
|
||||
#
|
||||
# A user may set ``LIBRT_ROOT`` to a realtime installation root to tell this
|
||||
# module where to look.
|
||||
|
||||
find_path(LIBRT_INCLUDE_DIRS
|
||||
NAMES time.h
|
||||
PATHS ${LIBRT_ROOT}/include/
|
||||
)
|
||||
find_library(LIBRT_LIBRARIES rt)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LibRt DEFAULT_MSG LIBRT_LIBRARIES LIBRT_INCLUDE_DIRS)
|
||||
mark_as_advanced(LIBRT_INCLUDE_DIRS LIBRT_LIBRARIES)
|
||||
|
||||
if(LIBRT_FOUND)
|
||||
if(NOT TARGET LIBRT::LIBRT)
|
||||
add_library(LIBRT::LIBRT UNKNOWN IMPORTED)
|
||||
set_target_properties(LIBRT::LIBRT PROPERTIES
|
||||
IMPORTED_LOCATION "${LIBRT_LIBRARIES}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBRT_INCLUDE_DIRS}")
|
||||
endif()
|
||||
endif()
|
34
source/OpenAMP/libmetal/cmake/modules/FindLibSysFS.cmake
Normal file
34
source/OpenAMP/libmetal/cmake/modules/FindLibSysFS.cmake
Normal file
@@ -0,0 +1,34 @@
|
||||
# FindLibSysFS
|
||||
# --------
|
||||
#
|
||||
# Find LibSysFS
|
||||
#
|
||||
# Find the native LibSysFS includes and library This module defines
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# LIBSYSFS_INCLUDE_DIR, where to find libsysfs.h, etc.
|
||||
# LIBSYSFS_LIBRARIES, the libraries needed to use LibSysFS.
|
||||
# LIBSYSFS_FOUND, If false, do not try to use LibSysFS.
|
||||
#
|
||||
# also defined, but not for general use are
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# LIBSYSFS_LIBRARY, where to find the LibSysFS library.
|
||||
|
||||
find_path (LIBSYSFS_INCLUDE_DIR sysfs/libsysfs.h)
|
||||
|
||||
set (LIBSYSFS_NAMES ${LIBSYSFS_NAMES} sysfs)
|
||||
find_library (LIBSYSFS_LIBRARY NAMES ${LIBSYSFS_NAMES})
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set LIBSYSFS_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
include (FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS (LIBSYSFS DEFAULT_MSG LIBSYSFS_LIBRARY LIBSYSFS_INCLUDE_DIR)
|
||||
|
||||
if (LIBSYSFS_FOUND)
|
||||
set (LIBSYSFS_LIBRARIES ${LIBSYSFS_LIBRARY})
|
||||
endif (LIBSYSFS_FOUND)
|
||||
|
||||
mark_as_advanced (LIBSYSFS_LIBRARY LIBSYSFS_INCLUDE_DIR)
|
63
source/OpenAMP/libmetal/cmake/options.cmake
Normal file
63
source/OpenAMP/libmetal/cmake/options.cmake
Normal file
@@ -0,0 +1,63 @@
|
||||
set (PROJECT_VER_MAJOR 0)
|
||||
set (PROJECT_VER_MINOR 1)
|
||||
set (PROJECT_VER_PATCH 0)
|
||||
set (PROJECT_VER 0.1.0)
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set (CMAKE_BUILD_TYPE Debug)
|
||||
endif (NOT CMAKE_BUILD_TYPE)
|
||||
message ("-- Build type: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
if (NOT CMAKE_INSTALL_LIBDIR)
|
||||
set (CMAKE_INSTALL_LIBDIR "lib")
|
||||
endif (NOT CMAKE_INSTALL_LIBDIR)
|
||||
|
||||
if (NOT CMAKE_INSTALL_BINDIR)
|
||||
set (CMAKE_INSTALL_BINDIR "bin")
|
||||
endif (NOT CMAKE_INSTALL_BINDIR)
|
||||
|
||||
set (_host "${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR}")
|
||||
message ("-- Host: ${_host}")
|
||||
|
||||
set (_target "${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}")
|
||||
message ("-- Target: ${_target}")
|
||||
|
||||
if (NOT DEFINED MACHINE)
|
||||
set (MACHINE "Generic")
|
||||
endif (NOT DEFINED MACHINE)
|
||||
message ("-- Machine: ${MACHINE}")
|
||||
|
||||
# handle if '-' in machine name
|
||||
string (REPLACE "-" "_" MACHINE ${MACHINE})
|
||||
|
||||
if (NOT DEFINED PROJECT_SYSTEM)
|
||||
string (TOLOWER ${CMAKE_SYSTEM_NAME} PROJECT_SYSTEM)
|
||||
string (TOUPPER ${CMAKE_SYSTEM_NAME} PROJECT_SYSTEM_UPPER)
|
||||
endif (NOT DEFINED PROJECT_SYSTEM)
|
||||
|
||||
string (TOLOWER ${CMAKE_SYSTEM_PROCESSOR} PROJECT_PROCESSOR)
|
||||
string (TOUPPER ${CMAKE_SYSTEM_PROCESSOR} PROJECT_PROCESSOR_UPPER)
|
||||
string (TOLOWER ${MACHINE} PROJECT_MACHINE)
|
||||
string (TOUPPER ${MACHINE} PROJECT_MACHINE_UPPER)
|
||||
|
||||
option (WITH_STATIC_LIB "Build with a static library" ON)
|
||||
|
||||
if ("${PROJECT_SYSTEM}" STREQUAL "linux")
|
||||
option (WITH_SHARED_LIB "Build with a shared library" ON)
|
||||
option (WITH_TESTS "Install test applications" ON)
|
||||
endif ("${PROJECT_SYSTEM}" STREQUAL "linux")
|
||||
|
||||
if (WITH_TESTS AND (${_host} STREQUAL ${_target}))
|
||||
option (WITH_TESTS_EXEC "Run test applications during build" ON)
|
||||
endif (WITH_TESTS AND (${_host} STREQUAL ${_target}))
|
||||
|
||||
if (WITH_ZEPHYR)
|
||||
option (WITH_ZEPHYR_LIB "Build libmetal as a zephyr library" OFF)
|
||||
endif (WITH_ZEPHYR)
|
||||
|
||||
option (WITH_DEFAULT_LOGGER "Build with default logger" ON)
|
||||
|
||||
option (WITH_DOC "Build with documentation" ON)
|
||||
|
||||
set (PROJECT_EC_FLAGS "-Wall -Werror -Wextra" CACHE STRING "")
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,8 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
|
||||
set (MACHINE "cortexm" CACHE STRING "")
|
||||
|
||||
set (CMAKE_C_FLAGS "-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Og -fmessage-length=0 -ffunction-sections -c" CACHE STRING "")
|
||||
|
||||
include (cross-generic-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,6 @@
|
||||
# cmake 3.3.2 does not know CMAKE_SYSTEM_NAME=FreeRTOS, we set it to Generic
|
||||
include (cross-generic-gcc)
|
||||
string (TOLOWER "FreeRTOS" PROJECT_SYSTEM)
|
||||
string (TOUPPER "FreeRTOS" PROJECT_SYSTEM_UPPER)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,10 @@
|
||||
set (CMAKE_SYSTEM_NAME "Generic" CACHE STRING "")
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE STRING "")
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER CACHE STRING "")
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER CACHE STRING "")
|
||||
|
||||
include (CMakeForceCompiler)
|
||||
CMAKE_FORCE_C_COMPILER ("${CROSS_PREFIX}gcc" GNU)
|
||||
CMAKE_FORCE_CXX_COMPILER ("${CROSS_PREFIX}g++" GNU)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,10 @@
|
||||
set (CMAKE_SYSTEM_NAME "Generic" CACHE STRING "")
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE STRING "")
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER CACHE STRING "")
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER CACHE STRING "")
|
||||
|
||||
include (CMakeForceCompiler)
|
||||
CMAKE_FORCE_C_COMPILER ("icc${CROSS_SUFFIX}" IAR)
|
||||
CMAKE_FORCE_CXX_COMPILER ("icc${CROSS_SUFFIX} --eec++" IAR)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,8 @@
|
||||
set (CMAKE_SYSTEM_NAME "Linux" CACHE STRING "")
|
||||
set (CMAKE_C_COMPILER "${CROSS_PREFIX}gcc" CACHE STRING "")
|
||||
set (CMAKE_CXX_COMPILER "${CROSS_PREFIX}g++" CACHE STRING "")
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE STRING "")
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER CACHE STRING "")
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER CACHE STRING "")
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,7 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "microblaze" CACHE STRING "")
|
||||
set (MACHINE "microblaze_generic" CACHE STRING "")
|
||||
set (CROSS_PREFIX "mb-" CACHE STRING "")
|
||||
# These flags are for a demo. If microblaze is changed, the flags need to be changed too.
|
||||
set (CMAKE_C_FLAGS "-mlittle-endian -mxl-barrel-shift -mxl-pattern-compare \
|
||||
-mcpu=v10.0 -mno-xl-soft-mul" CACHE STRING "")
|
||||
include (cross-generic-gcc)
|
@@ -0,0 +1,12 @@
|
||||
# Modify to match your needs. These setttings can also be overridden at the
|
||||
# command line. (eg. cmake -DCMAKE_C_FLAGS="-O3")
|
||||
|
||||
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
|
||||
set (MACHINE "template" CACHE STRING "")
|
||||
set (CROSS_PREFIX "arm-none-eabi-" CACHE STRING "")
|
||||
|
||||
set (CMAKE_C_FLAGS "" CACHE STRING "")
|
||||
|
||||
include (cross-freertos-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,12 @@
|
||||
# Modify to match your needs. These setttings can also be overridden at the
|
||||
# command line. (eg. cmake -DCMAKE_C_FLAGS="-O3")
|
||||
|
||||
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
|
||||
set (MACHINE "template" CACHE STRING "")
|
||||
set (CROSS_PREFIX "arm-none-eabi-" CACHE STRING "")
|
||||
|
||||
set (CMAKE_C_FLAGS "" CACHE STRING "")
|
||||
|
||||
include (cross-generic-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,9 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
|
||||
set (MACHINE "zynq7" CACHE STRING "")
|
||||
set (CROSS_PREFIX "arm-none-eabi-" CACHE STRING "")
|
||||
|
||||
set (CMAKE_C_FLAGS "-mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=hard" CACHE STRING "")
|
||||
|
||||
include (cross-freertos-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,5 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
|
||||
set (CROSS_SUFFIX "arm" CACHE STRING "")
|
||||
include (cross-generic-iar)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,9 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
|
||||
set (MACHINE "zynq7" CACHE STRING "")
|
||||
set (CROSS_PREFIX "arm-none-eabi-" CACHE STRING "")
|
||||
|
||||
set (CMAKE_C_FLAGS "-mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=hard" CACHE STRING "")
|
||||
|
||||
include (cross-generic-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,5 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
|
||||
set (CROSS_PREFIX "arm-xilinx-linux-gnueabi-" CACHE STRING "")
|
||||
include (cross-linux-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,8 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "aarch64" CACHE STRING "")
|
||||
set (MACHINE "zynqmp_a53" CACHE STRING "")
|
||||
set (CROSS_PREFIX "aarch64-none-elf-" CACHE STRING "")
|
||||
set (CMAKE_C_FLAGS "" CACHE STRING "")
|
||||
|
||||
include (cross-freertos-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,8 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "aarch64" CACHE STRING "")
|
||||
set (MACHINE "zynqmp_a53" CACHE STRING "")
|
||||
set (CROSS_PREFIX "aarch64-none-elf-" CACHE STRING "")
|
||||
set (CMAKE_C_FLAGS "" CACHE STRING "")
|
||||
|
||||
include (cross-generic-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,5 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "aarch64" CACHE STRING "")
|
||||
set (CROSS_PREFIX "aarch64-linux-gnu-" CACHE STRING "")
|
||||
include (cross-linux-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,8 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
|
||||
set (MACHINE "zynqmp_r5" CACHE STRING "")
|
||||
set (CROSS_PREFIX "armr5-none-eabi-" CACHE STRING "")
|
||||
set (CMAKE_C_FLAGS "-mfloat-abi=soft -mcpu=cortex-r5" CACHE STRING "")
|
||||
|
||||
include (cross-freertos-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,10 @@
|
||||
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
|
||||
set (MACHINE "zynqmp_r5" CACHE STRING "")
|
||||
set (CROSS_PREFIX "armr5-none-eabi-" CACHE STRING "")
|
||||
|
||||
# Xilinx SDK version earlier than 2017.2 use mfloat-abi=soft by default to generat libxil
|
||||
set (CMAKE_C_FLAGS "-mfloat-abi=hard -mfpu=vfpv3-d16 -mcpu=cortex-r5" CACHE STRING "")
|
||||
|
||||
include (cross-generic-gcc)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
13
source/OpenAMP/libmetal/cmake/syscheck.cmake
Normal file
13
source/OpenAMP/libmetal/cmake/syscheck.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
# use "Generic" as CMAKE_SYSTEM_NAME
|
||||
|
||||
if (WITH_ZEPHYR)
|
||||
set (CMAKE_SYSTEM_NAME "Generic" CACHE STRING "")
|
||||
string (TOLOWER "Zephyr" PROJECT_SYSTEM)
|
||||
string (TOUPPER "Zephyr" PROJECT_SYSTEM_UPPER)
|
||||
if (NOT WITH_ZEPHYR_LIB)
|
||||
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
|
||||
endif()
|
||||
if (CONFIG_CPU_CORTEX_M)
|
||||
set (MACHINE "cortexm" CACHE STRING "")
|
||||
endif (CONFIG_CPU_CORTEX_M)
|
||||
endif (WITH_ZEPHYR)
|
19
source/OpenAMP/libmetal/doc/CMakeLists.txt
Normal file
19
source/OpenAMP/libmetal/doc/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
if (DOXYGEN_FOUND)
|
||||
|
||||
configure_file (Doxyfile.in Doxyfile @ONLY)
|
||||
|
||||
add_custom_target (doc ALL
|
||||
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
||||
DESTINATION share/doc/${PROJECT_NAME})
|
||||
install (FILES ${PROJECT_SOURCE_DIR}/README.md
|
||||
DESTINATION share/doc/${PROJECT_NAME})
|
||||
install (FILES ${PROJECT_SOURCE_DIR}/LICENSE.md
|
||||
DESTINATION share/doc/${PROJECT_NAME})
|
||||
|
||||
endif (DOXYGEN_FOUND)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
2385
source/OpenAMP/libmetal/doc/Doxyfile.in
Normal file
2385
source/OpenAMP/libmetal/doc/Doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
5
source/OpenAMP/libmetal/examples/CMakeLists.txt
Normal file
5
source/OpenAMP/libmetal/examples/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
collect(PROJECT_LIB_DEPS metal)
|
||||
|
||||
add_subdirectory (system)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
4
source/OpenAMP/libmetal/examples/system/CMakeLists.txt
Normal file
4
source/OpenAMP/libmetal/examples/system/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_SYSTEM})
|
||||
add_subdirectory(${PROJECT_SYSTEM})
|
||||
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_SYSTEM})
|
@@ -0,0 +1,4 @@
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
|
||||
add_subdirectory(${PROJECT_MACHINE})
|
||||
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
|
@@ -0,0 +1,18 @@
|
||||
|
||||
collect(PROJECT_LIB_DEPS freertos)
|
||||
collect(PROJECT_LIB_DEPS metal)
|
||||
collect(PROJECT_LIB_DEPS xil)
|
||||
collect(PROJECT_LIB_DEPS c)
|
||||
collect(PROJECT_LIB_DEPS m)
|
||||
|
||||
set (_lib "xil")
|
||||
find_library (_lib_path ${_lib})
|
||||
if (NOT _lib_path)
|
||||
message ( "external library ${_lib_path} not found" )
|
||||
message ( "hint: you may need to pass -DCMAKE_LIBRARY_PATH=<path>" )
|
||||
message ( FATAL_ERROR "library ${_lib} is required to build the examples" )
|
||||
endif (NOT _lib_path)
|
||||
get_filename_component (_lib_path ${_lib_path} DIRECTORY)
|
||||
collect (PROJECT_LIB_DIRS ${_lib_path})
|
||||
|
||||
add_subdirectory(zynqmp_amp_demo)
|
@@ -0,0 +1,31 @@
|
||||
collector_list (_list PROJECT_INC_DIRS)
|
||||
include_directories (${_list} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
collector_list (_list PROJECT_LIB_DIRS)
|
||||
link_directories (${_list})
|
||||
|
||||
collector_list (_deps PROJECT_LIB_DEPS)
|
||||
|
||||
set (_linker_script ${CMAKE_CURRENT_SOURCE_DIR}/lscript.ld)
|
||||
|
||||
set (_src_common ${CMAKE_CURRENT_SOURCE_DIR}/init_${PROJECT_SYSTEM}.c)
|
||||
set (_app0 libmetal_amp_demod)
|
||||
set (_src0 ${CMAKE_CURRENT_SOURCE_DIR}/${_app0}.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/sys_init.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_demod.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_atomic_demod.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/ipi_shmem_demod.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/ipi_latency_demod.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_latency_demod.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_throughput_demod.c)
|
||||
|
||||
get_property (_linker_options GLOBAL PROPERTY TEST_LINKER_OPTIONS)
|
||||
add_executable (${_app0}.elf ${_src0})
|
||||
if (PROJECT_EC_FLAGS)
|
||||
string(REPLACE " " ";" _ec_flgs ${PROJECT_EC_FLAGS})
|
||||
target_compile_options (${_app0}.elf PUBLIC ${_ec_flgs})
|
||||
endif (PROJECT_EC_FLAGS)
|
||||
target_link_libraries(${_app0}.elf -Wl,-Map=${_app0}.map -Wl,--gc-sections -T\"${_linker_script}\" -Wl,--start-group ${_deps} -Wl,--end-group)
|
||||
install (TARGETS ${_app0}.elf RUNTIME DESTINATION bin)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef __COMMON_H__
|
||||
#define __COMMON_H__
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <metal/irq.h>
|
||||
#include <metal/errno.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/cpu.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <sys/types.h>
|
||||
#include "sys_init.h"
|
||||
|
||||
/* Devices names */
|
||||
#define BUS_NAME "generic"
|
||||
#define IPI_DEV_NAME "ff310000.ipi"
|
||||
#define SHM_DEV_NAME "3ed80000.shm"
|
||||
#define TTC_DEV_NAME "ff110000.ttc"
|
||||
|
||||
/* IPI registers offset */
|
||||
#define IPI_TRIG_OFFSET 0x0 /* IPI trigger reg offset */
|
||||
#define IPI_OBS_OFFSET 0x4 /* IPI observation reg offset */
|
||||
#define IPI_ISR_OFFSET 0x10 /* IPI interrupt status reg offset */
|
||||
#define IPI_IMR_OFFSET 0x14 /* IPI interrupt mask reg offset */
|
||||
#define IPI_IER_OFFSET 0x18 /* IPI interrupt enable reg offset */
|
||||
#define IPI_IDR_OFFSET 0x1C /* IPI interrup disable reg offset */
|
||||
|
||||
#define IPI_MASK 0x1000000 /* IPI mask for kick from APU.
|
||||
We use PL0 IPI in this demo. */
|
||||
|
||||
/* TTC counter offsets */
|
||||
#define XTTCPS_CLK_CNTRL_OFFSET 0x0 /* TTC counter clock control reg offset */
|
||||
#define XTTCPS_CNT_CNTRL_OFFSET 0xC /* TTC counter control reg offset */
|
||||
#define XTTCPS_CNT_VAL_OFFSET 0x18 /* TTC counter val reg offset */
|
||||
#define XTTCPS_CNT_OFFSET(ID) ((ID) == 1 ? 0 : 1 << (ID)) /* TTC counter offset
|
||||
ID is from 1 to 3 */
|
||||
|
||||
/* TTC counter control masks */
|
||||
#define XTTCPS_CNT_CNTRL_RST_MASK 0x10U /* TTC counter control reset mask */
|
||||
#define XTTCPS_CNT_CNTRL_DIS_MASK 0x01U /* TTC counter control disable mask */
|
||||
|
||||
#define LPRINTF(format, ...) \
|
||||
xil_printf("\r\nSERVER> " format, ##__VA_ARGS__)
|
||||
|
||||
#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__)
|
||||
|
||||
extern struct metal_device *ipi_dev; /* IPI metal device */
|
||||
extern struct metal_device *shm_dev; /* SHM metal device */
|
||||
extern struct metal_device *ttc_dev; /* TTC metal device */
|
||||
|
||||
/**
|
||||
* @brief atomic_shmem_demod() - Shared memory atomic operation demo
|
||||
* This task will:
|
||||
* * Wait for the remote to write to shared memory.
|
||||
* * Once it receives the notification via polling, start atomic add by
|
||||
* 1 for 1000 times to first 32 bits of memory in the
|
||||
* shared memory location at 3ed00000 which is pointed to by shm_io.
|
||||
* * Write to shared mem to notify the remote once it finishes
|
||||
* calculation.
|
||||
*
|
||||
* @return - If setup failed, return the corresponding error number. Otherwise
|
||||
* return 0 on success.
|
||||
*/
|
||||
int atomic_shmem_demod();
|
||||
|
||||
/**
|
||||
* @brief ipi_latency_demod() - Show performance of IPI with Libmetal.
|
||||
* Loop until APU tells RPU to stop via shared memory.
|
||||
* In loop, wait for interrupt (interrupt handler stops APU to
|
||||
* RPU timer). Then reset count on RPU to APU timer to 0, start
|
||||
* counting and send interrupt to notify APU.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int ipi_latency_demod();
|
||||
|
||||
/**
|
||||
* @brief ipi_shmem_demod() - shared memory IPI demo
|
||||
* This task will:
|
||||
* * Wait for IPI interrupt from the remote
|
||||
* * Once it received the interrupt, copy the content from
|
||||
* the ping buffer to the pong buffer.
|
||||
* * Update the shared memory descriptor for the new available
|
||||
* pong buffer.
|
||||
* * Trigger IPI to notifty the remote.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int ipi_shmem_demod();
|
||||
|
||||
/**
|
||||
* @brief shmem_demod() - Show use of shared memory with Libmetal.
|
||||
* Until KEEP_GOING signal is stopped, keep looping.
|
||||
* In the loop, read message from remote, add one to message and
|
||||
* then respond. After the loop, cleanup resources.
|
||||
*
|
||||
* @return - return 0 on success, otherwise return error number indicating
|
||||
* type of error
|
||||
*/
|
||||
int shmem_demod();
|
||||
|
||||
/**
|
||||
* @brief shmem_latency_demod() - Show performance of shared mem.
|
||||
* Loop until APU tells RPU to stop via shared memory.
|
||||
* In loop, wait for interrupt (interrupt handler stops APU to
|
||||
* RPU timer). Then reset count on RPU to APU timer to 0, start
|
||||
* counting and send interrupt to notify APU.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int shmem_latency_demod();
|
||||
|
||||
/**
|
||||
* @brief shmem_throughput_demod() - Show throughput of shared mem.
|
||||
* At signal of remote, record total time to do block read and write
|
||||
* operation Loop until APU tells RPU to stop via shared memory.
|
||||
* In loop, wait for interrupt (interrupt handler stops APU to
|
||||
* RPU timer). Then reset count on RPU to APU timer to 0, start
|
||||
* counting and send interrupt to notify APU.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int shmem_throughput_demod();
|
||||
|
||||
static inline void wait_for_interrupt()
|
||||
{
|
||||
asm volatile("wfi");
|
||||
}
|
||||
|
||||
/**
|
||||
* @breif wait_for_notified() - Loop until notified bit
|
||||
* in channel is set.
|
||||
*
|
||||
* @param[in] notified - pointer to the notified variable
|
||||
*/
|
||||
static inline void wait_for_notified(atomic_int *notified)
|
||||
{
|
||||
while (atomic_flag_test_and_set(notified));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief print_demo() - print demo string
|
||||
*
|
||||
* @param[in] name - demo name
|
||||
*/
|
||||
static inline void print_demo(char *name)
|
||||
{
|
||||
LPRINTF("====== libmetal demo: %s ======\n", name);
|
||||
}
|
||||
|
||||
#endif /* __COMMON_H__ */
|
||||
|
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* ipi_latency_demod.c
|
||||
* This is the remote side of the IPI latency measurement demo.
|
||||
* This demo does the follwing steps:
|
||||
*
|
||||
* 1. Open the shared memory device.
|
||||
* 1. Open the TTC timer device.
|
||||
* 2. Open the IPI device.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 6. When it receives IPI interrupt, the IPI interrupt handler to stop
|
||||
* the RPU to APU TTC counter.
|
||||
* 7. Check the shared memory to see if demo is on. If the demo is on,
|
||||
* reest the RPU to APU TTC counter and kick IPI to notify the remote.
|
||||
* 8. If the shared memory indicates the demo is off, cleanup resource:
|
||||
* disable IPI interrupt and deregister the IPI interrupt handler.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include "common.h"
|
||||
|
||||
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
|
||||
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
|
||||
|
||||
#define TTC_CLK_FREQ_HZ 100000000
|
||||
|
||||
/* Shared memory offset */
|
||||
#define SHM_DEMO_CNTRL_OFFSET 0x0
|
||||
|
||||
#define DEMO_STATUS_IDLE 0x0
|
||||
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
|
||||
|
||||
struct channel_s {
|
||||
struct metal_io_region *ipi_io; /* IPI metal i/o region */
|
||||
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
|
||||
struct metal_io_region *ttc_io; /* TTC metal i/o region */
|
||||
uint32_t ipi_mask; /* RPU IPI mask */
|
||||
atomic_int remote_nkicked; /* 0 - kicked from remote */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief reset_timer() - function to reset TTC counter
|
||||
* Set the RST bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void reset_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_RST_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop_timer() - function to stop TTC counter
|
||||
* Set the disable bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void stop_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_DIS_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_irq_handler() - IPI interrupt handler
|
||||
* It will clear the notified flag to mark it's got an IPI interrupt.
|
||||
* It will stop the RPU->APU timer and will clear the notified
|
||||
* flag to mark it's got an IPI interrupt
|
||||
*
|
||||
* @param[in] vect_id - IPI interrupt vector ID
|
||||
* @param[in/out] priv - communication channel data for this application.
|
||||
*
|
||||
* @return - If the IPI interrupt is triggered by its remote, it returns
|
||||
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
|
||||
* not the interrupt it expected.
|
||||
*
|
||||
*/
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)priv;
|
||||
uint32_t val;
|
||||
|
||||
(void)vect_id;
|
||||
|
||||
if (ch) {
|
||||
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ch->ipi_mask) {
|
||||
/* stop RPU -> APU timer */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
|
||||
ch->ipi_mask);
|
||||
atomic_flag_clear(&ch->remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief measure_ipi_latencyd() - measure IPI latency with libmetal
|
||||
* Loop until APU tells RPU to stop via shared memory.
|
||||
* In loop, wait for interrupt (interrupt handler stops APU to
|
||||
* RPU TTC counter). Then reset count on RPU to APU TTC counter
|
||||
* and kick IPI to notify APU.
|
||||
*
|
||||
* @param[in] ch - channel information
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
static int measure_ipi_latencyd(struct channel_s *ch)
|
||||
{
|
||||
LPRINTF("Starting IPI latency demo\r\n");
|
||||
while(1) {
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
if (metal_io_read32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET) ==
|
||||
DEMO_STATUS_START) {
|
||||
/* Reset RPU to APU TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
/* Kick IPI to notify the remote */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
|
||||
ch->ipi_mask);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipi_latency_demod()
|
||||
{
|
||||
struct channel_s ch;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("IPI latency");
|
||||
memset(&ch, 0, sizeof(ch));
|
||||
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!ch.shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get TTC IO region */
|
||||
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
|
||||
if (!ch.ttc_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ch.ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
|
||||
ch.ipi_mask = IPI_MASK;
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&ch.remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = measure_ipi_latencyd(&ch);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* ipi_shmem_demo.c - shared memory with IPI demo
|
||||
* This demo will:
|
||||
*
|
||||
* 1. Get the shared memory device I/O region.
|
||||
* 2. Get the IPI device I/O region.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 4. Wait for remote IPI notification to receive message.
|
||||
* 5. When message is received, check if it is shutdown message.
|
||||
* 6. If it is shutdown message, do cleanup, otherwise, echo it back to the
|
||||
* shared buffer.
|
||||
* 7. Kick IPI to notify there is a message written to the shared memory
|
||||
* if it echos back the message.
|
||||
* 8. Repeat 4.
|
||||
* 9. Clean up: disable IPI interrupt, deregister the IPI interrupt handler.
|
||||
*
|
||||
* Here is the Shared memory structure of this demo:
|
||||
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
|
||||
* |0x04 - 0x07 | number of APU to RPU buffers consumed by RPU |
|
||||
* |0x08 - 0x1FFC | address array for shared buffers from APU to RPU |
|
||||
* |0x2000 - 0x2003 | number of RPU to APU buffers available to APU |
|
||||
* |0x2004 - 0x2007 | number of RPU to APU buffers consumed by APU |
|
||||
* |0x2008 - 0x3FFC | address array for shared buffers from RPU to APU |
|
||||
* |0x04000 - 0x103FFC | APU to RPU buffers |
|
||||
* |0x104000 - 0x203FFC | RPU to APU buffers |
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include <metal/errno.h>
|
||||
#include "common.h"
|
||||
|
||||
#define BUF_SIZE_MAX 512
|
||||
#define SHUTDOWN "shutdown"
|
||||
|
||||
/* Shared memory offsets */
|
||||
#define SHM_DESC_OFFSET_RX 0x0
|
||||
#define SHM_BUFF_OFFSET_RX 0x04000
|
||||
#define SHM_DESC_OFFSET_TX 0x02000
|
||||
#define SHM_BUFF_OFFSET_TX 0x104000
|
||||
|
||||
/* Shared memory descriptors offset */
|
||||
#define SHM_DESC_AVAIL_OFFSET 0x00
|
||||
#define SHM_DESC_USED_OFFSET 0x04
|
||||
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x08
|
||||
|
||||
#define PKGS_TOTAL 1024
|
||||
|
||||
#define BUF_SIZE_MAX 512
|
||||
#define SHUTDOWN "shutdown"
|
||||
|
||||
struct msg_hdr_s {
|
||||
uint32_t index;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
|
||||
1 - not-kicked */
|
||||
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
(void)vect_id;
|
||||
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
uint64_t val = 1;
|
||||
|
||||
if (!ipi_io)
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ipi_mask) {
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
|
||||
atomic_flag_clear(&remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_shmem_echod() - shared memory IPI demo
|
||||
* This task will:
|
||||
* * Wait for IPI interrupt from the remote
|
||||
* * Once it received the interrupt, copy the content from
|
||||
* the ping buffer to the pong buffer.
|
||||
* * Update the shared memory descriptor for the new available
|
||||
* pong buffer.
|
||||
* * Trigger IPI to notifty the remote.
|
||||
* @param[in] ipi_io - IPI metal i/o region
|
||||
* @param[in] shm_io - shared memory metal i/o region
|
||||
* @return - return 0 on success, otherwise return error number indicating
|
||||
* type of error.
|
||||
*/
|
||||
static int ipi_shmem_echod(struct metal_io_region *ipi_io,
|
||||
struct metal_io_region *shm_io)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t rx_count, rx_avail;
|
||||
unsigned long tx_avail_offset, rx_avail_offset;
|
||||
unsigned long rx_used_offset;
|
||||
unsigned long tx_addr_offset, rx_addr_offset;
|
||||
unsigned long tx_data_offset, rx_data_offset;
|
||||
void *lbuf = NULL;
|
||||
struct msg_hdr_s *msg_hdr;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
|
||||
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
|
||||
if (!lbuf) {
|
||||
LPERROR("Failed to allocate local buffer for msg.\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
/* Clear shared memory */
|
||||
metal_io_block_set(shm_io, 0, 0, metal_io_region_size(shm_io));
|
||||
|
||||
/* Set tx/rx buffer address offset */
|
||||
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
|
||||
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
|
||||
rx_used_offset = SHM_DESC_OFFSET_RX + SHM_DESC_USED_OFFSET;
|
||||
tx_addr_offset = SHM_DESC_OFFSET_TX + SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
rx_addr_offset = SHM_DESC_OFFSET_RX + SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
|
||||
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
|
||||
|
||||
LPRINTF("Wait for echo test to start.\n");
|
||||
rx_count = 0;
|
||||
while (1) {
|
||||
wait_for_notified(&remote_nkicked);
|
||||
rx_avail = metal_io_read32(shm_io, rx_avail_offset);
|
||||
while(rx_count != rx_avail) {
|
||||
uint32_t buf_phy_addr_32;
|
||||
|
||||
/* Received ping from the other side */
|
||||
|
||||
/* Get the buffer location from the shared memory
|
||||
* rx address array.
|
||||
*/
|
||||
buf_phy_addr_32 = metal_io_read32(shm_io,
|
||||
rx_addr_offset);
|
||||
rx_data_offset = metal_io_phys_to_offset(shm_io,
|
||||
(metal_phys_addr_t)buf_phy_addr_32);
|
||||
if (rx_data_offset == METAL_BAD_OFFSET) {
|
||||
LPERROR("[%u]failed to get rx offset: 0x%x, 0x%lx.\n",
|
||||
rx_count, buf_phy_addr_32,
|
||||
metal_io_phys(shm_io, rx_addr_offset));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rx_addr_offset += sizeof(buf_phy_addr_32);
|
||||
|
||||
/* Read message header from shared memory */
|
||||
metal_io_block_read(shm_io, rx_data_offset, lbuf,
|
||||
sizeof(struct msg_hdr_s));
|
||||
msg_hdr = (struct msg_hdr_s *)lbuf;
|
||||
|
||||
/* Check if the message header is valid */
|
||||
if (msg_hdr->len > (BUF_SIZE_MAX - sizeof(*msg_hdr))) {
|
||||
LPERROR("wrong msg: length invalid: %u, %u.\n",
|
||||
BUF_SIZE_MAX - sizeof(*msg_hdr),
|
||||
msg_hdr->len);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rx_data_offset += sizeof(*msg_hdr);
|
||||
/* Read message */
|
||||
metal_io_block_read(shm_io,
|
||||
rx_data_offset,
|
||||
lbuf + sizeof(*msg_hdr), msg_hdr->len);
|
||||
rx_data_offset += msg_hdr->len;
|
||||
rx_count++;
|
||||
/* increase rx used count to indicate it has consumed
|
||||
* the received data */
|
||||
metal_io_write32(shm_io, rx_used_offset, rx_count);
|
||||
|
||||
/* Check if the it is the shutdown message */
|
||||
if (msg_hdr->len == strlen(SHUTDOWN) &&
|
||||
!strncmp(SHUTDOWN,
|
||||
(lbuf + sizeof(struct msg_hdr_s)),
|
||||
strlen(SHUTDOWN))) {
|
||||
LPRINTF("Received shutdown message\n");
|
||||
goto out;
|
||||
}
|
||||
/* Copy the message back to the other end */
|
||||
metal_io_block_write(shm_io, tx_data_offset, msg_hdr,
|
||||
sizeof(struct msg_hdr_s) + msg_hdr->len);
|
||||
|
||||
/* Write to the address array to tell the other end
|
||||
* the buffer address.
|
||||
*/
|
||||
buf_phy_addr_32 = (uint32_t)metal_io_phys(shm_io,
|
||||
tx_data_offset);
|
||||
metal_io_write32(shm_io, tx_addr_offset,
|
||||
buf_phy_addr_32);
|
||||
tx_data_offset += sizeof(struct msg_hdr_s) +
|
||||
msg_hdr->len;
|
||||
tx_addr_offset += sizeof(uint32_t);
|
||||
|
||||
/* Increase number of available buffers */
|
||||
metal_io_write32(shm_io, tx_avail_offset, rx_count);
|
||||
/* Kick IPI to notify data is in shared buffer */
|
||||
metal_io_write32(ipi_io, IPI_TRIG_OFFSET,
|
||||
ipi_mask);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
LPRINTF("IPI with shared memory demo finished with exit code: %i.\n",
|
||||
ret);
|
||||
|
||||
if (lbuf)
|
||||
metal_free_memory(lbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipi_shmem_demod()
|
||||
{
|
||||
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("IPI and shared memory");
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
if (!ipi_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = ipi_shmem_echod(ipi_io, shm_io);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/***************************************************************************
|
||||
* libmetal_amp_demo.c
|
||||
*
|
||||
* This application shows how to use IPI to trigger interrupt and how to
|
||||
* setup shared memory with libmetal API for communication between processors.
|
||||
*
|
||||
* This application does the following:
|
||||
* 1. Initialize the platform hardware such as UART, GIC.
|
||||
* 2. Connect the IPI interrupt.
|
||||
* 3. Register IPI device, shared memory descriptor device and shared memory
|
||||
* device with libmetal in the initialization.
|
||||
* 4. In the main application it does the following,
|
||||
* * open the registered libmetal devices: IPI device, shared memory
|
||||
* descriptor device and shared memory device.
|
||||
* * Map the shared memory descriptor as non-cached memory.
|
||||
* * Map the shared memory as non-cached memory. If you do not map the
|
||||
* shared memory as non-cached memory, make sure you flush the cache,
|
||||
* before you notify the remote.
|
||||
* 7. Register the IPI interrupt handler with libmetal.
|
||||
* 8. Run the atomic demo task ipi_task_shm_atomicd():
|
||||
* * Wait for the IPI interrupt from the remote.
|
||||
* * Once it receives the interrupt, it does atomic add by 1 to the
|
||||
* first 32bit of the shared memory descriptor location by 1000 times.
|
||||
* * It will then notify the remote after the calculation.
|
||||
* * As the remote side also does 1000 times add after it has notified
|
||||
* this end. The remote side will check if the result is 2000, if not,
|
||||
* it will error.
|
||||
* 9. Run the shared memory echo demo task ipi_task_echod()
|
||||
* * Wait for the IPI interrupt from the other end.
|
||||
* * If an IPI interrupt is received, copy the message to the current
|
||||
* available RPU to APU buffer, increase the available buffer indicator,
|
||||
* and trigger IPI to notify the remote.
|
||||
* * If "shutdown" message is received, cleanup the libmetal source.
|
||||
*/
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include "common.h"
|
||||
|
||||
static TaskHandle_t comm_task;
|
||||
|
||||
/**
|
||||
* @brief demo application main processing task
|
||||
* Here are the steps for the main function:
|
||||
* * Setup libmetal resources
|
||||
* * Run the IPI with shared memory demo.
|
||||
* * Run the shared memory demo.
|
||||
* * Run the atomic across shared memory demo.
|
||||
* * Run the ipi latency demo.
|
||||
* * Run the shared memory latency demo.
|
||||
* * Run the shared memory throughput demo.
|
||||
* * Cleanup libmetal resources before self killing task.
|
||||
* Report if any of the above demos failed.
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
static void processing(void *unused_arg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
(void)unused_arg;
|
||||
|
||||
ret = sys_init();
|
||||
|
||||
if (ret) {
|
||||
LPERROR("Failed to initialize system.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = shmem_demod();
|
||||
if (ret){
|
||||
LPERROR("shared memory demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = atomic_shmem_demod();
|
||||
if (ret){
|
||||
LPERROR("shared memory atomic demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ipi_shmem_demod();
|
||||
if (ret){
|
||||
LPERROR("shared memory atomic demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ipi_latency_demod();
|
||||
if (ret){
|
||||
LPERROR("IPI latency demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = shmem_latency_demod();
|
||||
if (ret){
|
||||
LPERROR("shared memory latency demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = shmem_throughput_demod();
|
||||
if (ret){
|
||||
LPERROR("shared memory thoughput demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
sys_cleanup();
|
||||
|
||||
out:
|
||||
/* Terminate this task */
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief main function of the demo application.
|
||||
* It starts the processing task and go wait forever.
|
||||
* @return 0 - succeeded, but in reality will never return.
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
BaseType_t stat;
|
||||
|
||||
Xil_ExceptionDisable();
|
||||
|
||||
/* Create the tasks */
|
||||
stat = xTaskCreate(processing, ( const char * ) "HW",
|
||||
1024, NULL, 2, &comm_task);
|
||||
if (stat != pdPASS) {
|
||||
LPERROR("Cannot create task\n");
|
||||
} else {
|
||||
/* Start running FreeRTOS tasks */
|
||||
vTaskStartScheduler();
|
||||
}
|
||||
|
||||
/* Will normally not get here */
|
||||
while (1) {
|
||||
wait_for_interrupt();
|
||||
}
|
||||
|
||||
/* suppress compilation warnings*/
|
||||
return 0;
|
||||
}
|
@@ -0,0 +1,317 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2017 Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Xilinx nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x1000;
|
||||
_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x6000;
|
||||
|
||||
_ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024;
|
||||
_SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048;
|
||||
_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024;
|
||||
_FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024;
|
||||
_UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024;
|
||||
|
||||
/* Define Memories in the system */
|
||||
|
||||
MEMORY
|
||||
{
|
||||
psu_r5_atcm_MEM_0 : ORIGIN = 0x0, LENGTH = 0x10000
|
||||
psu_r5_btcm_MEM_0 : ORIGIN = 0x20000, LENGTH = 0x10000
|
||||
psu_r5_ddr_0_MEM_0 : ORIGIN = 0x3ed00000, LENGTH = 0x80000
|
||||
}
|
||||
|
||||
/* Specify the default entry point to the program */
|
||||
|
||||
ENTRY(_boot)
|
||||
|
||||
/* Define the sections, and where they are mapped in memory */
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.vectors : {
|
||||
KEEP (*(.vectors))
|
||||
*(.boot)
|
||||
} > psu_r5_atcm_MEM_0
|
||||
|
||||
.text : {
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.gnu.linkonce.t.*)
|
||||
*(.plt)
|
||||
*(.gnu_warning)
|
||||
*(.gcc_execpt_table)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
*(.vfp11_veneer)
|
||||
*(.ARM.extab)
|
||||
*(.gnu.linkonce.armextab.*)
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.init : {
|
||||
KEEP (*(.init))
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.fini : {
|
||||
KEEP (*(.fini))
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.interp : {
|
||||
KEEP (*(.interp))
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.note-ABI-tag : {
|
||||
KEEP (*(.note-ABI-tag))
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.rodata : {
|
||||
__rodata_start = .;
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.gnu.linkonce.r.*)
|
||||
__rodata_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.rodata1 : {
|
||||
__rodata1_start = .;
|
||||
*(.rodata1)
|
||||
*(.rodata1.*)
|
||||
__rodata1_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.sdata2 : {
|
||||
__sdata2_start = .;
|
||||
*(.sdata2)
|
||||
*(.sdata2.*)
|
||||
*(.gnu.linkonce.s2.*)
|
||||
__sdata2_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.sbss2 : {
|
||||
__sbss2_start = .;
|
||||
*(.sbss2)
|
||||
*(.sbss2.*)
|
||||
*(.gnu.linkonce.sb2.*)
|
||||
__sbss2_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.data : {
|
||||
__data_start = .;
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.gnu.linkonce.d.*)
|
||||
*(.jcr)
|
||||
*(.got)
|
||||
*(.got.plt)
|
||||
__data_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.data1 : {
|
||||
__data1_start = .;
|
||||
*(.data1)
|
||||
*(.data1.*)
|
||||
__data1_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.got : {
|
||||
*(.got)
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.ctors : {
|
||||
__CTOR_LIST__ = .;
|
||||
___CTORS_LIST___ = .;
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
__CTOR_END__ = .;
|
||||
___CTORS_END___ = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.dtors : {
|
||||
__DTOR_LIST__ = .;
|
||||
___DTORS_LIST___ = .;
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
__DTOR_END__ = .;
|
||||
___DTORS_END___ = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.fixup : {
|
||||
__fixup_start = .;
|
||||
*(.fixup)
|
||||
__fixup_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.eh_frame : {
|
||||
*(.eh_frame)
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.eh_framehdr : {
|
||||
__eh_framehdr_start = .;
|
||||
*(.eh_framehdr)
|
||||
__eh_framehdr_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.gcc_except_table : {
|
||||
*(.gcc_except_table)
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.mmu_tbl (ALIGN(16384)) : {
|
||||
__mmu_tbl_start = .;
|
||||
*(.mmu_tbl)
|
||||
__mmu_tbl_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.ARM.exidx : {
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
*(.gnu.linkonce.armexidix.*.*)
|
||||
__exidx_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.preinit_array : {
|
||||
__preinit_array_start = .;
|
||||
KEEP (*(SORT(.preinit_array.*)))
|
||||
KEEP (*(.preinit_array))
|
||||
__preinit_array_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.init_array : {
|
||||
__init_array_start = .;
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array))
|
||||
__init_array_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.fini_array : {
|
||||
__fini_array_start = .;
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array))
|
||||
__fini_array_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.ARM.attributes : {
|
||||
__ARM.attributes_start = .;
|
||||
*(.ARM.attributes)
|
||||
__ARM.attributes_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.sdata : {
|
||||
__sdata_start = .;
|
||||
*(.sdata)
|
||||
*(.sdata.*)
|
||||
*(.gnu.linkonce.s.*)
|
||||
__sdata_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.sbss (NOLOAD) : {
|
||||
__sbss_start = .;
|
||||
*(.sbss)
|
||||
*(.sbss.*)
|
||||
*(.gnu.linkonce.sb.*)
|
||||
__sbss_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.tdata : {
|
||||
__tdata_start = .;
|
||||
*(.tdata)
|
||||
*(.tdata.*)
|
||||
*(.gnu.linkonce.td.*)
|
||||
__tdata_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.tbss : {
|
||||
__tbss_start = .;
|
||||
*(.tbss)
|
||||
*(.tbss.*)
|
||||
*(.gnu.linkonce.tb.*)
|
||||
__tbss_end = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
.bss (NOLOAD) : {
|
||||
. = ALIGN(4);
|
||||
__bss_start__ = .;
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
__bss_end__ = .;
|
||||
} > psu_r5_ddr_0_MEM_0
|
||||
|
||||
_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 );
|
||||
|
||||
_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 );
|
||||
|
||||
/* Generate Stack and Heap definitions */
|
||||
|
||||
.heap (NOLOAD) : {
|
||||
. = ALIGN(16);
|
||||
_heap = .;
|
||||
HeapBase = .;
|
||||
_heap_start = .;
|
||||
. += _HEAP_SIZE;
|
||||
_heap_end = .;
|
||||
HeapLimit = .;
|
||||
} > psu_r5_atcm_MEM_0
|
||||
|
||||
.stack (NOLOAD) : {
|
||||
. = ALIGN(16);
|
||||
_stack_end = .;
|
||||
. += _STACK_SIZE;
|
||||
_stack = .;
|
||||
__stack = _stack;
|
||||
. = ALIGN(16);
|
||||
_irq_stack_end = .;
|
||||
. += _IRQ_STACK_SIZE;
|
||||
__irq_stack = .;
|
||||
_supervisor_stack_end = .;
|
||||
. += _SUPERVISOR_STACK_SIZE;
|
||||
. = ALIGN(16);
|
||||
__supervisor_stack = .;
|
||||
_abort_stack_end = .;
|
||||
. += _ABORT_STACK_SIZE;
|
||||
. = ALIGN(16);
|
||||
__abort_stack = .;
|
||||
_fiq_stack_end = .;
|
||||
. += _FIQ_STACK_SIZE;
|
||||
. = ALIGN(16);
|
||||
__fiq_stack = .;
|
||||
_undef_stack_end = .;
|
||||
. += _UNDEF_STACK_SIZE;
|
||||
. = ALIGN(16);
|
||||
__undef_stack = .;
|
||||
} > psu_r5_atcm_MEM_0
|
||||
|
||||
_end = .;
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
#ifndef __PLATFORM_CONFIG_H_
|
||||
#define __PLATFORM_CONFIG_H_
|
||||
|
||||
#define STDOUT_IS_PSU_UART
|
||||
#define UART_DEVICE_ID 0
|
||||
#endif
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
/*****************************************************************************
|
||||
* atomic_shmem_demod.c - Shared memory atomic operation demo
|
||||
* This task will:
|
||||
* 1. Get the shared memory device I/O region.
|
||||
* 2. Get the IPI device I/O region.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 4. Wait for the APU to kick IPI to start the demo
|
||||
* 5. Once notification is received, start atomic add by
|
||||
* 1 for 5000 times over the shared memory
|
||||
* 6. Trigger IPI to notify the remote it has finished calculation.
|
||||
* 7. Clean up: Disable IPI interrupt, deregister the IPI interrupt handler.
|
||||
*/
|
||||
#include <metal/shmem.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/io.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdio.h>
|
||||
#include "common.h"
|
||||
#include "sys_init.h"
|
||||
|
||||
#define ATOMIC_INT_OFFSET 0x0 /* shared memory offset for atomic operation */
|
||||
#define ITERATIONS 5000
|
||||
|
||||
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
|
||||
1 - not-kicked */
|
||||
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
(void)vect_id;
|
||||
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
uint64_t val = 1;
|
||||
|
||||
if (!ipi_io)
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ipi_mask) {
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
|
||||
atomic_flag_clear(&remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief atomic_add_shmemd() - Shared memory atomic operation demo
|
||||
* This task will:
|
||||
* * Wait for the remote to write to shared memory.
|
||||
* * Once it receives the notification via polling, start atomic add by
|
||||
* 1 for 5000 times to first 32 bits of memory in the shared memory
|
||||
* which is pointed to by shm_io.
|
||||
* * Write to shared mem to notify the remote once it finishes
|
||||
* calculation.
|
||||
*
|
||||
* @param[in] ipi_io - IPI metal i/o region
|
||||
* @param[in] shm_io - shared memory metal i/o region
|
||||
* @return - If setup failed, return the corresponding error number. Otherwise
|
||||
* return 0 on success.
|
||||
*/
|
||||
int atomic_add_shmemd(struct metal_io_region *ipi_io,
|
||||
struct metal_io_region *shm_io)
|
||||
{
|
||||
atomic_int *shm_int;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
int i;
|
||||
|
||||
LPRINTF("Starting atomic add on shared memory demo.\n");
|
||||
shm_int = (atomic_int *)metal_io_virt(shm_io,
|
||||
ATOMIC_INT_OFFSET);
|
||||
|
||||
/* Wait for notification from the remote to start the demo */
|
||||
wait_for_notified(&remote_nkicked);
|
||||
|
||||
/* Do atomic add over the shared memory */
|
||||
for (i = 0; i < ITERATIONS; i++)
|
||||
atomic_fetch_add(shm_int, 1);
|
||||
|
||||
/* Write to IPI trigger register to notify the remote it has finished
|
||||
* the atomic operation. */
|
||||
metal_io_write32(ipi_io, IPI_TRIG_OFFSET, ipi_mask);
|
||||
|
||||
LPRINTF("Shared memory with atomics test finished\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atomic_shmem_demod()
|
||||
{
|
||||
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("atomic operation over shared memory");
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
if (!ipi_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = atomic_add_shmemd(ipi_io, shm_io);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
/*****************************************************************************
|
||||
* shmem_demod.c
|
||||
* This demo demonstrates the use of shared mem. between the APU and RPU.
|
||||
* This demo does so via the following steps:
|
||||
*
|
||||
* 1. Get the shared memory device I/O region.
|
||||
* 2. Clear the demo control value in shared memory.
|
||||
* 3. Check the demo control value in the shared memory to wait for APU
|
||||
* to start the demo.
|
||||
* 4. Once the demo control value indicates the demo starts, it polls on
|
||||
* RX available value to see if there is new RX message available.
|
||||
* 5. If there is a new RX message available, it reads the message from
|
||||
* the shared memory
|
||||
* 6. It echos back the message to the shared memory
|
||||
* 7. It increases the TX available value in the shared memory to notify
|
||||
* the other end there is a message available to read.
|
||||
* 8. Check if the demo control value and the RX available values to see
|
||||
* if demo finishes and if there is new RX data available.
|
||||
*
|
||||
* Here is the Shared memory structure of this demo:
|
||||
* |0 | 4Bytes | DEMO control status shows if demo starts or not |
|
||||
* |0x04 | 4Bytes | number of APU to RPU buffers available to RPU |
|
||||
* |0x08 | 4Bytes | number of APU to RPU buffers consumed by RPU |
|
||||
* |0x0c | 4Bytes | number of RPU to APU buffers available to APU |
|
||||
* |0x10 | 4Bytes | number of RPU to APU buffers consumed by APU |
|
||||
* |0x14 | 1KBytes | APU to RPU buffer |
|
||||
* ... ...
|
||||
* |0x800 | 1KBytes | RPU to APU buffer |
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/alloc.h>
|
||||
#include "common.h"
|
||||
|
||||
/* Shared memory offsets */
|
||||
#define SHM_DEMO_CNTRL_OFFSET 0x0
|
||||
#define SHM_RX_AVAIL_OFFSET 0x04
|
||||
#define SHM_RX_USED_OFFSET 0x08
|
||||
#define SHM_TX_AVAIL_OFFSET 0x0C
|
||||
#define SHM_TX_USED_OFFSET 0x10
|
||||
#define SHM_RX_BUFFER_OFFSET 0x14
|
||||
#define SHM_TX_BUFFER_OFFSET 0x800
|
||||
|
||||
#define SHM_BUFFER_SIZE 0x400
|
||||
|
||||
#define DEMO_STATUS_IDLE 0x0
|
||||
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
|
||||
|
||||
struct msg_hdr_s {
|
||||
uint32_t index;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief shmem_echod() - Show use of shared memory with libmetal.
|
||||
* Wait for message from APU. Once received, read and echo it back.
|
||||
*
|
||||
* @param[in] shm_io - metal i/o region of the shared memory
|
||||
* @return - return 0 on success, otherwise return error number indicating
|
||||
* type of error
|
||||
*/
|
||||
static int shmem_echod(struct metal_io_region *shm_io)
|
||||
{
|
||||
void *data = NULL;
|
||||
struct msg_hdr_s *msg_hdr;
|
||||
unsigned int rx_count = 0;
|
||||
unsigned int len;
|
||||
int ret = 0;
|
||||
|
||||
/* clear demo status value */
|
||||
metal_io_write32(shm_io, SHM_DEMO_CNTRL_OFFSET, 0);
|
||||
|
||||
/* allocate memory for receiving data */
|
||||
data = metal_allocate_memory(SHM_BUFFER_SIZE);
|
||||
if (!data) {
|
||||
LPERROR("Failed to allocate memory.\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LPRINTF("Wait for shared memory demo to start.\r\n");
|
||||
while (metal_io_read32(shm_io, SHM_DEMO_CNTRL_OFFSET) !=
|
||||
DEMO_STATUS_START);
|
||||
|
||||
LPRINTF("Demo has started.\r\n");
|
||||
/* wait for message is available */
|
||||
while(metal_io_read32(shm_io, SHM_DEMO_CNTRL_OFFSET) ==
|
||||
DEMO_STATUS_START) {
|
||||
if (metal_io_read32(shm_io, SHM_RX_AVAIL_OFFSET)
|
||||
== rx_count)
|
||||
continue;
|
||||
/* Message is available, read the message header */
|
||||
ret = metal_io_block_read(shm_io, SHM_RX_BUFFER_OFFSET,
|
||||
data, sizeof(struct msg_hdr_s));
|
||||
if (ret < 0){
|
||||
LPERROR("Unable to metal_io_block_read()\n");
|
||||
return ret;
|
||||
}
|
||||
msg_hdr = (struct msg_hdr_s *)data;
|
||||
/* Get the length of the data, if the data length is
|
||||
* too large, truncate it. */
|
||||
len = msg_hdr->len;
|
||||
if (msg_hdr->len >
|
||||
(SHM_BUFFER_SIZE - sizeof(*msg_hdr))) {
|
||||
LPERROR("Input message is too long %u.\n",
|
||||
msg_hdr->len);
|
||||
len = SHM_BUFFER_SIZE - sizeof(*msg_hdr);
|
||||
}
|
||||
/* Read the message data */
|
||||
ret = metal_io_block_read(shm_io,
|
||||
SHM_RX_BUFFER_OFFSET + sizeof(*msg_hdr),
|
||||
data + sizeof(*msg_hdr), len);
|
||||
|
||||
rx_count++;
|
||||
ret = metal_io_block_write(shm_io,
|
||||
SHM_TX_BUFFER_OFFSET,
|
||||
(void*)data, sizeof(*msg_hdr) + len);
|
||||
if (ret < 0){
|
||||
LPERROR("Unable to metal_io_block_write()\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* increase TX available value to notify the other end
|
||||
* there is data ready to read. */
|
||||
metal_io_write32(shm_io, SHM_TX_AVAIL_OFFSET, rx_count);
|
||||
}
|
||||
|
||||
metal_free_memory(data);
|
||||
LPRINTF("Shared memory test finished\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int shmem_demod()
|
||||
{
|
||||
struct metal_io_region *io = NULL;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("shared memory");
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
io = metal_device_io_region(shm_dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = shmem_echod(io);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* shmem_latency_demod.c
|
||||
* This is the remote side of the IPI latency measurement demo.
|
||||
* This demo does the follwing steps:
|
||||
*
|
||||
* 1. Get the shared memory device libmetal I/O region.
|
||||
* 1. Get the TTC timer device libemtal I/O region.
|
||||
* 2. Get IPI device libmetal I/O region and the IPI interrupt vector.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 6. When it receives IPI interrupt, the IPI interrupt handler marked the
|
||||
* remote has kicked.
|
||||
* 7. Check the shared memory to see if demo is on. If the demo is on,
|
||||
* copy data from the shared memory to local memory, stop the APU to RPU
|
||||
* timer. Reset the RPU to APU TTC counter, copy data from local memory
|
||||
* to shared memory, kick IPI to notify the remote.
|
||||
* 8. If the shared memory indicates the demo is off, cleanup resource:
|
||||
* disable IPI interrupt and deregister the IPI interrupt handler.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include "common.h"
|
||||
|
||||
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
|
||||
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
|
||||
|
||||
#define TTC_CLK_FREQ_HZ 100000000
|
||||
|
||||
/* Shared memory offset */
|
||||
#define SHM_DEMO_CNTRL_OFFSET 0x0 /* Shared memory for the demo status */
|
||||
#define SHM_BUFF_OFFSET_RX 0x1000 /* Shared memory RX buffer start offset */
|
||||
#define SHM_BUFF_OFFSET_TX 0x2000 /* Shared memory TX buffer start offset */
|
||||
|
||||
#define DEMO_STATUS_IDLE 0x0
|
||||
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
|
||||
|
||||
#define BUF_SIZE_MAX 4096
|
||||
|
||||
struct channel_s {
|
||||
struct metal_io_region *ipi_io; /* IPI metal i/o region */
|
||||
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
|
||||
struct metal_io_region *ttc_io; /* TTC metal i/o region */
|
||||
uint32_t ipi_mask; /* RPU IPI mask */
|
||||
atomic_int remote_nkicked; /* 0 - kicked from remote */
|
||||
};
|
||||
|
||||
struct msg_hdr_s {
|
||||
uint32_t index;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief reset_timer() - function to reset TTC counter
|
||||
* Set the RST bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void reset_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_RST_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop_timer() - function to stop TTC counter
|
||||
* Set the disable bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void stop_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_DIS_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_irq_handler() - IPI interrupt handler
|
||||
* It will clear the notified flag to mark it's got an IPI interrupt.
|
||||
* It will stop the RPU->APU timer and will clear the notified
|
||||
* flag to mark it's got an IPI interrupt
|
||||
*
|
||||
* @param[in] vect_id - IPI interrupt vector ID
|
||||
* @param[in/out] priv - communication channel data for this application.
|
||||
*
|
||||
* @return - If the IPI interrupt is triggered by its remote, it returns
|
||||
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
|
||||
* not the interrupt it expected.
|
||||
*
|
||||
*/
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)priv;
|
||||
uint32_t val;
|
||||
|
||||
(void)vect_id;
|
||||
|
||||
if (ch) {
|
||||
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ch->ipi_mask) {
|
||||
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
|
||||
ch->ipi_mask);
|
||||
atomic_flag_clear(&ch->remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief measure_shmem_latencyd() - measure shmem latency with libmetal
|
||||
* Loop until APU tells RPU to stop via shared memory.
|
||||
* In loop, wait for interrupt (interrupt handler stops APU to
|
||||
* RPU TTC counter). Then reset count on RPU to APU TTC counter
|
||||
* and kick IPI to notify APU.
|
||||
*
|
||||
* @param[in] ch - channel information
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
static int measure_shmem_latencyd(struct channel_s *ch)
|
||||
{
|
||||
void *lbuf = NULL;
|
||||
struct msg_hdr_s *msg_hdr;
|
||||
int ret = 0;
|
||||
|
||||
/* allocate memory for receiving data */
|
||||
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
|
||||
if (!lbuf) {
|
||||
LPERROR("Failed to allocate memory.\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LPRINTF("Starting IPI latency demo\r\n");
|
||||
while(1) {
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
if (metal_io_read32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET) ==
|
||||
DEMO_STATUS_START) {
|
||||
/* Read message header from shared memory */
|
||||
metal_io_block_read(ch->shm_io, SHM_BUFF_OFFSET_RX,
|
||||
lbuf, sizeof(struct msg_hdr_s));
|
||||
msg_hdr = (struct msg_hdr_s *)lbuf;
|
||||
|
||||
/* Check if the message header is valid */
|
||||
if (msg_hdr->len > (BUF_SIZE_MAX - sizeof(*msg_hdr))) {
|
||||
LPERROR("wrong msg: length invalid: %u, %u.\n",
|
||||
BUF_SIZE_MAX - sizeof(*msg_hdr),
|
||||
msg_hdr->len);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/* Read message */
|
||||
metal_io_block_read(ch->shm_io,
|
||||
SHM_BUFF_OFFSET_RX + sizeof(*msg_hdr),
|
||||
lbuf + sizeof(*msg_hdr), msg_hdr->len);
|
||||
/* Stop APU to RPU TTC counter */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
|
||||
/* Reset RPU to APU TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
/* Copy the message back to the other end */
|
||||
metal_io_block_write(ch->shm_io, SHM_BUFF_OFFSET_TX,
|
||||
msg_hdr,
|
||||
sizeof(*msg_hdr) + msg_hdr->len);
|
||||
|
||||
/* Kick IPI to notify the remote */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
|
||||
ch->ipi_mask);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
metal_free_memory(lbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int shmem_latency_demod()
|
||||
{
|
||||
struct channel_s ch;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("shared memory latency");
|
||||
memset(&ch, 0, sizeof(ch));
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!ch.shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get TTC IO region */
|
||||
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
|
||||
if (!ch.ttc_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ch.ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
|
||||
ch.ipi_mask = IPI_MASK;
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&ch.remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = measure_shmem_latencyd(&ch);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* shmem_throughput_demo_task.c
|
||||
* This is the remote side of the shared memory throughput demo.
|
||||
* This demo does the following steps:
|
||||
*
|
||||
* 1. Get the shared memory device libmetal I/O region.
|
||||
* 1. Get the TTC timer device libemtal I/O region.
|
||||
* 2. Get IPI device libmetal I/O region and the IPI interrupt vector.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 6. Download throughput measurement:
|
||||
* Start TTC RPU counter, wait for IPI kick, check if data is available,
|
||||
* if yes, read as much data as possible from shared memory. It will
|
||||
* iterates untill 1000 packages have been received, stop TTC RPU counter
|
||||
* and kick IPI to notify the remote. Repeat for different package size.
|
||||
* 7. Upload throughput measurement:
|
||||
* Start TTC RPU counter, write data to shared memory and kick IPI to
|
||||
* notify remote. It will iterate for 1000 times, stop TTC RPU counter.
|
||||
* wait for APU IPI kick to know APU has finished receiving packages.
|
||||
* Kick IPI to notify it TTC RPU conter value is ready to read.
|
||||
* Repeat for different package size.
|
||||
* 8. Cleanup resource:
|
||||
* disable IPI interrupt and deregister the IPI interrupt handler.
|
||||
*
|
||||
* Here is the Shared memory structure of this demo:
|
||||
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
|
||||
* |0x04 - 0x1FFFFF | address array for shared buffers from APU to RPU |
|
||||
* |0x200000 - 0x200004 | number of RPU to APU buffers available to APU |
|
||||
* |0x200004 - 0x3FFFFF | address array for shared buffers from RPU to APU |
|
||||
* |0x400000 - 0x7FFFFF | APU to RPU buffers |
|
||||
* |0x800000 - 0xAFFFFF | RPU to APU buffers |
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include <metal/alloc.h>
|
||||
#include "common.h"
|
||||
|
||||
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
|
||||
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
|
||||
|
||||
/* Shared memory offsets */
|
||||
#define SHM_DESC_OFFSET_RX 0x0
|
||||
#define SHM_BUFF_OFFSET_RX 0x400000
|
||||
#define SHM_DESC_OFFSET_TX 0x200000
|
||||
#define SHM_BUFF_OFFSET_TX 0x800000
|
||||
|
||||
/* Shared memory descriptors offset */
|
||||
#define SHM_DESC_AVAIL_OFFSET 0x00
|
||||
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x04
|
||||
|
||||
#define BUF_SIZE_MAX 4096
|
||||
#define PKG_SIZE_MAX 1024
|
||||
#define PKG_SIZE_MIN 16
|
||||
#define TOTAL_DATA_SIZE (1024 * 4096)
|
||||
|
||||
struct channel_s {
|
||||
struct metal_io_region *ipi_io; /* IPI metal i/o region */
|
||||
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
|
||||
struct metal_io_region *ttc_io; /* TTC metal i/o region */
|
||||
uint32_t ipi_mask; /* RPU IPI mask */
|
||||
atomic_int remote_nkicked; /* 0 - kicked from remote */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief reset_timer() - function to reset TTC counter
|
||||
* Set the RST bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void reset_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_RST_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop_timer() - function to stop TTC counter
|
||||
* Set the disable bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void stop_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_DIS_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_irq_handler() - IPI interrupt handler
|
||||
* It will clear the notified flag to mark it's got an IPI interrupt.
|
||||
* It will stop the RPU->APU timer and will clear the notified
|
||||
* flag to mark it's got an IPI interrupt
|
||||
*
|
||||
* @param[in] vect_id - IPI interrupt vector ID
|
||||
* @param[in/out] priv - communication channel data for this application.
|
||||
*
|
||||
* @return - If the IPI interrupt is triggered by its remote, it returns
|
||||
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
|
||||
* not the interrupt it expected.
|
||||
*
|
||||
*/
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)priv;
|
||||
uint32_t val;
|
||||
|
||||
(void)vect_id;
|
||||
|
||||
if (ch) {
|
||||
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ch->ipi_mask) {
|
||||
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
|
||||
ch->ipi_mask);
|
||||
atomic_flag_clear(&ch->remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief measure_shmem_throughputd() - measure shmem throughpput with libmetal
|
||||
* - Download throughput measurement:
|
||||
* Start TTC RPU counter, wait for IPI kick, check if data is
|
||||
* available, if yes, read as much data as possible from shared
|
||||
* memory. It will iterates untill 1000 packages have been received,
|
||||
* stop TTC RPU counter and kick IPI to notify the remote. Repeat
|
||||
* for different package size.
|
||||
* - Upload throughput measurement:
|
||||
* Start TTC RPU counter, write data to shared memory and kick IPI
|
||||
* to notify remote. It will iterate for 1000 times, stop TTC RPU
|
||||
* counter.Wait for APU IPI kick to know APU has received all the
|
||||
* packages. Kick IPI to notify it TTC RPU conter value is ready to
|
||||
* read. Repeat for different package size.
|
||||
*
|
||||
* @param[in] ch - channel information
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
static int measure_shmem_throughputd(struct channel_s *ch)
|
||||
{
|
||||
void *lbuf = NULL;
|
||||
int ret = 0;
|
||||
size_t s;
|
||||
uint32_t rx_count, rx_avail, tx_count, iterations;
|
||||
unsigned long tx_avail_offset, rx_avail_offset;
|
||||
unsigned long tx_addr_offset, rx_addr_offset;
|
||||
unsigned long tx_data_offset, rx_data_offset;
|
||||
uint32_t buf_phy_addr_32;
|
||||
|
||||
/* allocate memory for receiving data */
|
||||
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
|
||||
if (!lbuf) {
|
||||
LPERROR("Failed to allocate memory.\r\n");
|
||||
return -1;
|
||||
}
|
||||
memset(lbuf, 0xA, BUF_SIZE_MAX);
|
||||
|
||||
/* Clear shared memory */
|
||||
metal_io_block_set(ch->shm_io, 0, 0, metal_io_region_size(ch->shm_io));
|
||||
|
||||
LPRINTF("Starting shared mem throughput demo\n");
|
||||
|
||||
/* for each data size, measure block receive throughput */
|
||||
for (s = PKG_SIZE_MIN; s <= PKG_SIZE_MAX; s <<= 1) {
|
||||
rx_count = 0;
|
||||
iterations = TOTAL_DATA_SIZE / s;
|
||||
/* Set rx buffer address offset */
|
||||
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
|
||||
rx_addr_offset = SHM_DESC_OFFSET_RX +
|
||||
SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
/* Data has arrived, seasure start. Reset RPU TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
while (1) {
|
||||
rx_avail = metal_io_read32(ch->shm_io, rx_avail_offset);
|
||||
|
||||
while(rx_count != rx_avail) {
|
||||
/* Get the buffer location from the shared
|
||||
* memory rx address array.
|
||||
*/
|
||||
buf_phy_addr_32 = metal_io_read32(ch->shm_io,
|
||||
rx_addr_offset);
|
||||
rx_data_offset = metal_io_phys_to_offset(
|
||||
ch->shm_io,
|
||||
(metal_phys_addr_t)buf_phy_addr_32);
|
||||
if (rx_data_offset == METAL_BAD_OFFSET) {
|
||||
LPERROR(
|
||||
"[%u]failed to get rx offset: 0x%x, 0x%lx.\n",
|
||||
rx_count, buf_phy_addr_32,
|
||||
metal_io_phys(ch->shm_io,
|
||||
rx_addr_offset));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rx_addr_offset += sizeof(buf_phy_addr_32);
|
||||
/* Read data from shared memory */
|
||||
metal_io_block_read(ch->shm_io, rx_data_offset,
|
||||
lbuf, s);
|
||||
rx_count++;
|
||||
}
|
||||
if (rx_count < iterations)
|
||||
/* Need to wait for more data */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
else
|
||||
break;
|
||||
}
|
||||
/* Stop RPU TTC counter */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
/* Clear remote kicked flag -- 0 is kicked */
|
||||
atomic_init(&ch->remote_nkicked, 1);
|
||||
/* Kick IPI to notify RPU TTC counter value is ready */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
}
|
||||
|
||||
/* for each data size, measure send throughput */
|
||||
for (s = PKG_SIZE_MIN; s <= PKG_SIZE_MAX; s <<= 1) {
|
||||
tx_count = 0;
|
||||
iterations = TOTAL_DATA_SIZE / s;
|
||||
|
||||
/* Set tx buffer address offset */
|
||||
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
|
||||
tx_addr_offset = SHM_DESC_OFFSET_TX +
|
||||
SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
|
||||
/* Wait for APU to signal it is ready for the measurement */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
/* Data has arrived, seasure start. Reset RPU TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
while (tx_count < iterations) {
|
||||
/* Write data to the shared memory*/
|
||||
metal_io_block_write(ch->shm_io, tx_data_offset,
|
||||
lbuf, s);
|
||||
|
||||
/* Write to the address array to tell the other end
|
||||
* the buffer address.
|
||||
*/
|
||||
buf_phy_addr_32 = (uint32_t)metal_io_phys(ch->shm_io,
|
||||
tx_data_offset);
|
||||
metal_io_write32(ch->shm_io, tx_addr_offset,
|
||||
buf_phy_addr_32);
|
||||
tx_data_offset += s;
|
||||
tx_addr_offset += sizeof(buf_phy_addr_32);
|
||||
|
||||
/* Increase number of available buffers */
|
||||
tx_count++;
|
||||
metal_io_write32(ch->shm_io, tx_avail_offset, tx_count);
|
||||
/* Kick IPI to notify remote data is ready in the
|
||||
* shared memory */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
|
||||
ch->ipi_mask);
|
||||
}
|
||||
/* Stop RPU TTC counter */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
/* Wait for IPI kick to know when the remote is ready
|
||||
* to read the TTC counter value */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
/* Kick IPI to notify RPU TTC counter value is ready */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
}
|
||||
|
||||
out:
|
||||
if (lbuf)
|
||||
metal_free_memory(lbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int shmem_throughput_demod()
|
||||
{
|
||||
struct channel_s ch;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("shared memory throughput");
|
||||
memset(&ch, 0, sizeof(ch));
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!ch.shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get TTC IO region */
|
||||
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
|
||||
if (!ch.ttc_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ch.ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
|
||||
ch.ipi_mask = IPI_MASK;
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&ch.remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = measure_shmem_throughputd(&ch);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,360 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2010 - 2017 Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include <xparameters.h>
|
||||
#include <xil_cache.h>
|
||||
#include <xil_exception.h>
|
||||
#include <xstatus.h>
|
||||
#include <xscugic.h>
|
||||
#include <xreg_cortexr5.h>
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/irq.h>
|
||||
|
||||
#include "platform_config.h"
|
||||
#include "common.h"
|
||||
|
||||
#ifdef STDOUT_IS_16550
|
||||
#include <xuartns550_l.h>
|
||||
|
||||
#define UART_BAUD 9600
|
||||
#endif
|
||||
|
||||
#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
|
||||
|
||||
#define IPI_IRQ_VECT_ID 65
|
||||
|
||||
#define SHM_BASE_ADDR 0x3ED80000
|
||||
#define TTC0_BASE_ADDR 0xFF110000
|
||||
#define IPI_BASE_ADDR 0xFF310000
|
||||
|
||||
/* Default generic I/O region page shift */
|
||||
/* Each I/O region can contain multiple pages.
|
||||
* In FreeRTOS system, the memory mapping is flat, there is no
|
||||
* virtual memory.
|
||||
* We can assume there is only one page in the FreeRTOS system.
|
||||
*/
|
||||
#define DEFAULT_PAGE_SHIFT (-1UL)
|
||||
#define DEFAULT_PAGE_MASK (-1UL)
|
||||
|
||||
extern XScuGic xInterruptController;
|
||||
|
||||
const metal_phys_addr_t metal_phys[] = {
|
||||
IPI_BASE_ADDR, /**< base IPI address */
|
||||
SHM_BASE_ADDR, /**< shared memory base address */
|
||||
TTC0_BASE_ADDR, /**< base TTC0 address */
|
||||
};
|
||||
|
||||
/* Define metal devices table for IPI, shared memory and TTC devices.
|
||||
* Linux system uses device tree to describe devices. Unlike Linux,
|
||||
* there is no standard device abstraction for FreeRTOS system, we
|
||||
* uses libmetal devices structure to describe the devices we used in
|
||||
* the example.
|
||||
* The IPI, shared memory and TTC devices are memory mapped
|
||||
* devices. For this type of devices, it is required to provide
|
||||
* accessible memory mapped regions, and interrupt information.
|
||||
* In FreeRTOS system, the memory mapping is flat. As you can see
|
||||
* in the table before, we set the virtual address "virt" the same
|
||||
* as the physical address.
|
||||
*/
|
||||
static struct metal_device metal_dev_table[] = {
|
||||
{
|
||||
/* IPI device */
|
||||
.name = IPI_DEV_NAME,
|
||||
.bus = NULL,
|
||||
.num_regions = 1,
|
||||
.regions = {
|
||||
{
|
||||
.virt = (void *)IPI_BASE_ADDR,
|
||||
.physmap = &metal_phys[0],
|
||||
.size = 0x1000,
|
||||
.page_shift = DEFAULT_PAGE_SHIFT,
|
||||
.page_mask = DEFAULT_PAGE_MASK,
|
||||
.mem_flags = DEVICE_NONSHARED | PRIV_RW_USER_RW,
|
||||
.ops = {NULL},
|
||||
}
|
||||
},
|
||||
.node = {NULL},
|
||||
.irq_num = 1,
|
||||
.irq_info = (void *)IPI_IRQ_VECT_ID,
|
||||
},
|
||||
{
|
||||
/* Shared memory management device */
|
||||
.name = SHM_DEV_NAME,
|
||||
.bus = NULL,
|
||||
.num_regions = 1,
|
||||
.regions = {
|
||||
{
|
||||
.virt = (void *)SHM_BASE_ADDR,
|
||||
.physmap = &metal_phys[1],
|
||||
.size = 0x1000000,
|
||||
.page_shift = DEFAULT_PAGE_SHIFT,
|
||||
.page_mask = DEFAULT_PAGE_MASK,
|
||||
.mem_flags = NORM_SHARED_NCACHE |
|
||||
PRIV_RW_USER_RW,
|
||||
.ops = {NULL},
|
||||
}
|
||||
},
|
||||
.node = {NULL},
|
||||
.irq_num = 0,
|
||||
.irq_info = NULL,
|
||||
},
|
||||
{
|
||||
/* ttc0 */
|
||||
.name = TTC_DEV_NAME,
|
||||
.bus = NULL,
|
||||
.num_regions = 1,
|
||||
.regions = {
|
||||
{
|
||||
.virt = (void *)TTC0_BASE_ADDR ,
|
||||
.physmap = &metal_phys[2],
|
||||
.size = 0x1000,
|
||||
.page_shift = DEFAULT_PAGE_SHIFT,
|
||||
.page_mask = DEFAULT_PAGE_MASK,
|
||||
.mem_flags = DEVICE_NONSHARED | PRIV_RW_USER_RW,
|
||||
.ops = {NULL},
|
||||
}
|
||||
},
|
||||
.node = {NULL},
|
||||
.irq_num = 0,
|
||||
.irq_info = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Extern global variables
|
||||
*/
|
||||
struct metal_device *ipi_dev = NULL;
|
||||
struct metal_device *shm_dev = NULL;
|
||||
struct metal_device *ttc_dev = NULL;
|
||||
|
||||
/**
|
||||
* @brief enable_caches() - Enable caches
|
||||
*/
|
||||
void enable_caches()
|
||||
{
|
||||
#ifdef __MICROBLAZE__
|
||||
#ifdef XPAR_MICROBLAZE_USE_ICACHE
|
||||
Xil_ICacheEnable();
|
||||
#endif
|
||||
#ifdef XPAR_MICROBLAZE_USE_DCACHE
|
||||
Xil_DCacheEnable();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief disable_caches() - Disable caches
|
||||
*/
|
||||
void disable_caches()
|
||||
{
|
||||
Xil_DCacheDisable();
|
||||
Xil_ICacheDisable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief init_uart() - Initialize UARTs
|
||||
*/
|
||||
void init_uart()
|
||||
{
|
||||
#ifdef STDOUT_IS_16550
|
||||
XUartNs550_SetBaud(STDOUT_BASEADDR, XPAR_XUARTNS550_CLOCK_HZ,
|
||||
UART_BAUD);
|
||||
XUartNs550_SetLineControlReg(STDOUT_BASEADDR, XUN_LCR_8_DATA_BITS);
|
||||
#endif
|
||||
/* Bootrom/BSP configures PS7/PSU UART to 115200 bps */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief init_irq() - Initialize GIC and connect IPI interrupt
|
||||
* This function will initialize the GIC and connect the IPI
|
||||
* interrupt.
|
||||
*
|
||||
* @return 0 - succeeded, non-0 for failures
|
||||
*/
|
||||
int init_irq()
|
||||
{
|
||||
int ret = 0;
|
||||
XScuGic_Config *IntcConfig; /* The configuration parameters of
|
||||
* the interrupt controller */
|
||||
|
||||
Xil_ExceptionDisable();
|
||||
/*
|
||||
* Initialize the interrupt controller driver
|
||||
*/
|
||||
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
|
||||
if (NULL == IntcConfig) {
|
||||
return (int)XST_FAILURE;
|
||||
}
|
||||
|
||||
ret = XScuGic_CfgInitialize(&xInterruptController, IntcConfig,
|
||||
IntcConfig->CpuBaseAddress);
|
||||
if (ret != XST_SUCCESS) {
|
||||
return (int)XST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the interrupt handler to the hardware interrupt handling
|
||||
* logic in the ARM processor.
|
||||
*/
|
||||
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
|
||||
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
|
||||
&xInterruptController);
|
||||
|
||||
Xil_ExceptionEnable();
|
||||
/* Connect IPI Interrupt ID with libmetal ISR */
|
||||
XScuGic_Connect(&xInterruptController, IPI_IRQ_VECT_ID,
|
||||
(Xil_ExceptionHandler)metal_irq_isr,
|
||||
(void *)IPI_IRQ_VECT_ID);
|
||||
|
||||
XScuGic_Enable(&xInterruptController, IPI_IRQ_VECT_ID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief platform_register_metal_device() - Statically Register libmetal
|
||||
* devices.
|
||||
* This function registers the IPI, shared memory and
|
||||
* TTC devices to the libmetal generic bus.
|
||||
* Libmetal uses bus structure to group the devices. Before you can
|
||||
* access the device with libmetal device operation, you will need to
|
||||
* register the device to a libmetal supported bus.
|
||||
* For non-Linux system, libmetal only supports "generic" bus, which is
|
||||
* used to manage the memory mapped devices.
|
||||
*
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
int platform_register_metal_device(void)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
struct metal_device *dev;
|
||||
|
||||
for (i = 0; i < sizeof(metal_dev_table)/sizeof(struct metal_device);
|
||||
i++) {
|
||||
dev = &metal_dev_table[i];
|
||||
xil_printf("registering: %d, name=%s\n", i, dev->name);
|
||||
ret = metal_register_generic_device(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief open_metal_devices() - Open registered libmetal devices.
|
||||
* This function opens all the registered libmetal devices.
|
||||
*
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
int open_metal_devices(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Open shared memory device */
|
||||
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &shm_dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Open IPI device */
|
||||
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &ipi_dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Open TTC device */
|
||||
ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &ttc_dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", TTC_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief close_metal_devices() - close libmetal devices
|
||||
* This function closes all the libmetal devices which have
|
||||
* been opened.
|
||||
*
|
||||
*/
|
||||
void close_metal_devices(void)
|
||||
{
|
||||
/* Close shared memory device */
|
||||
if (shm_dev)
|
||||
metal_device_close(shm_dev);
|
||||
|
||||
/* Close IPI device */
|
||||
if (ipi_dev)
|
||||
metal_device_close(ipi_dev);
|
||||
|
||||
/* Close TTC device */
|
||||
if (ttc_dev)
|
||||
metal_device_close(ttc_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sys_init() - Register libmetal devices.
|
||||
* This function register the libmetal generic bus, and then
|
||||
* register the IPI, shared memory descriptor and shared memory
|
||||
* devices to the libmetal generic bus.
|
||||
*
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
int sys_init()
|
||||
{
|
||||
struct metal_init_params metal_param = METAL_INIT_DEFAULTS;
|
||||
int ret;
|
||||
|
||||
enable_caches();
|
||||
init_uart();
|
||||
if (init_irq()) {
|
||||
LPERROR("Failed to initialize interrupt\n");
|
||||
}
|
||||
|
||||
/* Initialize libmetal environment */
|
||||
metal_init(&metal_param);
|
||||
/* Register libmetal devices */
|
||||
ret = platform_register_metal_device();
|
||||
if (ret) {
|
||||
LPERROR("%s: failed to register devices: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Open libmetal devices which have been registered */
|
||||
ret = open_metal_devices();
|
||||
if (ret) {
|
||||
LPERROR("%s: failed to open devices: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sys_cleanup() - system cleanup
|
||||
* This function finish the libmetal environment
|
||||
* and disable caches.
|
||||
*
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
void sys_cleanup()
|
||||
{
|
||||
/* Close libmetal devices which have been opened */
|
||||
close_metal_devices();
|
||||
/* Finish libmetal environment */
|
||||
metal_finish();
|
||||
disable_caches();
|
||||
}
|
||||
|
@@ -0,0 +1,17 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 - 2014 Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __SYS_INIT_H__
|
||||
#define __SYS_INIT_H__
|
||||
|
||||
#include "platform_config.h"
|
||||
|
||||
int sys_init();
|
||||
void sys_cleanup();
|
||||
|
||||
#endif /* __SYS_INIT_H__ */
|
@@ -0,0 +1,4 @@
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
|
||||
add_subdirectory(${PROJECT_MACHINE})
|
||||
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
|
@@ -0,0 +1,17 @@
|
||||
|
||||
collect(PROJECT_LIB_DEPS metal)
|
||||
collect(PROJECT_LIB_DEPS xil)
|
||||
collect(PROJECT_LIB_DEPS c)
|
||||
collect(PROJECT_LIB_DEPS m)
|
||||
|
||||
set (_lib "xil")
|
||||
find_library (_lib_path ${_lib})
|
||||
if (NOT _lib_path)
|
||||
message ( "external library ${_lib_path} not found" )
|
||||
message ( "hint: you may need to pass -DCMAKE_LIBRARY_PATH=<path>" )
|
||||
message ( FATAL_ERROR "library ${_lib} is required to build the examples" )
|
||||
endif (NOT _lib_path)
|
||||
get_filename_component (_lib_path ${_lib_path} DIRECTORY)
|
||||
collect (PROJECT_LIB_DIRS ${_lib_path})
|
||||
|
||||
add_subdirectory(zynqmp_amp_demo)
|
@@ -0,0 +1,31 @@
|
||||
collector_list (_list PROJECT_INC_DIRS)
|
||||
include_directories (${_list} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
collector_list (_list PROJECT_LIB_DIRS)
|
||||
link_directories (${_list})
|
||||
|
||||
collector_list (_deps PROJECT_LIB_DEPS)
|
||||
|
||||
set (_linker_script ${CMAKE_CURRENT_SOURCE_DIR}/lscript.ld)
|
||||
|
||||
set (_src_common ${CMAKE_CURRENT_SOURCE_DIR}/init_${PROJECT_SYSTEM}.c)
|
||||
set (_app0 libmetal_amp_demod)
|
||||
set (_src0 ${CMAKE_CURRENT_SOURCE_DIR}/${_app0}.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/sys_init.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_demod.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_atomic_demod.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/ipi_shmem_demod.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/ipi_latency_demod.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_latency_demod.c)
|
||||
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_throughput_demod.c)
|
||||
|
||||
get_property (_linker_options GLOBAL PROPERTY TEST_LINKER_OPTIONS)
|
||||
add_executable (${_app0}.elf ${_src0})
|
||||
if (PROJECT_EC_FLAGS)
|
||||
string(REPLACE " " ";" _ec_flgs ${PROJECT_EC_FLAGS})
|
||||
target_compile_options (${_app0}.elf PUBLIC ${_ec_flgs})
|
||||
endif (PROJECT_EC_FLAGS)
|
||||
target_link_libraries(${_app0}.elf -Wl,-Map=${_app0}.map -Wl,--gc-sections -T\"${_linker_script}\" -Wl,--start-group ${_deps} -Wl,--end-group)
|
||||
install (TARGETS ${_app0}.elf RUNTIME DESTINATION bin)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef __COMMON_H__
|
||||
#define __COMMON_H__
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <metal/irq.h>
|
||||
#include <metal/errno.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/cpu.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <sys/types.h>
|
||||
#include "sys_init.h"
|
||||
|
||||
/* Devices names */
|
||||
#define BUS_NAME "generic"
|
||||
#define IPI_DEV_NAME "ff310000.ipi"
|
||||
#define SHM_DEV_NAME "3ed80000.shm"
|
||||
#define TTC_DEV_NAME "ff110000.ttc"
|
||||
|
||||
/* IPI registers offset */
|
||||
#define IPI_TRIG_OFFSET 0x0 /* IPI trigger reg offset */
|
||||
#define IPI_OBS_OFFSET 0x4 /* IPI observation reg offset */
|
||||
#define IPI_ISR_OFFSET 0x10 /* IPI interrupt status reg offset */
|
||||
#define IPI_IMR_OFFSET 0x14 /* IPI interrupt mask reg offset */
|
||||
#define IPI_IER_OFFSET 0x18 /* IPI interrupt enable reg offset */
|
||||
#define IPI_IDR_OFFSET 0x1C /* IPI interrup disable reg offset */
|
||||
|
||||
#define IPI_MASK 0x1000000 /* IPI mask for kick from APU.
|
||||
We use PL0 IPI in this demo. */
|
||||
|
||||
/* TTC counter offsets */
|
||||
#define XTTCPS_CLK_CNTRL_OFFSET 0x0 /* TTC counter clock control reg offset */
|
||||
#define XTTCPS_CNT_CNTRL_OFFSET 0xC /* TTC counter control reg offset */
|
||||
#define XTTCPS_CNT_VAL_OFFSET 0x18 /* TTC counter val reg offset */
|
||||
#define XTTCPS_CNT_OFFSET(ID) ((ID) == 1 ? 0 : 1 << (ID)) /* TTC counter offset
|
||||
ID is from 1 to 3 */
|
||||
|
||||
/* TTC counter control masks */
|
||||
#define XTTCPS_CNT_CNTRL_RST_MASK 0x10U /* TTC counter control reset mask */
|
||||
#define XTTCPS_CNT_CNTRL_DIS_MASK 0x01U /* TTC counter control disable mask */
|
||||
|
||||
#define LPRINTF(format, ...) \
|
||||
xil_printf("\r\nSERVER> " format, ##__VA_ARGS__)
|
||||
|
||||
#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__)
|
||||
|
||||
extern struct metal_device *ipi_dev; /* IPI metal device */
|
||||
extern struct metal_device *shm_dev; /* SHM metal device */
|
||||
extern struct metal_device *ttc_dev; /* TTC metal device */
|
||||
|
||||
/**
|
||||
* @brief atomic_shmem_demod() - Shared memory atomic operation demo
|
||||
* This task will:
|
||||
* * Wait for the remote to write to shared memory.
|
||||
* * Once it receives the notification via polling, start atomic add by
|
||||
* 1 for 1000 times to first 32 bits of memory in the
|
||||
* shared memory location at 3ed00000 which is pointed to by shm_io.
|
||||
* * Write to shared mem to notify the remote once it finishes
|
||||
* calculation.
|
||||
*
|
||||
* @return - If setup failed, return the corresponding error number. Otherwise
|
||||
* return 0 on success.
|
||||
*/
|
||||
int atomic_shmem_demod();
|
||||
|
||||
/**
|
||||
* @brief ipi_latency_demod() - Show performance of IPI with Libmetal.
|
||||
* Loop until APU tells RPU to stop via shared memory.
|
||||
* In loop, wait for interrupt (interrupt handler stops APU to
|
||||
* RPU timer). Then reset count on RPU to APU timer to 0, start
|
||||
* counting and send interrupt to notify APU.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int ipi_latency_demod();
|
||||
|
||||
/**
|
||||
* @brief ipi_shmem_demod() - shared memory IPI demo
|
||||
* This task will:
|
||||
* * Wait for IPI interrupt from the remote
|
||||
* * Once it received the interrupt, copy the content from
|
||||
* the ping buffer to the pong buffer.
|
||||
* * Update the shared memory descriptor for the new available
|
||||
* pong buffer.
|
||||
* * Trigger IPI to notifty the remote.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int ipi_shmem_demod();
|
||||
|
||||
/**
|
||||
* @brief shmem_demod() - Show use of shared memory with Libmetal.
|
||||
* Until KEEP_GOING signal is stopped, keep looping.
|
||||
* In the loop, read message from remote, add one to message and
|
||||
* then respond. After the loop, cleanup resources.
|
||||
*
|
||||
* @return - return 0 on success, otherwise return error number indicating
|
||||
* type of error
|
||||
*/
|
||||
int shmem_demod();
|
||||
|
||||
/**
|
||||
* @brief shmem_latency_demod() - Show performance of shared mem.
|
||||
* Loop until APU tells RPU to stop via shared memory.
|
||||
* In loop, wait for interrupt (interrupt handler stops APU to
|
||||
* RPU timer). Then reset count on RPU to APU timer to 0, start
|
||||
* counting and send interrupt to notify APU.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int shmem_latency_demod();
|
||||
|
||||
/**
|
||||
* @brief shmem_throughput_demod() - Show throughput of shared mem.
|
||||
* At signal of remote, record total time to do block read and write
|
||||
* operation Loop until APU tells RPU to stop via shared memory.
|
||||
* In loop, wait for interrupt (interrupt handler stops APU to
|
||||
* RPU timer). Then reset count on RPU to APU timer to 0, start
|
||||
* counting and send interrupt to notify APU.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int shmem_throughput_demod();
|
||||
|
||||
static inline void wait_for_interrupt()
|
||||
{
|
||||
asm volatile("wfi");
|
||||
}
|
||||
|
||||
/**
|
||||
* @breif wait_for_notified() - Loop until notified bit
|
||||
* in channel is set.
|
||||
*
|
||||
* @param[in] notified - pointer to the notified variable
|
||||
*/
|
||||
static inline void wait_for_notified(atomic_int *notified)
|
||||
{
|
||||
unsigned int flags;
|
||||
|
||||
do {
|
||||
flags = metal_irq_save_disable();
|
||||
if (!atomic_flag_test_and_set(notified)) {
|
||||
metal_irq_restore_enable(flags);
|
||||
break;
|
||||
}
|
||||
wait_for_interrupt();
|
||||
metal_irq_restore_enable(flags);
|
||||
} while(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief print_demo() - print demo string
|
||||
*
|
||||
* @param[in] name - demo name
|
||||
*/
|
||||
static inline void print_demo(char *name)
|
||||
{
|
||||
LPRINTF("====== libmetal demo: %s ======\n", name);
|
||||
}
|
||||
|
||||
#endif /* __COMMON_H__ */
|
||||
|
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* ipi_latency_demod.c
|
||||
* This is the remote side of the IPI latency measurement demo.
|
||||
* This demo does the follwing steps:
|
||||
*
|
||||
* 1. Open the shared memory device.
|
||||
* 1. Open the TTC timer device.
|
||||
* 2. Open the IPI device.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 6. When it receives IPI interrupt, the IPI interrupt handler to stop
|
||||
* the RPU to APU TTC counter.
|
||||
* 7. Check the shared memory to see if demo is on. If the demo is on,
|
||||
* reest the RPU to APU TTC counter and kick IPI to notify the remote.
|
||||
* 8. If the shared memory indicates the demo is off, cleanup resource:
|
||||
* disable IPI interrupt and deregister the IPI interrupt handler.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include "common.h"
|
||||
|
||||
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
|
||||
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
|
||||
|
||||
#define TTC_CLK_FREQ_HZ 100000000
|
||||
|
||||
/* Shared memory offset */
|
||||
#define SHM_DEMO_CNTRL_OFFSET 0x0
|
||||
|
||||
#define DEMO_STATUS_IDLE 0x0
|
||||
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
|
||||
|
||||
struct channel_s {
|
||||
struct metal_io_region *ipi_io; /* IPI metal i/o region */
|
||||
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
|
||||
struct metal_io_region *ttc_io; /* TTC metal i/o region */
|
||||
uint32_t ipi_mask; /* RPU IPI mask */
|
||||
atomic_int remote_nkicked; /* 0 - kicked from remote */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief reset_timer() - function to reset TTC counter
|
||||
* Set the RST bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void reset_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_RST_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop_timer() - function to stop TTC counter
|
||||
* Set the disable bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void stop_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_DIS_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_irq_handler() - IPI interrupt handler
|
||||
* It will clear the notified flag to mark it's got an IPI interrupt.
|
||||
* It will stop the RPU->APU timer and will clear the notified
|
||||
* flag to mark it's got an IPI interrupt
|
||||
*
|
||||
* @param[in] vect_id - IPI interrupt vector ID
|
||||
* @param[in/out] priv - communication channel data for this application.
|
||||
*
|
||||
* @return - If the IPI interrupt is triggered by its remote, it returns
|
||||
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
|
||||
* not the interrupt it expected.
|
||||
*
|
||||
*/
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)priv;
|
||||
uint32_t val;
|
||||
|
||||
(void)vect_id;
|
||||
|
||||
if (ch) {
|
||||
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ch->ipi_mask) {
|
||||
/* stop RPU -> APU timer */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
|
||||
ch->ipi_mask);
|
||||
atomic_flag_clear(&ch->remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief measure_ipi_latencyd() - measure IPI latency with libmetal
|
||||
* Loop until APU tells RPU to stop via shared memory.
|
||||
* In loop, wait for interrupt (interrupt handler stops APU to
|
||||
* RPU TTC counter). Then reset count on RPU to APU TTC counter
|
||||
* and kick IPI to notify APU.
|
||||
*
|
||||
* @param[in] ch - channel information
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
static int measure_ipi_latencyd(struct channel_s *ch)
|
||||
{
|
||||
LPRINTF("Starting IPI latency demo\r\n");
|
||||
while(1) {
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
if (metal_io_read32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET) ==
|
||||
DEMO_STATUS_START) {
|
||||
/* Reset RPU to APU TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
/* Kick IPI to notify the remote */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
|
||||
ch->ipi_mask);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipi_latency_demod()
|
||||
{
|
||||
struct channel_s ch;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("IPI latency");
|
||||
memset(&ch, 0, sizeof(ch));
|
||||
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!ch.shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get TTC IO region */
|
||||
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
|
||||
if (!ch.ttc_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ch.ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
|
||||
ch.ipi_mask = IPI_MASK;
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&ch.remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = measure_ipi_latencyd(&ch);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* ipi_shmem_demo.c - shared memory with IPI demo
|
||||
* This demo will:
|
||||
*
|
||||
* 1. Get the shared memory device I/O region.
|
||||
* 2. Get the IPI device I/O region.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 4. Wait for remote IPI notification to receive message.
|
||||
* 5. When message is received, check if it is shutdown message.
|
||||
* 6. If it is shutdown message, do cleanup, otherwise, echo it back to the
|
||||
* shared buffer.
|
||||
* 7. Kick IPI to notify there is a message written to the shared memory
|
||||
* if it echos back the message.
|
||||
* 8. Repeat 4.
|
||||
* 9. Clean up: disable IPI interrupt, deregister the IPI interrupt handler.
|
||||
*
|
||||
* Here is the Shared memory structure of this demo:
|
||||
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
|
||||
* |0x04 - 0x07 | number of APU to RPU buffers consumed by RPU |
|
||||
* |0x08 - 0x1FFC | address array for shared buffers from APU to RPU |
|
||||
* |0x2000 - 0x2003 | number of RPU to APU buffers available to APU |
|
||||
* |0x2004 - 0x2007 | number of RPU to APU buffers consumed by APU |
|
||||
* |0x2008 - 0x3FFC | address array for shared buffers from RPU to APU |
|
||||
* |0x04000 - 0x103FFC | APU to RPU buffers |
|
||||
* |0x104000 - 0x203FFC | RPU to APU buffers |
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include <metal/errno.h>
|
||||
#include "common.h"
|
||||
|
||||
#define BUF_SIZE_MAX 512
|
||||
#define SHUTDOWN "shutdown"
|
||||
|
||||
/* Shared memory offsets */
|
||||
#define SHM_DESC_OFFSET_RX 0x0
|
||||
#define SHM_BUFF_OFFSET_RX 0x04000
|
||||
#define SHM_DESC_OFFSET_TX 0x02000
|
||||
#define SHM_BUFF_OFFSET_TX 0x104000
|
||||
|
||||
/* Shared memory descriptors offset */
|
||||
#define SHM_DESC_AVAIL_OFFSET 0x00
|
||||
#define SHM_DESC_USED_OFFSET 0x04
|
||||
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x08
|
||||
|
||||
#define PKGS_TOTAL 1024
|
||||
|
||||
#define BUF_SIZE_MAX 512
|
||||
#define SHUTDOWN "shutdown"
|
||||
|
||||
struct msg_hdr_s {
|
||||
uint32_t index;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
|
||||
1 - not-kicked */
|
||||
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
(void)vect_id;
|
||||
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
uint64_t val = 1;
|
||||
|
||||
if (!ipi_io)
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ipi_mask) {
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
|
||||
atomic_flag_clear(&remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_shmem_echod() - shared memory IPI demo
|
||||
* This task will:
|
||||
* * Wait for IPI interrupt from the remote
|
||||
* * Once it received the interrupt, copy the content from
|
||||
* the ping buffer to the pong buffer.
|
||||
* * Update the shared memory descriptor for the new available
|
||||
* pong buffer.
|
||||
* * Trigger IPI to notifty the remote.
|
||||
* @param[in] ipi_io - IPI metal i/o region
|
||||
* @param[in] shm_io - shared memory metal i/o region
|
||||
* @return - return 0 on success, otherwise return error number indicating
|
||||
* type of error.
|
||||
*/
|
||||
static int ipi_shmem_echod(struct metal_io_region *ipi_io,
|
||||
struct metal_io_region *shm_io)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t rx_count, rx_avail;
|
||||
unsigned long tx_avail_offset, rx_avail_offset;
|
||||
unsigned long rx_used_offset;
|
||||
unsigned long tx_addr_offset, rx_addr_offset;
|
||||
unsigned long tx_data_offset, rx_data_offset;
|
||||
void *lbuf = NULL;
|
||||
struct msg_hdr_s *msg_hdr;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
|
||||
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
|
||||
if (!lbuf) {
|
||||
LPERROR("Failed to allocate local buffer for msg.\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
/* Clear shared memory */
|
||||
metal_io_block_set(shm_io, 0, 0, metal_io_region_size(shm_io));
|
||||
|
||||
/* Set tx/rx buffer address offset */
|
||||
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
|
||||
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
|
||||
rx_used_offset = SHM_DESC_OFFSET_RX + SHM_DESC_USED_OFFSET;
|
||||
tx_addr_offset = SHM_DESC_OFFSET_TX + SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
rx_addr_offset = SHM_DESC_OFFSET_RX + SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
|
||||
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
|
||||
|
||||
LPRINTF("Wait for echo test to start.\n");
|
||||
rx_count = 0;
|
||||
while (1) {
|
||||
wait_for_notified(&remote_nkicked);
|
||||
rx_avail = metal_io_read32(shm_io, rx_avail_offset);
|
||||
while(rx_count != rx_avail) {
|
||||
uint32_t buf_phy_addr_32;
|
||||
|
||||
/* Received ping from the other side */
|
||||
|
||||
/* Get the buffer location from the shared memory
|
||||
* rx address array.
|
||||
*/
|
||||
buf_phy_addr_32 = metal_io_read32(shm_io,
|
||||
rx_addr_offset);
|
||||
rx_data_offset = metal_io_phys_to_offset(shm_io,
|
||||
(metal_phys_addr_t)buf_phy_addr_32);
|
||||
if (rx_data_offset == METAL_BAD_OFFSET) {
|
||||
LPERROR("[%u]failed to get rx offset: 0x%x, 0x%lx.\n",
|
||||
rx_count, buf_phy_addr_32,
|
||||
metal_io_phys(shm_io, rx_addr_offset));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rx_addr_offset += sizeof(buf_phy_addr_32);
|
||||
|
||||
/* Read message header from shared memory */
|
||||
metal_io_block_read(shm_io, rx_data_offset, lbuf,
|
||||
sizeof(struct msg_hdr_s));
|
||||
msg_hdr = (struct msg_hdr_s *)lbuf;
|
||||
|
||||
/* Check if the message header is valid */
|
||||
if (msg_hdr->len > (BUF_SIZE_MAX - sizeof(*msg_hdr))) {
|
||||
LPERROR("wrong msg: length invalid: %u, %u.\n",
|
||||
BUF_SIZE_MAX - sizeof(*msg_hdr),
|
||||
msg_hdr->len);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rx_data_offset += sizeof(*msg_hdr);
|
||||
/* Read message */
|
||||
metal_io_block_read(shm_io,
|
||||
rx_data_offset,
|
||||
lbuf + sizeof(*msg_hdr), msg_hdr->len);
|
||||
rx_data_offset += msg_hdr->len;
|
||||
rx_count++;
|
||||
/* increase rx used count to indicate it has consumed
|
||||
* the received data */
|
||||
metal_io_write32(shm_io, rx_used_offset, rx_count);
|
||||
|
||||
/* Check if the it is the shutdown message */
|
||||
if (msg_hdr->len == strlen(SHUTDOWN) &&
|
||||
!strncmp(SHUTDOWN,
|
||||
(lbuf + sizeof(struct msg_hdr_s)),
|
||||
strlen(SHUTDOWN))) {
|
||||
LPRINTF("Received shutdown message\n");
|
||||
goto out;
|
||||
}
|
||||
/* Copy the message back to the other end */
|
||||
metal_io_block_write(shm_io, tx_data_offset, msg_hdr,
|
||||
sizeof(struct msg_hdr_s) + msg_hdr->len);
|
||||
|
||||
/* Write to the address array to tell the other end
|
||||
* the buffer address.
|
||||
*/
|
||||
buf_phy_addr_32 = (uint32_t)metal_io_phys(shm_io,
|
||||
tx_data_offset);
|
||||
metal_io_write32(shm_io, tx_addr_offset,
|
||||
buf_phy_addr_32);
|
||||
tx_data_offset += sizeof(struct msg_hdr_s) +
|
||||
msg_hdr->len;
|
||||
tx_addr_offset += sizeof(uint32_t);
|
||||
|
||||
/* Increase number of available buffers */
|
||||
metal_io_write32(shm_io, tx_avail_offset, rx_count);
|
||||
/* Kick IPI to notify data is in shared buffer */
|
||||
metal_io_write32(ipi_io, IPI_TRIG_OFFSET,
|
||||
ipi_mask);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
LPRINTF("IPI with shared memory demo finished with exit code: %i.\n",
|
||||
ret);
|
||||
|
||||
if (lbuf)
|
||||
metal_free_memory(lbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipi_shmem_demod()
|
||||
{
|
||||
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("IPI and shared memory");
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
if (!ipi_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = ipi_shmem_echod(ipi_io, shm_io);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/***************************************************************************
|
||||
* libmetal_amp_demo.c
|
||||
*
|
||||
* This application shows how to use IPI to trigger interrupt and how to
|
||||
* setup shared memory with libmetal API for communication between processors.
|
||||
*
|
||||
* This app does the following:
|
||||
* 1. Initialize the platform hardware such as UART, GIC.
|
||||
* 2. Connect the IPI interrupt.
|
||||
* 3. Register IPI device, shared memory descriptor device and shared memory
|
||||
* device with libmetal in the intialization.
|
||||
* 4. In the main application it does the following,
|
||||
* * open the registered libmetal devices: IPI device, shared memory
|
||||
* descriptor device and shared memory device.
|
||||
* * Map the shared memory descriptor as non-cached memory.
|
||||
* * Map the shared memory as non-cached memory. If you do not map the
|
||||
* shared memory as non-cached memory, make sure you flush the cache,
|
||||
* before you notify the remote.
|
||||
* 7. Register the IPI interrupt handler with libmetal.
|
||||
* 8. Run the atomic demo task ipi_task_shm_atomicd():
|
||||
* * Wait for the IPI interrupt from the remote.
|
||||
* * Once it receives the interrupt, it does atomic add by 1 to the
|
||||
* first 32bit of the shared memory descriptor location by 1000 times.
|
||||
* * It will then notify the remote after the calucation.
|
||||
* * As the remote side also does 1000 times add after it has notified
|
||||
* this end. The remote side will check if the result is 2000, if not,
|
||||
* it will error.
|
||||
* 9. Run the shared memory echo demo task ipi_task_echod()
|
||||
* * Wait for the IPI interrupt from the other end.
|
||||
* * If an IPI interrupt is received, copy the message to the current
|
||||
* available RPU to APU buffer, increase the available buffer indicator,
|
||||
* and trigger IPI to notify the remote.
|
||||
* * If "shutdown" message is received, cleanup the libmetal source.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include "common.h"
|
||||
|
||||
/**
|
||||
* @brief main function of the demo application.
|
||||
* Here are the steps for the main function:
|
||||
* * Setup libmetal resources
|
||||
* * Run the IPI with shared memory demo.
|
||||
* * Run the shared memory demo.
|
||||
* * Run the atomic across shared memory demo.
|
||||
* * Run the ipi latency demo.
|
||||
* * Run the shared memory latency demo.
|
||||
* * Run the shared memory throughput demo.
|
||||
* * Cleanup libmetal resources
|
||||
* Report if any of the above demos failed.
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sys_init();
|
||||
|
||||
if (ret) {
|
||||
LPERROR("Failed to initialize system.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = shmem_demod();
|
||||
if (ret){
|
||||
LPERROR("shared memory demo failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = atomic_shmem_demod();
|
||||
if (ret){
|
||||
LPERROR("shared memory atomic demo failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ipi_shmem_demod();
|
||||
if (ret){
|
||||
LPERROR("shared memory atomic demo failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ipi_latency_demod();
|
||||
if (ret){
|
||||
LPERROR("IPI latency demo failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = shmem_latency_demod();
|
||||
if (ret){
|
||||
LPERROR("shared memory latency demo failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = shmem_throughput_demod();
|
||||
if (ret){
|
||||
LPERROR("shared memory thoughput demo failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sys_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
@@ -0,0 +1,317 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2017 Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Xilinx nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x2000;
|
||||
_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x4000;
|
||||
|
||||
_ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024;
|
||||
_SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048;
|
||||
_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024;
|
||||
_FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024;
|
||||
_UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024;
|
||||
|
||||
/* Define Memories in the system */
|
||||
|
||||
MEMORY
|
||||
{
|
||||
psu_r5_atcm_MEM_0 : ORIGIN = 0x0, LENGTH = 0x10000
|
||||
psu_r5_btcm_MEM_0 : ORIGIN = 0x20000, LENGTH = 0x10000
|
||||
psu_r5_ddr_0_MEM_0 : ORIGIN = 0x3ed00000, LENGTH = 0x80000
|
||||
}
|
||||
|
||||
/* Specify the default entry point to the program */
|
||||
|
||||
ENTRY(_boot)
|
||||
|
||||
/* Define the sections, and where they are mapped in memory */
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.vectors : {
|
||||
KEEP (*(.vectors))
|
||||
*(.boot)
|
||||
} > psu_r5_atcm_MEM_0
|
||||
|
||||
.text : {
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.gnu.linkonce.t.*)
|
||||
*(.plt)
|
||||
*(.gnu_warning)
|
||||
*(.gcc_execpt_table)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
*(.vfp11_veneer)
|
||||
*(.ARM.extab)
|
||||
*(.gnu.linkonce.armextab.*)
|
||||
} > psu_r5_atcm_MEM_0
|
||||
|
||||
.init : {
|
||||
KEEP (*(.init))
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.fini : {
|
||||
KEEP (*(.fini))
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.interp : {
|
||||
KEEP (*(.interp))
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.note-ABI-tag : {
|
||||
KEEP (*(.note-ABI-tag))
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.rodata : {
|
||||
__rodata_start = .;
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.gnu.linkonce.r.*)
|
||||
__rodata_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.rodata1 : {
|
||||
__rodata1_start = .;
|
||||
*(.rodata1)
|
||||
*(.rodata1.*)
|
||||
__rodata1_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.sdata2 : {
|
||||
__sdata2_start = .;
|
||||
*(.sdata2)
|
||||
*(.sdata2.*)
|
||||
*(.gnu.linkonce.s2.*)
|
||||
__sdata2_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.sbss2 : {
|
||||
__sbss2_start = .;
|
||||
*(.sbss2)
|
||||
*(.sbss2.*)
|
||||
*(.gnu.linkonce.sb2.*)
|
||||
__sbss2_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.data : {
|
||||
__data_start = .;
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.gnu.linkonce.d.*)
|
||||
*(.jcr)
|
||||
*(.got)
|
||||
*(.got.plt)
|
||||
__data_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.data1 : {
|
||||
__data1_start = .;
|
||||
*(.data1)
|
||||
*(.data1.*)
|
||||
__data1_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.got : {
|
||||
*(.got)
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.ctors : {
|
||||
__CTOR_LIST__ = .;
|
||||
___CTORS_LIST___ = .;
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
__CTOR_END__ = .;
|
||||
___CTORS_END___ = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.dtors : {
|
||||
__DTOR_LIST__ = .;
|
||||
___DTORS_LIST___ = .;
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
__DTOR_END__ = .;
|
||||
___DTORS_END___ = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.fixup : {
|
||||
__fixup_start = .;
|
||||
*(.fixup)
|
||||
__fixup_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.eh_frame : {
|
||||
*(.eh_frame)
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.eh_framehdr : {
|
||||
__eh_framehdr_start = .;
|
||||
*(.eh_framehdr)
|
||||
__eh_framehdr_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.gcc_except_table : {
|
||||
*(.gcc_except_table)
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.mmu_tbl (ALIGN(16384)) : {
|
||||
__mmu_tbl_start = .;
|
||||
*(.mmu_tbl)
|
||||
__mmu_tbl_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.ARM.exidx : {
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
*(.gnu.linkonce.armexidix.*.*)
|
||||
__exidx_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.preinit_array : {
|
||||
__preinit_array_start = .;
|
||||
KEEP (*(SORT(.preinit_array.*)))
|
||||
KEEP (*(.preinit_array))
|
||||
__preinit_array_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.init_array : {
|
||||
__init_array_start = .;
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array))
|
||||
__init_array_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.fini_array : {
|
||||
__fini_array_start = .;
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array))
|
||||
__fini_array_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.ARM.attributes : {
|
||||
__ARM.attributes_start = .;
|
||||
*(.ARM.attributes)
|
||||
__ARM.attributes_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.sdata : {
|
||||
__sdata_start = .;
|
||||
*(.sdata)
|
||||
*(.sdata.*)
|
||||
*(.gnu.linkonce.s.*)
|
||||
__sdata_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.sbss (NOLOAD) : {
|
||||
__sbss_start = .;
|
||||
*(.sbss)
|
||||
*(.sbss.*)
|
||||
*(.gnu.linkonce.sb.*)
|
||||
__sbss_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.tdata : {
|
||||
__tdata_start = .;
|
||||
*(.tdata)
|
||||
*(.tdata.*)
|
||||
*(.gnu.linkonce.td.*)
|
||||
__tdata_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.tbss : {
|
||||
__tbss_start = .;
|
||||
*(.tbss)
|
||||
*(.tbss.*)
|
||||
*(.gnu.linkonce.tb.*)
|
||||
__tbss_end = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.bss (NOLOAD) : {
|
||||
. = ALIGN(4);
|
||||
__bss_start__ = .;
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
__bss_end__ = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 );
|
||||
|
||||
_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 );
|
||||
|
||||
/* Generate Stack and Heap definitions */
|
||||
|
||||
.heap (NOLOAD) : {
|
||||
. = ALIGN(16);
|
||||
_heap = .;
|
||||
HeapBase = .;
|
||||
_heap_start = .;
|
||||
. += _HEAP_SIZE;
|
||||
_heap_end = .;
|
||||
HeapLimit = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
.stack (NOLOAD) : {
|
||||
. = ALIGN(16);
|
||||
_stack_end = .;
|
||||
. += _STACK_SIZE;
|
||||
_stack = .;
|
||||
__stack = _stack;
|
||||
. = ALIGN(16);
|
||||
_irq_stack_end = .;
|
||||
. += _IRQ_STACK_SIZE;
|
||||
__irq_stack = .;
|
||||
_supervisor_stack_end = .;
|
||||
. += _SUPERVISOR_STACK_SIZE;
|
||||
. = ALIGN(16);
|
||||
__supervisor_stack = .;
|
||||
_abort_stack_end = .;
|
||||
. += _ABORT_STACK_SIZE;
|
||||
. = ALIGN(16);
|
||||
__abort_stack = .;
|
||||
_fiq_stack_end = .;
|
||||
. += _FIQ_STACK_SIZE;
|
||||
. = ALIGN(16);
|
||||
__fiq_stack = .;
|
||||
_undef_stack_end = .;
|
||||
. += _UNDEF_STACK_SIZE;
|
||||
. = ALIGN(16);
|
||||
__undef_stack = .;
|
||||
} > psu_r5_btcm_MEM_0
|
||||
|
||||
_end = .;
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
#ifndef __PLATFORM_CONFIG_H_
|
||||
#define __PLATFORM_CONFIG_H_
|
||||
|
||||
#define STDOUT_IS_PSU_UART
|
||||
#define UART_DEVICE_ID 0
|
||||
#endif
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
/*****************************************************************************
|
||||
* atomic_shmem_demod.c - Shared memory atomic operation demo
|
||||
* This task will:
|
||||
* 1. Get the shared memory device I/O region.
|
||||
* 2. Get the IPI device I/O region.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 4. Wait for the APU to kick IPI to start the demo
|
||||
* 5. Once notification is received, start atomic add by
|
||||
* 1 for 5000 times over the shared memory
|
||||
* 6. Trigger IPI to notify the remote it has finished calculation.
|
||||
* 7. Clean up: Disable IPI interrupt, deregister the IPI interrupt handler.
|
||||
*/
|
||||
#include <metal/shmem.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/io.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdio.h>
|
||||
#include "common.h"
|
||||
#include "sys_init.h"
|
||||
|
||||
#define ATOMIC_INT_OFFSET 0x0 /* shared memory offset for atomic operation */
|
||||
#define ITERATIONS 5000
|
||||
|
||||
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
|
||||
1 - not-kicked */
|
||||
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
(void)vect_id;
|
||||
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
uint64_t val = 1;
|
||||
|
||||
if (!ipi_io)
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ipi_mask) {
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
|
||||
atomic_flag_clear(&remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief atomic_add_shmemd() - Shared memory atomic operation demo
|
||||
* This task will:
|
||||
* * Wait for the remote to write to shared memory.
|
||||
* * Once it receives the notification via polling, start atomic add by
|
||||
* 1 for 5000 times to first 32 bits of memory in the shared memory
|
||||
* which is pointed to by shm_io.
|
||||
* * Write to shared mem to notify the remote once it finishes
|
||||
* calculation.
|
||||
*
|
||||
* @param[in] ipi_io - IPI metal i/o region
|
||||
* @param[in] shm_io - shared memory metal i/o region
|
||||
* @return - If setup failed, return the corresponding error number. Otherwise
|
||||
* return 0 on success.
|
||||
*/
|
||||
int atomic_add_shmemd(struct metal_io_region *ipi_io,
|
||||
struct metal_io_region *shm_io)
|
||||
{
|
||||
atomic_int *shm_int;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
int i;
|
||||
|
||||
LPRINTF("Starting atomic add on shared memory demo.\n");
|
||||
shm_int = (atomic_int *)metal_io_virt(shm_io,
|
||||
ATOMIC_INT_OFFSET);
|
||||
|
||||
/* Wait for notification from the remote to start the demo */
|
||||
wait_for_notified(&remote_nkicked);
|
||||
|
||||
/* Do atomic add over the shared memory */
|
||||
for (i = 0; i < ITERATIONS; i++)
|
||||
atomic_fetch_add(shm_int, 1);
|
||||
|
||||
/* Write to IPI trigger register to notify the remote it has finished
|
||||
* the atomic operation. */
|
||||
metal_io_write32(ipi_io, IPI_TRIG_OFFSET, ipi_mask);
|
||||
|
||||
LPRINTF("Shared memory with atomics test finished\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atomic_shmem_demod()
|
||||
{
|
||||
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("atomic operation over shared memory");
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
if (!ipi_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = atomic_add_shmemd(ipi_io, shm_io);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
/*****************************************************************************
|
||||
* shmem_demod.c
|
||||
* This demo demonstrates the use of shared mem. between the APU and RPU.
|
||||
* This demo does so via the following steps:
|
||||
*
|
||||
* 1. Get the shared memory device I/O region.
|
||||
* 2. Clear the demo control value in shared memory.
|
||||
* 3. Check the demo control value in the shared memory to wait for APU
|
||||
* to start the demo.
|
||||
* 4. Once the demo control value indicates the demo starts, it polls on
|
||||
* RX available value to see if there is new RX message available.
|
||||
* 5. If there is a new RX message available, it reads the message from
|
||||
* the shared memory
|
||||
* 6. It echos back the message to the shared memory
|
||||
* 7. It increases the TX available value in the shared memory to notify
|
||||
* the other end there is a message available to read.
|
||||
* 8. Check if the demo control value and the RX available values to see
|
||||
* if demo finishes and if there is new RX data available.
|
||||
*
|
||||
* Here is the Shared memory structure of this demo:
|
||||
* |0 | 4Bytes | DEMO control status shows if demo starts or not |
|
||||
* |0x04 | 4Bytes | number of APU to RPU buffers available to RPU |
|
||||
* |0x08 | 4Bytes | number of APU to RPU buffers consumed by RPU |
|
||||
* |0x0c | 4Bytes | number of RPU to APU buffers available to APU |
|
||||
* |0x10 | 4Bytes | number of RPU to APU buffers consumed by APU |
|
||||
* |0x14 | 1KBytes | APU to RPU buffer |
|
||||
* ... ...
|
||||
* |0x800 | 1KBytes | RPU to APU buffer |
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/alloc.h>
|
||||
#include "common.h"
|
||||
|
||||
/* Shared memory offsets */
|
||||
#define SHM_DEMO_CNTRL_OFFSET 0x0
|
||||
#define SHM_RX_AVAIL_OFFSET 0x04
|
||||
#define SHM_RX_USED_OFFSET 0x08
|
||||
#define SHM_TX_AVAIL_OFFSET 0x0C
|
||||
#define SHM_TX_USED_OFFSET 0x10
|
||||
#define SHM_RX_BUFFER_OFFSET 0x14
|
||||
#define SHM_TX_BUFFER_OFFSET 0x800
|
||||
|
||||
#define SHM_BUFFER_SIZE 0x400
|
||||
|
||||
#define DEMO_STATUS_IDLE 0x0
|
||||
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
|
||||
|
||||
struct msg_hdr_s {
|
||||
uint32_t index;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief shmem_echod() - Show use of shared memory with libmetal.
|
||||
* Wait for message from APU. Once received, read and echo it back.
|
||||
*
|
||||
* @param[in] shm_io - metal i/o region of the shared memory
|
||||
* @return - return 0 on success, otherwise return error number indicating
|
||||
* type of error
|
||||
*/
|
||||
static int shmem_echod(struct metal_io_region *shm_io)
|
||||
{
|
||||
void *data = NULL;
|
||||
struct msg_hdr_s *msg_hdr;
|
||||
unsigned int rx_count = 0;
|
||||
unsigned int len;
|
||||
int ret = 0;
|
||||
|
||||
/* clear demo status value */
|
||||
metal_io_write32(shm_io, SHM_DEMO_CNTRL_OFFSET, 0);
|
||||
|
||||
/* allocate memory for receiving data */
|
||||
data = metal_allocate_memory(SHM_BUFFER_SIZE);
|
||||
if (!data) {
|
||||
LPERROR("Failed to allocate memory.\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LPRINTF("Wait for shared memory demo to start.\r\n");
|
||||
while (metal_io_read32(shm_io, SHM_DEMO_CNTRL_OFFSET) !=
|
||||
DEMO_STATUS_START);
|
||||
|
||||
LPRINTF("Demo has started.\r\n");
|
||||
/* wait for message is available */
|
||||
while(metal_io_read32(shm_io, SHM_DEMO_CNTRL_OFFSET) ==
|
||||
DEMO_STATUS_START) {
|
||||
if (metal_io_read32(shm_io, SHM_RX_AVAIL_OFFSET)
|
||||
== rx_count)
|
||||
continue;
|
||||
/* Message is available, read the message header */
|
||||
ret = metal_io_block_read(shm_io, SHM_RX_BUFFER_OFFSET,
|
||||
data, sizeof(struct msg_hdr_s));
|
||||
if (ret < 0){
|
||||
LPERROR("Unable to metal_io_block_read()\n");
|
||||
return ret;
|
||||
}
|
||||
msg_hdr = (struct msg_hdr_s *)data;
|
||||
/* Get the length of the data, if the data length is
|
||||
* too large, truncate it. */
|
||||
len = msg_hdr->len;
|
||||
if (msg_hdr->len >
|
||||
(SHM_BUFFER_SIZE - sizeof(*msg_hdr))) {
|
||||
LPERROR("Input message is too long %u.\n",
|
||||
msg_hdr->len);
|
||||
len = SHM_BUFFER_SIZE - sizeof(*msg_hdr);
|
||||
}
|
||||
/* Read the message data */
|
||||
ret = metal_io_block_read(shm_io,
|
||||
SHM_RX_BUFFER_OFFSET + sizeof(*msg_hdr),
|
||||
data + sizeof(*msg_hdr), len);
|
||||
|
||||
rx_count++;
|
||||
ret = metal_io_block_write(shm_io,
|
||||
SHM_TX_BUFFER_OFFSET,
|
||||
(void*)data, sizeof(*msg_hdr) + len);
|
||||
if (ret < 0){
|
||||
LPERROR("Unable to metal_io_block_write()\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* increase TX available value to notify the other end
|
||||
* there is data ready to read. */
|
||||
metal_io_write32(shm_io, SHM_TX_AVAIL_OFFSET, rx_count);
|
||||
}
|
||||
|
||||
metal_free_memory(data);
|
||||
LPRINTF("Shared memory test finished\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int shmem_demod()
|
||||
{
|
||||
struct metal_io_region *io = NULL;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("shared memory");
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
io = metal_device_io_region(shm_dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = shmem_echod(io);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* shmem_latency_demod.c
|
||||
* This is the remote side of the IPI latency measurement demo.
|
||||
* This demo does the follwing steps:
|
||||
*
|
||||
* 1. Get the shared memory device libmetal I/O region.
|
||||
* 1. Get the TTC timer device libemtal I/O region.
|
||||
* 2. Get IPI device libmetal I/O region and the IPI interrupt vector.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 6. When it receives IPI interrupt, the IPI interrupt handler marked the
|
||||
* remote has kicked.
|
||||
* 7. Check the shared memory to see if demo is on. If the demo is on,
|
||||
* copy data from the shared memory to local memory, stop the APU to RPU
|
||||
* timer. Reset the RPU to APU TTC counter, copy data from local memory
|
||||
* to shared memory, kick IPI to notify the remote.
|
||||
* 8. If the shared memory indicates the demo is off, cleanup resource:
|
||||
* disable IPI interrupt and deregister the IPI interrupt handler.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include "common.h"
|
||||
|
||||
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
|
||||
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
|
||||
|
||||
#define TTC_CLK_FREQ_HZ 100000000
|
||||
|
||||
/* Shared memory offset */
|
||||
#define SHM_DEMO_CNTRL_OFFSET 0x0 /* Shared memory for the demo status */
|
||||
#define SHM_BUFF_OFFSET_RX 0x1000 /* Shared memory RX buffer start offset */
|
||||
#define SHM_BUFF_OFFSET_TX 0x2000 /* Shared memory TX buffer start offset */
|
||||
|
||||
#define DEMO_STATUS_IDLE 0x0
|
||||
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
|
||||
|
||||
#define BUF_SIZE_MAX 4096
|
||||
|
||||
struct channel_s {
|
||||
struct metal_io_region *ipi_io; /* IPI metal i/o region */
|
||||
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
|
||||
struct metal_io_region *ttc_io; /* TTC metal i/o region */
|
||||
uint32_t ipi_mask; /* RPU IPI mask */
|
||||
atomic_int remote_nkicked; /* 0 - kicked from remote */
|
||||
};
|
||||
|
||||
struct msg_hdr_s {
|
||||
uint32_t index;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief reset_timer() - function to reset TTC counter
|
||||
* Set the RST bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void reset_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_RST_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop_timer() - function to stop TTC counter
|
||||
* Set the disable bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void stop_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_DIS_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_irq_handler() - IPI interrupt handler
|
||||
* It will clear the notified flag to mark it's got an IPI interrupt.
|
||||
* It will stop the RPU->APU timer and will clear the notified
|
||||
* flag to mark it's got an IPI interrupt
|
||||
*
|
||||
* @param[in] vect_id - IPI interrupt vector ID
|
||||
* @param[in/out] priv - communication channel data for this application.
|
||||
*
|
||||
* @return - If the IPI interrupt is triggered by its remote, it returns
|
||||
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
|
||||
* not the interrupt it expected.
|
||||
*
|
||||
*/
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)priv;
|
||||
uint32_t val;
|
||||
|
||||
(void)vect_id;
|
||||
|
||||
if (ch) {
|
||||
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ch->ipi_mask) {
|
||||
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
|
||||
ch->ipi_mask);
|
||||
atomic_flag_clear(&ch->remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief measure_shmem_latencyd() - measure shmem latency with libmetal
|
||||
* Loop until APU tells RPU to stop via shared memory.
|
||||
* In loop, wait for interrupt (interrupt handler stops APU to
|
||||
* RPU TTC counter). Then reset count on RPU to APU TTC counter
|
||||
* and kick IPI to notify APU.
|
||||
*
|
||||
* @param[in] ch - channel information
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
static int measure_shmem_latencyd(struct channel_s *ch)
|
||||
{
|
||||
void *lbuf = NULL;
|
||||
struct msg_hdr_s *msg_hdr;
|
||||
int ret = 0;
|
||||
|
||||
/* allocate memory for receiving data */
|
||||
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
|
||||
if (!lbuf) {
|
||||
LPERROR("Failed to allocate memory.\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LPRINTF("Starting IPI latency demo\r\n");
|
||||
while(1) {
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
if (metal_io_read32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET) ==
|
||||
DEMO_STATUS_START) {
|
||||
/* Read message header from shared memory */
|
||||
metal_io_block_read(ch->shm_io, SHM_BUFF_OFFSET_RX,
|
||||
lbuf, sizeof(struct msg_hdr_s));
|
||||
msg_hdr = (struct msg_hdr_s *)lbuf;
|
||||
|
||||
/* Check if the message header is valid */
|
||||
if (msg_hdr->len > (BUF_SIZE_MAX - sizeof(*msg_hdr))) {
|
||||
LPERROR("wrong msg: length invalid: %u, %u.\n",
|
||||
BUF_SIZE_MAX - sizeof(*msg_hdr),
|
||||
msg_hdr->len);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/* Read message */
|
||||
metal_io_block_read(ch->shm_io,
|
||||
SHM_BUFF_OFFSET_RX + sizeof(*msg_hdr),
|
||||
lbuf + sizeof(*msg_hdr), msg_hdr->len);
|
||||
/* Stop APU to RPU TTC counter */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
|
||||
/* Reset RPU to APU TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
/* Copy the message back to the other end */
|
||||
metal_io_block_write(ch->shm_io, SHM_BUFF_OFFSET_TX,
|
||||
msg_hdr,
|
||||
sizeof(*msg_hdr) + msg_hdr->len);
|
||||
|
||||
/* Kick IPI to notify the remote */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
|
||||
ch->ipi_mask);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
metal_free_memory(lbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int shmem_latency_demod()
|
||||
{
|
||||
struct channel_s ch;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("shared memory latency");
|
||||
memset(&ch, 0, sizeof(ch));
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!ch.shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get TTC IO region */
|
||||
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
|
||||
if (!ch.ttc_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ch.ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
|
||||
ch.ipi_mask = IPI_MASK;
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&ch.remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = measure_shmem_latencyd(&ch);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* shmem_throughput_demo_task.c
|
||||
* This is the remote side of the shared memory throughput demo.
|
||||
* This demo does the following steps:
|
||||
*
|
||||
* 1. Get the shared memory device libmetal I/O region.
|
||||
* 1. Get the TTC timer device libemtal I/O region.
|
||||
* 2. Get IPI device libmetal I/O region and the IPI interrupt vector.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 6. Download throughput measurement:
|
||||
* Start TTC RPU counter, wait for IPI kick, check if data is available,
|
||||
* if yes, read as much data as possible from shared memory. It will
|
||||
* iterates untill 1000 packages have been received, stop TTC RPU counter
|
||||
* and kick IPI to notify the remote. Repeat for different package size.
|
||||
* 7. Upload throughput measurement:
|
||||
* Start TTC RPU counter, write data to shared memory and kick IPI to
|
||||
* notify remote. It will iterate for 1000 times, stop TTC RPU counter.
|
||||
* wait for APU IPI kick to know APU has finished receiving packages.
|
||||
* Kick IPI to notify it TTC RPU conter value is ready to read.
|
||||
* Repeat for different package size.
|
||||
* 8. Cleanup resource:
|
||||
* disable IPI interrupt and deregister the IPI interrupt handler.
|
||||
*
|
||||
* Here is the Shared memory structure of this demo:
|
||||
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
|
||||
* |0x04 - 0x1FFFFF | address array for shared buffers from APU to RPU |
|
||||
* |0x200000 - 0x200004 | number of RPU to APU buffers available to APU |
|
||||
* |0x200004 - 0x3FFFFF | address array for shared buffers from RPU to APU |
|
||||
* |0x400000 - 0x7FFFFF | APU to RPU buffers |
|
||||
* |0x800000 - 0xAFFFFF | RPU to APU buffers |
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include <metal/alloc.h>
|
||||
#include "common.h"
|
||||
|
||||
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
|
||||
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
|
||||
|
||||
/* Shared memory offsets */
|
||||
#define SHM_DESC_OFFSET_RX 0x0
|
||||
#define SHM_BUFF_OFFSET_RX 0x400000
|
||||
#define SHM_DESC_OFFSET_TX 0x200000
|
||||
#define SHM_BUFF_OFFSET_TX 0x800000
|
||||
|
||||
/* Shared memory descriptors offset */
|
||||
#define SHM_DESC_AVAIL_OFFSET 0x00
|
||||
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x04
|
||||
|
||||
#define BUF_SIZE_MAX 4096
|
||||
#define PKG_SIZE_MAX 1024
|
||||
#define PKG_SIZE_MIN 16
|
||||
#define TOTAL_DATA_SIZE (1024 * 4096)
|
||||
|
||||
struct channel_s {
|
||||
struct metal_io_region *ipi_io; /* IPI metal i/o region */
|
||||
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
|
||||
struct metal_io_region *ttc_io; /* TTC metal i/o region */
|
||||
uint32_t ipi_mask; /* RPU IPI mask */
|
||||
atomic_int remote_nkicked; /* 0 - kicked from remote */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief reset_timer() - function to reset TTC counter
|
||||
* Set the RST bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void reset_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_RST_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop_timer() - function to stop TTC counter
|
||||
* Set the disable bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void stop_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_DIS_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_irq_handler() - IPI interrupt handler
|
||||
* It will clear the notified flag to mark it's got an IPI interrupt.
|
||||
* It will stop the RPU->APU timer and will clear the notified
|
||||
* flag to mark it's got an IPI interrupt
|
||||
*
|
||||
* @param[in] vect_id - IPI interrupt vector ID
|
||||
* @param[in/out] priv - communication channel data for this application.
|
||||
*
|
||||
* @return - If the IPI interrupt is triggered by its remote, it returns
|
||||
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
|
||||
* not the interrupt it expected.
|
||||
*
|
||||
*/
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)priv;
|
||||
uint32_t val;
|
||||
|
||||
(void)vect_id;
|
||||
|
||||
if (ch) {
|
||||
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ch->ipi_mask) {
|
||||
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
|
||||
ch->ipi_mask);
|
||||
atomic_flag_clear(&ch->remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief measure_shmem_throughputd() - measure shmem throughpput with libmetal
|
||||
* - Download throughput measurement:
|
||||
* Start TTC RPU counter, wait for IPI kick, check if data is
|
||||
* available, if yes, read as much data as possible from shared
|
||||
* memory. It will iterates untill 1000 packages have been received,
|
||||
* stop TTC RPU counter and kick IPI to notify the remote. Repeat
|
||||
* for different package size.
|
||||
* - Upload throughput measurement:
|
||||
* Start TTC RPU counter, write data to shared memory and kick IPI
|
||||
* to notify remote. It will iterate for 1000 times, stop TTC RPU
|
||||
* counter.Wait for APU IPI kick to know APU has received all the
|
||||
* packages. Kick IPI to notify it TTC RPU conter value is ready to
|
||||
* read. Repeat for different package size.
|
||||
*
|
||||
* @param[in] ch - channel information
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
static int measure_shmem_throughputd(struct channel_s *ch)
|
||||
{
|
||||
void *lbuf = NULL;
|
||||
int ret = 0;
|
||||
size_t s;
|
||||
uint32_t rx_count, rx_avail, tx_count, iterations;
|
||||
unsigned long tx_avail_offset, rx_avail_offset;
|
||||
unsigned long tx_addr_offset, rx_addr_offset;
|
||||
unsigned long tx_data_offset, rx_data_offset;
|
||||
uint32_t buf_phy_addr_32;
|
||||
|
||||
/* allocate memory for receiving data */
|
||||
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
|
||||
if (!lbuf) {
|
||||
LPERROR("Failed to allocate memory.\r\n");
|
||||
return -1;
|
||||
}
|
||||
memset(lbuf, 0xA, BUF_SIZE_MAX);
|
||||
|
||||
/* Clear shared memory */
|
||||
metal_io_block_set(ch->shm_io, 0, 0, metal_io_region_size(ch->shm_io));
|
||||
|
||||
LPRINTF("Starting shared mem throughput demo\n");
|
||||
|
||||
/* for each data size, measure block receive throughput */
|
||||
for (s = PKG_SIZE_MIN; s <= PKG_SIZE_MAX; s <<= 1) {
|
||||
rx_count = 0;
|
||||
iterations = TOTAL_DATA_SIZE / s;
|
||||
/* Set rx buffer address offset */
|
||||
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
|
||||
rx_addr_offset = SHM_DESC_OFFSET_RX +
|
||||
SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
/* Data has arrived, seasure start. Reset RPU TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
while (1) {
|
||||
rx_avail = metal_io_read32(ch->shm_io, rx_avail_offset);
|
||||
|
||||
while(rx_count != rx_avail) {
|
||||
/* Get the buffer location from the shared
|
||||
* memory rx address array.
|
||||
*/
|
||||
buf_phy_addr_32 = metal_io_read32(ch->shm_io,
|
||||
rx_addr_offset);
|
||||
rx_data_offset = metal_io_phys_to_offset(
|
||||
ch->shm_io,
|
||||
(metal_phys_addr_t)buf_phy_addr_32);
|
||||
if (rx_data_offset == METAL_BAD_OFFSET) {
|
||||
LPERROR(
|
||||
"[%u]failed to get rx offset: 0x%x, 0x%lx.\n",
|
||||
rx_count, buf_phy_addr_32,
|
||||
metal_io_phys(ch->shm_io,
|
||||
rx_addr_offset));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rx_addr_offset += sizeof(buf_phy_addr_32);
|
||||
/* Read data from shared memory */
|
||||
metal_io_block_read(ch->shm_io, rx_data_offset,
|
||||
lbuf, s);
|
||||
rx_count++;
|
||||
}
|
||||
if (rx_count < iterations)
|
||||
/* Need to wait for more data */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
else
|
||||
break;
|
||||
}
|
||||
/* Stop RPU TTC counter */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
/* Clear remote kicked flag -- 0 is kicked */
|
||||
atomic_init(&ch->remote_nkicked, 1);
|
||||
/* Kick IPI to notify RPU TTC counter value is ready */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
}
|
||||
|
||||
/* for each data size, measure send throughput */
|
||||
for (s = PKG_SIZE_MIN; s <= PKG_SIZE_MAX; s <<= 1) {
|
||||
tx_count = 0;
|
||||
iterations = TOTAL_DATA_SIZE / s;
|
||||
|
||||
/* Set tx buffer address offset */
|
||||
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
|
||||
tx_addr_offset = SHM_DESC_OFFSET_TX +
|
||||
SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
|
||||
/* Wait for APU to signal it is ready for the measurement */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
/* Data has arrived, seasure start. Reset RPU TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
while (tx_count < iterations) {
|
||||
/* Write data to the shared memory*/
|
||||
metal_io_block_write(ch->shm_io, tx_data_offset,
|
||||
lbuf, s);
|
||||
|
||||
/* Write to the address array to tell the other end
|
||||
* the buffer address.
|
||||
*/
|
||||
buf_phy_addr_32 = (uint32_t)metal_io_phys(ch->shm_io,
|
||||
tx_data_offset);
|
||||
metal_io_write32(ch->shm_io, tx_addr_offset,
|
||||
buf_phy_addr_32);
|
||||
tx_data_offset += s;
|
||||
tx_addr_offset += sizeof(buf_phy_addr_32);
|
||||
|
||||
/* Increase number of available buffers */
|
||||
tx_count++;
|
||||
metal_io_write32(ch->shm_io, tx_avail_offset, tx_count);
|
||||
/* Kick IPI to notify remote data is ready in the
|
||||
* shared memory */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
|
||||
ch->ipi_mask);
|
||||
}
|
||||
/* Stop RPU TTC counter */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
/* Wait for IPI kick to know when the remote is ready
|
||||
* to read the TTC counter value */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
/* Kick IPI to notify RPU TTC counter value is ready */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
}
|
||||
|
||||
out:
|
||||
if (lbuf)
|
||||
metal_free_memory(lbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int shmem_throughput_demod()
|
||||
{
|
||||
struct channel_s ch;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("shared memory throughput");
|
||||
memset(&ch, 0, sizeof(ch));
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
if (!shm_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!ch.shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get TTC IO region */
|
||||
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
|
||||
if (!ch.ttc_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ch.ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
|
||||
ch.ipi_mask = IPI_MASK;
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&ch.remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = measure_shmem_throughputd(&ch);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,360 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2017 Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include <xparameters.h>
|
||||
#include <xil_cache.h>
|
||||
#include <xil_exception.h>
|
||||
#include <xstatus.h>
|
||||
#include <xscugic.h>
|
||||
#include <xreg_cortexr5.h>
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/irq.h>
|
||||
|
||||
#include "platform_config.h"
|
||||
#include "common.h"
|
||||
|
||||
#ifdef STDOUT_IS_16550
|
||||
#include <xuartns550_l.h>
|
||||
|
||||
#define UART_BAUD 9600
|
||||
#endif
|
||||
|
||||
#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
|
||||
|
||||
#define IPI_IRQ_VECT_ID 65
|
||||
|
||||
#define SHM_BASE_ADDR 0x3ED80000
|
||||
#define TTC0_BASE_ADDR 0xFF110000
|
||||
#define IPI_BASE_ADDR 0xFF310000
|
||||
|
||||
/* Default generic I/O region page shift */
|
||||
/* Each I/O region can contain multiple pages.
|
||||
* In baremetal system, the memory mapping is flat, there is no
|
||||
* virtual memory.
|
||||
* We can assume there is only one page in the whole baremetal system.
|
||||
*/
|
||||
#define DEFAULT_PAGE_SHIFT (-1UL)
|
||||
#define DEFAULT_PAGE_MASK (-1UL)
|
||||
|
||||
static XScuGic xInterruptController;
|
||||
|
||||
const metal_phys_addr_t metal_phys[] = {
|
||||
IPI_BASE_ADDR, /**< base IPI address */
|
||||
SHM_BASE_ADDR, /**< shared memory base address */
|
||||
TTC0_BASE_ADDR, /**< base TTC0 address */
|
||||
};
|
||||
|
||||
/* Define metal devices table for IPI, shared memory and TTC devices.
|
||||
* Linux system uses device tree to describe devices. Unlike Linux,
|
||||
* there is no standard device abstraction for baremetal system, we
|
||||
* uses libmetal devices structure to describe the devices we used in
|
||||
* the example.
|
||||
* The IPI, shared memory and TTC devices are memory mapped
|
||||
* devices. For this type of devices, it is required to provide
|
||||
* accessible memory mapped regions, and interrupt information.
|
||||
* In baremetal system, the memory mapping is flat. As you can see
|
||||
* in the table before, we set the virtual address "virt" the same
|
||||
* as the physical address.
|
||||
*/
|
||||
static struct metal_device metal_dev_table[] = {
|
||||
{
|
||||
/* IPI device */
|
||||
.name = IPI_DEV_NAME,
|
||||
.bus = NULL,
|
||||
.num_regions = 1,
|
||||
.regions = {
|
||||
{
|
||||
.virt = (void *)IPI_BASE_ADDR,
|
||||
.physmap = &metal_phys[0],
|
||||
.size = 0x1000,
|
||||
.page_shift = DEFAULT_PAGE_SHIFT,
|
||||
.page_mask = DEFAULT_PAGE_MASK,
|
||||
.mem_flags = DEVICE_NONSHARED | PRIV_RW_USER_RW,
|
||||
.ops = {NULL},
|
||||
}
|
||||
},
|
||||
.node = {NULL},
|
||||
.irq_num = 1,
|
||||
.irq_info = (void *)IPI_IRQ_VECT_ID,
|
||||
},
|
||||
{
|
||||
/* Shared memory management device */
|
||||
.name = SHM_DEV_NAME,
|
||||
.bus = NULL,
|
||||
.num_regions = 1,
|
||||
.regions = {
|
||||
{
|
||||
.virt = (void *)SHM_BASE_ADDR,
|
||||
.physmap = &metal_phys[1],
|
||||
.size = 0x1000000,
|
||||
.page_shift = DEFAULT_PAGE_SHIFT,
|
||||
.page_mask = DEFAULT_PAGE_MASK,
|
||||
.mem_flags = NORM_SHARED_NCACHE |
|
||||
PRIV_RW_USER_RW,
|
||||
.ops = {NULL},
|
||||
}
|
||||
},
|
||||
.node = {NULL},
|
||||
.irq_num = 0,
|
||||
.irq_info = NULL,
|
||||
},
|
||||
{
|
||||
/* ttc0 */
|
||||
.name = TTC_DEV_NAME,
|
||||
.bus = NULL,
|
||||
.num_regions = 1,
|
||||
.regions = {
|
||||
{
|
||||
.virt = (void *)TTC0_BASE_ADDR ,
|
||||
.physmap = &metal_phys[2],
|
||||
.size = 0x1000,
|
||||
.page_shift = DEFAULT_PAGE_SHIFT,
|
||||
.page_mask = DEFAULT_PAGE_MASK,
|
||||
.mem_flags = DEVICE_NONSHARED | PRIV_RW_USER_RW,
|
||||
.ops = {NULL},
|
||||
}
|
||||
},
|
||||
.node = {NULL},
|
||||
.irq_num = 0,
|
||||
.irq_info = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Extern global variables
|
||||
*/
|
||||
struct metal_device *ipi_dev = NULL;
|
||||
struct metal_device *shm_dev = NULL;
|
||||
struct metal_device *ttc_dev = NULL;
|
||||
|
||||
/**
|
||||
* @brief enable_caches() - Enable caches
|
||||
*/
|
||||
void enable_caches()
|
||||
{
|
||||
#ifdef __MICROBLAZE__
|
||||
#ifdef XPAR_MICROBLAZE_USE_ICACHE
|
||||
Xil_ICacheEnable();
|
||||
#endif
|
||||
#ifdef XPAR_MICROBLAZE_USE_DCACHE
|
||||
Xil_DCacheEnable();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief disable_caches() - Disable caches
|
||||
*/
|
||||
void disable_caches()
|
||||
{
|
||||
Xil_DCacheDisable();
|
||||
Xil_ICacheDisable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief init_uart() - Initialize UARTs
|
||||
*/
|
||||
void init_uart()
|
||||
{
|
||||
#ifdef STDOUT_IS_16550
|
||||
XUartNs550_SetBaud(STDOUT_BASEADDR, XPAR_XUARTNS550_CLOCK_HZ,
|
||||
UART_BAUD);
|
||||
XUartNs550_SetLineControlReg(STDOUT_BASEADDR, XUN_LCR_8_DATA_BITS);
|
||||
#endif
|
||||
/* Bootrom/BSP configures PS7/PSU UART to 115200 bps */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief init_irq() - Initialize GIC and connect IPI interrupt
|
||||
* This function will initialize the GIC and connect the IPI
|
||||
* interrupt.
|
||||
*
|
||||
* @return 0 - succeeded, non-0 for failures
|
||||
*/
|
||||
int init_irq()
|
||||
{
|
||||
int ret = 0;
|
||||
XScuGic_Config *IntcConfig; /* The configuration parameters of
|
||||
* the interrupt controller */
|
||||
|
||||
Xil_ExceptionDisable();
|
||||
/*
|
||||
* Initialize the interrupt controller driver
|
||||
*/
|
||||
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
|
||||
if (NULL == IntcConfig) {
|
||||
return (int)XST_FAILURE;
|
||||
}
|
||||
|
||||
ret = XScuGic_CfgInitialize(&xInterruptController, IntcConfig,
|
||||
IntcConfig->CpuBaseAddress);
|
||||
if (ret != XST_SUCCESS) {
|
||||
return (int)XST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the interrupt handler to the hardware interrupt handling
|
||||
* logic in the ARM processor.
|
||||
*/
|
||||
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
|
||||
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
|
||||
&xInterruptController);
|
||||
|
||||
Xil_ExceptionEnable();
|
||||
/* Connect IPI Interrupt ID with libmetal ISR */
|
||||
XScuGic_Connect(&xInterruptController, IPI_IRQ_VECT_ID,
|
||||
(Xil_ExceptionHandler)metal_irq_isr,
|
||||
(void *)IPI_IRQ_VECT_ID);
|
||||
|
||||
XScuGic_Enable(&xInterruptController, IPI_IRQ_VECT_ID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief platform_register_metal_device() - Statically Register libmetal
|
||||
* devices.
|
||||
* This function registers the IPI, shared memory and
|
||||
* TTC devices to the libmetal generic bus.
|
||||
* Libmetal uses bus structure to group the devices. Before you can
|
||||
* access the device with libmetal device operation, you will need to
|
||||
* register the device to a libmetal supported bus.
|
||||
* For non-Linux system, libmetal only supports "generic" bus, which is
|
||||
* used to manage the memory mapped devices.
|
||||
*
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
int platform_register_metal_device(void)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
struct metal_device *dev;
|
||||
|
||||
for (i = 0; i < sizeof(metal_dev_table)/sizeof(struct metal_device);
|
||||
i++) {
|
||||
dev = &metal_dev_table[i];
|
||||
xil_printf("registering: %d, name=%s\n", i, dev->name);
|
||||
ret = metal_register_generic_device(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief open_metal_devices() - Open registered libmetal devices.
|
||||
* This function opens all the registered libmetal devices.
|
||||
*
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
int open_metal_devices(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Open shared memory device */
|
||||
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &shm_dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Open IPI device */
|
||||
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &ipi_dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Open TTC device */
|
||||
ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &ttc_dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", TTC_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief close_metal_devices() - close libmetal devices
|
||||
* This function closes all the libmetal devices which have
|
||||
* been opened.
|
||||
*
|
||||
*/
|
||||
void close_metal_devices(void)
|
||||
{
|
||||
/* Close shared memory device */
|
||||
if (shm_dev)
|
||||
metal_device_close(shm_dev);
|
||||
|
||||
/* Close IPI device */
|
||||
if (ipi_dev)
|
||||
metal_device_close(ipi_dev);
|
||||
|
||||
/* Close TTC device */
|
||||
if (ttc_dev)
|
||||
metal_device_close(ttc_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sys_init() - Register libmetal devices.
|
||||
* This function register the libmetal generic bus, and then
|
||||
* register the IPI, shared memory descriptor and shared memory
|
||||
* devices to the libmetal generic bus.
|
||||
*
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
int sys_init()
|
||||
{
|
||||
struct metal_init_params metal_param = METAL_INIT_DEFAULTS;
|
||||
int ret;
|
||||
|
||||
enable_caches();
|
||||
init_uart();
|
||||
if (init_irq()) {
|
||||
LPERROR("Failed to initialize interrupt\n");
|
||||
}
|
||||
|
||||
/* Initialize libmetal environment */
|
||||
metal_init(&metal_param);
|
||||
/* Register libmetal devices */
|
||||
ret = platform_register_metal_device();
|
||||
if (ret) {
|
||||
LPERROR("%s: failed to register devices: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Open libmetal devices which have been registered */
|
||||
ret = open_metal_devices();
|
||||
if (ret) {
|
||||
LPERROR("%s: failed to open devices: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sys_cleanup() - system cleanup
|
||||
* This function finish the libmetal environment
|
||||
* and disable caches.
|
||||
*
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
void sys_cleanup()
|
||||
{
|
||||
/* Close libmetal devices which have been opened */
|
||||
close_metal_devices();
|
||||
/* Finish libmetal environment */
|
||||
metal_finish();
|
||||
disable_caches();
|
||||
}
|
||||
|
@@ -0,0 +1,17 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2017 Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __SYS_INIT_H__
|
||||
#define __SYS_INIT_H__
|
||||
|
||||
#include "platform_config.h"
|
||||
|
||||
int sys_init();
|
||||
void sys_cleanup();
|
||||
|
||||
#endif /* __SYS_INIT_H__ */
|
@@ -0,0 +1,4 @@
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
|
||||
add_subdirectory(${PROJECT_MACHINE})
|
||||
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
|
@@ -0,0 +1 @@
|
||||
add_subdirectory(zynqmp_amp_demo)
|
@@ -0,0 +1,44 @@
|
||||
collector_list (_list PROJECT_INC_DIRS)
|
||||
include_directories (${_list} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
collector_list (_list PROJECT_LIB_DIRS)
|
||||
link_directories (${_list})
|
||||
|
||||
collector_list (_deps PROJECT_LIB_DEPS)
|
||||
|
||||
set (_src_common ${CMAKE_CURRENT_SOURCE_DIR}/sys_init.c)
|
||||
foreach (_app libmetal_amp_demo libmetal_amp_demod)
|
||||
set (_src ${CMAKE_CURRENT_SOURCE_DIR}/${_app}.c)
|
||||
list(APPEND _src ${_src_common})
|
||||
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/shmem_demo.c)
|
||||
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/shmem_atomic_demo.c)
|
||||
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/ipi_shmem_demo.c)
|
||||
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/ipi_latency_demo.c)
|
||||
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/shmem_latency_demo.c)
|
||||
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/shmem_throughput_demo.c)
|
||||
if (WITH_SHARED_LIB)
|
||||
add_executable (${_app}-share ${_src})
|
||||
if (PROJECT_EC_FLAGS)
|
||||
string(REPLACE " " ";" _ec_flgs ${PROJECT_EC_FLAGS})
|
||||
target_compile_options (${_app}-share PUBLIC ${_ec_flgs})
|
||||
endif (PROJECT_EC_FLAGS)
|
||||
target_link_libraries (${_app}-share ${PROJECT_NAME}-shared ${_deps})
|
||||
install (TARGETS ${_app}-share RUNTIME DESTINATION bin)
|
||||
add_dependencies (${_app}-share ${PROJECT_NAME}-shared)
|
||||
endif (WITH_SHARED_LIB)
|
||||
|
||||
if (WITH_STATIC_LIB)
|
||||
if (${PROJECT_SYSTEM} STREQUAL "linux")
|
||||
add_executable (${_app}-static ${_src})
|
||||
if (PROJECT_EC_FLAGS)
|
||||
string(REPLACE " " ";" _ec_flgs ${PROJECT_EC_FLAGS})
|
||||
target_compile_options (${_app}-static PUBLIC ${_ec_flgs})
|
||||
endif (PROJECT_EC_FLAGS)
|
||||
target_link_libraries (${_app}-static ${PROJECT_NAME}-static ${_deps})
|
||||
install (TARGETS ${_app}-static RUNTIME DESTINATION bin)
|
||||
endif (${PROJECT_SYSTEM} STREQUAL "linux")
|
||||
endif (WITH_STATIC_LIB)
|
||||
endforeach (_app)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
||||
|
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef __COMMON_H__
|
||||
#define __COMMON_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <metal/irq.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/cpu.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define BUS_NAME "platform"
|
||||
#define IPI_DEV_NAME "ff340000.ipi"
|
||||
#define SHM_DEV_NAME "3ed80000.shm"
|
||||
#define TTC_DEV_NAME "ff110000.timer"
|
||||
|
||||
/* Apply this snippet to the device tree in an overlay so that
|
||||
* Linux userspace can see and use TTC0:
|
||||
* &TTC0 {
|
||||
* compatible = "ttc0_libmetal_demo";
|
||||
* status = "okay";
|
||||
* };
|
||||
*/
|
||||
|
||||
|
||||
/* IPI registers offset */
|
||||
#define IPI_TRIG_OFFSET 0x0 /* IPI trigger reg offset */
|
||||
#define IPI_OBS_OFFSET 0x4 /* IPI observation reg offset */
|
||||
#define IPI_ISR_OFFSET 0x10 /* IPI interrupt status reg offset */
|
||||
#define IPI_IMR_OFFSET 0x14 /* IPI interrupt mask reg offset */
|
||||
#define IPI_IER_OFFSET 0x18 /* IPI interrupt enable reg offset */
|
||||
#define IPI_IDR_OFFSET 0x1C /* IPI interrup disable reg offset */
|
||||
|
||||
#define IPI_MASK 0x100 /* IPI mask for kick from RPU. */
|
||||
|
||||
/* TTC counter offsets */
|
||||
#define XTTCPS_CLK_CNTRL_OFFSET 0x0 /* TTC counter clock control reg offset */
|
||||
#define XTTCPS_CNT_CNTRL_OFFSET 0xC /* TTC counter control reg offset */
|
||||
#define XTTCPS_CNT_VAL_OFFSET 0x18 /* TTC counter val reg offset */
|
||||
#define XTTCPS_CNT_OFFSET(ID) ((ID) == 1 ? 0 : 1 << (ID)) /* TTC counter offset
|
||||
ID is from 1 to 3 */
|
||||
|
||||
/* TTC counter control masks */
|
||||
#define XTTCPS_CNT_CNTRL_RST_MASK 0x10U /* TTC counter control reset mask */
|
||||
#define XTTCPS_CNT_CNTRL_DIS_MASK 0x01U /* TTC counter control disable mask */
|
||||
|
||||
#define LPRINTF(format, ...) \
|
||||
printf("CLIENT> " format, ##__VA_ARGS__)
|
||||
|
||||
#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief shmem_demo() - Show use of shared memory with Libmetal.
|
||||
* For NUM_TIMES times, send message to RPU and notify RPU by writing to
|
||||
* share mem that RPU is polling. Once detected, RPU will then similarly
|
||||
* write message and notify APU and the APU will then verify the
|
||||
* response. If the message does not match expected response, record
|
||||
* error. Afterwards, report test result and clean up.
|
||||
* Notes:
|
||||
* * The RPU will repeatedly wait for shared mem. from APU until APU
|
||||
* notifies remote by changing the KEEP_GOING value in shared memory.
|
||||
*
|
||||
* @return - return 0 on success, otherwise return error number indicating
|
||||
* type of error
|
||||
*/
|
||||
int shmem_demo();
|
||||
|
||||
/**
|
||||
* @brief ipi_shmem_demo() - shared memory IPI demo
|
||||
* This task will:
|
||||
* * Get the timestamp and put it into the ping shared memory
|
||||
* * Update the shared memory descriptor for the new available
|
||||
* ping buffer.
|
||||
* * Trigger IPI to notifty the remote.
|
||||
* * Repeat the above steps until it sends out all the packages.
|
||||
* * Monitor IPI interrupt, verify every received package.
|
||||
* * After all the packages are received, it sends out shutdown
|
||||
* message to the remote.
|
||||
*
|
||||
* @return - return 0 on success, otherwise return error number indicating
|
||||
* type of error.
|
||||
*/
|
||||
int ipi_shmem_demo();
|
||||
|
||||
/**
|
||||
* @brief atomic_shmem_demo() - Shared memory atomic operation demo
|
||||
* This task will:
|
||||
* - Write to shared memory to notify the remote to start atomic add
|
||||
* on the shared memory descriptor memory for 1000 times.
|
||||
* - Start atomic add by 1 for 1000 times to first 32 bits of memory
|
||||
* in the shared memory location at 3ed00000 which is
|
||||
* pointed to by shm_io.
|
||||
* - Wait for the remote to write to shared memory
|
||||
* - Once it received the polling kick from the remote, it will check
|
||||
* if the value stored in the shared memory for the atomic add is
|
||||
* 2000.
|
||||
* - It will print if the atomic add test has passed or not.
|
||||
*
|
||||
* @param[in] channel- hold shared mem. device
|
||||
* @return - If setup failed, return the corresponding error number. Otherwise
|
||||
* return 0 on success.
|
||||
*/
|
||||
int atomic_shmem_demo();
|
||||
|
||||
/**
|
||||
* @brief ipi_latency_demo() - Show performance of IPI with Libmetal.
|
||||
* For NUM_TIMES times, repeatedly send an IPI from APU and then detect
|
||||
* this IPI from RPU and measure the latency. Similarly, measure the
|
||||
* latency from RPU to APU. Each iteration, record this latency and
|
||||
* after the loop has finished, report the total latency in nanseconds.
|
||||
* Notes:
|
||||
* * The RPU will repeatedly wait for IPI from APU until APU notifies
|
||||
* remote by changing the KEEPGOING value in shared memory.
|
||||
* * To further ensure the accuracy of the readings a different thread
|
||||
* (i.e. the IRQ handler) will stop the timer measuring RPU to APU
|
||||
* latency.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int ipi_latency_demo();
|
||||
|
||||
/**
|
||||
* @brief shmem_latency_demo_demo() - Show performance of shared memory
|
||||
* For 8, 512, and 1024 bytes, measure latency from block write to block
|
||||
* read on remote side in shared memory. For each size, find average
|
||||
* latency by running NUM_TIMES times and reporting the average latency
|
||||
* for both APU block write to RPU block read as well as RPU block write
|
||||
* to APU block read.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int shmem_latency_demo();
|
||||
|
||||
/**
|
||||
* @brief shmem_throughput_demo_demo() - Show performance of shared memory
|
||||
* Record average throughput for APU block read, write, RPU block read
|
||||
* and write for sizes 1/2KB, 1KB and 2KB. For each size, run 1000 times
|
||||
* each operation and record average.
|
||||
*
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
int shmem_throughput_demo();
|
||||
|
||||
/**
|
||||
* @breif wait_for_notified() - Loop until notified bit in channel is set.
|
||||
*
|
||||
* @param[in] notified - pointer to the notified variable
|
||||
*/
|
||||
static inline void wait_for_notified(atomic_int *notified)
|
||||
{
|
||||
unsigned int flags;
|
||||
|
||||
do {
|
||||
|
||||
flags = metal_irq_save_disable();
|
||||
if (!atomic_flag_test_and_set(notified)) {
|
||||
metal_irq_restore_enable(flags);
|
||||
break;
|
||||
}
|
||||
metal_cpu_yield();
|
||||
metal_irq_restore_enable(flags);
|
||||
} while(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @breif dump_buffer() - print hex value of each byte in the buffer
|
||||
*
|
||||
* @param[in] buf - pointer to the buffer
|
||||
* @param[in] len - len of the buffer
|
||||
*/
|
||||
static inline void dump_buffer(void *buf, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned char *tmp = (unsigned char *)buf;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
printf(" %02x", *(tmp++));
|
||||
if (!(i % 20))
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief print_demo() - print demo string
|
||||
*
|
||||
* @param[in] name - demo name
|
||||
*/
|
||||
static inline void print_demo(char *name)
|
||||
{
|
||||
LPRINTF("****** libmetal demo: %s ******\n", name);
|
||||
}
|
||||
|
||||
#endif /* __COMMON_H__ */
|
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* ipi_latency_demo.c
|
||||
* This demo measures the IPI latency between the APU and RPU.
|
||||
* This demo does the follwing steps:
|
||||
*
|
||||
* 1. Get the shared memory device I/O region.
|
||||
* 1. Get the TTC timer device I/O region.
|
||||
* 2. Get the IPI device I/O region.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 4. Write to shared memory to indicate demo starts
|
||||
* 5. Reset the APU to RPU TTC counter and then kick IPI to notify the
|
||||
* remote.
|
||||
* 6. When it receives IPI interrupt, the IPI interrupt handler to stop
|
||||
* the RPU to APU TTC counter.
|
||||
* 7. Accumulate APU to RPU and RPU to APU counter values.
|
||||
* 8. Repeat step 5, 6 and 7 for 1000 times
|
||||
* 9. Write shared memory to indicate RPU about demo finishes and kick
|
||||
* IPI to notify.
|
||||
* 10. Clean up: disable IPI interrupt, deregister the IPI interrupt handler.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <metal/errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include "common.h"
|
||||
|
||||
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
|
||||
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
|
||||
|
||||
#define TTC_CLK_FREQ_HZ 100000000
|
||||
#define NS_PER_SEC 1000000000
|
||||
|
||||
/* Shared memory offset */
|
||||
#define SHM_DEMO_CNTRL_OFFSET 0x0
|
||||
|
||||
#define DEMO_STATUS_IDLE 0x0
|
||||
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
|
||||
|
||||
#define ITERATIONS 1000
|
||||
|
||||
struct channel_s {
|
||||
struct metal_device *ipi_dev; /* IPI metal device */
|
||||
struct metal_io_region *ipi_io; /* IPI metal i/o region */
|
||||
struct metal_device *shm_dev; /* Shared memory metal device */
|
||||
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
|
||||
struct metal_device *ttc_dev; /* TTC metal device */
|
||||
struct metal_io_region *ttc_io; /* TTC metal i/o region */
|
||||
uint32_t ipi_mask; /* RPU IPI mask */
|
||||
atomic_int remote_nkicked; /* 0 - kicked from remote */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief read_timer() - return TTC counter value
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter ID
|
||||
*/
|
||||
static inline uint32_t read_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
unsigned long offset = XTTCPS_CNT_VAL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
return metal_io_read32(ttc_io, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief reset_timer() - function to reset TTC counter
|
||||
* Set the RST bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void reset_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_RST_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop_timer() - function to stop TTC counter
|
||||
* Set the disable bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void stop_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_DIS_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_irq_handler() - IPI interrupt handler
|
||||
* It will clear the notified flag to mark it's got an IPI interrupt.
|
||||
* It will stop the RPU->APU timer and will clear the notified
|
||||
* flag to mark it's got an IPI interrupt
|
||||
*
|
||||
* @param[in] vect_id - IPI interrupt vector ID
|
||||
* @param[in/out] priv - communication channel data for this application.
|
||||
*
|
||||
* @return - If the IPI interrupt is triggered by its remote, it returns
|
||||
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
|
||||
* not the interrupt it expected.
|
||||
*
|
||||
*/
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)priv;
|
||||
uint32_t val;
|
||||
|
||||
(void)vect_id;
|
||||
|
||||
if (ch) {
|
||||
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ch->ipi_mask) {
|
||||
/* stop RPU -> APU timer */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
|
||||
ch->ipi_mask);
|
||||
atomic_flag_clear(&ch->remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief measure_ipi_latency() - Measure latency of IPI
|
||||
* Repeatedly kick IPI to notify the remote and then wait for IPI kick
|
||||
* from RPU and measure the latency. Similarly, measure the latency
|
||||
* from RPU to APU. Each iteration, record this latency and after the
|
||||
* loop has finished, report the total latency in nanseconds.
|
||||
* Notes:
|
||||
* - RPU will repeatedly wait for IPI from APU until APU
|
||||
* notifies remote demo has finished by setting the value in the
|
||||
* shared memory.
|
||||
*
|
||||
* @param[in] ch - channel information, which contains the IPI i/o region,
|
||||
* shared memory i/o region and the ttc timer i/o region.
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
static int measure_ipi_latency(struct channel_s *ch)
|
||||
{
|
||||
uint32_t apu_to_rpu_sum = 0, rpu_to_apu_sum = 0;
|
||||
int i;
|
||||
|
||||
LPRINTF("Starting IPI latency task\n");
|
||||
/* write to shared memory to indicate demo has started */
|
||||
metal_io_write32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET, DEMO_STATUS_START);
|
||||
|
||||
for ( i = 1; i <= ITERATIONS; i++) {
|
||||
/* Reset TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
/* Kick IPI to notify the remote */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
/* irq handler stops timer for rpu->apu irq */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
|
||||
apu_to_rpu_sum += read_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
rpu_to_apu_sum += read_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
}
|
||||
|
||||
/* write to shared memory to indicate demo has finished */
|
||||
metal_io_write32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET, 0);
|
||||
/* Kick IPI to notify the remote */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
|
||||
/* report avg latencies */
|
||||
LPRINTF("IPI latency result with %i iterations:\n", ITERATIONS);
|
||||
LPRINTF("APU to RPU average latency: %u ns \n",
|
||||
apu_to_rpu_sum / ITERATIONS * NS_PER_SEC / TTC_CLK_FREQ_HZ );
|
||||
LPRINTF("RPU to APU average latency: %u ns \n",
|
||||
rpu_to_apu_sum / ITERATIONS * NS_PER_SEC / TTC_CLK_FREQ_HZ );
|
||||
LPRINTF("Finished IPI latency task\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipi_latency_demo()
|
||||
{
|
||||
struct metal_device *dev;
|
||||
struct metal_io_region *io;
|
||||
struct channel_s ch;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("IPI latency");
|
||||
memset(&ch, 0, sizeof(ch));
|
||||
|
||||
/* Open shared memory device */
|
||||
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
io = metal_device_io_region(dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.shm_dev = dev;
|
||||
ch.shm_io = io;
|
||||
|
||||
/* Open TTC device */
|
||||
ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", TTC_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get TTC IO region */
|
||||
io = metal_device_io_region(dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.ttc_dev = dev;
|
||||
ch.ttc_io = io;
|
||||
|
||||
/* Open IPI device */
|
||||
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
io = metal_device_io_region(dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.ipi_dev = dev;
|
||||
ch.ipi_io = io;
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ch.ipi_dev->irq_info;
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&ch.remote_nkicked, 1);
|
||||
ch.ipi_mask = IPI_MASK;
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ch.ipi_dev, &ch);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = measure_ipi_latency(&ch);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler by setting the handler to 0 */
|
||||
metal_irq_unregister(ipi_irq, 0, ch.ipi_dev, &ch);
|
||||
|
||||
out:
|
||||
if (ch.ttc_dev)
|
||||
metal_device_close(ch.ttc_dev);
|
||||
if (ch.shm_dev)
|
||||
metal_device_close(ch.shm_dev);
|
||||
if (ch.ipi_dev)
|
||||
metal_device_close(ch.ipi_dev);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* ipi_shmem_demo.c - shared memory with IPI demo
|
||||
* This demo will:
|
||||
* 1. Open the shared memory device.
|
||||
* 2. Open the IPI device.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 4. Write message to the shared memory.
|
||||
* 5. Kick IPI to notify there is a message written to the shared memory
|
||||
* 6. Wait until the remote has kicked the IPI to notify the remote
|
||||
* has echoed back the message.
|
||||
* 7. Read the message from shared memory.
|
||||
* 8. Verify the message
|
||||
* 9. Repeat step 4 to 8 for 100 times.
|
||||
* 10. Clean up: deregister the IPI interrupt handler, close the IPI device
|
||||
* , close the shared memory device.
|
||||
*
|
||||
* Here is the Shared memory structure of this demo:
|
||||
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
|
||||
* |0x04 - 0x07 | number of APU to RPU buffers consumed by RPU |
|
||||
* |0x08 - 0x1FFC | address array for shared buffers from APU to RPU |
|
||||
* |0x2000 - 0x2003 | number of RPU to APU buffers available to APU |
|
||||
* |0x2004 - 0x2007 | number of RPU to APU buffers consumed by APU |
|
||||
* |0x2008 - 0x3FFC | address array for shared buffers from RPU to APU |
|
||||
* |0x04000 - 0x103FFC | APU to RPU buffers |
|
||||
* |0x104000 - 0x203FFC | RPU to APU buffers |
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include <metal/errno.h>
|
||||
#include "common.h"
|
||||
|
||||
/* Shared memory offsets */
|
||||
#define SHM_DESC_OFFSET_TX 0x0
|
||||
#define SHM_BUFF_OFFSET_TX 0x04000
|
||||
#define SHM_DESC_OFFSET_RX 0x02000
|
||||
#define SHM_BUFF_OFFSET_RX 0x104000
|
||||
|
||||
/* Shared memory descriptors offset */
|
||||
#define SHM_DESC_AVAIL_OFFSET 0x00
|
||||
#define SHM_DESC_USED_OFFSET 0x04
|
||||
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x08
|
||||
|
||||
#define PKGS_TOTAL 1024
|
||||
|
||||
#define BUF_SIZE_MAX 512
|
||||
#define SHUTDOWN "shutdown"
|
||||
|
||||
#define NS_PER_S (1000 * 1000 * 1000)
|
||||
|
||||
struct msg_hdr_s {
|
||||
uint32_t index;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
|
||||
1 - not-kicked */
|
||||
|
||||
/**
|
||||
* @breif get_timestamp() - Get the timestamp
|
||||
* IT gets the timestamp and return nanoseconds.
|
||||
*
|
||||
* @return nano seconds.
|
||||
*/
|
||||
static unsigned long long get_timestamp (void)
|
||||
{
|
||||
unsigned long long t = 0;
|
||||
struct timespec tp;
|
||||
int r;
|
||||
|
||||
r = clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
if (r == -1) {
|
||||
LPERROR("Bad clock_gettime!\n");
|
||||
return t;
|
||||
} else {
|
||||
t = tp.tv_sec * (NS_PER_S);
|
||||
t += tp.tv_nsec;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
(void)vect_id;
|
||||
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
uint64_t val = 1;
|
||||
|
||||
if (!ipi_io)
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ipi_mask) {
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
|
||||
atomic_flag_clear(&remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_shmem_echo() - shared memory IPI demo
|
||||
* This task will:
|
||||
* * Get the timestamp and put it into the ping shared memory
|
||||
* * Update the shared memory descriptor for the new available
|
||||
* ping buffer.
|
||||
* * Trigger IPI to notifty the remote.
|
||||
* * Repeat the above steps until it sends out all the packages.
|
||||
* * Monitor IPI interrupt, verify every received package.
|
||||
* * After all the packages are received, it sends out shutdown
|
||||
* message to the remote.
|
||||
*
|
||||
* @param[in] ipi_io - IPI metal i/o region
|
||||
* @param[in] shm_io - shared memory metal i/o region
|
||||
* @return - return 0 on success, otherwise return error number indicating
|
||||
* type of error.
|
||||
*/
|
||||
static int ipi_shmem_echo(struct metal_io_region *ipi_io,
|
||||
struct metal_io_region *shm_io)
|
||||
{
|
||||
int ret;
|
||||
uint32_t i;
|
||||
uint32_t rx_avail;
|
||||
unsigned long tx_avail_offset, rx_avail_offset;
|
||||
unsigned long rx_used_offset;
|
||||
unsigned long tx_addr_offset, rx_addr_offset;
|
||||
unsigned long tx_data_offset, rx_data_offset;
|
||||
unsigned long long tstart, tend;
|
||||
long long tdiff;
|
||||
long long tdiff_avg_s = 0, tdiff_avg_ns = 0;
|
||||
void *txbuf = NULL, *rxbuf = NULL, *tmpptr;
|
||||
struct msg_hdr_s *msg_hdr;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
uint32_t tx_phy_addr_32;
|
||||
|
||||
txbuf = metal_allocate_memory(BUF_SIZE_MAX);
|
||||
if (!txbuf) {
|
||||
LPERROR("Failed to allocate local tx buffer for msg.\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rxbuf = metal_allocate_memory(BUF_SIZE_MAX);
|
||||
if (!rxbuf) {
|
||||
LPERROR("Failed to allocate local rx buffer for msg.\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Clear shared memory */
|
||||
metal_io_block_set(shm_io, 0, 0, metal_io_region_size(shm_io));
|
||||
|
||||
/* Set tx/rx buffer address offset */
|
||||
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
|
||||
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
|
||||
rx_used_offset = SHM_DESC_OFFSET_RX + SHM_DESC_USED_OFFSET;
|
||||
tx_addr_offset = SHM_DESC_OFFSET_TX + SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
rx_addr_offset = SHM_DESC_OFFSET_RX + SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
|
||||
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
|
||||
|
||||
LPRINTF("Start echo flood testing....\n");
|
||||
LPRINTF("Sending msgs to the remote.\n");
|
||||
|
||||
for (i = 0; i < PKGS_TOTAL; i++) {
|
||||
|
||||
/* Construct a message to send */
|
||||
tmpptr = txbuf;
|
||||
msg_hdr = tmpptr;
|
||||
msg_hdr->index = i;
|
||||
msg_hdr->len = sizeof(tstart);
|
||||
tmpptr += sizeof(struct msg_hdr_s);
|
||||
tstart = get_timestamp();
|
||||
*(unsigned long long *)tmpptr = tstart;
|
||||
|
||||
/* copy message to shared buffer */
|
||||
metal_io_block_write(shm_io, tx_data_offset, msg_hdr,
|
||||
sizeof(struct msg_hdr_s) + msg_hdr->len);
|
||||
|
||||
/* Write to the address array to tell the other end
|
||||
* the buffer address.
|
||||
*/
|
||||
tx_phy_addr_32 = (uint32_t)metal_io_phys(shm_io,
|
||||
tx_data_offset);
|
||||
metal_io_write32(shm_io, tx_addr_offset, tx_phy_addr_32);
|
||||
tx_data_offset += sizeof(struct msg_hdr_s) + msg_hdr->len;
|
||||
tx_addr_offset += sizeof(uint32_t);
|
||||
|
||||
/* Increase number of available buffers */
|
||||
metal_io_write32(shm_io, tx_avail_offset, (i + 1));
|
||||
/* Kick IPI to notify data has been put to shared buffer */
|
||||
metal_io_write32(ipi_io, IPI_TRIG_OFFSET, ipi_mask);
|
||||
}
|
||||
|
||||
LPRINTF("Waiting for messages to echo back and verify.\n");
|
||||
i = 0;
|
||||
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
|
||||
while (i != PKGS_TOTAL) {
|
||||
wait_for_notified(&remote_nkicked);
|
||||
rx_avail = metal_io_read32(shm_io, rx_avail_offset);
|
||||
while (i != rx_avail) {
|
||||
uint32_t rx_phy_addr_32;
|
||||
|
||||
/* Received pong from the other side */
|
||||
|
||||
/* Get the buffer location from the shared memory
|
||||
* rx address array.
|
||||
*/
|
||||
rx_phy_addr_32 = metal_io_read32(shm_io,
|
||||
rx_addr_offset);
|
||||
rx_data_offset = metal_io_phys_to_offset(shm_io,
|
||||
(metal_phys_addr_t)rx_phy_addr_32);
|
||||
if (rx_data_offset == METAL_BAD_OFFSET) {
|
||||
LPERROR("failed to get rx [%d] offset: 0x%x.\n",
|
||||
i, rx_phy_addr_32);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rx_addr_offset += sizeof(rx_phy_addr_32);
|
||||
|
||||
/* Read message header from shared memory */
|
||||
metal_io_block_read(shm_io, rx_data_offset, rxbuf,
|
||||
sizeof(struct msg_hdr_s));
|
||||
msg_hdr = (struct msg_hdr_s *)rxbuf;
|
||||
|
||||
/* Check if the message header is valid */
|
||||
if (msg_hdr->index != (uint32_t)i) {
|
||||
LPERROR("wrong msg: expected: %d, actual: %d\n",
|
||||
i, msg_hdr->index);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (msg_hdr->len != sizeof(tstart)) {
|
||||
LPERROR("wrong msg: length invalid: %lu, %u.\n",
|
||||
sizeof(tstart), msg_hdr->len);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/* Read message */
|
||||
rx_data_offset += sizeof(*msg_hdr);
|
||||
metal_io_block_read(shm_io,
|
||||
rx_data_offset,
|
||||
rxbuf + sizeof(*msg_hdr), msg_hdr->len);
|
||||
rx_data_offset += msg_hdr->len;
|
||||
/* increase rx used count to indicate it has consumed
|
||||
* the received data */
|
||||
metal_io_write32(shm_io, rx_used_offset, (i + 1));
|
||||
|
||||
/* Verify message */
|
||||
/* Get tx message previously sent*/
|
||||
metal_io_block_read(shm_io, tx_data_offset, txbuf,
|
||||
sizeof(*msg_hdr) + sizeof(tstart));
|
||||
tx_data_offset += sizeof(*msg_hdr) + sizeof(tstart);
|
||||
/* Compare the received message and the sent message */
|
||||
ret = memcmp(rxbuf, txbuf,
|
||||
sizeof(*msg_hdr) + sizeof(tstart));
|
||||
if (ret) {
|
||||
LPERROR("data[%u] verification failed.\n", i);
|
||||
LPRINTF("Expected:");
|
||||
dump_buffer(txbuf,
|
||||
sizeof(*msg_hdr) + sizeof(tstart));
|
||||
LPRINTF("Actual:");
|
||||
dump_buffer(rxbuf,
|
||||
sizeof(*msg_hdr) + sizeof(tstart));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
tend = get_timestamp();
|
||||
tdiff = tend - tstart;
|
||||
|
||||
/* Send shutdown message */
|
||||
tmpptr = txbuf;
|
||||
msg_hdr = tmpptr;
|
||||
msg_hdr->index = i;
|
||||
msg_hdr->len = strlen(SHUTDOWN);
|
||||
tmpptr += sizeof(struct msg_hdr_s);
|
||||
sprintf(tmpptr, SHUTDOWN);
|
||||
/* copy message to shared buffer */
|
||||
metal_io_block_write(shm_io,
|
||||
tx_data_offset,
|
||||
msg_hdr,
|
||||
sizeof(struct msg_hdr_s) + msg_hdr->len);
|
||||
|
||||
tx_phy_addr_32 = (uint32_t)metal_io_phys(shm_io,
|
||||
tx_data_offset);
|
||||
metal_io_write32(shm_io, tx_addr_offset, tx_phy_addr_32);
|
||||
metal_io_write32(shm_io, tx_avail_offset, PKGS_TOTAL + 1);
|
||||
LPRINTF("Kick remote to notify shutdown message sent...\n");
|
||||
metal_io_write32(ipi_io, IPI_TRIG_OFFSET, ipi_mask);
|
||||
|
||||
tdiff /= PKGS_TOTAL;
|
||||
tdiff_avg_s = tdiff / NS_PER_S;
|
||||
tdiff_avg_ns = tdiff % NS_PER_S;
|
||||
LPRINTF("Total packages: %d, time_avg = %lds, %ldns\n",
|
||||
i, (long int)tdiff_avg_s, (long int)tdiff_avg_ns);
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (txbuf)
|
||||
metal_free_memory(txbuf);
|
||||
if (rxbuf)
|
||||
metal_free_memory(rxbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipi_shmem_demo()
|
||||
{
|
||||
struct metal_device *ipi_dev = NULL, *shm_dev = NULL;
|
||||
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("IPI and shared memory");
|
||||
|
||||
/* Open shared memory device */
|
||||
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &shm_dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Open IPI device */
|
||||
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &ipi_dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = ipi_shmem_echo(ipi_io, shm_io);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler by setting the handler to 0 */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
|
||||
|
||||
out:
|
||||
if (shm_dev)
|
||||
metal_device_close(shm_dev);
|
||||
if (ipi_dev)
|
||||
metal_device_close(ipi_dev);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* libmetal_amp_demo.c
|
||||
*
|
||||
* This application shows how to use IPI to trigger interrupt and how to
|
||||
* setup shared memory with libmetal API for communication between processors.
|
||||
*
|
||||
* This app does the following:
|
||||
* 1. Run the shared memory echo demo task ipi_shmem_task()
|
||||
* * Write message to the APU to RPU shared buffer.
|
||||
* * Update the APU to RPU shared memory available index.
|
||||
* * Trigger IPI to the remote.
|
||||
* * Repeat the above 3 sub steps until it sends all the packages.
|
||||
* * Wait for IPI to receive all the packages
|
||||
* * If "shutdown" message is received, cleanup the libmetal source.
|
||||
* 2. Run shared memory demo with shmem_task().
|
||||
* * Open shared memory device.
|
||||
* * For 1000 times, communicate between local and remote processes
|
||||
* using shared memory and polling via shared memory.
|
||||
* * Cleanup shared memory device.
|
||||
* 3. Run the atomic demo task atomic_shmem_task():
|
||||
* * Trigger the IPI to the remote, the remote will then start doing atomic
|
||||
* add calculation.
|
||||
* * Start atomic add by 1 for 1000 times to the first 32bit of the shared
|
||||
* memory descriptor location.
|
||||
* * Once it receives the IPI interrupt, it will check if the value stored
|
||||
* in the shared memory descriptor location is 2000. If yes, the atomic
|
||||
* across the shared memory passed, otherwise, it failed.
|
||||
* 4. Demonstrate IPI latency with ipi_latency_demo_task()
|
||||
* * Open IPI and timer devices.
|
||||
* * For 1000 times, record APU to RPU IPI latency and RPU to APU
|
||||
* latency. Then report average time for each direction.
|
||||
* * Cleanup libmetal resources
|
||||
* 5. Demonstrate shared memory latency with shmem_latency_demo_task()
|
||||
* * Open shared memory and timer devices.
|
||||
* * For 1000 times, record APU to RPU shared memory latency and RPU to APU
|
||||
* latency for 8 bytes, 1/2K and 1K. Then report average time for each
|
||||
* direction.
|
||||
* * Cleanup libmetal resources
|
||||
* 6. Demonstrate shared memory throughput with shmem_throughput_demo_task()
|
||||
* * Open shared memory, IPI and timer devices.
|
||||
* * For 1000 times, record APU block read and write times. Notify remote
|
||||
* to run test, then similarly record RPU block read and write times for
|
||||
* 1/2KB, 1KB and 2KB. Then report average throughput for each data size
|
||||
* and operation.
|
||||
* * Cleanup libmetal resources
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include "common.h"
|
||||
#include "sys_init.h"
|
||||
|
||||
/**
|
||||
* @brief main function of the demo application.
|
||||
* Here are the steps for the main function:
|
||||
* * initialize libmetal environment
|
||||
* * Run the IPI with shared memory demo.
|
||||
* * Run the shared memory demo.
|
||||
* * Run the atomic across shared memory demo.
|
||||
* * Run the ipi latency demo.
|
||||
* * Run the shared memory latency demo.
|
||||
* * Run the shared memory throughput demo.
|
||||
* * Cleanup libmetal environment
|
||||
* Report if any of the above tasks failed.
|
||||
* @return 0 - succeeded, non-zero for failures.
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sys_init();
|
||||
if (ret) {
|
||||
LPERROR("Failed to initialize system.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = shmem_demo();
|
||||
if (ret) {
|
||||
LPERROR("shared memory demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* sleep, to give time for RPU to clean up the last demo. */
|
||||
sleep(1);
|
||||
ret = atomic_shmem_demo();
|
||||
if (ret) {
|
||||
LPERROR("shared memory atomic demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* sleep, to give time for RPU to clean up the last demo. */
|
||||
sleep(1);
|
||||
ret = ipi_shmem_demo();
|
||||
if (ret) {
|
||||
LPERROR("IPI and shared memory demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* sleep, to give time for RPU to clean up the last demo. */
|
||||
sleep(1);
|
||||
ret = ipi_latency_demo();
|
||||
if (ret) {
|
||||
LPERROR("IPI latency demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* sleep, to give time for RPU to clean up the last demo. */
|
||||
sleep(1);
|
||||
ret = shmem_latency_demo();
|
||||
if (ret) {
|
||||
LPERROR("shared memory latency demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* sleep, to give time for RPU to clean up the last demo. */
|
||||
sleep(1);
|
||||
ret = shmem_throughput_demo();
|
||||
if (ret) {
|
||||
LPERROR("shared memory throughput demo failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
sys_cleanup();
|
||||
|
||||
return ret;
|
||||
}
|
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <metal/sys.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/cpu.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sys_init.h"
|
||||
|
||||
#define IPI_TRIG_OFFSET 0x0
|
||||
#define IPI_OBS_OFFSET 0x4
|
||||
#define IPI_ISR_OFFSET 0x10
|
||||
#define IPI_IMR_OFFSET 0x14
|
||||
#define IPI_IER_OFFSET 0x18
|
||||
#define IPI_IDR_OFFSET 0x1C
|
||||
|
||||
#define IPI_MASK 0x1000000
|
||||
|
||||
#define IPI_DEV_NAME "ff310000.ipi"
|
||||
#define SHM0_DESC_DEV_NAME "3ed00000.shm_desc"
|
||||
#define SHM1_DESC_DEV_NAME "3ed10000.shm_desc"
|
||||
#define SHM_DEV_NAME "3ed20000.shm"
|
||||
#define BUS_NAME "platform"
|
||||
#define D0_SHM_OFFSET 0x00000
|
||||
#define D1_SHM_OFFSET 0x20000
|
||||
|
||||
#define BUF_SIZE_MAX 512
|
||||
#define SHUTDOWN "shutdown"
|
||||
|
||||
#define LPRINTF(format, ...) \
|
||||
printf("SERVER> " format, ##__VA_ARGS__)
|
||||
|
||||
struct shm_mg_s {
|
||||
uint32_t avails;
|
||||
uint32_t used;
|
||||
};
|
||||
|
||||
typedef uint64_t shm_addr_t;
|
||||
|
||||
struct msg_hdr_s {
|
||||
uint32_t index;
|
||||
int32_t len;
|
||||
};
|
||||
|
||||
struct channel_s {
|
||||
struct metal_device *ipi_dev;
|
||||
struct metal_io_region *ipi_io;
|
||||
unsigned int ipi_mask;
|
||||
struct metal_device *shm0_desc_dev;
|
||||
struct metal_io_region *shm0_desc_io;
|
||||
struct metal_device *shm1_desc_dev;
|
||||
struct metal_io_region *shm1_desc_io;
|
||||
struct metal_device *shm_dev;
|
||||
struct metal_io_region *shm_io;
|
||||
atomic_int notified;
|
||||
unsigned long d0_start_offset;
|
||||
unsigned long d1_start_offset;
|
||||
};
|
||||
|
||||
static struct channel_s ch0;
|
||||
|
||||
extern int system_init();
|
||||
extern int run_comm_task(void *task, void *arg);
|
||||
extern void wait_for_interrupt(void);
|
||||
|
||||
static int ipi_irq_isr (int vect_id, void *priv)
|
||||
{
|
||||
(void)vect_id;
|
||||
struct channel_s *ch = (struct channel_s *)priv;
|
||||
uint64_t val = 1;
|
||||
|
||||
if (!ch)
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ch->ipi_mask) {
|
||||
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET, ch->ipi_mask);
|
||||
atomic_flag_clear(&ch->notified);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
static int ipi_task_shm_atomicd(void *arg)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)arg;
|
||||
atomic_int *shm_int;
|
||||
unsigned int flags;
|
||||
int i;
|
||||
|
||||
shm_int = (atomic_int *)metal_io_virt(ch->shm0_desc_io, 0);
|
||||
|
||||
LPRINTF("Wait for atomic test to start.\n");
|
||||
while (1) {
|
||||
do {
|
||||
flags = metal_irq_save_disable();
|
||||
if (!atomic_flag_test_and_set(&ch->notified)) {
|
||||
metal_irq_restore_enable(flags);
|
||||
break;
|
||||
}
|
||||
wait_for_interrupt();
|
||||
metal_irq_restore_enable(flags);
|
||||
} while(1);
|
||||
for (i = 0; i < 1000; i++)
|
||||
atomic_fetch_add(shm_int, 1);
|
||||
//*((unsigned int volatile *)shm_int) += 1;
|
||||
/* memory barrier */
|
||||
atomic_thread_fence(memory_order_acq_rel);
|
||||
|
||||
/* Send the message */
|
||||
LPRINTF("SENDING message...\n");
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipi_task_echod(void *arg)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)arg;
|
||||
struct shm_mg_s *shm0_mg, *shm1_mg;
|
||||
shm_addr_t *shm0_addr_array, *shm1_addr_array;
|
||||
struct msg_hdr_s *msg_hdr;
|
||||
unsigned int flags;
|
||||
void *d0, *d1, *lbuf;
|
||||
metal_phys_addr_t d0_pa;
|
||||
int len;
|
||||
|
||||
shm0_mg = (struct shm_mg_s *)metal_io_virt(ch->shm0_desc_io, 0);
|
||||
shm1_mg = (struct shm_mg_s *)metal_io_virt(ch->shm1_desc_io, 0);
|
||||
shm0_addr_array = (void *)shm0_mg + sizeof(struct shm_mg_s);
|
||||
shm1_addr_array = (void *)shm1_mg + sizeof(struct shm_mg_s);
|
||||
d1 = metal_io_virt(ch->shm_io, ch->d1_start_offset);
|
||||
lbuf = malloc(BUF_SIZE_MAX);
|
||||
if (!lbuf) {
|
||||
LPRINTF("ERROR: Failed to allocate local buffer for msg.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LPRINTF("Wait for echo test to start.\n");
|
||||
while (1) {
|
||||
do {
|
||||
flags = metal_irq_save_disable();
|
||||
if (!atomic_flag_test_and_set(&ch->notified)) {
|
||||
metal_irq_restore_enable(flags);
|
||||
break;
|
||||
}
|
||||
wait_for_interrupt();
|
||||
metal_irq_restore_enable(flags);
|
||||
} while(1);
|
||||
atomic_thread_fence(memory_order_acq_rel);
|
||||
while(shm0_mg->used != shm0_mg->avails) {
|
||||
d0_pa = (metal_phys_addr_t)shm0_addr_array[shm0_mg->used];
|
||||
d0 = metal_io_phys_to_virt(ch->shm_io, d0_pa);
|
||||
if (!d0) {
|
||||
LPRINTF("ERROR: failed to get rx addr:0x%lx.\n",
|
||||
d0_pa);
|
||||
goto out;
|
||||
}
|
||||
/* Copy msg header from shared buf to local mem */
|
||||
len = metal_io_block_read(ch->shm_io,
|
||||
metal_io_virt_to_offset(ch->shm_io, d0),
|
||||
lbuf, sizeof(struct msg_hdr_s));
|
||||
if (len < (int)sizeof(struct msg_hdr_s)) {
|
||||
LPRINTF("ERROR: failed to get msg header.\n");
|
||||
goto out;
|
||||
}
|
||||
msg_hdr = lbuf;
|
||||
if (msg_hdr->len < 0) {
|
||||
LPRINTF("ERROR: wrong msg length: %d.\n",
|
||||
(int)msg_hdr->len);
|
||||
goto out;
|
||||
} else {
|
||||
/* copy msg data from shared buf to local mem */
|
||||
d0 += sizeof(struct msg_hdr_s);
|
||||
len = metal_io_block_read(ch->shm_io,
|
||||
metal_io_virt_to_offset(ch->shm_io, d0),
|
||||
lbuf + sizeof(struct msg_hdr_s),
|
||||
msg_hdr->len);
|
||||
#if DEBUG
|
||||
LPRINTF("received: %d, %d\n",
|
||||
(int)msg_hdr->index, (int)msg_hdr->len);
|
||||
#endif
|
||||
/* Check if the it is the shutdown message */
|
||||
if (!strncmp((lbuf + sizeof(struct msg_hdr_s)),
|
||||
SHUTDOWN, sizeof(SHUTDOWN))) {
|
||||
LPRINTF("Received shutdown message\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/* Copy the message back to the other end */
|
||||
metal_io_block_write(ch->shm_io,
|
||||
metal_io_virt_to_offset(ch->shm_io, d1),
|
||||
lbuf,
|
||||
sizeof(struct msg_hdr_s) + msg_hdr->len);
|
||||
|
||||
/* Update the d1 address */
|
||||
shm1_addr_array[shm1_mg->avails] =
|
||||
(uint64_t)metal_io_virt_to_phys(
|
||||
ch->shm_io, d1);
|
||||
d1 += (sizeof(struct msg_hdr_s) + msg_hdr->len);
|
||||
shm0_mg->used++;
|
||||
shm1_mg->avails++;
|
||||
/* memory barrier */
|
||||
atomic_thread_fence(memory_order_acq_rel);
|
||||
|
||||
/* Send the message */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
|
||||
ch->ipi_mask);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(lbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct metal_device *device;
|
||||
struct metal_io_region *io;
|
||||
int irq;
|
||||
uint32_t val;
|
||||
int ret = 0;
|
||||
|
||||
ret = sys_init();
|
||||
if (ret) {
|
||||
LPRINTF("ERROR: Failed to initialize system\n");
|
||||
return -1;
|
||||
}
|
||||
memset(&ch0, 0, sizeof(ch0));
|
||||
|
||||
atomic_store(&ch0.notified, 1);
|
||||
|
||||
/* Open IPI device */
|
||||
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &device);
|
||||
if (ret) {
|
||||
LPRINTF("ERROR: Failed to open device %s.\n", IPI_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Map IPI device IO region */
|
||||
io = metal_device_io_region(device, 0);
|
||||
if (!io) {
|
||||
LPRINTF("ERROR: Failed to map io regio for %s.\n",
|
||||
device->name);
|
||||
metal_device_close(device);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Store the IPI device and I/O region */
|
||||
ch0.ipi_dev = device;
|
||||
ch0.ipi_io = io;
|
||||
|
||||
/* Open shared memory0 descriptor device */
|
||||
ret = metal_device_open(BUS_NAME, SHM0_DESC_DEV_NAME, &device);
|
||||
if (ret) {
|
||||
LPRINTF("ERROR: Failed to open device %s.\n",
|
||||
SHM0_DESC_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Map shared memory0 descriptor device IO region */
|
||||
io = metal_device_io_region(device, 0);
|
||||
if (!io) {
|
||||
LPRINTF("ERROR: Failed to map io regio for %s.\n",
|
||||
device->name);
|
||||
metal_device_close(device);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
/* Store the shared memory0 descriptor device and I/O region */
|
||||
ch0.shm0_desc_dev = device;
|
||||
ch0.shm0_desc_io = io;
|
||||
|
||||
/* Open shared memory1 descriptor device */
|
||||
ret = metal_device_open(BUS_NAME, SHM1_DESC_DEV_NAME, &device);
|
||||
if (ret) {
|
||||
LPRINTF("ERROR: Failed to open device %s.\n",
|
||||
SHM1_DESC_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Map shared memory1 descriptor device IO region */
|
||||
io = metal_device_io_region(device, 0);
|
||||
if (!io) {
|
||||
LPRINTF("ERROR: Failed to map io regio for %s.\n",
|
||||
device->name);
|
||||
metal_device_close(device);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
/* Store the shared memory0 descriptor device and I/O region */
|
||||
ch0.shm1_desc_dev = device;
|
||||
ch0.shm1_desc_io = io;
|
||||
|
||||
/* Open shared memory device */
|
||||
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &device);
|
||||
if (ret) {
|
||||
LPRINTF("ERROR: Failed to open device %s.\n", SHM_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Map shared memory device IO region */
|
||||
io = metal_device_io_region(device, 0);
|
||||
if (!io) {
|
||||
LPRINTF("ERROR: Failed to map io regio for %s.\n",
|
||||
device->name);
|
||||
metal_device_close(device);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Store the shared memory device and I/O region */
|
||||
ch0.shm_dev = device;
|
||||
ch0.shm_io = io;
|
||||
ch0.d1_start_offset = D1_SHM_OFFSET;
|
||||
|
||||
/* Get interrupt ID from IPI metal device */
|
||||
irq = (intptr_t)ch0.ipi_dev->irq_info;
|
||||
if (irq < 0) {
|
||||
LPRINTF("ERROR: Failed to request interrupt for %s.\n",
|
||||
device->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ch0.ipi_mask = IPI_MASK;
|
||||
|
||||
LPRINTF("Try to register IPI interrupt.\n");
|
||||
ret = metal_irq_register(irq, ipi_irq_isr, ch0.ipi_dev, &ch0);
|
||||
LPRINTF("registered IPI interrupt.\n");
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Enable interrupt */
|
||||
metal_io_write32(ch0.ipi_io, IPI_IER_OFFSET, ch0.ipi_mask);
|
||||
val = metal_io_read32(ch0.ipi_io, IPI_IMR_OFFSET);
|
||||
if (val & ch0.ipi_mask) {
|
||||
LPRINTF("ERROR: Failed to enable IPI interrupt.\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
LPRINTF("enabled IPI interrupt.\n");
|
||||
ret = ipi_task_shm_atomicd((void *)&ch0);
|
||||
if (ret) {
|
||||
LPRINTF("ERROR: Failed to run shared memory atomic task.\n");
|
||||
goto out;
|
||||
}
|
||||
ret = ipi_task_echod((void*)&ch0);
|
||||
if (ret)
|
||||
LPRINTF("ERROR: Failed to run IPI communication task.\n");
|
||||
|
||||
out:
|
||||
if (ch0.ipi_dev)
|
||||
metal_device_close(ch0.ipi_dev);
|
||||
if (ch0.shm0_desc_dev)
|
||||
metal_device_close(ch0.shm0_desc_dev);
|
||||
if (ch0.shm1_desc_dev)
|
||||
metal_device_close(ch0.shm1_desc_dev);
|
||||
if (ch0.shm_dev)
|
||||
metal_device_close(ch0.shm_dev);
|
||||
sys_cleanup();
|
||||
|
||||
return ret;
|
||||
}
|
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* atomic_shmem_demod.c - Shared memory atomic operation demo
|
||||
* This demo will:
|
||||
*
|
||||
* 1. Open the shared memory device.
|
||||
* 2. Open the IPI device.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 4. Kick IPI to notify the other end to start the demo
|
||||
* 5. Start atomic add by 1 for 5000 times over the shared memory
|
||||
* 6. Wait for remote IPI kick to know when the remote has finished the demo.
|
||||
* 7. Verify the result. As two sides both have done 5000 times of adding 1,
|
||||
* check if the end result is 5000*2.
|
||||
* 8. Clean up: deregister the IPI interrupt handler, close the IPI device
|
||||
* , close the shared memory device.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/cpu.h>
|
||||
#include <metal/io.h>
|
||||
#include <sys/types.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include <metal/errno.h>
|
||||
#include <time.h>
|
||||
#include "common.h"
|
||||
|
||||
#define ATOMIC_INT_OFFSET 0x0 /* shared memory offset for atomic operation */
|
||||
#define ITERATIONS 5000
|
||||
|
||||
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
|
||||
1 - not-kicked */
|
||||
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
(void)vect_id;
|
||||
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
uint64_t val = 1;
|
||||
|
||||
if (!ipi_io)
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ipi_mask) {
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
|
||||
atomic_flag_clear(&remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief atomic_add_shmem() - Shared memory atomic operation demo
|
||||
* This task will:
|
||||
* * Write to shared memory to notify the remote to start atomic add on
|
||||
* the shared memory for 1000 times.
|
||||
* * Start atomic add by 1 for 5000 times to first 32 bits of memory in
|
||||
* the shared memory which is pointed to by shm_io.
|
||||
* * Wait for the remote to write to shared memory
|
||||
* * Once it received the polling kick from the remote, it will check
|
||||
* if the value stored in the shared memory is the same as the
|
||||
* expected.
|
||||
* * It will print if the atomic add test has passed or not.
|
||||
* @param[in] ipi_io - IPI metal i/o region
|
||||
* @param[in] shm_io - shared memory metal i/o region
|
||||
* @return - If setup failed, return the corresponding error number. Otherwise
|
||||
* return 0 on success.
|
||||
*/
|
||||
static int atomic_add_shmem(struct metal_io_region *ipi_io,
|
||||
struct metal_io_region *shm_io)
|
||||
{
|
||||
int i, ret;
|
||||
atomic_int *shm_int;
|
||||
uint32_t ipi_mask = IPI_MASK;
|
||||
|
||||
LPRINTF("Starting atomic shared memory task.\n");
|
||||
|
||||
/* Initialize the shared memory on which we run the atomic add */
|
||||
shm_int = (atomic_int *)metal_io_virt(shm_io,
|
||||
ATOMIC_INT_OFFSET);
|
||||
atomic_store(shm_int, 0);
|
||||
|
||||
/* Kick the remote to notify demo starts. */
|
||||
metal_io_write32(ipi_io, IPI_TRIG_OFFSET, ipi_mask);
|
||||
|
||||
/* Do atomic add over the shared memory */
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
atomic_fetch_add(shm_int, 1);
|
||||
}
|
||||
|
||||
/* Wait for kick from RPU to know when RPU finishes the demo */
|
||||
wait_for_notified(&remote_nkicked);
|
||||
|
||||
if (atomic_load(shm_int) == (ITERATIONS << 1 )) {
|
||||
LPRINTF("shm atomic demo PASSED!\n");
|
||||
ret = 0;
|
||||
} else {
|
||||
LPRINTF("shm atomic demo FAILED. expected: %u, actual: %u\n",
|
||||
(unsigned int)(ITERATIONS << 1), atomic_load(shm_int));
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int atomic_shmem_demo()
|
||||
{
|
||||
struct metal_device *ipi_dev = NULL, *shm_dev = NULL;
|
||||
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("atomic operation over shared memory");
|
||||
|
||||
/* Open shared memory device */
|
||||
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &shm_dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
shm_io = metal_device_io_region(shm_dev, 0);
|
||||
if (!shm_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Open IPI device */
|
||||
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &ipi_dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
ipi_io = metal_device_io_region(ipi_dev, 0);
|
||||
if (!ipi_io) {
|
||||
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ipi_dev->irq_info;
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&remote_nkicked, 1);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = atomic_add_shmem(ipi_io, shm_io);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler by setting the handler to 0 */
|
||||
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
|
||||
|
||||
out:
|
||||
if (shm_dev)
|
||||
metal_device_close(shm_dev);
|
||||
if (ipi_dev)
|
||||
metal_device_close(ipi_dev);
|
||||
return ret;
|
||||
|
||||
}
|
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* shmem_demo.c
|
||||
* This demo demonstrates the use of shared mem. between the APU and RPU.
|
||||
* This demo does so via the following steps:
|
||||
*
|
||||
* 1. Open the shared memory device.
|
||||
* 2. Clear the demo control TX/RX available values in shared memory.
|
||||
* 3. APU set demo control in shared memory to notify RPU demo has started
|
||||
* 4. APU will write message to the shared memory.
|
||||
* 5. APU will increase TX avail values in the shared memory to notify RPU
|
||||
* there is a message ready to read.
|
||||
* 6. APU will poll the RX avail value in th shared memory to see if RPU
|
||||
* has echoed back the message into the shared memory.
|
||||
* 7. When APU knows there is new RX message available, it will read the
|
||||
* RX message from the shared memory.
|
||||
* 8. APU will verify the message to see if it matches the one it has sent.
|
||||
* 9. Close the shared memory device.
|
||||
*
|
||||
* Here is the Shared memory structure of this demo:
|
||||
* |0 | 4Bytes | DEMO control status shows if demo starts or not |
|
||||
* |0x04 | 4Bytes | number of APU to RPU buffers available to RPU |
|
||||
* |0x08 | 4Bytes | number of APU to RPU buffers consumed by RPU |
|
||||
* |0x0c | 4Bytes | number of RPU to APU buffers available to APU |
|
||||
* |0x10 | 4Bytes | number of RPU to APU buffers consumed by APU |
|
||||
* |0x14 | 1KBytes | APU to RPU buffer |
|
||||
* ... ...
|
||||
* |0x800 | 1KBytes | RPU to APU buffer |
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <metal/errno.h>
|
||||
#include <string.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <metal/device.h>
|
||||
#include "common.h"
|
||||
|
||||
/* Shared memory offsets */
|
||||
#define SHM_DEMO_CNTRL_OFFSET 0x0
|
||||
#define SHM_TX_AVAIL_OFFSET 0x04
|
||||
#define SHM_RX_AVAIL_OFFSET 0x0C
|
||||
#define SHM_TX_BUFFER_OFFSET 0x14
|
||||
#define SHM_RX_BUFFER_OFFSET 0x800
|
||||
|
||||
#define SHM_BUFFER_SIZE 0x400
|
||||
|
||||
#define DEMO_STATUS_IDLE 0x0
|
||||
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
|
||||
|
||||
#define TEST_MSG "Hello World - libmetal shared memory demo"
|
||||
|
||||
struct msg_hdr_s {
|
||||
uint32_t index;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief shmem_task() - Show use of shared memory with Libmetal.
|
||||
* Write message to RPU. RPU will then read and echo
|
||||
* back. Confirm if echoed message is identical.
|
||||
* If messages differ, report error.
|
||||
*
|
||||
* Steps:
|
||||
* 1. Clear demo control and TX/RX avaiable values
|
||||
*
|
||||
* @param[in] shm_io - metal i/o region of the shared memory
|
||||
* @return - return 0 on success, otherwise return error number indicating
|
||||
* type of error
|
||||
*/
|
||||
int shmem_echo(struct metal_io_region *shm_io)
|
||||
{
|
||||
void *tx_data = NULL;
|
||||
void *rx_data = NULL;
|
||||
unsigned int tx_count = 0;
|
||||
unsigned int rx_count = 0;
|
||||
struct msg_hdr_s *msg_hdr;
|
||||
unsigned int data_len;
|
||||
int ret;
|
||||
|
||||
LPRINTF("Setting up shared memory demo.\n");
|
||||
/* clear demo status value */
|
||||
metal_io_write32(shm_io, SHM_DEMO_CNTRL_OFFSET, 0);
|
||||
/* Clear TX/RX avail */
|
||||
metal_io_write32(shm_io, SHM_TX_AVAIL_OFFSET, 0);
|
||||
metal_io_write32(shm_io, SHM_RX_AVAIL_OFFSET, 0);
|
||||
|
||||
LPRINTF("Starting shared memory demo.\n");
|
||||
/* Notify the remote the demo starts */
|
||||
metal_io_write32(shm_io, SHM_DEMO_CNTRL_OFFSET, DEMO_STATUS_START);
|
||||
|
||||
/* preparing data to send */
|
||||
data_len = sizeof(struct msg_hdr_s) + strlen(TEST_MSG) + 1;
|
||||
tx_data = metal_allocate_memory(data_len);
|
||||
if (!tx_data) {
|
||||
LPERROR("Failed to allocate memory.\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
msg_hdr = (struct msg_hdr_s *)tx_data;
|
||||
msg_hdr->index = tx_count;
|
||||
msg_hdr->len = strlen(TEST_MSG) + 1;
|
||||
sprintf(tx_data + sizeof(*msg_hdr), TEST_MSG);
|
||||
LPRINTF("Sending message: %s\n",
|
||||
(char *)(tx_data + sizeof(*msg_hdr)));
|
||||
|
||||
/* write data to the shared memory*/
|
||||
ret = metal_io_block_write(shm_io, SHM_TX_BUFFER_OFFSET,
|
||||
tx_data, data_len);
|
||||
if (ret < 0){
|
||||
LPERROR("Unable to metal_io_block_write()\n");
|
||||
goto out;
|
||||
}
|
||||
/* Increase number of buffers available to notify the remote */
|
||||
tx_count++;
|
||||
metal_io_write32(shm_io, SHM_TX_AVAIL_OFFSET, tx_count);
|
||||
|
||||
/* wait for remote to echo back the data */
|
||||
while (metal_io_read32(shm_io, SHM_RX_AVAIL_OFFSET) == rx_count);
|
||||
rx_count++;
|
||||
/* New RX data is available, allocate buffer to received data */
|
||||
rx_data = metal_allocate_memory(data_len);
|
||||
if (!rx_data) {
|
||||
LPERROR("Failed to allocate memory\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
/* read data from the shared memory*/
|
||||
metal_io_block_read(shm_io, SHM_RX_BUFFER_OFFSET,
|
||||
rx_data, data_len);
|
||||
if (ret < 0){
|
||||
LPERROR("Unable to metal_io_block_read()\n");
|
||||
goto out;
|
||||
}
|
||||
/* verify the received data */
|
||||
ret = memcmp(tx_data, rx_data, data_len);
|
||||
if (ret) {
|
||||
LPERROR("Received data verification failed.\n");
|
||||
LPRINTF("Expected:");
|
||||
dump_buffer(tx_data, data_len);
|
||||
LPRINTF("Actual:");
|
||||
dump_buffer(rx_data, data_len);
|
||||
} else {
|
||||
LPRINTF("Message Received: %s\n",
|
||||
(char *)(rx_data + sizeof(*msg_hdr)));
|
||||
}
|
||||
/* Notify the remote the demo has finished. */
|
||||
metal_io_write32(shm_io, SHM_DEMO_CNTRL_OFFSET, DEMO_STATUS_IDLE);
|
||||
|
||||
out:
|
||||
if (tx_data)
|
||||
metal_free_memory(tx_data);
|
||||
if (rx_data)
|
||||
metal_free_memory(rx_data);
|
||||
LPRINTF("Shared memory demo: %s.\n", ret ? "Failed": "Passed" );
|
||||
return ret;
|
||||
}
|
||||
|
||||
int shmem_demo()
|
||||
{
|
||||
struct metal_device *device = NULL;
|
||||
struct metal_io_region *io = NULL;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("shared memory");
|
||||
|
||||
/* Open the shared memory device */
|
||||
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &device);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get shared memory device IO region */
|
||||
io = metal_device_io_region(device, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to get io region for %s.\n", device->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Run the demo */
|
||||
ret = shmem_echo(io);
|
||||
|
||||
out:
|
||||
if (device)
|
||||
metal_device_close(device);
|
||||
return ret;
|
||||
}
|
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* shmem_latency_demo.c
|
||||
* This demo demonstrates the shared mem. latency between the APU and RPU.
|
||||
* This demo does so via the following steps:
|
||||
*
|
||||
* 1. Get the shared memory device I/O region.
|
||||
* 1. Get the TTC timer device I/O region.
|
||||
* 2. Get the IPI device I/O region.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 4. Write to shared memory to indicate demo starts
|
||||
* 5. Reset the APU to RPU TTC counter, write data to the shared memory, then
|
||||
* kick IPI to notify the remote.
|
||||
* 6. When it receives IPI interrupt, the IPI interrupt handler marks the
|
||||
* remote has kicked.
|
||||
* 7. Accumulate APU to RPU and RPU to APU counter values.
|
||||
* 8. Repeat step 5, 6 and 7 for 1000 times
|
||||
* 9. Write shared memory to indicate RPU about demo finishes and kick
|
||||
* IPI to notify.
|
||||
* 10. Clean up: disable IPI interrupt, deregister the IPI interrupt handler.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#include <metal/errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/cpu.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/irq.h>
|
||||
#include "common.h"
|
||||
|
||||
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
|
||||
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
|
||||
|
||||
#define TTC_CLK_FREQ_HZ 100000000
|
||||
#define NS_PER_SEC 1000000000
|
||||
|
||||
/* Shared memory offset */
|
||||
#define SHM_DEMO_CNTRL_OFFSET 0x0 /* Shared memory for the demo status */
|
||||
#define SHM_BUFF_OFFSET_TX 0x1000 /* Shared memory TX buffer start offset */
|
||||
#define SHM_BUFF_OFFSET_RX 0x2000 /* Shared memory RX buffer start offset */
|
||||
|
||||
#define DEMO_STATUS_IDLE 0x0
|
||||
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
|
||||
|
||||
#define ITERATIONS 1000
|
||||
|
||||
#define BUF_SIZE_MAX 4096
|
||||
#define PKG_SIZE_MIN 16
|
||||
#define PKG_SIZE_MAX 1024
|
||||
|
||||
struct channel_s {
|
||||
struct metal_device *ipi_dev; /* IPI metal device */
|
||||
struct metal_io_region *ipi_io; /* IPI metal i/o region */
|
||||
struct metal_device *shm_dev; /* Shared memory metal device */
|
||||
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
|
||||
struct metal_device *ttc_dev; /* TTC metal device */
|
||||
struct metal_io_region *ttc_io; /* TTC metal i/o region */
|
||||
uint32_t ipi_mask; /* RPU IPI mask */
|
||||
atomic_int remote_nkicked; /* 0 - kicked from remote */
|
||||
};
|
||||
|
||||
struct msg_hdr_s {
|
||||
uint32_t index;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief read_timer() - return TTC counter value
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter ID
|
||||
*/
|
||||
static inline uint32_t read_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
unsigned long offset = XTTCPS_CNT_VAL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
return metal_io_read32(ttc_io, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief reset_timer() - function to reset TTC counter
|
||||
* Set the RST bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void reset_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_RST_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop_timer() - function to stop TTC counter
|
||||
* Set the disable bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void stop_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_DIS_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_irq_handler() - IPI interrupt handler
|
||||
* It will clear the notified flag to mark it's got an IPI interrupt.
|
||||
* It will stop the RPU->APU timer and will clear the notified
|
||||
* flag to mark it's got an IPI interrupt
|
||||
*
|
||||
* @param[in] vect_id - IPI interrupt vector ID
|
||||
* @param[in/out] priv - communication channel data for this application.
|
||||
*
|
||||
* @return - If the IPI interrupt is triggered by its remote, it returns
|
||||
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
|
||||
* not the interrupt it expected.
|
||||
*
|
||||
*/
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)priv;
|
||||
uint32_t val;
|
||||
|
||||
(void)vect_id;
|
||||
|
||||
if (ch) {
|
||||
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ch->ipi_mask) {
|
||||
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
|
||||
ch->ipi_mask);
|
||||
atomic_flag_clear(&ch->remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief measure_shmem_latency() - Measure latency of using shared memory
|
||||
* and IPI with libmetal.
|
||||
* Repeatedly send a message to RPU and then detect IPI from RPU
|
||||
* and measure the latency. Similarly, measure the latency from RPU
|
||||
* to APU. Each iteration, record this latency and after the loop
|
||||
* has finished, report the total latency in nanseconds.
|
||||
* Notes:
|
||||
* - RPU will repeatedly wait for IPI from APU until APU
|
||||
* notifies remote demo has finished by setting the value in the
|
||||
* shared memory.
|
||||
*
|
||||
* @param[in] ch - channel information, which contains the IPI i/o region,
|
||||
* shared memory i/o region and the ttc timer i/o region.
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
static int measure_shmem_latency(struct channel_s *ch)
|
||||
{
|
||||
uint32_t apu_to_rpu_sum = 0, rpu_to_apu_sum = 0;
|
||||
int i;
|
||||
size_t s;
|
||||
struct msg_hdr_s *msg_hdr;
|
||||
void *lbuf;
|
||||
int ret;
|
||||
|
||||
LPRINTF("Starting IPI latency task\n");
|
||||
/* allocate memory for receiving data */
|
||||
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
|
||||
if (!lbuf) {
|
||||
LPERROR("Failed to allocate memory.\r\n");
|
||||
return -1;
|
||||
}
|
||||
memset(lbuf, 0xA, BUF_SIZE_MAX);
|
||||
|
||||
/* write to shared memory to indicate demo has started */
|
||||
metal_io_write32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET, DEMO_STATUS_START);
|
||||
|
||||
for (s = PKG_SIZE_MIN; s <= PKG_SIZE_MAX; s <<= 1) {
|
||||
for (i = 1; i <= ITERATIONS; i++) {
|
||||
/* Reset TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
/* prepare data */
|
||||
msg_hdr = lbuf;
|
||||
msg_hdr->index = i;
|
||||
msg_hdr->len = s - sizeof(*msg_hdr);
|
||||
/* Copy data to the shared memory */
|
||||
ret = metal_io_block_write(ch->shm_io,
|
||||
SHM_BUFF_OFFSET_TX, lbuf, s);
|
||||
if ((size_t)ret != s) {
|
||||
LPERROR("Write shm failure: %lu,%lu\n",
|
||||
s, (size_t)ret);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
/* Kick IPI to notify the remote */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
|
||||
ch->ipi_mask);
|
||||
/* irq handler stops timer for rpu->apu irq */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
/* Read message */
|
||||
metal_io_block_read(ch->shm_io,
|
||||
SHM_BUFF_OFFSET_RX,
|
||||
lbuf, s);
|
||||
msg_hdr = lbuf;
|
||||
if (msg_hdr->len != (s - sizeof(*msg_hdr))) {
|
||||
LPERROR("Read shm failure: %lu,%lu\n",
|
||||
s, msg_hdr->len + sizeof(*msg_hdr));
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
/* Stop RPU to APU TTC counter */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
|
||||
apu_to_rpu_sum += read_timer(ch->ttc_io,
|
||||
TTC_CNT_APU_TO_RPU);
|
||||
rpu_to_apu_sum += read_timer(ch->ttc_io,
|
||||
TTC_CNT_RPU_TO_APU);
|
||||
}
|
||||
|
||||
/* report avg latencies */
|
||||
LPRINTF("package size %lu latency result:\n", s);
|
||||
LPRINTF("APU to RPU average latency: %u ns \n",
|
||||
apu_to_rpu_sum / ITERATIONS * NS_PER_SEC / TTC_CLK_FREQ_HZ );
|
||||
LPRINTF("RPU to APU average latency: %u ns \n",
|
||||
rpu_to_apu_sum / ITERATIONS * NS_PER_SEC / TTC_CLK_FREQ_HZ );
|
||||
}
|
||||
|
||||
/* write to shared memory to indicate demo has finished */
|
||||
metal_io_write32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET, 0);
|
||||
/* Kick IPI to notify the remote */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
|
||||
LPRINTF("Finished shared memory latency task\n");
|
||||
|
||||
out:
|
||||
metal_free_memory(lbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int shmem_latency_demo()
|
||||
{
|
||||
struct metal_device *dev;
|
||||
struct metal_io_region *io;
|
||||
struct channel_s ch;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("shared memory latency");
|
||||
memset(&ch, 0, sizeof(ch));
|
||||
|
||||
/* Open shared memory device */
|
||||
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
io = metal_device_io_region(dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.shm_dev = dev;
|
||||
ch.shm_io = io;
|
||||
|
||||
/* Open TTC device */
|
||||
ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", TTC_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get TTC IO region */
|
||||
io = metal_device_io_region(dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.ttc_dev = dev;
|
||||
ch.ttc_io = io;
|
||||
|
||||
/* Open IPI device */
|
||||
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
io = metal_device_io_region(dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.ipi_dev = dev;
|
||||
ch.ipi_io = io;
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ch.ipi_dev->irq_info;
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&ch.remote_nkicked, 1);
|
||||
ch.ipi_mask = IPI_MASK;
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ch.ipi_dev, &ch);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = measure_shmem_latency(&ch);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler by setting the handler to 0 */
|
||||
metal_irq_unregister(ipi_irq, 0, ch.ipi_dev, &ch);
|
||||
|
||||
out:
|
||||
if (ch.ttc_dev)
|
||||
metal_device_close(ch.ttc_dev);
|
||||
if (ch.shm_dev)
|
||||
metal_device_close(ch.shm_dev);
|
||||
if (ch.ipi_dev)
|
||||
metal_device_close(ch.ipi_dev);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* shmem_throughput_demo_task.c
|
||||
* This is the remote side of the shared memory throughput demo.
|
||||
* This demo does the following steps:
|
||||
*
|
||||
* 1. Get the shared memory device libmetal I/O region.
|
||||
* 1. Get the TTC timer device libemtal I/O region.
|
||||
* 2. Get IPI device libmetal I/O region and the IPI interrupt vector.
|
||||
* 3. Register IPI interrupt handler.
|
||||
* 6. Upload throughput measurement:
|
||||
* Start TTC APU counter, write data to shared memory and kick IPI to
|
||||
* notify remote. It will iterate for 1000 times, stop TTC APU counter.
|
||||
* Wait for RPU IPI kick to know RPU has finished receiving packages
|
||||
* and RPU TX counter is ready to read. Read the APU TX and RPU RX
|
||||
* counter values and save them. Repeat for different package sizes.
|
||||
* After this measurement, kick IPI to notify the remote, the
|
||||
* measurement has finished.
|
||||
* 7. Download throughput measurement:
|
||||
* Start TTC APU counter, wait for IPI kick, check if data is available,
|
||||
* if yes, read as much data as possible from shared memory. It will
|
||||
* iterates untill 1000 packages have been received, stop TTC APU counter.
|
||||
* Wait for RPU IPI kick so that APU can get the TTC RPU TX counter
|
||||
* value. Kick IPI to notify the remote it has read the TTCi counter.
|
||||
* Repeat for different package size.
|
||||
* 8. Cleanup resource:
|
||||
* disable IPI interrupt and deregister the IPI interrupt handler.
|
||||
*
|
||||
* Here is the Shared memory structure of this demo:
|
||||
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
|
||||
* |0x04 - 0x1FFFFF | address array for shared buffers from APU to RPU |
|
||||
* |0x200000 - 0x200004 | number of RPU to APU buffers available to APU |
|
||||
* |0x200004 - 0x3FFFFF | address array for shared buffers from RPU to APU |
|
||||
* |0x400000 - 0x7FFFFF | APU to RPU buffers |
|
||||
* |0x800000 - 0xAFFFFF | RPU to APU buffers |
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <error.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <metal/irq.h>
|
||||
#include "common.h"
|
||||
|
||||
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
|
||||
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
|
||||
|
||||
#define TTC_CLK_FREQ_HZ 100000000
|
||||
#define NS_PER_SEC 1000000000
|
||||
|
||||
/* Shared memory offsets */
|
||||
#define SHM_DESC_OFFSET_TX 0x0
|
||||
#define SHM_BUFF_OFFSET_TX 0x400000
|
||||
#define SHM_DESC_OFFSET_RX 0x200000
|
||||
#define SHM_BUFF_OFFSET_RX 0x800000
|
||||
|
||||
/* Shared memory descriptors offset */
|
||||
#define SHM_DESC_AVAIL_OFFSET 0x00
|
||||
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x04
|
||||
|
||||
#define ITERATIONS 1000
|
||||
|
||||
#define BUF_SIZE_MAX 4096
|
||||
#define PKG_SIZE_MAX 1024
|
||||
#define PKG_SIZE_MIN 16
|
||||
#define TOTAL_DATA_SIZE (1024 * 4096)
|
||||
|
||||
#define MB (1024 * 1024) /* Mega Bytes */
|
||||
|
||||
struct channel_s {
|
||||
struct metal_device *ipi_dev; /* IPI metal device */
|
||||
struct metal_io_region *ipi_io; /* IPI metal i/o region */
|
||||
struct metal_device *shm_dev; /* Shared memory metal device */
|
||||
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
|
||||
struct metal_device *ttc_dev; /* TTC metal device */
|
||||
struct metal_io_region *ttc_io; /* TTC metal i/o region */
|
||||
uint32_t ipi_mask; /* RPU IPI mask */
|
||||
atomic_int remote_nkicked; /* 0 - kicked from remote */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief read_timer() - return TTC counter value
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter ID
|
||||
*/
|
||||
static inline uint32_t read_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
unsigned long offset = XTTCPS_CNT_VAL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
return metal_io_read32(ttc_io, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief reset_timer() - function to reset TTC counter
|
||||
* Set the RST bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void reset_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_RST_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop_timer() - function to stop TTC counter
|
||||
* Set the disable bit in the Count Control Reg.
|
||||
*
|
||||
* @param[in] ttc_io - TTC timer i/o region
|
||||
* @param[in] cnt_id - counter id
|
||||
*/
|
||||
static inline void stop_timer(struct metal_io_region *ttc_io,
|
||||
unsigned long cnt_id)
|
||||
{
|
||||
uint32_t val;
|
||||
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
|
||||
XTTCPS_CNT_OFFSET(cnt_id);
|
||||
|
||||
val = XTTCPS_CNT_CNTRL_DIS_MASK;
|
||||
metal_io_write32(ttc_io, offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ipi_irq_handler() - IPI interrupt handler
|
||||
* It will clear the notified flag to mark it's got an IPI interrupt.
|
||||
* It will stop the RPU->APU timer and will clear the notified
|
||||
* flag to mark it's got an IPI interrupt
|
||||
*
|
||||
* @param[in] vect_id - IPI interrupt vector ID
|
||||
* @param[in/out] priv - communication channel data for this application.
|
||||
*
|
||||
* @return - If the IPI interrupt is triggered by its remote, it returns
|
||||
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
|
||||
* not the interrupt it expected.
|
||||
*
|
||||
*/
|
||||
static int ipi_irq_handler (int vect_id, void *priv)
|
||||
{
|
||||
struct channel_s *ch = (struct channel_s *)priv;
|
||||
uint32_t val;
|
||||
|
||||
(void)vect_id;
|
||||
|
||||
if (ch) {
|
||||
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
|
||||
if (val & ch->ipi_mask) {
|
||||
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
|
||||
ch->ipi_mask);
|
||||
atomic_flag_clear(&ch->remote_nkicked);
|
||||
return METAL_IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief measure_shmem_throughput() - Show throughput of using shared memory.
|
||||
* - Upload throughput measurement:
|
||||
* Start TTC APU counter, write data to shared memory and kick IPI to
|
||||
* notify remote. It will iterate for 1000 times, stop TTC APU
|
||||
* counter. Wait for RPU IPI kick to know RPU has finished receiving
|
||||
* packages and RPU TX counter is ready to read. Read the APU TX and
|
||||
* RPU RX counter values and save them. Repeat for different package
|
||||
* sizes. After this measurement, kick IPI to notify the remote, the
|
||||
* measurement has finished.
|
||||
* - Download throughput measurement:
|
||||
* Start TTC APU counter, wait for IPI kick, check if data is
|
||||
* available, if yes, read as much data as possible from shared
|
||||
* memory. It will iterates untill 1000 packages have been received,
|
||||
* stop TTC APU counter. Wait for RPU IPI kick so that APU can get
|
||||
* the TTC RPU TX counter value. Kick IPI to notify the remote it
|
||||
* has read the TTCi counter. Repeat for different package size.
|
||||
*
|
||||
* @param[in] ch - channel information, which contains the IPI i/o region,
|
||||
* shared memory i/o region and the ttc timer i/o region.
|
||||
* @return - 0 on success, error code if failure.
|
||||
*/
|
||||
static int measure_shmem_throughput(struct channel_s* ch)
|
||||
{
|
||||
void *lbuf = NULL;
|
||||
int ret = 0;
|
||||
size_t s, i;
|
||||
uint32_t rx_count, rx_avail, tx_count, iterations;
|
||||
unsigned long tx_avail_offset, rx_avail_offset;
|
||||
unsigned long tx_addr_offset, rx_addr_offset;
|
||||
unsigned long tx_data_offset, rx_data_offset;
|
||||
uint32_t buf_phy_addr_32;
|
||||
uint32_t *apu_tx_count = NULL;
|
||||
uint32_t *apu_rx_count = NULL;
|
||||
uint32_t *rpu_tx_count = NULL;
|
||||
uint32_t *rpu_rx_count = NULL;
|
||||
|
||||
/* allocate memory for receiving data */
|
||||
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
|
||||
if (!lbuf) {
|
||||
LPERROR("Failed to allocate memory.\r\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(lbuf, 0xA, BUF_SIZE_MAX);
|
||||
|
||||
/* allocate memory for saving counter values */
|
||||
for (s = PKG_SIZE_MIN, i = 0; s <= PKG_SIZE_MAX; s <<=1, i++);
|
||||
apu_tx_count = metal_allocate_memory(i * sizeof(uint32_t));
|
||||
apu_rx_count = metal_allocate_memory(i * sizeof(uint32_t));
|
||||
rpu_tx_count = metal_allocate_memory(i * sizeof(uint32_t));
|
||||
rpu_rx_count = metal_allocate_memory(i * sizeof(uint32_t));
|
||||
if (!apu_tx_count || !apu_rx_count || !rpu_tx_count || !rpu_rx_count) {
|
||||
LPERROR("Failed to allocate memory.\r\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Clear shared memory */
|
||||
metal_io_block_set(ch->shm_io, 0, 0, metal_io_region_size(ch->shm_io));
|
||||
|
||||
LPRINTF("Starting shared mem throughput demo\n");
|
||||
|
||||
/* for each data size, measure send throughput */
|
||||
for (s = PKG_SIZE_MIN, i = 0; s <= PKG_SIZE_MAX; s <<= 1, i++) {
|
||||
tx_count = 0;
|
||||
iterations = TOTAL_DATA_SIZE / s;
|
||||
/* Set tx buffer address offset */
|
||||
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
|
||||
tx_addr_offset = SHM_DESC_OFFSET_TX +
|
||||
SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
|
||||
/* Reset APU TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
while (tx_count < iterations) {
|
||||
/* Write data to the shared memory*/
|
||||
metal_io_block_write(ch->shm_io, tx_data_offset,
|
||||
lbuf, s);
|
||||
|
||||
/* Write to the address array to tell the other end
|
||||
* the buffer address.
|
||||
*/
|
||||
buf_phy_addr_32 = (uint32_t)metal_io_phys(ch->shm_io,
|
||||
tx_data_offset);
|
||||
metal_io_write32(ch->shm_io, tx_addr_offset,
|
||||
buf_phy_addr_32);
|
||||
tx_data_offset += s;
|
||||
tx_addr_offset += sizeof(buf_phy_addr_32);
|
||||
|
||||
/* Increase number of available buffers */
|
||||
tx_count++;
|
||||
metal_io_write32(ch->shm_io, tx_avail_offset,
|
||||
tx_count);
|
||||
/* Kick IPI to notify RPU data is ready in
|
||||
* the shared memory */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
|
||||
ch->ipi_mask);
|
||||
}
|
||||
/* Stop RPU TTC counter */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
/* Wait for RPU to signal RPU RX TTC counter is ready to
|
||||
* read */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
/* Read TTC counter values */
|
||||
apu_tx_count[i] = read_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
rpu_rx_count[i] = read_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
}
|
||||
|
||||
/* Kick IPI to notify RPU that APU has read the RPU RX TTC counter
|
||||
* value */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
|
||||
/* for each data size, meaasure block read throughput */
|
||||
for (s = PKG_SIZE_MIN, i = 0; s <= PKG_SIZE_MAX; s <<= 1, i++) {
|
||||
rx_count = 0;
|
||||
iterations = TOTAL_DATA_SIZE / s;
|
||||
/* Set rx buffer address offset */
|
||||
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
|
||||
rx_addr_offset = SHM_DESC_OFFSET_RX +
|
||||
SHM_DESC_ADDR_ARRAY_OFFSET;
|
||||
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
|
||||
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
/* Data has arrived, seasure start. Reset RPU TTC counter */
|
||||
reset_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
while (1) {
|
||||
rx_avail = metal_io_read32(ch->shm_io, rx_avail_offset);
|
||||
while(rx_count != rx_avail) {
|
||||
/* Get the buffer location from the shared
|
||||
* memory rx address array.
|
||||
*/
|
||||
buf_phy_addr_32 = metal_io_read32(ch->shm_io,
|
||||
rx_addr_offset);
|
||||
rx_data_offset = metal_io_phys_to_offset(
|
||||
ch->shm_io,
|
||||
(metal_phys_addr_t)buf_phy_addr_32);
|
||||
if (rx_data_offset == METAL_BAD_OFFSET) {
|
||||
LPERROR(
|
||||
"[%u]failed to get rx offset: 0x%x, 0x%lx.\n",
|
||||
rx_count, buf_phy_addr_32,
|
||||
metal_io_phys(ch->shm_io,
|
||||
rx_addr_offset));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rx_addr_offset += sizeof(buf_phy_addr_32);
|
||||
/* Read data from shared memory */
|
||||
metal_io_block_read(ch->shm_io, rx_data_offset,
|
||||
lbuf, s);
|
||||
rx_count++;
|
||||
}
|
||||
if (rx_count < iterations)
|
||||
/* Need to wait for more data */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
else
|
||||
break;
|
||||
}
|
||||
/* Stop RPU TTC counter */
|
||||
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
/* Clear remote kicked flag -- 0 is kicked */
|
||||
atomic_init(&ch->remote_nkicked, 1);
|
||||
/* Kick IPI to notify remote it is ready to read data */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
/* Wait for RPU to signal RPU TX TTC counter is ready to
|
||||
* read */
|
||||
wait_for_notified(&ch->remote_nkicked);
|
||||
/* Read TTC counter values */
|
||||
apu_rx_count[i] = read_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
|
||||
rpu_tx_count[i] = read_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
|
||||
/* Kick IPI to notify RPU APU has read the RPU TX TTC counter
|
||||
* value */
|
||||
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
|
||||
}
|
||||
|
||||
/* Print the measurement result */
|
||||
for (s = PKG_SIZE_MIN, i = 0; s <= PKG_SIZE_MAX; s <<= 1, i++) {
|
||||
LPRINTF("Shared memory throughput of pkg size %lu : \n", s);
|
||||
LPRINTF(" APU send: %x, %lu MB/s\n", apu_tx_count[i],
|
||||
s * iterations * TTC_CLK_FREQ_HZ / apu_tx_count[i] / MB);
|
||||
LPRINTF(" APU receive: %x, %lu MB/s\n", apu_rx_count[i],
|
||||
s * iterations * TTC_CLK_FREQ_HZ / apu_rx_count[i] / MB);
|
||||
LPRINTF(" RPU send: %x, %lu MB/s\n", rpu_tx_count[i],
|
||||
s * iterations * TTC_CLK_FREQ_HZ / rpu_tx_count[i] / MB);
|
||||
LPRINTF(" RPU receive: %x, %lu MB/s\n", rpu_rx_count[i],
|
||||
s * iterations * TTC_CLK_FREQ_HZ / rpu_rx_count[i] / MB);
|
||||
}
|
||||
|
||||
LPRINTF("Finished shared memory throughput\n");
|
||||
|
||||
out:
|
||||
if (lbuf)
|
||||
metal_free_memory(lbuf);
|
||||
if (apu_tx_count)
|
||||
metal_free_memory(apu_tx_count);
|
||||
if (apu_rx_count)
|
||||
metal_free_memory(apu_rx_count);
|
||||
if (rpu_tx_count)
|
||||
metal_free_memory(rpu_tx_count);
|
||||
if (rpu_rx_count)
|
||||
metal_free_memory(rpu_rx_count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int shmem_throughput_demo()
|
||||
{
|
||||
struct metal_device *dev;
|
||||
struct metal_io_region *io;
|
||||
struct channel_s ch;
|
||||
int ipi_irq;
|
||||
int ret = 0;
|
||||
|
||||
print_demo("shared memory throughput");
|
||||
memset(&ch, 0, sizeof(ch));
|
||||
|
||||
/* Open shared memory device */
|
||||
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get shared memory device IO region */
|
||||
io = metal_device_io_region(dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.shm_dev = dev;
|
||||
ch.shm_io = io;
|
||||
|
||||
/* Open TTC device */
|
||||
ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", TTC_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get TTC IO region */
|
||||
io = metal_device_io_region(dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.ttc_dev = dev;
|
||||
ch.ttc_io = io;
|
||||
|
||||
/* Open IPI device */
|
||||
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &dev);
|
||||
if (ret) {
|
||||
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get IPI device IO region */
|
||||
io = metal_device_io_region(dev, 0);
|
||||
if (!io) {
|
||||
LPERROR("Failed to map io region for %s.\n", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ch.ipi_dev = dev;
|
||||
ch.ipi_io = io;
|
||||
|
||||
/* Get the IPI IRQ from the opened IPI device */
|
||||
ipi_irq = (intptr_t)ch.ipi_dev->irq_info;
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* clear old IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
|
||||
/* initialize remote_nkicked */
|
||||
atomic_init(&ch.remote_nkicked, 1);
|
||||
ch.ipi_mask = IPI_MASK;
|
||||
/* Register IPI irq handler */
|
||||
metal_irq_register(ipi_irq, ipi_irq_handler, ch.ipi_dev, &ch);
|
||||
/* Enable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
|
||||
|
||||
/* Run atomic operation demo */
|
||||
ret = measure_shmem_throughput(&ch);
|
||||
|
||||
/* disable IPI interrupt */
|
||||
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
|
||||
/* unregister IPI irq handler by setting the handler to 0 */
|
||||
metal_irq_unregister(ipi_irq, 0, ch.ipi_dev, &ch);
|
||||
|
||||
out:
|
||||
if (ch.ttc_dev)
|
||||
metal_device_close(ch.ttc_dev);
|
||||
if (ch.shm_dev)
|
||||
metal_device_close(ch.shm_dev);
|
||||
if (ch.ipi_dev)
|
||||
metal_device_close(ch.ipi_dev);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,31 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <metal/sys.h>
|
||||
#include "common.h"
|
||||
|
||||
int sys_init()
|
||||
{
|
||||
struct metal_init_params init_param = METAL_INIT_DEFAULTS;
|
||||
int ret;
|
||||
|
||||
ret = metal_init(&init_param);
|
||||
if (ret)
|
||||
LPERROR("Failed to initialize libmetal\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sys_cleanup()
|
||||
{
|
||||
metal_finish();
|
||||
}
|
||||
|
||||
void wait_for_interrupt(void) {
|
||||
return;
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2017 Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __SYS_INIT_H__
|
||||
#define __SYS_INIT_H__
|
||||
|
||||
int sys_init();
|
||||
void sys_cleanup();
|
||||
|
||||
#endif /* __SYS_INIT_H__ */
|
||||
|
101
source/OpenAMP/libmetal/lib/CMakeLists.txt
Normal file
101
source/OpenAMP/libmetal/lib/CMakeLists.txt
Normal file
@@ -0,0 +1,101 @@
|
||||
collector_create (PROJECT_LIB_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
collector_create (PROJECT_LIB_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
collect (PROJECT_LIB_DIRS "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
collect (PROJECT_INC_DIRS "${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||
|
||||
collect (PROJECT_LIB_HEADERS alloc.h)
|
||||
collect (PROJECT_LIB_HEADERS assert.h)
|
||||
collect (PROJECT_LIB_HEADERS atomic.h)
|
||||
collect (PROJECT_LIB_HEADERS cache.h)
|
||||
collect (PROJECT_LIB_HEADERS compiler.h)
|
||||
collect (PROJECT_LIB_HEADERS condition.h)
|
||||
collect (PROJECT_LIB_HEADERS config.h)
|
||||
collect (PROJECT_LIB_HEADERS cpu.h)
|
||||
collect (PROJECT_LIB_HEADERS device.h)
|
||||
collect (PROJECT_LIB_HEADERS dma.h)
|
||||
collect (PROJECT_LIB_HEADERS io.h)
|
||||
collect (PROJECT_LIB_HEADERS irq.h)
|
||||
collect (PROJECT_LIB_HEADERS list.h)
|
||||
collect (PROJECT_LIB_HEADERS log.h)
|
||||
collect (PROJECT_LIB_HEADERS mutex.h)
|
||||
collect (PROJECT_LIB_HEADERS shmem.h)
|
||||
collect (PROJECT_LIB_HEADERS sleep.h)
|
||||
collect (PROJECT_LIB_HEADERS spinlock.h)
|
||||
collect (PROJECT_LIB_HEADERS sys.h)
|
||||
collect (PROJECT_LIB_HEADERS time.h)
|
||||
collect (PROJECT_LIB_HEADERS utilities.h)
|
||||
collect (PROJECT_LIB_HEADERS version.h)
|
||||
|
||||
collect (PROJECT_LIB_SOURCES dma.c)
|
||||
collect (PROJECT_LIB_SOURCES device.c)
|
||||
collect (PROJECT_LIB_SOURCES init.c)
|
||||
collect (PROJECT_LIB_SOURCES io.c)
|
||||
collect (PROJECT_LIB_SOURCES log.c)
|
||||
collect (PROJECT_LIB_SOURCES shmem.c)
|
||||
collect (PROJECT_LIB_SOURCES version.c)
|
||||
|
||||
add_subdirectory (compiler)
|
||||
add_subdirectory (processor)
|
||||
add_subdirectory (system)
|
||||
|
||||
collector_list (_inc_dirs PROJECT_INC_DIRS)
|
||||
collector_list (_sources PROJECT_LIB_SOURCES)
|
||||
collector_list (_headers PROJECT_LIB_HEADERS)
|
||||
collector_list (_deps PROJECT_LIB_DEPS)
|
||||
|
||||
foreach (f ${_headers})
|
||||
configure_file (${f} include/${PROJECT_NAME}/${f} @ONLY)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/include/${PROJECT_NAME}/${f}
|
||||
DESTINATION include RENAME ${PROJECT_NAME}/${f})
|
||||
if (${f} MATCHES "^[^/]*\\.h")
|
||||
collect (PROJECT_HDR_TESTS "metal/${f}")
|
||||
endif (${f} MATCHES "^[^/]*\\.h")
|
||||
endforeach (f)
|
||||
|
||||
include_directories (${_inc_dirs})
|
||||
add_definitions (-DMETAL_INTERNAL)
|
||||
|
||||
if (WITH_DEFAULT_LOGGER)
|
||||
add_definitions (-DDEFAULT_LOGGER_ON)
|
||||
endif (WITH_DEFAULT_LOGGER)
|
||||
|
||||
if (WITH_ZEPHYR)
|
||||
zephyr_library_named(metal)
|
||||
add_dependencies(metal offsets_h)
|
||||
zephyr_library_sources(${_sources})
|
||||
zephyr_include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
|
||||
else (WITH_ZEPHYR)
|
||||
# Build a shared library if so configured.
|
||||
if (WITH_SHARED_LIB)
|
||||
set (_lib ${PROJECT_NAME}-shared)
|
||||
add_library (${_lib} SHARED ${_sources})
|
||||
target_link_libraries (${_lib} ${_deps})
|
||||
install (TARGETS ${_lib} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
if (PROJECT_EC_FLAGS)
|
||||
string(REPLACE " " ";" _ec_flgs ${PROJECT_EC_FLAGS})
|
||||
target_compile_options (${_lib} PUBLIC ${_ec_flgs})
|
||||
endif (PROJECT_EC_FLAGS)
|
||||
set_target_properties (${_lib} PROPERTIES
|
||||
OUTPUT_NAME "${PROJECT_NAME}"
|
||||
VERSION "${PROJECT_VER}"
|
||||
SOVERSION "${PROJECT_VER_MAJOR}"
|
||||
)
|
||||
endif (WITH_SHARED_LIB)
|
||||
|
||||
# Build a static library if so configured.
|
||||
if (WITH_STATIC_LIB)
|
||||
set (_lib ${PROJECT_NAME}-static)
|
||||
add_library (${_lib} STATIC ${_sources})
|
||||
install (TARGETS ${_lib} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
if (PROJECT_EC_FLAGS)
|
||||
string(REPLACE " " ";" _ec_flgs ${PROJECT_EC_FLAGS})
|
||||
target_compile_options (${_lib} PUBLIC ${_ec_flgs})
|
||||
endif (PROJECT_EC_FLAGS)
|
||||
set_target_properties (${_lib} PROPERTIES
|
||||
OUTPUT_NAME "${PROJECT_NAME}"
|
||||
)
|
||||
endif (WITH_STATIC_LIB)
|
||||
endif (WITH_ZEPHYR)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
46
source/OpenAMP/libmetal/lib/alloc.h
Normal file
46
source/OpenAMP/libmetal/lib/alloc.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file alloc.h
|
||||
* @brief Memory allocation handling primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ALLOC__H__
|
||||
#define __METAL_ALLOC__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup Memory Allocation Interfaces
|
||||
* @{ */
|
||||
|
||||
/**
|
||||
* @brief allocate requested memory size
|
||||
* return a pointer to the allocated memory
|
||||
*
|
||||
* @param[in] size size in byte of requested memory
|
||||
* @return memory pointer, or 0 if it failed to allocate
|
||||
*/
|
||||
static inline void *metal_allocate_memory(unsigned int size);
|
||||
|
||||
/**
|
||||
* @brief free the memory previously allocated
|
||||
*
|
||||
* @param[in] ptr pointer to memory
|
||||
*/
|
||||
static inline void metal_free_memory(void *ptr);
|
||||
|
||||
#include <metal/system/@PROJECT_SYSTEM@/alloc.h>
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_ALLOC__H__ */
|
24
source/OpenAMP/libmetal/lib/assert.h
Normal file
24
source/OpenAMP/libmetal/lib/assert.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file assert.h
|
||||
* @brief Assertion support.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ASSERT__H__
|
||||
#define __METAL_ASSERT__H__
|
||||
|
||||
#include <metal/system/@PROJECT_SYSTEM@/assert.h>
|
||||
|
||||
/**
|
||||
* @brief Assertion macro.
|
||||
* @param cond Condition to test.
|
||||
*/
|
||||
#define metal_assert(cond) metal_sys_assert(cond)
|
||||
|
||||
#endif /* __METAL_ASSERT_H__ */
|
||||
|
26
source/OpenAMP/libmetal/lib/atomic.h
Normal file
26
source/OpenAMP/libmetal/lib/atomic.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file atomic.h
|
||||
* @brief Atomic primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ATOMIC__H__
|
||||
#define __METAL_ATOMIC__H__
|
||||
|
||||
#include <metal/config.h>
|
||||
|
||||
#if defined(HAVE_STDATOMIC_H) && !defined(__STDC_NO_ATOMICS__) && \
|
||||
!defined(__cplusplus)
|
||||
# include <stdatomic.h>
|
||||
#elif defined(__GNUC__)
|
||||
# include <metal/compiler/gcc/atomic.h>
|
||||
#else
|
||||
# include <metal/processor/@PROJECT_PROCESSOR@/atomic.h>
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_ATOMIC__H__ */
|
57
source/OpenAMP/libmetal/lib/cache.h
Normal file
57
source/OpenAMP/libmetal/lib/cache.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file cache.h
|
||||
* @brief CACHE operation primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CACHE__H__
|
||||
#define __METAL_CACHE__H__
|
||||
|
||||
#include <metal/system/@PROJECT_SYSTEM@/cache.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/** \defgroup cache CACHE Interfaces
|
||||
* @{ */
|
||||
|
||||
/**
|
||||
* @brief flush specified data cache
|
||||
*
|
||||
* @param[in] addr start memory logical address
|
||||
* @param[in] len length of memory
|
||||
* If addr is NULL, and len is 0,
|
||||
* It will flush the whole data cache.
|
||||
*/
|
||||
static inline void metal_cache_flush(void *addr, unsigned int len)
|
||||
{
|
||||
__metal_cache_flush(addr, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief invalidate specified data cache
|
||||
*
|
||||
* @param[in] addr start memory logical address
|
||||
* @param[in] len length of memory
|
||||
* If addr is NULL, and len is 0,
|
||||
* It will invalidate the whole data cache.
|
||||
*/
|
||||
static inline void metal_cache_invalidate(void *addr, unsigned int len)
|
||||
{
|
||||
__metal_cache_invalidate(addr, len);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_CACHE__H__ */
|
23
source/OpenAMP/libmetal/lib/compiler.h
Normal file
23
source/OpenAMP/libmetal/lib/compiler.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file compiler.h
|
||||
* @brief Compiler specific primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_COMPILER__H__
|
||||
#define __METAL_COMPILER__H__
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# include <metal/compiler/gcc/compiler.h>
|
||||
#elif defined(__ICCARM__)
|
||||
# include <metal/compiler/iar/compiler.h>
|
||||
#else
|
||||
# error "Missing compiler support"
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_COMPILER__H__ */
|
4
source/OpenAMP/libmetal/lib/compiler/CMakeLists.txt
Normal file
4
source/OpenAMP/libmetal/lib/compiler/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
add_subdirectory (gcc)
|
||||
add_subdirectory (iar)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
4
source/OpenAMP/libmetal/lib/compiler/gcc/CMakeLists.txt
Normal file
4
source/OpenAMP/libmetal/lib/compiler/gcc/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
collect (PROJECT_LIB_HEADERS atomic.h)
|
||||
collect (PROJECT_LIB_HEADERS compiler.h)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
123
source/OpenAMP/libmetal/lib/compiler/gcc/atomic.h
Normal file
123
source/OpenAMP/libmetal/lib/compiler/gcc/atomic.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file gcc/atomic.h
|
||||
* @brief GCC specific atomic primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_GCC_ATOMIC__H__
|
||||
#define __METAL_GCC_ATOMIC__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef int atomic_flag;
|
||||
typedef char atomic_char;
|
||||
typedef unsigned char atomic_uchar;
|
||||
typedef short atomic_short;
|
||||
typedef unsigned short atomic_ushort;
|
||||
typedef int atomic_int;
|
||||
typedef unsigned int atomic_uint;
|
||||
typedef long atomic_long;
|
||||
typedef unsigned long atomic_ulong;
|
||||
typedef long long atomic_llong;
|
||||
typedef unsigned long long atomic_ullong;
|
||||
|
||||
#define ATOMIC_FLAG_INIT 0
|
||||
#define ATOMIC_VAR_INIT(VAL) (VAL)
|
||||
|
||||
typedef enum {
|
||||
memory_order_relaxed,
|
||||
memory_order_consume,
|
||||
memory_order_acquire,
|
||||
memory_order_release,
|
||||
memory_order_acq_rel,
|
||||
memory_order_seq_cst,
|
||||
} memory_order;
|
||||
|
||||
#define atomic_flag_test_and_set(FLAG) \
|
||||
__sync_lock_test_and_set((FLAG), 1)
|
||||
#define atomic_flag_test_and_set_explicit(FLAG, MO) \
|
||||
atomic_flag_test_and_set(FLAG)
|
||||
#define atomic_flag_clear(FLAG) \
|
||||
__sync_lock_release((FLAG))
|
||||
#define atomic_flag_clear_explicit(FLAG, MO) \
|
||||
atomic_flag_clear(FLAG)
|
||||
#define atomic_init(OBJ, VAL) \
|
||||
do { *(OBJ) = (VAL); } while (0)
|
||||
#define atomic_is_lock_free(OBJ) \
|
||||
(sizeof(*(OBJ)) <= sizeof(long))
|
||||
#define atomic_store(OBJ, VAL) \
|
||||
do { *(OBJ) = (VAL); __sync_synchronize(); } while (0)
|
||||
#define atomic_store_explicit(OBJ, VAL, MO) \
|
||||
atomic_store((OBJ), (VAL))
|
||||
#define atomic_load(OBJ) \
|
||||
({ __sync_synchronize(); *(OBJ); })
|
||||
#define atomic_load_explicit(OBJ, MO) \
|
||||
atomic_load(OBJ)
|
||||
#define atomic_exchange(OBJ, DES) \
|
||||
({ \
|
||||
typeof(OBJ) obj = (OBJ); \
|
||||
typeof(*obj) des = (DES); \
|
||||
typeof(*obj) expval; \
|
||||
typeof(*obj) oldval = atomic_load(obj); \
|
||||
do { \
|
||||
expval = oldval; \
|
||||
oldval = __sync_val_compare_and_swap( \
|
||||
obj, expval, des); \
|
||||
} while (oldval != expval); \
|
||||
oldval; \
|
||||
})
|
||||
#define atomic_exchange_explicit(OBJ, DES, MO) \
|
||||
atomic_exchange((OBJ), (DES))
|
||||
#define atomic_compare_exchange_strong(OBJ, EXP, DES) \
|
||||
({ \
|
||||
typeof(OBJ) obj = (OBJ); \
|
||||
typeof(EXP) exp = (EXP); \
|
||||
typeof(*obj) expval = *exp; \
|
||||
typeof(*obj) oldval = __sync_val_compare_and_swap( \
|
||||
obj, expval, (DES)); \
|
||||
*exp = oldval; \
|
||||
oldval == expval; \
|
||||
})
|
||||
#define atomic_compare_exchange_strong_explicit(OBJ, EXP, DES, MO) \
|
||||
atomic_compare_exchange_strong((OBJ), (EXP), (DES))
|
||||
#define atomic_compare_exchange_weak(OBJ, EXP, DES) \
|
||||
atomic_compare_exchange_strong((OBJ), (EXP), (DES))
|
||||
#define atomic_compare_exchange_weak_explicit(OBJ, EXP, DES, MO) \
|
||||
atomic_compare_exchange_weak((OBJ), (EXP), (DES))
|
||||
#define atomic_fetch_add(OBJ, VAL) \
|
||||
__sync_fetch_and_add((OBJ), (VAL))
|
||||
#define atomic_fetch_add_explicit(OBJ, VAL, MO) \
|
||||
atomic_fetch_add((OBJ), (VAL))
|
||||
#define atomic_fetch_sub(OBJ, VAL) \
|
||||
__sync_fetch_and_sub((OBJ), (VAL))
|
||||
#define atomic_fetch_sub_explicit(OBJ, VAL, MO) \
|
||||
atomic_fetch_sub((OBJ), (VAL))
|
||||
#define atomic_fetch_or(OBJ, VAL) \
|
||||
__sync_fetch_and_or((OBJ), (VAL))
|
||||
#define atomic_fetch_or_explicit(OBJ, VAL, MO) \
|
||||
atomic_fetch_or((OBJ), (VAL))
|
||||
#define atomic_fetch_xor(OBJ, VAL) \
|
||||
__sync_fetch_and_xor((OBJ), (VAL))
|
||||
#define atomic_fetch_xor_explicit(OBJ, VAL, MO) \
|
||||
atomic_fetch_xor((OBJ), (VAL))
|
||||
#define atomic_fetch_and(OBJ, VAL) \
|
||||
__sync_fetch_and_and((OBJ), (VAL))
|
||||
#define atomic_fetch_and_explicit(OBJ, VAL, MO) \
|
||||
atomic_fetch_and((OBJ), (VAL))
|
||||
#define atomic_thread_fence(MO) \
|
||||
__sync_synchronize()
|
||||
#define atomic_signal_fence(MO) \
|
||||
__sync_synchronize()
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GCC_ATOMIC__H__ */
|
27
source/OpenAMP/libmetal/lib/compiler/gcc/compiler.h
Normal file
27
source/OpenAMP/libmetal/lib/compiler/gcc/compiler.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file gcc/compiler.h
|
||||
* @brief GCC specific primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_GCC_COMPILER__H__
|
||||
#define __METAL_GCC_COMPILER__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define restrict __restrict__
|
||||
#define metal_align(n) __attribute__((aligned(n)))
|
||||
#define metal_weak __attribute__((weak))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GCC_COMPILER__H__ */
|
3
source/OpenAMP/libmetal/lib/compiler/iar/CMakeLists.txt
Normal file
3
source/OpenAMP/libmetal/lib/compiler/iar/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
collect (PROJECT_LIB_HEADERS compiler.h)
|
||||
|
||||
# vim: expandtab:ts=2:sw=2:smartindent
|
27
source/OpenAMP/libmetal/lib/compiler/iar/compiler.h
Normal file
27
source/OpenAMP/libmetal/lib/compiler/iar/compiler.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2018, ST Microelectronics. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file iar/compiler.h
|
||||
* @brief IAR specific primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_IAR_COMPILER__H__
|
||||
#define __METAL_IAR_COMPILER__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define restrict __restrict__
|
||||
#define metal_align(n) __attribute__((aligned(n)))
|
||||
#define metal_weak __attribute__((weak))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_IAR_COMPILER__H__ */
|
73
source/OpenAMP/libmetal/lib/condition.h
Normal file
73
source/OpenAMP/libmetal/lib/condition.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file condition.h
|
||||
* @brief Condition variable for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CONDITION__H__
|
||||
#define __METAL_CONDITION__H__
|
||||
|
||||
#include <metal/mutex.h>
|
||||
#include <metal/utilities.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup condition Condition Variable Interfaces
|
||||
* @{ */
|
||||
|
||||
/** Opaque libmetal condition variable data structure. */
|
||||
struct metal_condition;
|
||||
|
||||
/**
|
||||
* @brief Initialize a libmetal condition variable.
|
||||
* @param[in] cv condition variable to initialize.
|
||||
*/
|
||||
static inline void metal_condition_init(struct metal_condition *cv);
|
||||
|
||||
/**
|
||||
* @brief Notify one waiter.
|
||||
* Before calling this function, the caller
|
||||
* should have acquired the mutex.
|
||||
* @param[in] cv condition variable
|
||||
* @return zero on no errors, non-zero on errors
|
||||
* @see metal_condition_wait, metal_condition_broadcast
|
||||
*/
|
||||
static inline int metal_condition_signal(struct metal_condition *cv);
|
||||
|
||||
/**
|
||||
* @brief Notify all waiters.
|
||||
* Before calling this function, the caller
|
||||
* should have acquired the mutex.
|
||||
* @param[in] cv condition variable
|
||||
* @return zero on no errors, non-zero on errors
|
||||
* @see metal_condition_wait, metal_condition_signal
|
||||
*/
|
||||
static inline int metal_condition_broadcast(struct metal_condition *cv);
|
||||
|
||||
/**
|
||||
* @brief Block until the condition variable is notified.
|
||||
* Before calling this function, the caller should
|
||||
* have acquired the mutex.
|
||||
* @param[in] cv condition variable
|
||||
* @param[in] m mutex
|
||||
* @return 0 on success, non-zero on failure.
|
||||
* @see metal_condition_signal
|
||||
*/
|
||||
int metal_condition_wait(struct metal_condition *cv, metal_mutex_t *m);
|
||||
|
||||
#include <metal/system/@PROJECT_SYSTEM@/condition.h>
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_CONDITION__H__ */
|
50
source/OpenAMP/libmetal/lib/config.h
Normal file
50
source/OpenAMP/libmetal/lib/config.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file config.h
|
||||
* @brief Generated configuration settings for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CONFIG__H__
|
||||
#define __METAL_CONFIG__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Library major version number. */
|
||||
#define METAL_VER_MAJOR @PROJECT_VER_MAJOR@
|
||||
|
||||
/** Library minor version number. */
|
||||
#define METAL_VER_MINOR @PROJECT_VER_MINOR@
|
||||
|
||||
/** Library patch level. */
|
||||
#define METAL_VER_PATCH @PROJECT_VER_PATCH@
|
||||
|
||||
/** Library version string. */
|
||||
#define METAL_VER "@PROJECT_VER@"
|
||||
|
||||
/** System type (linux, generic, ...). */
|
||||
#define METAL_SYSTEM "@PROJECT_SYSTEM@"
|
||||
#define METAL_SYSTEM_@PROJECT_SYSTEM_UPPER@
|
||||
|
||||
/** Processor type (arm, x86_64, ...). */
|
||||
#define METAL_PROCESSOR "@PROJECT_PROCESSOR@"
|
||||
#define METAL_PROCESSOR_@PROJECT_PROCESSOR_UPPER@
|
||||
|
||||
/** Machine type (zynq, zynqmp, ...). */
|
||||
#define METAL_MACHINE "@PROJECT_MACHINE@"
|
||||
#define METAL_MACHINE_@PROJECT_MACHINE_UPPER@
|
||||
|
||||
#cmakedefine HAVE_STDATOMIC_H
|
||||
#cmakedefine HAVE_FUTEX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_CONFIG__H__ */
|
17
source/OpenAMP/libmetal/lib/cpu.h
Normal file
17
source/OpenAMP/libmetal/lib/cpu.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file cpu.h
|
||||
* @brief CPU primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CPU__H__
|
||||
#define __METAL_CPU__H__
|
||||
|
||||
# include <metal/processor/@PROJECT_PROCESSOR@/cpu.h>
|
||||
|
||||
#endif /* __METAL_CPU__H__ */
|
167
source/OpenAMP/libmetal/lib/device.c
Normal file
167
source/OpenAMP/libmetal/lib/device.c
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <metal/errno.h>
|
||||
#include <metal/assert.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/list.h>
|
||||
#include <metal/log.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/utilities.h>
|
||||
#include <metal/dma.h>
|
||||
#include <metal/cache.h>
|
||||
|
||||
int metal_bus_register(struct metal_bus *bus)
|
||||
{
|
||||
if (!bus || !bus->name || !strlen(bus->name))
|
||||
return -EINVAL;
|
||||
if (metal_bus_find(bus->name, NULL) == 0)
|
||||
return -EEXIST;
|
||||
metal_list_init(&bus->devices);
|
||||
metal_list_add_tail(&_metal.common.bus_list, &bus->node);
|
||||
metal_log(METAL_LOG_DEBUG, "registered %s bus\n", bus->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metal_bus_unregister(struct metal_bus *bus)
|
||||
{
|
||||
metal_list_del(&bus->node);
|
||||
if (bus->ops.bus_close)
|
||||
bus->ops.bus_close(bus);
|
||||
metal_log(METAL_LOG_DEBUG, "unregistered %s bus\n", bus->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metal_bus_find(const char *name, struct metal_bus **result)
|
||||
{
|
||||
struct metal_list *node;
|
||||
struct metal_bus *bus;
|
||||
|
||||
metal_list_for_each(&_metal.common.bus_list, node) {
|
||||
bus = metal_container_of(node, struct metal_bus, node);
|
||||
if (strcmp(bus->name, name) != 0)
|
||||
continue;
|
||||
if (result)
|
||||
*result = bus;
|
||||
return 0;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int metal_device_open(const char *bus_name, const char *dev_name,
|
||||
struct metal_device **device)
|
||||
{
|
||||
struct metal_bus *bus;
|
||||
int error;
|
||||
|
||||
if (!bus_name || !strlen(bus_name) ||
|
||||
!dev_name || !strlen(dev_name) ||
|
||||
!device)
|
||||
return -EINVAL;
|
||||
|
||||
error = metal_bus_find(bus_name, &bus);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (!bus->ops.dev_open)
|
||||
return -ENODEV;
|
||||
|
||||
error = (*bus->ops.dev_open)(bus, dev_name, device);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void metal_device_close(struct metal_device *device)
|
||||
{
|
||||
metal_assert(device && device->bus);
|
||||
if (device->bus->ops.dev_close)
|
||||
device->bus->ops.dev_close(device->bus, device);
|
||||
}
|
||||
|
||||
int metal_register_generic_device(struct metal_device *device)
|
||||
{
|
||||
if (!device->name || !strlen(device->name) ||
|
||||
device->num_regions > METAL_MAX_DEVICE_REGIONS)
|
||||
return -EINVAL;
|
||||
|
||||
device->bus = &metal_generic_bus;
|
||||
metal_list_add_tail(&_metal.common.generic_device_list,
|
||||
&device->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metal_generic_dev_open(struct metal_bus *bus, const char *dev_name,
|
||||
struct metal_device **device)
|
||||
{
|
||||
struct metal_list *node;
|
||||
struct metal_device *dev;
|
||||
|
||||
(void)bus;
|
||||
|
||||
metal_list_for_each(&_metal.common.generic_device_list, node) {
|
||||
dev = metal_container_of(node, struct metal_device, node);
|
||||
if (strcmp(dev->name, dev_name) != 0)
|
||||
continue;
|
||||
*device = dev;
|
||||
return metal_generic_dev_sys_open(dev);
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int metal_generic_dev_dma_map(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg_in,
|
||||
int nents_in,
|
||||
struct metal_sg *sg_out)
|
||||
{
|
||||
(void)bus;
|
||||
(void)device;
|
||||
int i;
|
||||
|
||||
if (sg_out != sg_in)
|
||||
memcpy(sg_out, sg_in, nents_in*(sizeof(struct metal_sg)));
|
||||
for (i = 0; i < nents_in; i++) {
|
||||
if (dir == METAL_DMA_DEV_W) {
|
||||
metal_cache_flush(sg_out[i].virt, sg_out[i].len);
|
||||
}
|
||||
metal_cache_invalidate(sg_out[i].virt, sg_out[i].len);
|
||||
}
|
||||
|
||||
return nents_in;
|
||||
}
|
||||
|
||||
void metal_generic_dev_dma_unmap(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg,
|
||||
int nents)
|
||||
{
|
||||
(void)bus;
|
||||
(void)device;
|
||||
(void)dir;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nents; i++) {
|
||||
metal_cache_invalidate(sg[i].virt, sg[i].len);
|
||||
}
|
||||
}
|
||||
|
||||
struct metal_bus metal_weak metal_generic_bus = {
|
||||
.name = "generic",
|
||||
.ops = {
|
||||
.bus_close = NULL,
|
||||
.dev_open = metal_generic_dev_open,
|
||||
.dev_close = NULL,
|
||||
.dev_irq_ack = NULL,
|
||||
.dev_dma_map = metal_generic_dev_dma_map,
|
||||
.dev_dma_unmap = metal_generic_dev_dma_unmap,
|
||||
},
|
||||
};
|
176
source/OpenAMP/libmetal/lib/device.h
Normal file
176
source/OpenAMP/libmetal/lib/device.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file device.h
|
||||
* @brief Bus abstraction for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_BUS__H__
|
||||
#define __METAL_BUS__H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/list.h>
|
||||
#include <metal/dma.h>
|
||||
#include <metal/sys.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup device Bus Abstraction
|
||||
* @{ */
|
||||
|
||||
#ifndef METAL_MAX_DEVICE_REGIONS
|
||||
#define METAL_MAX_DEVICE_REGIONS 32
|
||||
#endif
|
||||
|
||||
struct metal_bus;
|
||||
struct metal_device;
|
||||
|
||||
/** Bus operations. */
|
||||
struct metal_bus_ops {
|
||||
void (*bus_close)(struct metal_bus *bus);
|
||||
int (*dev_open)(struct metal_bus *bus,
|
||||
const char *dev_name,
|
||||
struct metal_device **device);
|
||||
void (*dev_close)(struct metal_bus *bus,
|
||||
struct metal_device *device);
|
||||
void (*dev_irq_ack)(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
int irq);
|
||||
int (*dev_dma_map)(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg_in,
|
||||
int nents_in,
|
||||
struct metal_sg *sg_out);
|
||||
void (*dev_dma_unmap)(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg,
|
||||
int nents);
|
||||
};
|
||||
|
||||
/** Libmetal bus structure. */
|
||||
struct metal_bus {
|
||||
const char *name;
|
||||
struct metal_bus_ops ops;
|
||||
struct metal_list devices;
|
||||
struct metal_list node;
|
||||
};
|
||||
|
||||
/** Libmetal generic bus. */
|
||||
extern struct metal_bus metal_generic_bus;
|
||||
|
||||
/** Libmetal device structure. */
|
||||
struct metal_device {
|
||||
const char *name; /**< Device name */
|
||||
struct metal_bus *bus; /**< Bus that contains device */
|
||||
unsigned num_regions; /**< Number of I/O regions in
|
||||
device */
|
||||
struct metal_io_region regions[METAL_MAX_DEVICE_REGIONS]; /**< Array of
|
||||
I/O regions in device*/
|
||||
struct metal_list node; /**< Node on bus' list of devices */
|
||||
int irq_num; /**< Number of IRQs per device */
|
||||
void *irq_info; /**< IRQ ID */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Register a libmetal bus.
|
||||
* @param[in] bus Pre-initialized bus structure.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_bus_register(struct metal_bus *bus);
|
||||
|
||||
/**
|
||||
* @brief Unregister a libmetal bus.
|
||||
* @param[in] bus Pre-registered bus structure.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_bus_unregister(struct metal_bus *bus);
|
||||
|
||||
/**
|
||||
* @brief Find a libmetal bus by name.
|
||||
* @param[in] name Bus name.
|
||||
* @param[out] bus Returned bus handle.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_bus_find(const char *name, struct metal_bus **bus);
|
||||
|
||||
/**
|
||||
* @brief Statically register a generic libmetal device.
|
||||
*
|
||||
* In non-Linux systems, devices are always required to be statically
|
||||
* registered at application initialization.
|
||||
* In Linux system, devices can be dynamically opened via sysfs or libfdt based
|
||||
* enumeration at runtime.
|
||||
* This interface is used for static registration of devices. Subsequent calls
|
||||
* to metal_device_open() look up in this list of pre-registered devices on the
|
||||
* "generic" bus.
|
||||
* "generic" bus is used on non-Linux system to group the memory mapped devices.
|
||||
*
|
||||
* @param[in] device Generic device.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_register_generic_device(struct metal_device *device);
|
||||
|
||||
/**
|
||||
* @brief Open a libmetal device by name.
|
||||
* @param[in] bus_name Bus name.
|
||||
* @param[in] dev_name Device name.
|
||||
* @param[out] device Returned device handle.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_device_open(const char *bus_name, const char *dev_name,
|
||||
struct metal_device **device);
|
||||
|
||||
/**
|
||||
* @brief Close a libmetal device.
|
||||
* @param[in] device Device handle.
|
||||
*/
|
||||
extern void metal_device_close(struct metal_device *device);
|
||||
|
||||
/**
|
||||
* @brief Get an I/O region accessor for a device region.
|
||||
*
|
||||
* @param[in] device Device handle.
|
||||
* @param[in] index Region index.
|
||||
* @return I/O accessor handle, or NULL on failure.
|
||||
*/
|
||||
static inline struct metal_io_region *
|
||||
metal_device_io_region(struct metal_device *device, unsigned index)
|
||||
{
|
||||
return (index < device->num_regions
|
||||
? &device->regions[index]
|
||||
: NULL);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef METAL_INTERNAL
|
||||
extern int metal_generic_dev_sys_open(struct metal_device *dev);
|
||||
extern int metal_generic_dev_open(struct metal_bus *bus, const char *dev_name,
|
||||
struct metal_device **device);
|
||||
extern int metal_generic_dev_dma_map(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg_in,
|
||||
int nents_in,
|
||||
struct metal_sg *sg_out);
|
||||
extern void metal_generic_dev_dma_unmap(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg,
|
||||
int nents);
|
||||
#endif /* METAL_INTERNAL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_BUS__H__ */
|
58
source/OpenAMP/libmetal/lib/dma.c
Normal file
58
source/OpenAMP/libmetal/lib/dma.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <metal/errno.h>
|
||||
#include <string.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/log.h>
|
||||
#include <metal/dma.h>
|
||||
#include <metal/atomic.h>
|
||||
|
||||
int metal_dma_map(struct metal_device *dev,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg_in,
|
||||
int nents_in,
|
||||
struct metal_sg *sg_out)
|
||||
{
|
||||
int nents_out;
|
||||
|
||||
if (!dev || !sg_in || !sg_out)
|
||||
return -EINVAL;
|
||||
if (!dev->bus->ops.dev_dma_map)
|
||||
return -ENODEV;
|
||||
|
||||
/* memory barrier */
|
||||
if (dir == METAL_DMA_DEV_R)
|
||||
/* If it is device read, apply memory write fence. */
|
||||
atomic_thread_fence(memory_order_release);
|
||||
else
|
||||
/* If it is device write or device r/w,
|
||||
apply memory r/w fence. */
|
||||
atomic_thread_fence(memory_order_acq_rel);
|
||||
nents_out = dev->bus->ops.dev_dma_map(dev->bus,
|
||||
dev, dir, sg_in, nents_in, sg_out);
|
||||
return nents_out;
|
||||
}
|
||||
|
||||
void metal_dma_unmap(struct metal_device *dev,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg,
|
||||
int nents)
|
||||
{
|
||||
/* memory barrier */
|
||||
if (dir == METAL_DMA_DEV_R)
|
||||
/* If it is device read, apply memory write fence. */
|
||||
atomic_thread_fence(memory_order_release);
|
||||
else
|
||||
/* If it is device write or device r/w,
|
||||
apply memory r/w fence. */
|
||||
atomic_thread_fence(memory_order_acq_rel);
|
||||
|
||||
if (!dev || !dev->bus->ops.dev_dma_unmap || !sg)
|
||||
return;
|
||||
dev->bus->ops.dev_dma_unmap(dev->bus,
|
||||
dev, dir, sg, nents);
|
||||
}
|
79
source/OpenAMP/libmetal/lib/dma.h
Normal file
79
source/OpenAMP/libmetal/lib/dma.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file dma.h
|
||||
* @brief DMA primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_DMA__H__
|
||||
#define __METAL_DMA__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup dma DMA Interfaces
|
||||
* @{ */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <metal/sys.h>
|
||||
|
||||
#define METAL_DMA_DEV_R 1 /**< DMA direction, device read */
|
||||
#define METAL_DMA_DEV_W 2 /**< DMA direction, device write */
|
||||
#define METAL_DMA_DEV_WR 3 /**< DMA direction, device read/write */
|
||||
|
||||
/**
|
||||
* @brief scatter/gather list element structure
|
||||
*/
|
||||
struct metal_sg {
|
||||
void *virt; /**< CPU virtual address */
|
||||
struct metal_io_region *io; /**< IO region */
|
||||
int len; /**< length */
|
||||
};
|
||||
|
||||
struct metal_device;
|
||||
|
||||
/**
|
||||
* @brief Map memory for DMA transaction.
|
||||
* After the memory is DMA mapped, the memory should be
|
||||
* accessed by the DMA device but not the CPU.
|
||||
*
|
||||
* @param[in] dev DMA device
|
||||
* @param[in] dir DMA direction
|
||||
* @param[in] sg_in sg list of memory to map
|
||||
* @param[in] nents_in number of sg list entries of memory to map
|
||||
* @param[out] sg_out sg list of mapped memory
|
||||
* @return number of mapped sg entries, -error on failure.
|
||||
*/
|
||||
int metal_dma_map(struct metal_device *dev,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg_in,
|
||||
int nents_in,
|
||||
struct metal_sg *sg_out);
|
||||
|
||||
/**
|
||||
* @brief Unmap DMA memory
|
||||
* After the memory is DMA unmapped, the memory should
|
||||
* be accessed by the CPU but not the DMA device.
|
||||
*
|
||||
* @param[in] dev DMA device
|
||||
* @param[in] dir DMA direction
|
||||
* @param[in] sg sg list of mapped DMA memory
|
||||
* @param[in] nents number of sg list entries of DMA memory
|
||||
*/
|
||||
void metal_dma_unmap(struct metal_device *dev,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg,
|
||||
int nents);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_DMA__H__ */
|
46
source/OpenAMP/libmetal/lib/include/metal/alloc.h
Normal file
46
source/OpenAMP/libmetal/lib/include/metal/alloc.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file alloc.h
|
||||
* @brief Memory allocation handling primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ALLOC__H__
|
||||
#define __METAL_ALLOC__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup Memory Allocation Interfaces
|
||||
* @{ */
|
||||
|
||||
/**
|
||||
* @brief allocate requested memory size
|
||||
* return a pointer to the allocated memory
|
||||
*
|
||||
* @param[in] size size in byte of requested memory
|
||||
* @return memory pointer, or 0 if it failed to allocate
|
||||
*/
|
||||
static inline void *metal_allocate_memory(unsigned int size);
|
||||
|
||||
/**
|
||||
* @brief free the memory previously allocated
|
||||
*
|
||||
* @param[in] ptr pointer to memory
|
||||
*/
|
||||
static inline void metal_free_memory(void *ptr);
|
||||
|
||||
#include <metal/system/generic/alloc.h>
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_ALLOC__H__ */
|
24
source/OpenAMP/libmetal/lib/include/metal/assert.h
Normal file
24
source/OpenAMP/libmetal/lib/include/metal/assert.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file assert.h
|
||||
* @brief Assertion support.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ASSERT__H__
|
||||
#define __METAL_ASSERT__H__
|
||||
|
||||
#include <metal/system/generic/assert.h>
|
||||
|
||||
/**
|
||||
* @brief Assertion macro.
|
||||
* @param cond Condition to test.
|
||||
*/
|
||||
#define metal_assert(cond) metal_sys_assert(cond)
|
||||
|
||||
#endif /* __METAL_ASSERT_H__ */
|
||||
|
32
source/OpenAMP/libmetal/lib/include/metal/atomic.h
Normal file
32
source/OpenAMP/libmetal/lib/include/metal/atomic.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file atomic.h
|
||||
* @brief Atomic primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ATOMIC__H__
|
||||
#define __METAL_ATOMIC__H__
|
||||
|
||||
#include <metal/config.h>
|
||||
|
||||
#if defined(HAVE_STDATOMIC_H) && !defined (__CC_ARM) && \
|
||||
!defined(__STDC_NO_ATOMICS__) && !defined(__cplusplus)
|
||||
|
||||
# include <stdatomic.h>
|
||||
|
||||
#ifndef atomic_thread_fence
|
||||
#define atomic_thread_fence(order)
|
||||
#endif
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
# include <metal/compiler/gcc/atomic.h>
|
||||
#else
|
||||
# include <metal/processor/arm/atomic.h>
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_ATOMIC__H__ */
|
57
source/OpenAMP/libmetal/lib/include/metal/cache.h
Normal file
57
source/OpenAMP/libmetal/lib/include/metal/cache.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file cache.h
|
||||
* @brief CACHE operation primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CACHE__H__
|
||||
#define __METAL_CACHE__H__
|
||||
|
||||
#include <metal/system/generic/cache.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/** \defgroup cache CACHE Interfaces
|
||||
* @{ */
|
||||
|
||||
/**
|
||||
* @brief flush specified data cache
|
||||
*
|
||||
* @param[in] addr start memory logical address
|
||||
* @param[in] len length of memory
|
||||
* If addr is NULL, and len is 0,
|
||||
* It will flush the whole data cache.
|
||||
*/
|
||||
static inline void metal_cache_flush(void *addr, unsigned int len)
|
||||
{
|
||||
__metal_cache_flush(addr, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief invalidate specified data cache
|
||||
*
|
||||
* @param[in] addr start memory logical address
|
||||
* @param[in] len length of memory
|
||||
* If addr is NULL, and len is 0,
|
||||
* It will invalidate the whole data cache.
|
||||
*/
|
||||
static inline void metal_cache_invalidate(void *addr, unsigned int len)
|
||||
{
|
||||
__metal_cache_invalidate(addr, len);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_CACHE__H__ */
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user