建立工程,成功创建两个虚拟串口

This commit is contained in:
ranchuan
2023-06-21 18:00:56 +08:00
commit 3604192d8f
872 changed files with 428764 additions and 0 deletions

View 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

View 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

View 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/

View 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>

View 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.

View 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

View 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

View 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)

View 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()

View 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)

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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)

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
collect(PROJECT_LIB_DEPS metal)
add_subdirectory (system)
# vim: expandtab:ts=2:sw=2:smartindent

View 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})

View File

@@ -0,0 +1,4 @@
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
add_subdirectory(${PROJECT_MACHINE})
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})

View File

@@ -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)

View File

@@ -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

View File

@@ -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__ */

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 = .;
}

View File

@@ -0,0 +1,6 @@
#ifndef __PLATFORM_CONFIG_H_
#define __PLATFORM_CONFIG_H_
#define STDOUT_IS_PSU_UART
#define UART_DEVICE_ID 0
#endif

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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__ */

View File

@@ -0,0 +1,4 @@
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
add_subdirectory(${PROJECT_MACHINE})
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})

View File

@@ -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)

View File

@@ -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

View File

@@ -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__ */

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 = .;
}

View File

@@ -0,0 +1,6 @@
#ifndef __PLATFORM_CONFIG_H_
#define __PLATFORM_CONFIG_H_
#define STDOUT_IS_PSU_UART
#define UART_DEVICE_ID 0
#endif

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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__ */

View File

@@ -0,0 +1,4 @@
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
add_subdirectory(${PROJECT_MACHINE})
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})

View File

@@ -0,0 +1 @@
add_subdirectory(zynqmp_amp_demo)

View File

@@ -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

View File

@@ -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__ */

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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__ */

View 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

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View File

@@ -0,0 +1,4 @@
add_subdirectory (gcc)
add_subdirectory (iar)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,4 @@
collect (PROJECT_LIB_HEADERS atomic.h)
collect (PROJECT_LIB_HEADERS compiler.h)
# vim: expandtab:ts=2:sw=2:smartindent

View 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__ */

View 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__ */

View File

@@ -0,0 +1,3 @@
collect (PROJECT_LIB_HEADERS compiler.h)
# vim: expandtab:ts=2:sw=2:smartindent

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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,
},
};

View 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__ */

View 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);
}

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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