Sync inav to Gitea
Make sure docs are updated / settings_md (push) Canceled after 0s
Build firmware / test (push) Canceled after 0s
Build firmware / build-SITL-Windows (push) Canceled after 0s
Build firmware / build-SITL-Mac (push) Canceled after 0s
Build firmware / build-SITL-Linux (push) Canceled after 0s
Build firmware / build-SITL-Linux-arm64 (push) Canceled after 0s
Build firmware / upload-artifacts (push) Canceled after 0s
Build firmware / build-single-target (push) Canceled after 0s
Build firmware / build (9) (push) Canceled after 0s
Build firmware / build (8) (push) Canceled after 0s
Build firmware / build (7) (push) Canceled after 0s
Build firmware / build (6) (push) Canceled after 0s
Build firmware / build (5) (push) Canceled after 0s
Build firmware / build (4) (push) Canceled after 0s
Build firmware / build (3) (push) Canceled after 0s
Build firmware / build (2) (push) Canceled after 0s
Build firmware / build (14) (push) Canceled after 0s
Build firmware / build (13) (push) Canceled after 0s
Build firmware / build (12) (push) Canceled after 0s
Build firmware / build (11) (push) Canceled after 0s
Build firmware / build (10) (push) Canceled after 0s
Build firmware / build (1) (push) Canceled after 0s
Build firmware / build (0) (push) Canceled after 0s
Build firmware / detect (push) Canceled after 0s
Build pre-release / build (push) Canceled after 0s
Build pre-release / Release (push) Canceled after 0s

This commit is contained in:
2026-08-03 16:37:40 +08:00
commit dac37fd077
14095 changed files with 5603119 additions and 0 deletions
@@ -0,0 +1,5 @@
collect(PROJECT_LIB_DEPS metal)
add_subdirectory (system)
# vim: expandtab:ts=2:sw=2:smartindent
@@ -0,0 +1,4 @@
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_SYSTEM})
add_subdirectory(${PROJECT_SYSTEM})
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_SYSTEM})
@@ -0,0 +1,4 @@
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
add_subdirectory(${PROJECT_MACHINE})
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
@@ -0,0 +1,18 @@
collect(PROJECT_LIB_DEPS freertos)
collect(PROJECT_LIB_DEPS metal)
collect(PROJECT_LIB_DEPS xil)
collect(PROJECT_LIB_DEPS c)
collect(PROJECT_LIB_DEPS m)
set (_lib "xil")
find_library (_lib_path ${_lib})
if (NOT _lib_path)
message ( "external library ${_lib_path} not found" )
message ( "hint: you may need to pass -DCMAKE_LIBRARY_PATH=<path>" )
message ( FATAL_ERROR "library ${_lib} is required to build the examples" )
endif (NOT _lib_path)
get_filename_component (_lib_path ${_lib_path} DIRECTORY)
collect (PROJECT_LIB_DIRS ${_lib_path})
add_subdirectory(zynqmp_amp_demo)
@@ -0,0 +1,31 @@
collector_list (_list PROJECT_INC_DIRS)
include_directories (${_list} ${CMAKE_CURRENT_SOURCE_DIR})
collector_list (_list PROJECT_LIB_DIRS)
link_directories (${_list})
collector_list (_deps PROJECT_LIB_DEPS)
set (_linker_script ${CMAKE_CURRENT_SOURCE_DIR}/lscript.ld)
set (_src_common ${CMAKE_CURRENT_SOURCE_DIR}/init_${PROJECT_SYSTEM}.c)
set (_app0 libmetal_amp_demod)
set (_src0 ${CMAKE_CURRENT_SOURCE_DIR}/${_app0}.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/sys_init.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_demod.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_atomic_demod.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/ipi_shmem_demod.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/ipi_latency_demod.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_latency_demod.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_throughput_demod.c)
get_property (_linker_options GLOBAL PROPERTY TEST_LINKER_OPTIONS)
add_executable (${_app0}.elf ${_src0})
if (PROJECT_EC_FLAGS)
string(REPLACE " " ";" _ec_flgs ${PROJECT_EC_FLAGS})
target_compile_options (${_app0}.elf PUBLIC ${_ec_flgs})
endif (PROJECT_EC_FLAGS)
target_link_libraries(${_app0}.elf -Wl,-Map=${_app0}.map -Wl,--gc-sections -T\"${_linker_script}\" -Wl,--start-group ${_deps} -Wl,--end-group)
install (TARGETS ${_app0}.elf RUNTIME DESTINATION bin)
# vim: expandtab:ts=2:sw=2:smartindent
@@ -0,0 +1,163 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __COMMON_H__
#define __COMMON_H__
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <metal/atomic.h>
#include <metal/alloc.h>
#include <metal/irq.h>
#include <metal/errno.h>
#include <metal/sys.h>
#include <metal/cpu.h>
#include <metal/io.h>
#include <metal/device.h>
#include <sys/types.h>
#include "sys_init.h"
/* Devices names */
#define BUS_NAME "generic"
#define IPI_DEV_NAME "ff310000.ipi"
#define SHM_DEV_NAME "3ed80000.shm"
#define TTC_DEV_NAME "ff110000.ttc"
/* IPI registers offset */
#define IPI_TRIG_OFFSET 0x0 /* IPI trigger reg offset */
#define IPI_OBS_OFFSET 0x4 /* IPI observation reg offset */
#define IPI_ISR_OFFSET 0x10 /* IPI interrupt status reg offset */
#define IPI_IMR_OFFSET 0x14 /* IPI interrupt mask reg offset */
#define IPI_IER_OFFSET 0x18 /* IPI interrupt enable reg offset */
#define IPI_IDR_OFFSET 0x1C /* IPI interrup disable reg offset */
#define IPI_MASK 0x1000000 /* IPI mask for kick from APU.
We use PL0 IPI in this demo. */
/* TTC counter offsets */
#define XTTCPS_CLK_CNTRL_OFFSET 0x0 /* TTC counter clock control reg offset */
#define XTTCPS_CNT_CNTRL_OFFSET 0xC /* TTC counter control reg offset */
#define XTTCPS_CNT_VAL_OFFSET 0x18 /* TTC counter val reg offset */
#define XTTCPS_CNT_OFFSET(ID) ((ID) == 1 ? 0 : 1 << (ID)) /* TTC counter offset
ID is from 1 to 3 */
/* TTC counter control masks */
#define XTTCPS_CNT_CNTRL_RST_MASK 0x10U /* TTC counter control reset mask */
#define XTTCPS_CNT_CNTRL_DIS_MASK 0x01U /* TTC counter control disable mask */
#define LPRINTF(format, ...) \
xil_printf("\r\nSERVER> " format, ##__VA_ARGS__)
#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__)
extern struct metal_device *ipi_dev; /* IPI metal device */
extern struct metal_device *shm_dev; /* SHM metal device */
extern struct metal_device *ttc_dev; /* TTC metal device */
/**
* @brief atomic_shmem_demod() - Shared memory atomic operation demo
* This task will:
* * Wait for the remote to write to shared memory.
* * Once it receives the notification via polling, start atomic add by
* 1 for 1000 times to first 32 bits of memory in the
* shared memory location at 3ed00000 which is pointed to by shm_io.
* * Write to shared mem to notify the remote once it finishes
* calculation.
*
* @return - If setup failed, return the corresponding error number. Otherwise
* return 0 on success.
*/
int atomic_shmem_demod();
/**
* @brief ipi_latency_demod() - Show performance of IPI with Libmetal.
* Loop until APU tells RPU to stop via shared memory.
* In loop, wait for interrupt (interrupt handler stops APU to
* RPU timer). Then reset count on RPU to APU timer to 0, start
* counting and send interrupt to notify APU.
*
* @return - 0 on success, error code if failure.
*/
int ipi_latency_demod();
/**
* @brief ipi_shmem_demod() - shared memory IPI demo
* This task will:
* * Wait for IPI interrupt from the remote
* * Once it received the interrupt, copy the content from
* the ping buffer to the pong buffer.
* * Update the shared memory descriptor for the new available
* pong buffer.
* * Trigger IPI to notifty the remote.
*
* @return - 0 on success, error code if failure.
*/
int ipi_shmem_demod();
/**
* @brief shmem_demod() - Show use of shared memory with Libmetal.
* Until KEEP_GOING signal is stopped, keep looping.
* In the loop, read message from remote, add one to message and
* then respond. After the loop, cleanup resources.
*
* @return - return 0 on success, otherwise return error number indicating
* type of error
*/
int shmem_demod();
/**
* @brief shmem_latency_demod() - Show performance of shared mem.
* Loop until APU tells RPU to stop via shared memory.
* In loop, wait for interrupt (interrupt handler stops APU to
* RPU timer). Then reset count on RPU to APU timer to 0, start
* counting and send interrupt to notify APU.
*
* @return - 0 on success, error code if failure.
*/
int shmem_latency_demod();
/**
* @brief shmem_throughput_demod() - Show throughput of shared mem.
* At signal of remote, record total time to do block read and write
* operation Loop until APU tells RPU to stop via shared memory.
* In loop, wait for interrupt (interrupt handler stops APU to
* RPU timer). Then reset count on RPU to APU timer to 0, start
* counting and send interrupt to notify APU.
*
* @return - 0 on success, error code if failure.
*/
int shmem_throughput_demod();
static inline void wait_for_interrupt()
{
asm volatile("wfi");
}
/**
* @breif wait_for_notified() - Loop until notified bit
* in channel is set.
*
* @param[in] notified - pointer to the notified variable
*/
static inline void wait_for_notified(atomic_int *notified)
{
while (atomic_flag_test_and_set(notified));
}
/**
* @brief print_demo() - print demo string
*
* @param[in] name - demo name
*/
static inline void print_demo(char *name)
{
LPRINTF("====== libmetal demo: %s ======\n", name);
}
#endif /* __COMMON_H__ */
@@ -0,0 +1,218 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* ipi_latency_demod.c
* This is the remote side of the IPI latency measurement demo.
* This demo does the follwing steps:
*
* 1. Open the shared memory device.
* 1. Open the TTC timer device.
* 2. Open the IPI device.
* 3. Register IPI interrupt handler.
* 6. When it receives IPI interrupt, the IPI interrupt handler to stop
* the RPU to APU TTC counter.
* 7. Check the shared memory to see if demo is on. If the demo is on,
* reest the RPU to APU TTC counter and kick IPI to notify the remote.
* 8. If the shared memory indicates the demo is off, cleanup resource:
* disable IPI interrupt and deregister the IPI interrupt handler.
*/
#include <unistd.h>
#include <metal/atomic.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/irq.h>
#include "common.h"
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
#define TTC_CLK_FREQ_HZ 100000000
/* Shared memory offset */
#define SHM_DEMO_CNTRL_OFFSET 0x0
#define DEMO_STATUS_IDLE 0x0
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
struct channel_s {
struct metal_io_region *ipi_io; /* IPI metal i/o region */
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
struct metal_io_region *ttc_io; /* TTC metal i/o region */
uint32_t ipi_mask; /* RPU IPI mask */
atomic_int remote_nkicked; /* 0 - kicked from remote */
};
/**
* @brief reset_timer() - function to reset TTC counter
* Set the RST bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void reset_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_RST_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief stop_timer() - function to stop TTC counter
* Set the disable bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void stop_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_DIS_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief ipi_irq_handler() - IPI interrupt handler
* It will clear the notified flag to mark it's got an IPI interrupt.
* It will stop the RPU->APU timer and will clear the notified
* flag to mark it's got an IPI interrupt
*
* @param[in] vect_id - IPI interrupt vector ID
* @param[in/out] priv - communication channel data for this application.
*
* @return - If the IPI interrupt is triggered by its remote, it returns
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
* not the interrupt it expected.
*
*/
static int ipi_irq_handler (int vect_id, void *priv)
{
struct channel_s *ch = (struct channel_s *)priv;
uint32_t val;
(void)vect_id;
if (ch) {
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
if (val & ch->ipi_mask) {
/* stop RPU -> APU timer */
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
ch->ipi_mask);
atomic_flag_clear(&ch->remote_nkicked);
return METAL_IRQ_HANDLED;
}
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief measure_ipi_latencyd() - measure IPI latency with libmetal
* Loop until APU tells RPU to stop via shared memory.
* In loop, wait for interrupt (interrupt handler stops APU to
* RPU TTC counter). Then reset count on RPU to APU TTC counter
* and kick IPI to notify APU.
*
* @param[in] ch - channel information
* @return - 0 on success, error code if failure.
*/
static int measure_ipi_latencyd(struct channel_s *ch)
{
LPRINTF("Starting IPI latency demo\r\n");
while(1) {
wait_for_notified(&ch->remote_nkicked);
if (metal_io_read32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET) ==
DEMO_STATUS_START) {
/* Reset RPU to APU TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
/* Kick IPI to notify the remote */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
ch->ipi_mask);
} else {
break;
}
}
return 0;
}
int ipi_latency_demod()
{
struct channel_s ch;
int ipi_irq;
int ret = 0;
print_demo("IPI latency");
memset(&ch, 0, sizeof(ch));
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
ch.shm_io = metal_device_io_region(shm_dev, 0);
if (!ch.shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Get TTC IO region */
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
if (!ch.ttc_io) {
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
ret = -ENODEV;
goto out;
}
/* Get IPI device IO region */
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ch.ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
ch.ipi_mask = IPI_MASK;
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
/* initialize remote_nkicked */
atomic_init(&ch.remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = measure_ipi_latencyd(&ch);
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler */
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
out:
return ret;
}
@@ -0,0 +1,282 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* ipi_shmem_demo.c - shared memory with IPI demo
* This demo will:
*
* 1. Get the shared memory device I/O region.
* 2. Get the IPI device I/O region.
* 3. Register IPI interrupt handler.
* 4. Wait for remote IPI notification to receive message.
* 5. When message is received, check if it is shutdown message.
* 6. If it is shutdown message, do cleanup, otherwise, echo it back to the
* shared buffer.
* 7. Kick IPI to notify there is a message written to the shared memory
* if it echos back the message.
* 8. Repeat 4.
* 9. Clean up: disable IPI interrupt, deregister the IPI interrupt handler.
*
* Here is the Shared memory structure of this demo:
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
* |0x04 - 0x07 | number of APU to RPU buffers consumed by RPU |
* |0x08 - 0x1FFC | address array for shared buffers from APU to RPU |
* |0x2000 - 0x2003 | number of RPU to APU buffers available to APU |
* |0x2004 - 0x2007 | number of RPU to APU buffers consumed by APU |
* |0x2008 - 0x3FFC | address array for shared buffers from RPU to APU |
* |0x04000 - 0x103FFC | APU to RPU buffers |
* |0x104000 - 0x203FFC | RPU to APU buffers |
*/
#include <sys/types.h>
#include <metal/sys.h>
#include <metal/io.h>
#include <metal/alloc.h>
#include <metal/device.h>
#include <metal/irq.h>
#include <metal/errno.h>
#include "common.h"
#define BUF_SIZE_MAX 512
#define SHUTDOWN "shutdown"
/* Shared memory offsets */
#define SHM_DESC_OFFSET_RX 0x0
#define SHM_BUFF_OFFSET_RX 0x04000
#define SHM_DESC_OFFSET_TX 0x02000
#define SHM_BUFF_OFFSET_TX 0x104000
/* Shared memory descriptors offset */
#define SHM_DESC_AVAIL_OFFSET 0x00
#define SHM_DESC_USED_OFFSET 0x04
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x08
#define PKGS_TOTAL 1024
#define BUF_SIZE_MAX 512
#define SHUTDOWN "shutdown"
struct msg_hdr_s {
uint32_t index;
uint32_t len;
};
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
1 - not-kicked */
static int ipi_irq_handler (int vect_id, void *priv)
{
(void)vect_id;
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
uint32_t ipi_mask = IPI_MASK;
uint64_t val = 1;
if (!ipi_io)
return METAL_IRQ_NOT_HANDLED;
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
if (val & ipi_mask) {
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
atomic_flag_clear(&remote_nkicked);
return METAL_IRQ_HANDLED;
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief ipi_shmem_echod() - shared memory IPI demo
* This task will:
* * Wait for IPI interrupt from the remote
* * Once it received the interrupt, copy the content from
* the ping buffer to the pong buffer.
* * Update the shared memory descriptor for the new available
* pong buffer.
* * Trigger IPI to notifty the remote.
* @param[in] ipi_io - IPI metal i/o region
* @param[in] shm_io - shared memory metal i/o region
* @return - return 0 on success, otherwise return error number indicating
* type of error.
*/
static int ipi_shmem_echod(struct metal_io_region *ipi_io,
struct metal_io_region *shm_io)
{
int ret = 0;
uint32_t rx_count, rx_avail;
unsigned long tx_avail_offset, rx_avail_offset;
unsigned long rx_used_offset;
unsigned long tx_addr_offset, rx_addr_offset;
unsigned long tx_data_offset, rx_data_offset;
void *lbuf = NULL;
struct msg_hdr_s *msg_hdr;
uint32_t ipi_mask = IPI_MASK;
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
if (!lbuf) {
LPERROR("Failed to allocate local buffer for msg.\n");
ret = -ENOMEM;
goto out;
}
/* Clear shared memory */
metal_io_block_set(shm_io, 0, 0, metal_io_region_size(shm_io));
/* Set tx/rx buffer address offset */
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
rx_used_offset = SHM_DESC_OFFSET_RX + SHM_DESC_USED_OFFSET;
tx_addr_offset = SHM_DESC_OFFSET_TX + SHM_DESC_ADDR_ARRAY_OFFSET;
rx_addr_offset = SHM_DESC_OFFSET_RX + SHM_DESC_ADDR_ARRAY_OFFSET;
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
LPRINTF("Wait for echo test to start.\n");
rx_count = 0;
while (1) {
wait_for_notified(&remote_nkicked);
rx_avail = metal_io_read32(shm_io, rx_avail_offset);
while(rx_count != rx_avail) {
uint32_t buf_phy_addr_32;
/* Received ping from the other side */
/* Get the buffer location from the shared memory
* rx address array.
*/
buf_phy_addr_32 = metal_io_read32(shm_io,
rx_addr_offset);
rx_data_offset = metal_io_phys_to_offset(shm_io,
(metal_phys_addr_t)buf_phy_addr_32);
if (rx_data_offset == METAL_BAD_OFFSET) {
LPERROR("[%u]failed to get rx offset: 0x%x, 0x%lx.\n",
rx_count, buf_phy_addr_32,
metal_io_phys(shm_io, rx_addr_offset));
ret = -EINVAL;
goto out;
}
rx_addr_offset += sizeof(buf_phy_addr_32);
/* Read message header from shared memory */
metal_io_block_read(shm_io, rx_data_offset, lbuf,
sizeof(struct msg_hdr_s));
msg_hdr = (struct msg_hdr_s *)lbuf;
/* Check if the message header is valid */
if (msg_hdr->len > (BUF_SIZE_MAX - sizeof(*msg_hdr))) {
LPERROR("wrong msg: length invalid: %u, %u.\n",
BUF_SIZE_MAX - sizeof(*msg_hdr),
msg_hdr->len);
ret = -EINVAL;
goto out;
}
rx_data_offset += sizeof(*msg_hdr);
/* Read message */
metal_io_block_read(shm_io,
rx_data_offset,
lbuf + sizeof(*msg_hdr), msg_hdr->len);
rx_data_offset += msg_hdr->len;
rx_count++;
/* increase rx used count to indicate it has consumed
* the received data */
metal_io_write32(shm_io, rx_used_offset, rx_count);
/* Check if the it is the shutdown message */
if (msg_hdr->len == strlen(SHUTDOWN) &&
!strncmp(SHUTDOWN,
(lbuf + sizeof(struct msg_hdr_s)),
strlen(SHUTDOWN))) {
LPRINTF("Received shutdown message\n");
goto out;
}
/* Copy the message back to the other end */
metal_io_block_write(shm_io, tx_data_offset, msg_hdr,
sizeof(struct msg_hdr_s) + msg_hdr->len);
/* Write to the address array to tell the other end
* the buffer address.
*/
buf_phy_addr_32 = (uint32_t)metal_io_phys(shm_io,
tx_data_offset);
metal_io_write32(shm_io, tx_addr_offset,
buf_phy_addr_32);
tx_data_offset += sizeof(struct msg_hdr_s) +
msg_hdr->len;
tx_addr_offset += sizeof(uint32_t);
/* Increase number of available buffers */
metal_io_write32(shm_io, tx_avail_offset, rx_count);
/* Kick IPI to notify data is in shared buffer */
metal_io_write32(ipi_io, IPI_TRIG_OFFSET,
ipi_mask);
}
}
out:
LPRINTF("IPI with shared memory demo finished with exit code: %i.\n",
ret);
if (lbuf)
metal_free_memory(lbuf);
return ret;
}
int ipi_shmem_demod()
{
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
int ipi_irq;
int ret = 0;
print_demo("IPI and shared memory");
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
shm_io = metal_device_io_region(shm_dev, 0);
if (!shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Get IPI device IO region */
if (!ipi_dev) {
ret = -ENODEV;
goto out;
}
ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
/* initialize remote_nkicked */
atomic_init(&remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = ipi_shmem_echod(ipi_io, shm_io);
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler */
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
out:
return ret;
}
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/***************************************************************************
* libmetal_amp_demo.c
*
* This application shows how to use IPI to trigger interrupt and how to
* setup shared memory with libmetal API for communication between processors.
*
* This application does the following:
* 1. Initialize the platform hardware such as UART, GIC.
* 2. Connect the IPI interrupt.
* 3. Register IPI device, shared memory descriptor device and shared memory
* device with libmetal in the initialization.
* 4. In the main application it does the following,
* * open the registered libmetal devices: IPI device, shared memory
* descriptor device and shared memory device.
* * Map the shared memory descriptor as non-cached memory.
* * Map the shared memory as non-cached memory. If you do not map the
* shared memory as non-cached memory, make sure you flush the cache,
* before you notify the remote.
* 7. Register the IPI interrupt handler with libmetal.
* 8. Run the atomic demo task ipi_task_shm_atomicd():
* * Wait for the IPI interrupt from the remote.
* * Once it receives the interrupt, it does atomic add by 1 to the
* first 32bit of the shared memory descriptor location by 1000 times.
* * It will then notify the remote after the calculation.
* * As the remote side also does 1000 times add after it has notified
* this end. The remote side will check if the result is 2000, if not,
* it will error.
* 9. Run the shared memory echo demo task ipi_task_echod()
* * Wait for the IPI interrupt from the other end.
* * If an IPI interrupt is received, copy the message to the current
* available RPU to APU buffer, increase the available buffer indicator,
* and trigger IPI to notify the remote.
* * If "shutdown" message is received, cleanup the libmetal source.
*/
#include <FreeRTOS.h>
#include <task.h>
#include <unistd.h>
#include <metal/atomic.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/irq.h>
#include "common.h"
static TaskHandle_t comm_task;
/**
* @brief demo application main processing task
* Here are the steps for the main function:
* * Setup libmetal resources
* * Run the IPI with shared memory demo.
* * Run the shared memory demo.
* * Run the atomic across shared memory demo.
* * Run the ipi latency demo.
* * Run the shared memory latency demo.
* * Run the shared memory throughput demo.
* * Cleanup libmetal resources before self killing task.
* Report if any of the above demos failed.
* @return 0 - succeeded, non-zero for failures.
*/
static void processing(void *unused_arg)
{
int ret;
(void)unused_arg;
ret = sys_init();
if (ret) {
LPERROR("Failed to initialize system.\n");
goto out;
}
ret = shmem_demod();
if (ret){
LPERROR("shared memory demo failed.\n");
goto out;
}
ret = atomic_shmem_demod();
if (ret){
LPERROR("shared memory atomic demo failed.\n");
goto out;
}
ret = ipi_shmem_demod();
if (ret){
LPERROR("shared memory atomic demo failed.\n");
goto out;
}
ret = ipi_latency_demod();
if (ret){
LPERROR("IPI latency demo failed.\n");
goto out;
}
ret = shmem_latency_demod();
if (ret){
LPERROR("shared memory latency demo failed.\n");
goto out;
}
ret = shmem_throughput_demod();
if (ret){
LPERROR("shared memory thoughput demo failed.\n");
goto out;
}
sys_cleanup();
out:
/* Terminate this task */
vTaskDelete(NULL);
}
/**
* @brief main function of the demo application.
* It starts the processing task and go wait forever.
* @return 0 - succeeded, but in reality will never return.
*/
int main(void)
{
BaseType_t stat;
Xil_ExceptionDisable();
/* Create the tasks */
stat = xTaskCreate(processing, ( const char * ) "HW",
1024, NULL, 2, &comm_task);
if (stat != pdPASS) {
LPERROR("Cannot create task\n");
} else {
/* Start running FreeRTOS tasks */
vTaskStartScheduler();
}
/* Will normally not get here */
while (1) {
wait_for_interrupt();
}
/* suppress compilation warnings*/
return 0;
}
@@ -0,0 +1,317 @@
/******************************************************************************
*
* Copyright (C) 2017 Xilinx, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of Xilinx nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x1000;
_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x6000;
_ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024;
_SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048;
_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024;
_FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024;
_UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024;
/* Define Memories in the system */
MEMORY
{
psu_r5_atcm_MEM_0 : ORIGIN = 0x0, LENGTH = 0x10000
psu_r5_btcm_MEM_0 : ORIGIN = 0x20000, LENGTH = 0x10000
psu_r5_ddr_0_MEM_0 : ORIGIN = 0x3ed00000, LENGTH = 0x80000
}
/* Specify the default entry point to the program */
ENTRY(_boot)
/* Define the sections, and where they are mapped in memory */
SECTIONS
{
.vectors : {
KEEP (*(.vectors))
*(.boot)
} > psu_r5_atcm_MEM_0
.text : {
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
*(.plt)
*(.gnu_warning)
*(.gcc_execpt_table)
*(.glue_7)
*(.glue_7t)
*(.vfp11_veneer)
*(.ARM.extab)
*(.gnu.linkonce.armextab.*)
} > psu_r5_ddr_0_MEM_0
.init : {
KEEP (*(.init))
} > psu_r5_ddr_0_MEM_0
.fini : {
KEEP (*(.fini))
} > psu_r5_ddr_0_MEM_0
.interp : {
KEEP (*(.interp))
} > psu_r5_ddr_0_MEM_0
.note-ABI-tag : {
KEEP (*(.note-ABI-tag))
} > psu_r5_ddr_0_MEM_0
.rodata : {
__rodata_start = .;
*(.rodata)
*(.rodata.*)
*(.gnu.linkonce.r.*)
__rodata_end = .;
} > psu_r5_ddr_0_MEM_0
.rodata1 : {
__rodata1_start = .;
*(.rodata1)
*(.rodata1.*)
__rodata1_end = .;
} > psu_r5_ddr_0_MEM_0
.sdata2 : {
__sdata2_start = .;
*(.sdata2)
*(.sdata2.*)
*(.gnu.linkonce.s2.*)
__sdata2_end = .;
} > psu_r5_ddr_0_MEM_0
.sbss2 : {
__sbss2_start = .;
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
__sbss2_end = .;
} > psu_r5_ddr_0_MEM_0
.data : {
__data_start = .;
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
*(.jcr)
*(.got)
*(.got.plt)
__data_end = .;
} > psu_r5_ddr_0_MEM_0
.data1 : {
__data1_start = .;
*(.data1)
*(.data1.*)
__data1_end = .;
} > psu_r5_ddr_0_MEM_0
.got : {
*(.got)
} > psu_r5_ddr_0_MEM_0
.ctors : {
__CTOR_LIST__ = .;
___CTORS_LIST___ = .;
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
__CTOR_END__ = .;
___CTORS_END___ = .;
} > psu_r5_ddr_0_MEM_0
.dtors : {
__DTOR_LIST__ = .;
___DTORS_LIST___ = .;
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
__DTOR_END__ = .;
___DTORS_END___ = .;
} > psu_r5_ddr_0_MEM_0
.fixup : {
__fixup_start = .;
*(.fixup)
__fixup_end = .;
} > psu_r5_ddr_0_MEM_0
.eh_frame : {
*(.eh_frame)
} > psu_r5_ddr_0_MEM_0
.eh_framehdr : {
__eh_framehdr_start = .;
*(.eh_framehdr)
__eh_framehdr_end = .;
} > psu_r5_ddr_0_MEM_0
.gcc_except_table : {
*(.gcc_except_table)
} > psu_r5_ddr_0_MEM_0
.mmu_tbl (ALIGN(16384)) : {
__mmu_tbl_start = .;
*(.mmu_tbl)
__mmu_tbl_end = .;
} > psu_r5_ddr_0_MEM_0
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx*)
*(.gnu.linkonce.armexidix.*.*)
__exidx_end = .;
} > psu_r5_ddr_0_MEM_0
.preinit_array : {
__preinit_array_start = .;
KEEP (*(SORT(.preinit_array.*)))
KEEP (*(.preinit_array))
__preinit_array_end = .;
} > psu_r5_ddr_0_MEM_0
.init_array : {
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
} > psu_r5_ddr_0_MEM_0
.fini_array : {
__fini_array_start = .;
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array))
__fini_array_end = .;
} > psu_r5_ddr_0_MEM_0
.ARM.attributes : {
__ARM.attributes_start = .;
*(.ARM.attributes)
__ARM.attributes_end = .;
} > psu_r5_ddr_0_MEM_0
.sdata : {
__sdata_start = .;
*(.sdata)
*(.sdata.*)
*(.gnu.linkonce.s.*)
__sdata_end = .;
} > psu_r5_ddr_0_MEM_0
.sbss (NOLOAD) : {
__sbss_start = .;
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
__sbss_end = .;
} > psu_r5_ddr_0_MEM_0
.tdata : {
__tdata_start = .;
*(.tdata)
*(.tdata.*)
*(.gnu.linkonce.td.*)
__tdata_end = .;
} > psu_r5_ddr_0_MEM_0
.tbss : {
__tbss_start = .;
*(.tbss)
*(.tbss.*)
*(.gnu.linkonce.tb.*)
__tbss_end = .;
} > psu_r5_ddr_0_MEM_0
.bss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > psu_r5_ddr_0_MEM_0
_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 );
_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 );
/* Generate Stack and Heap definitions */
.heap (NOLOAD) : {
. = ALIGN(16);
_heap = .;
HeapBase = .;
_heap_start = .;
. += _HEAP_SIZE;
_heap_end = .;
HeapLimit = .;
} > psu_r5_atcm_MEM_0
.stack (NOLOAD) : {
. = ALIGN(16);
_stack_end = .;
. += _STACK_SIZE;
_stack = .;
__stack = _stack;
. = ALIGN(16);
_irq_stack_end = .;
. += _IRQ_STACK_SIZE;
__irq_stack = .;
_supervisor_stack_end = .;
. += _SUPERVISOR_STACK_SIZE;
. = ALIGN(16);
__supervisor_stack = .;
_abort_stack_end = .;
. += _ABORT_STACK_SIZE;
. = ALIGN(16);
__abort_stack = .;
_fiq_stack_end = .;
. += _FIQ_STACK_SIZE;
. = ALIGN(16);
__fiq_stack = .;
_undef_stack_end = .;
. += _UNDEF_STACK_SIZE;
. = ALIGN(16);
__undef_stack = .;
} > psu_r5_atcm_MEM_0
_end = .;
}
@@ -0,0 +1,6 @@
#ifndef __PLATFORM_CONFIG_H_
#define __PLATFORM_CONFIG_H_
#define STDOUT_IS_PSU_UART
#define UART_DEVICE_ID 0
#endif
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* atomic_shmem_demod.c - Shared memory atomic operation demo
* This task will:
* 1. Get the shared memory device I/O region.
* 2. Get the IPI device I/O region.
* 3. Register IPI interrupt handler.
* 4. Wait for the APU to kick IPI to start the demo
* 5. Once notification is received, start atomic add by
* 1 for 5000 times over the shared memory
* 6. Trigger IPI to notify the remote it has finished calculation.
* 7. Clean up: Disable IPI interrupt, deregister the IPI interrupt handler.
*/
#include <metal/shmem.h>
#include <metal/atomic.h>
#include <metal/device.h>
#include <metal/io.h>
#include <sys/time.h>
#include <stdio.h>
#include "common.h"
#include "sys_init.h"
#define ATOMIC_INT_OFFSET 0x0 /* shared memory offset for atomic operation */
#define ITERATIONS 5000
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
1 - not-kicked */
static int ipi_irq_handler (int vect_id, void *priv)
{
(void)vect_id;
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
uint32_t ipi_mask = IPI_MASK;
uint64_t val = 1;
if (!ipi_io)
return METAL_IRQ_NOT_HANDLED;
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
if (val & ipi_mask) {
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
atomic_flag_clear(&remote_nkicked);
return METAL_IRQ_HANDLED;
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief atomic_add_shmemd() - Shared memory atomic operation demo
* This task will:
* * Wait for the remote to write to shared memory.
* * Once it receives the notification via polling, start atomic add by
* 1 for 5000 times to first 32 bits of memory in the shared memory
* which is pointed to by shm_io.
* * Write to shared mem to notify the remote once it finishes
* calculation.
*
* @param[in] ipi_io - IPI metal i/o region
* @param[in] shm_io - shared memory metal i/o region
* @return - If setup failed, return the corresponding error number. Otherwise
* return 0 on success.
*/
int atomic_add_shmemd(struct metal_io_region *ipi_io,
struct metal_io_region *shm_io)
{
atomic_int *shm_int;
uint32_t ipi_mask = IPI_MASK;
int i;
LPRINTF("Starting atomic add on shared memory demo.\n");
shm_int = (atomic_int *)metal_io_virt(shm_io,
ATOMIC_INT_OFFSET);
/* Wait for notification from the remote to start the demo */
wait_for_notified(&remote_nkicked);
/* Do atomic add over the shared memory */
for (i = 0; i < ITERATIONS; i++)
atomic_fetch_add(shm_int, 1);
/* Write to IPI trigger register to notify the remote it has finished
* the atomic operation. */
metal_io_write32(ipi_io, IPI_TRIG_OFFSET, ipi_mask);
LPRINTF("Shared memory with atomics test finished\n");
return 0;
}
int atomic_shmem_demod()
{
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
int ipi_irq;
int ret = 0;
print_demo("atomic operation over shared memory");
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
shm_io = metal_device_io_region(shm_dev, 0);
if (!shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Get IPI device IO region */
if (!ipi_dev) {
ret = -ENODEV;
goto out;
}
ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
/* initialize remote_nkicked */
atomic_init(&remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = atomic_add_shmemd(ipi_io, shm_io);
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler */
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
out:
return ret;
}
@@ -0,0 +1,168 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* shmem_demod.c
* This demo demonstrates the use of shared mem. between the APU and RPU.
* This demo does so via the following steps:
*
* 1. Get the shared memory device I/O region.
* 2. Clear the demo control value in shared memory.
* 3. Check the demo control value in the shared memory to wait for APU
* to start the demo.
* 4. Once the demo control value indicates the demo starts, it polls on
* RX available value to see if there is new RX message available.
* 5. If there is a new RX message available, it reads the message from
* the shared memory
* 6. It echos back the message to the shared memory
* 7. It increases the TX available value in the shared memory to notify
* the other end there is a message available to read.
* 8. Check if the demo control value and the RX available values to see
* if demo finishes and if there is new RX data available.
*
* Here is the Shared memory structure of this demo:
* |0 | 4Bytes | DEMO control status shows if demo starts or not |
* |0x04 | 4Bytes | number of APU to RPU buffers available to RPU |
* |0x08 | 4Bytes | number of APU to RPU buffers consumed by RPU |
* |0x0c | 4Bytes | number of RPU to APU buffers available to APU |
* |0x10 | 4Bytes | number of RPU to APU buffers consumed by APU |
* |0x14 | 1KBytes | APU to RPU buffer |
* ... ...
* |0x800 | 1KBytes | RPU to APU buffer |
*/
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <metal/sys.h>
#include <metal/device.h>
#include <metal/io.h>
#include <metal/alloc.h>
#include "common.h"
/* Shared memory offsets */
#define SHM_DEMO_CNTRL_OFFSET 0x0
#define SHM_RX_AVAIL_OFFSET 0x04
#define SHM_RX_USED_OFFSET 0x08
#define SHM_TX_AVAIL_OFFSET 0x0C
#define SHM_TX_USED_OFFSET 0x10
#define SHM_RX_BUFFER_OFFSET 0x14
#define SHM_TX_BUFFER_OFFSET 0x800
#define SHM_BUFFER_SIZE 0x400
#define DEMO_STATUS_IDLE 0x0
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
struct msg_hdr_s {
uint32_t index;
uint32_t len;
};
/**
* @brief shmem_echod() - Show use of shared memory with libmetal.
* Wait for message from APU. Once received, read and echo it back.
*
* @param[in] shm_io - metal i/o region of the shared memory
* @return - return 0 on success, otherwise return error number indicating
* type of error
*/
static int shmem_echod(struct metal_io_region *shm_io)
{
void *data = NULL;
struct msg_hdr_s *msg_hdr;
unsigned int rx_count = 0;
unsigned int len;
int ret = 0;
/* clear demo status value */
metal_io_write32(shm_io, SHM_DEMO_CNTRL_OFFSET, 0);
/* allocate memory for receiving data */
data = metal_allocate_memory(SHM_BUFFER_SIZE);
if (!data) {
LPERROR("Failed to allocate memory.\r\n");
return -1;
}
LPRINTF("Wait for shared memory demo to start.\r\n");
while (metal_io_read32(shm_io, SHM_DEMO_CNTRL_OFFSET) !=
DEMO_STATUS_START);
LPRINTF("Demo has started.\r\n");
/* wait for message is available */
while(metal_io_read32(shm_io, SHM_DEMO_CNTRL_OFFSET) ==
DEMO_STATUS_START) {
if (metal_io_read32(shm_io, SHM_RX_AVAIL_OFFSET)
== rx_count)
continue;
/* Message is available, read the message header */
ret = metal_io_block_read(shm_io, SHM_RX_BUFFER_OFFSET,
data, sizeof(struct msg_hdr_s));
if (ret < 0){
LPERROR("Unable to metal_io_block_read()\n");
return ret;
}
msg_hdr = (struct msg_hdr_s *)data;
/* Get the length of the data, if the data length is
* too large, truncate it. */
len = msg_hdr->len;
if (msg_hdr->len >
(SHM_BUFFER_SIZE - sizeof(*msg_hdr))) {
LPERROR("Input message is too long %u.\n",
msg_hdr->len);
len = SHM_BUFFER_SIZE - sizeof(*msg_hdr);
}
/* Read the message data */
ret = metal_io_block_read(shm_io,
SHM_RX_BUFFER_OFFSET + sizeof(*msg_hdr),
data + sizeof(*msg_hdr), len);
rx_count++;
ret = metal_io_block_write(shm_io,
SHM_TX_BUFFER_OFFSET,
(void*)data, sizeof(*msg_hdr) + len);
if (ret < 0){
LPERROR("Unable to metal_io_block_write()\n");
return ret;
}
/* increase TX available value to notify the other end
* there is data ready to read. */
metal_io_write32(shm_io, SHM_TX_AVAIL_OFFSET, rx_count);
}
metal_free_memory(data);
LPRINTF("Shared memory test finished\r\n");
return 0;
}
int shmem_demod()
{
struct metal_io_region *io = NULL;
int ret = 0;
print_demo("shared memory");
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
io = metal_device_io_region(shm_dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
ret = shmem_echod(io);
out:
return ret;
}
@@ -0,0 +1,265 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* shmem_latency_demod.c
* This is the remote side of the IPI latency measurement demo.
* This demo does the follwing steps:
*
* 1. Get the shared memory device libmetal I/O region.
* 1. Get the TTC timer device libemtal I/O region.
* 2. Get IPI device libmetal I/O region and the IPI interrupt vector.
* 3. Register IPI interrupt handler.
* 6. When it receives IPI interrupt, the IPI interrupt handler marked the
* remote has kicked.
* 7. Check the shared memory to see if demo is on. If the demo is on,
* copy data from the shared memory to local memory, stop the APU to RPU
* timer. Reset the RPU to APU TTC counter, copy data from local memory
* to shared memory, kick IPI to notify the remote.
* 8. If the shared memory indicates the demo is off, cleanup resource:
* disable IPI interrupt and deregister the IPI interrupt handler.
*/
#include <unistd.h>
#include <metal/atomic.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/irq.h>
#include "common.h"
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
#define TTC_CLK_FREQ_HZ 100000000
/* Shared memory offset */
#define SHM_DEMO_CNTRL_OFFSET 0x0 /* Shared memory for the demo status */
#define SHM_BUFF_OFFSET_RX 0x1000 /* Shared memory RX buffer start offset */
#define SHM_BUFF_OFFSET_TX 0x2000 /* Shared memory TX buffer start offset */
#define DEMO_STATUS_IDLE 0x0
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
#define BUF_SIZE_MAX 4096
struct channel_s {
struct metal_io_region *ipi_io; /* IPI metal i/o region */
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
struct metal_io_region *ttc_io; /* TTC metal i/o region */
uint32_t ipi_mask; /* RPU IPI mask */
atomic_int remote_nkicked; /* 0 - kicked from remote */
};
struct msg_hdr_s {
uint32_t index;
uint32_t len;
};
/**
* @brief reset_timer() - function to reset TTC counter
* Set the RST bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void reset_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_RST_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief stop_timer() - function to stop TTC counter
* Set the disable bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void stop_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_DIS_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief ipi_irq_handler() - IPI interrupt handler
* It will clear the notified flag to mark it's got an IPI interrupt.
* It will stop the RPU->APU timer and will clear the notified
* flag to mark it's got an IPI interrupt
*
* @param[in] vect_id - IPI interrupt vector ID
* @param[in/out] priv - communication channel data for this application.
*
* @return - If the IPI interrupt is triggered by its remote, it returns
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
* not the interrupt it expected.
*
*/
static int ipi_irq_handler (int vect_id, void *priv)
{
struct channel_s *ch = (struct channel_s *)priv;
uint32_t val;
(void)vect_id;
if (ch) {
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
if (val & ch->ipi_mask) {
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
ch->ipi_mask);
atomic_flag_clear(&ch->remote_nkicked);
return METAL_IRQ_HANDLED;
}
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief measure_shmem_latencyd() - measure shmem latency with libmetal
* Loop until APU tells RPU to stop via shared memory.
* In loop, wait for interrupt (interrupt handler stops APU to
* RPU TTC counter). Then reset count on RPU to APU TTC counter
* and kick IPI to notify APU.
*
* @param[in] ch - channel information
* @return - 0 on success, error code if failure.
*/
static int measure_shmem_latencyd(struct channel_s *ch)
{
void *lbuf = NULL;
struct msg_hdr_s *msg_hdr;
int ret = 0;
/* allocate memory for receiving data */
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
if (!lbuf) {
LPERROR("Failed to allocate memory.\r\n");
return -1;
}
LPRINTF("Starting IPI latency demo\r\n");
while(1) {
wait_for_notified(&ch->remote_nkicked);
if (metal_io_read32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET) ==
DEMO_STATUS_START) {
/* Read message header from shared memory */
metal_io_block_read(ch->shm_io, SHM_BUFF_OFFSET_RX,
lbuf, sizeof(struct msg_hdr_s));
msg_hdr = (struct msg_hdr_s *)lbuf;
/* Check if the message header is valid */
if (msg_hdr->len > (BUF_SIZE_MAX - sizeof(*msg_hdr))) {
LPERROR("wrong msg: length invalid: %u, %u.\n",
BUF_SIZE_MAX - sizeof(*msg_hdr),
msg_hdr->len);
ret = -EINVAL;
goto out;
}
/* Read message */
metal_io_block_read(ch->shm_io,
SHM_BUFF_OFFSET_RX + sizeof(*msg_hdr),
lbuf + sizeof(*msg_hdr), msg_hdr->len);
/* Stop APU to RPU TTC counter */
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
/* Reset RPU to APU TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
/* Copy the message back to the other end */
metal_io_block_write(ch->shm_io, SHM_BUFF_OFFSET_TX,
msg_hdr,
sizeof(*msg_hdr) + msg_hdr->len);
/* Kick IPI to notify the remote */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
ch->ipi_mask);
} else {
break;
}
}
out:
metal_free_memory(lbuf);
return ret;
}
int shmem_latency_demod()
{
struct channel_s ch;
int ipi_irq;
int ret = 0;
print_demo("shared memory latency");
memset(&ch, 0, sizeof(ch));
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
ch.shm_io = metal_device_io_region(shm_dev, 0);
if (!ch.shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Get TTC IO region */
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
if (!ch.ttc_io) {
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
ret = -ENODEV;
goto out;
}
/* Get IPI device IO region */
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ch.ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
ch.ipi_mask = IPI_MASK;
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
/* initialize remote_nkicked */
atomic_init(&ch.remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = measure_shmem_latencyd(&ch);
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler */
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
out:
return ret;
}
@@ -0,0 +1,355 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* shmem_throughput_demo_task.c
* This is the remote side of the shared memory throughput demo.
* This demo does the following steps:
*
* 1. Get the shared memory device libmetal I/O region.
* 1. Get the TTC timer device libemtal I/O region.
* 2. Get IPI device libmetal I/O region and the IPI interrupt vector.
* 3. Register IPI interrupt handler.
* 6. Download throughput measurement:
* Start TTC RPU counter, wait for IPI kick, check if data is available,
* if yes, read as much data as possible from shared memory. It will
* iterates untill 1000 packages have been received, stop TTC RPU counter
* and kick IPI to notify the remote. Repeat for different package size.
* 7. Upload throughput measurement:
* Start TTC RPU counter, write data to shared memory and kick IPI to
* notify remote. It will iterate for 1000 times, stop TTC RPU counter.
* wait for APU IPI kick to know APU has finished receiving packages.
* Kick IPI to notify it TTC RPU conter value is ready to read.
* Repeat for different package size.
* 8. Cleanup resource:
* disable IPI interrupt and deregister the IPI interrupt handler.
*
* Here is the Shared memory structure of this demo:
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
* |0x04 - 0x1FFFFF | address array for shared buffers from APU to RPU |
* |0x200000 - 0x200004 | number of RPU to APU buffers available to APU |
* |0x200004 - 0x3FFFFF | address array for shared buffers from RPU to APU |
* |0x400000 - 0x7FFFFF | APU to RPU buffers |
* |0x800000 - 0xAFFFFF | RPU to APU buffers |
*/
#include <unistd.h>
#include <metal/atomic.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/irq.h>
#include <metal/alloc.h>
#include "common.h"
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
/* Shared memory offsets */
#define SHM_DESC_OFFSET_RX 0x0
#define SHM_BUFF_OFFSET_RX 0x400000
#define SHM_DESC_OFFSET_TX 0x200000
#define SHM_BUFF_OFFSET_TX 0x800000
/* Shared memory descriptors offset */
#define SHM_DESC_AVAIL_OFFSET 0x00
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x04
#define BUF_SIZE_MAX 4096
#define PKG_SIZE_MAX 1024
#define PKG_SIZE_MIN 16
#define TOTAL_DATA_SIZE (1024 * 4096)
struct channel_s {
struct metal_io_region *ipi_io; /* IPI metal i/o region */
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
struct metal_io_region *ttc_io; /* TTC metal i/o region */
uint32_t ipi_mask; /* RPU IPI mask */
atomic_int remote_nkicked; /* 0 - kicked from remote */
};
/**
* @brief reset_timer() - function to reset TTC counter
* Set the RST bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void reset_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_RST_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief stop_timer() - function to stop TTC counter
* Set the disable bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void stop_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_DIS_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief ipi_irq_handler() - IPI interrupt handler
* It will clear the notified flag to mark it's got an IPI interrupt.
* It will stop the RPU->APU timer and will clear the notified
* flag to mark it's got an IPI interrupt
*
* @param[in] vect_id - IPI interrupt vector ID
* @param[in/out] priv - communication channel data for this application.
*
* @return - If the IPI interrupt is triggered by its remote, it returns
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
* not the interrupt it expected.
*
*/
static int ipi_irq_handler (int vect_id, void *priv)
{
struct channel_s *ch = (struct channel_s *)priv;
uint32_t val;
(void)vect_id;
if (ch) {
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
if (val & ch->ipi_mask) {
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
ch->ipi_mask);
atomic_flag_clear(&ch->remote_nkicked);
return METAL_IRQ_HANDLED;
}
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief measure_shmem_throughputd() - measure shmem throughpput with libmetal
* - Download throughput measurement:
* Start TTC RPU counter, wait for IPI kick, check if data is
* available, if yes, read as much data as possible from shared
* memory. It will iterates untill 1000 packages have been received,
* stop TTC RPU counter and kick IPI to notify the remote. Repeat
* for different package size.
* - Upload throughput measurement:
* Start TTC RPU counter, write data to shared memory and kick IPI
* to notify remote. It will iterate for 1000 times, stop TTC RPU
* counter.Wait for APU IPI kick to know APU has received all the
* packages. Kick IPI to notify it TTC RPU conter value is ready to
* read. Repeat for different package size.
*
* @param[in] ch - channel information
* @return - 0 on success, error code if failure.
*/
static int measure_shmem_throughputd(struct channel_s *ch)
{
void *lbuf = NULL;
int ret = 0;
size_t s;
uint32_t rx_count, rx_avail, tx_count, iterations;
unsigned long tx_avail_offset, rx_avail_offset;
unsigned long tx_addr_offset, rx_addr_offset;
unsigned long tx_data_offset, rx_data_offset;
uint32_t buf_phy_addr_32;
/* allocate memory for receiving data */
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
if (!lbuf) {
LPERROR("Failed to allocate memory.\r\n");
return -1;
}
memset(lbuf, 0xA, BUF_SIZE_MAX);
/* Clear shared memory */
metal_io_block_set(ch->shm_io, 0, 0, metal_io_region_size(ch->shm_io));
LPRINTF("Starting shared mem throughput demo\n");
/* for each data size, measure block receive throughput */
for (s = PKG_SIZE_MIN; s <= PKG_SIZE_MAX; s <<= 1) {
rx_count = 0;
iterations = TOTAL_DATA_SIZE / s;
/* Set rx buffer address offset */
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
rx_addr_offset = SHM_DESC_OFFSET_RX +
SHM_DESC_ADDR_ARRAY_OFFSET;
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
wait_for_notified(&ch->remote_nkicked);
/* Data has arrived, seasure start. Reset RPU TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
while (1) {
rx_avail = metal_io_read32(ch->shm_io, rx_avail_offset);
while(rx_count != rx_avail) {
/* Get the buffer location from the shared
* memory rx address array.
*/
buf_phy_addr_32 = metal_io_read32(ch->shm_io,
rx_addr_offset);
rx_data_offset = metal_io_phys_to_offset(
ch->shm_io,
(metal_phys_addr_t)buf_phy_addr_32);
if (rx_data_offset == METAL_BAD_OFFSET) {
LPERROR(
"[%u]failed to get rx offset: 0x%x, 0x%lx.\n",
rx_count, buf_phy_addr_32,
metal_io_phys(ch->shm_io,
rx_addr_offset));
ret = -EINVAL;
goto out;
}
rx_addr_offset += sizeof(buf_phy_addr_32);
/* Read data from shared memory */
metal_io_block_read(ch->shm_io, rx_data_offset,
lbuf, s);
rx_count++;
}
if (rx_count < iterations)
/* Need to wait for more data */
wait_for_notified(&ch->remote_nkicked);
else
break;
}
/* Stop RPU TTC counter */
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
/* Clear remote kicked flag -- 0 is kicked */
atomic_init(&ch->remote_nkicked, 1);
/* Kick IPI to notify RPU TTC counter value is ready */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
}
/* for each data size, measure send throughput */
for (s = PKG_SIZE_MIN; s <= PKG_SIZE_MAX; s <<= 1) {
tx_count = 0;
iterations = TOTAL_DATA_SIZE / s;
/* Set tx buffer address offset */
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
tx_addr_offset = SHM_DESC_OFFSET_TX +
SHM_DESC_ADDR_ARRAY_OFFSET;
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
/* Wait for APU to signal it is ready for the measurement */
wait_for_notified(&ch->remote_nkicked);
/* Data has arrived, seasure start. Reset RPU TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
while (tx_count < iterations) {
/* Write data to the shared memory*/
metal_io_block_write(ch->shm_io, tx_data_offset,
lbuf, s);
/* Write to the address array to tell the other end
* the buffer address.
*/
buf_phy_addr_32 = (uint32_t)metal_io_phys(ch->shm_io,
tx_data_offset);
metal_io_write32(ch->shm_io, tx_addr_offset,
buf_phy_addr_32);
tx_data_offset += s;
tx_addr_offset += sizeof(buf_phy_addr_32);
/* Increase number of available buffers */
tx_count++;
metal_io_write32(ch->shm_io, tx_avail_offset, tx_count);
/* Kick IPI to notify remote data is ready in the
* shared memory */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
ch->ipi_mask);
}
/* Stop RPU TTC counter */
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
/* Wait for IPI kick to know when the remote is ready
* to read the TTC counter value */
wait_for_notified(&ch->remote_nkicked);
/* Kick IPI to notify RPU TTC counter value is ready */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
}
out:
if (lbuf)
metal_free_memory(lbuf);
return ret;
}
int shmem_throughput_demod()
{
struct channel_s ch;
int ipi_irq;
int ret = 0;
print_demo("shared memory throughput");
memset(&ch, 0, sizeof(ch));
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
ch.shm_io = metal_device_io_region(shm_dev, 0);
if (!ch.shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Get TTC IO region */
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
if (!ch.ttc_io) {
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
ret = -ENODEV;
goto out;
}
/* Get IPI device IO region */
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ch.ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
ch.ipi_mask = IPI_MASK;
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
/* initialize remote_nkicked */
atomic_init(&ch.remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = measure_shmem_throughputd(&ch);
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler */
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
out:
return ret;
}
@@ -0,0 +1,360 @@
/******************************************************************************
*
* Copyright (C) 2010 - 2017 Xilinx, Inc. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
******************************************************************************/
#include <xparameters.h>
#include <xil_cache.h>
#include <xil_exception.h>
#include <xstatus.h>
#include <xscugic.h>
#include <xreg_cortexr5.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/sys.h>
#include <metal/irq.h>
#include "platform_config.h"
#include "common.h"
#ifdef STDOUT_IS_16550
#include <xuartns550_l.h>
#define UART_BAUD 9600
#endif
#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
#define IPI_IRQ_VECT_ID 65
#define SHM_BASE_ADDR 0x3ED80000
#define TTC0_BASE_ADDR 0xFF110000
#define IPI_BASE_ADDR 0xFF310000
/* Default generic I/O region page shift */
/* Each I/O region can contain multiple pages.
* In FreeRTOS system, the memory mapping is flat, there is no
* virtual memory.
* We can assume there is only one page in the FreeRTOS system.
*/
#define DEFAULT_PAGE_SHIFT (-1UL)
#define DEFAULT_PAGE_MASK (-1UL)
extern XScuGic xInterruptController;
const metal_phys_addr_t metal_phys[] = {
IPI_BASE_ADDR, /**< base IPI address */
SHM_BASE_ADDR, /**< shared memory base address */
TTC0_BASE_ADDR, /**< base TTC0 address */
};
/* Define metal devices table for IPI, shared memory and TTC devices.
* Linux system uses device tree to describe devices. Unlike Linux,
* there is no standard device abstraction for FreeRTOS system, we
* uses libmetal devices structure to describe the devices we used in
* the example.
* The IPI, shared memory and TTC devices are memory mapped
* devices. For this type of devices, it is required to provide
* accessible memory mapped regions, and interrupt information.
* In FreeRTOS system, the memory mapping is flat. As you can see
* in the table before, we set the virtual address "virt" the same
* as the physical address.
*/
static struct metal_device metal_dev_table[] = {
{
/* IPI device */
.name = IPI_DEV_NAME,
.bus = NULL,
.num_regions = 1,
.regions = {
{
.virt = (void *)IPI_BASE_ADDR,
.physmap = &metal_phys[0],
.size = 0x1000,
.page_shift = DEFAULT_PAGE_SHIFT,
.page_mask = DEFAULT_PAGE_MASK,
.mem_flags = DEVICE_NONSHARED | PRIV_RW_USER_RW,
.ops = {NULL},
}
},
.node = {NULL},
.irq_num = 1,
.irq_info = (void *)IPI_IRQ_VECT_ID,
},
{
/* Shared memory management device */
.name = SHM_DEV_NAME,
.bus = NULL,
.num_regions = 1,
.regions = {
{
.virt = (void *)SHM_BASE_ADDR,
.physmap = &metal_phys[1],
.size = 0x1000000,
.page_shift = DEFAULT_PAGE_SHIFT,
.page_mask = DEFAULT_PAGE_MASK,
.mem_flags = NORM_SHARED_NCACHE |
PRIV_RW_USER_RW,
.ops = {NULL},
}
},
.node = {NULL},
.irq_num = 0,
.irq_info = NULL,
},
{
/* ttc0 */
.name = TTC_DEV_NAME,
.bus = NULL,
.num_regions = 1,
.regions = {
{
.virt = (void *)TTC0_BASE_ADDR ,
.physmap = &metal_phys[2],
.size = 0x1000,
.page_shift = DEFAULT_PAGE_SHIFT,
.page_mask = DEFAULT_PAGE_MASK,
.mem_flags = DEVICE_NONSHARED | PRIV_RW_USER_RW,
.ops = {NULL},
}
},
.node = {NULL},
.irq_num = 0,
.irq_info = NULL,
},
};
/**
* Extern global variables
*/
struct metal_device *ipi_dev = NULL;
struct metal_device *shm_dev = NULL;
struct metal_device *ttc_dev = NULL;
/**
* @brief enable_caches() - Enable caches
*/
void enable_caches()
{
#ifdef __MICROBLAZE__
#ifdef XPAR_MICROBLAZE_USE_ICACHE
Xil_ICacheEnable();
#endif
#ifdef XPAR_MICROBLAZE_USE_DCACHE
Xil_DCacheEnable();
#endif
#endif
}
/**
* @brief disable_caches() - Disable caches
*/
void disable_caches()
{
Xil_DCacheDisable();
Xil_ICacheDisable();
}
/**
* @brief init_uart() - Initialize UARTs
*/
void init_uart()
{
#ifdef STDOUT_IS_16550
XUartNs550_SetBaud(STDOUT_BASEADDR, XPAR_XUARTNS550_CLOCK_HZ,
UART_BAUD);
XUartNs550_SetLineControlReg(STDOUT_BASEADDR, XUN_LCR_8_DATA_BITS);
#endif
/* Bootrom/BSP configures PS7/PSU UART to 115200 bps */
}
/**
* @brief init_irq() - Initialize GIC and connect IPI interrupt
* This function will initialize the GIC and connect the IPI
* interrupt.
*
* @return 0 - succeeded, non-0 for failures
*/
int init_irq()
{
int ret = 0;
XScuGic_Config *IntcConfig; /* The configuration parameters of
* the interrupt controller */
Xil_ExceptionDisable();
/*
* Initialize the interrupt controller driver
*/
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return (int)XST_FAILURE;
}
ret = XScuGic_CfgInitialize(&xInterruptController, IntcConfig,
IntcConfig->CpuBaseAddress);
if (ret != XST_SUCCESS) {
return (int)XST_FAILURE;
}
/*
* Register the interrupt handler to the hardware interrupt handling
* logic in the ARM processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
&xInterruptController);
Xil_ExceptionEnable();
/* Connect IPI Interrupt ID with libmetal ISR */
XScuGic_Connect(&xInterruptController, IPI_IRQ_VECT_ID,
(Xil_ExceptionHandler)metal_irq_isr,
(void *)IPI_IRQ_VECT_ID);
XScuGic_Enable(&xInterruptController, IPI_IRQ_VECT_ID);
return 0;
}
/**
* @brief platform_register_metal_device() - Statically Register libmetal
* devices.
* This function registers the IPI, shared memory and
* TTC devices to the libmetal generic bus.
* Libmetal uses bus structure to group the devices. Before you can
* access the device with libmetal device operation, you will need to
* register the device to a libmetal supported bus.
* For non-Linux system, libmetal only supports "generic" bus, which is
* used to manage the memory mapped devices.
*
* @return 0 - succeeded, non-zero for failures.
*/
int platform_register_metal_device(void)
{
unsigned int i;
int ret;
struct metal_device *dev;
for (i = 0; i < sizeof(metal_dev_table)/sizeof(struct metal_device);
i++) {
dev = &metal_dev_table[i];
xil_printf("registering: %d, name=%s\n", i, dev->name);
ret = metal_register_generic_device(dev);
if (ret)
return ret;
}
return 0;
}
/**
* @brief open_metal_devices() - Open registered libmetal devices.
* This function opens all the registered libmetal devices.
*
* @return 0 - succeeded, non-zero for failures.
*/
int open_metal_devices(void)
{
int ret;
/* Open shared memory device */
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &shm_dev);
if (ret) {
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
goto out;
}
/* Open IPI device */
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &ipi_dev);
if (ret) {
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
goto out;
}
/* Open TTC device */
ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &ttc_dev);
if (ret) {
LPERROR("Failed to open device %s.\n", TTC_DEV_NAME);
goto out;
}
out:
return ret;
}
/**
* @brief close_metal_devices() - close libmetal devices
* This function closes all the libmetal devices which have
* been opened.
*
*/
void close_metal_devices(void)
{
/* Close shared memory device */
if (shm_dev)
metal_device_close(shm_dev);
/* Close IPI device */
if (ipi_dev)
metal_device_close(ipi_dev);
/* Close TTC device */
if (ttc_dev)
metal_device_close(ttc_dev);
}
/**
* @brief sys_init() - Register libmetal devices.
* This function register the libmetal generic bus, and then
* register the IPI, shared memory descriptor and shared memory
* devices to the libmetal generic bus.
*
* @return 0 - succeeded, non-zero for failures.
*/
int sys_init()
{
struct metal_init_params metal_param = METAL_INIT_DEFAULTS;
int ret;
enable_caches();
init_uart();
if (init_irq()) {
LPERROR("Failed to initialize interrupt\n");
}
/* Initialize libmetal environment */
metal_init(&metal_param);
/* Register libmetal devices */
ret = platform_register_metal_device();
if (ret) {
LPERROR("%s: failed to register devices: %d\n", __func__, ret);
return ret;
}
/* Open libmetal devices which have been registered */
ret = open_metal_devices();
if (ret) {
LPERROR("%s: failed to open devices: %d\n", __func__, ret);
return ret;
}
return 0;
}
/**
* @brief sys_cleanup() - system cleanup
* This function finish the libmetal environment
* and disable caches.
*
* @return 0 - succeeded, non-zero for failures.
*/
void sys_cleanup()
{
/* Close libmetal devices which have been opened */
close_metal_devices();
/* Finish libmetal environment */
metal_finish();
disable_caches();
}
@@ -0,0 +1,17 @@
/******************************************************************************
*
* Copyright (C) 2008 - 2014 Xilinx, Inc. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
******************************************************************************/
#ifndef __SYS_INIT_H__
#define __SYS_INIT_H__
#include "platform_config.h"
int sys_init();
void sys_cleanup();
#endif /* __SYS_INIT_H__ */
@@ -0,0 +1,4 @@
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
add_subdirectory(${PROJECT_MACHINE})
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
@@ -0,0 +1,17 @@
collect(PROJECT_LIB_DEPS metal)
collect(PROJECT_LIB_DEPS xil)
collect(PROJECT_LIB_DEPS c)
collect(PROJECT_LIB_DEPS m)
set (_lib "xil")
find_library (_lib_path ${_lib})
if (NOT _lib_path)
message ( "external library ${_lib_path} not found" )
message ( "hint: you may need to pass -DCMAKE_LIBRARY_PATH=<path>" )
message ( FATAL_ERROR "library ${_lib} is required to build the examples" )
endif (NOT _lib_path)
get_filename_component (_lib_path ${_lib_path} DIRECTORY)
collect (PROJECT_LIB_DIRS ${_lib_path})
add_subdirectory(zynqmp_amp_demo)
@@ -0,0 +1,31 @@
collector_list (_list PROJECT_INC_DIRS)
include_directories (${_list} ${CMAKE_CURRENT_SOURCE_DIR})
collector_list (_list PROJECT_LIB_DIRS)
link_directories (${_list})
collector_list (_deps PROJECT_LIB_DEPS)
set (_linker_script ${CMAKE_CURRENT_SOURCE_DIR}/lscript.ld)
set (_src_common ${CMAKE_CURRENT_SOURCE_DIR}/init_${PROJECT_SYSTEM}.c)
set (_app0 libmetal_amp_demod)
set (_src0 ${CMAKE_CURRENT_SOURCE_DIR}/${_app0}.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/sys_init.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_demod.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_atomic_demod.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/ipi_shmem_demod.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/ipi_latency_demod.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_latency_demod.c)
list(APPEND _src0 ${CMAKE_CURRENT_SOURCE_DIR}/shmem_throughput_demod.c)
get_property (_linker_options GLOBAL PROPERTY TEST_LINKER_OPTIONS)
add_executable (${_app0}.elf ${_src0})
if (PROJECT_EC_FLAGS)
string(REPLACE " " ";" _ec_flgs ${PROJECT_EC_FLAGS})
target_compile_options (${_app0}.elf PUBLIC ${_ec_flgs})
endif (PROJECT_EC_FLAGS)
target_link_libraries(${_app0}.elf -Wl,-Map=${_app0}.map -Wl,--gc-sections -T\"${_linker_script}\" -Wl,--start-group ${_deps} -Wl,--end-group)
install (TARGETS ${_app0}.elf RUNTIME DESTINATION bin)
# vim: expandtab:ts=2:sw=2:smartindent
@@ -0,0 +1,173 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __COMMON_H__
#define __COMMON_H__
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <metal/atomic.h>
#include <metal/alloc.h>
#include <metal/irq.h>
#include <metal/errno.h>
#include <metal/sys.h>
#include <metal/cpu.h>
#include <metal/io.h>
#include <metal/device.h>
#include <sys/types.h>
#include "sys_init.h"
/* Devices names */
#define BUS_NAME "generic"
#define IPI_DEV_NAME "ff310000.ipi"
#define SHM_DEV_NAME "3ed80000.shm"
#define TTC_DEV_NAME "ff110000.ttc"
/* IPI registers offset */
#define IPI_TRIG_OFFSET 0x0 /* IPI trigger reg offset */
#define IPI_OBS_OFFSET 0x4 /* IPI observation reg offset */
#define IPI_ISR_OFFSET 0x10 /* IPI interrupt status reg offset */
#define IPI_IMR_OFFSET 0x14 /* IPI interrupt mask reg offset */
#define IPI_IER_OFFSET 0x18 /* IPI interrupt enable reg offset */
#define IPI_IDR_OFFSET 0x1C /* IPI interrup disable reg offset */
#define IPI_MASK 0x1000000 /* IPI mask for kick from APU.
We use PL0 IPI in this demo. */
/* TTC counter offsets */
#define XTTCPS_CLK_CNTRL_OFFSET 0x0 /* TTC counter clock control reg offset */
#define XTTCPS_CNT_CNTRL_OFFSET 0xC /* TTC counter control reg offset */
#define XTTCPS_CNT_VAL_OFFSET 0x18 /* TTC counter val reg offset */
#define XTTCPS_CNT_OFFSET(ID) ((ID) == 1 ? 0 : 1 << (ID)) /* TTC counter offset
ID is from 1 to 3 */
/* TTC counter control masks */
#define XTTCPS_CNT_CNTRL_RST_MASK 0x10U /* TTC counter control reset mask */
#define XTTCPS_CNT_CNTRL_DIS_MASK 0x01U /* TTC counter control disable mask */
#define LPRINTF(format, ...) \
xil_printf("\r\nSERVER> " format, ##__VA_ARGS__)
#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__)
extern struct metal_device *ipi_dev; /* IPI metal device */
extern struct metal_device *shm_dev; /* SHM metal device */
extern struct metal_device *ttc_dev; /* TTC metal device */
/**
* @brief atomic_shmem_demod() - Shared memory atomic operation demo
* This task will:
* * Wait for the remote to write to shared memory.
* * Once it receives the notification via polling, start atomic add by
* 1 for 1000 times to first 32 bits of memory in the
* shared memory location at 3ed00000 which is pointed to by shm_io.
* * Write to shared mem to notify the remote once it finishes
* calculation.
*
* @return - If setup failed, return the corresponding error number. Otherwise
* return 0 on success.
*/
int atomic_shmem_demod();
/**
* @brief ipi_latency_demod() - Show performance of IPI with Libmetal.
* Loop until APU tells RPU to stop via shared memory.
* In loop, wait for interrupt (interrupt handler stops APU to
* RPU timer). Then reset count on RPU to APU timer to 0, start
* counting and send interrupt to notify APU.
*
* @return - 0 on success, error code if failure.
*/
int ipi_latency_demod();
/**
* @brief ipi_shmem_demod() - shared memory IPI demo
* This task will:
* * Wait for IPI interrupt from the remote
* * Once it received the interrupt, copy the content from
* the ping buffer to the pong buffer.
* * Update the shared memory descriptor for the new available
* pong buffer.
* * Trigger IPI to notifty the remote.
*
* @return - 0 on success, error code if failure.
*/
int ipi_shmem_demod();
/**
* @brief shmem_demod() - Show use of shared memory with Libmetal.
* Until KEEP_GOING signal is stopped, keep looping.
* In the loop, read message from remote, add one to message and
* then respond. After the loop, cleanup resources.
*
* @return - return 0 on success, otherwise return error number indicating
* type of error
*/
int shmem_demod();
/**
* @brief shmem_latency_demod() - Show performance of shared mem.
* Loop until APU tells RPU to stop via shared memory.
* In loop, wait for interrupt (interrupt handler stops APU to
* RPU timer). Then reset count on RPU to APU timer to 0, start
* counting and send interrupt to notify APU.
*
* @return - 0 on success, error code if failure.
*/
int shmem_latency_demod();
/**
* @brief shmem_throughput_demod() - Show throughput of shared mem.
* At signal of remote, record total time to do block read and write
* operation Loop until APU tells RPU to stop via shared memory.
* In loop, wait for interrupt (interrupt handler stops APU to
* RPU timer). Then reset count on RPU to APU timer to 0, start
* counting and send interrupt to notify APU.
*
* @return - 0 on success, error code if failure.
*/
int shmem_throughput_demod();
static inline void wait_for_interrupt()
{
asm volatile("wfi");
}
/**
* @breif wait_for_notified() - Loop until notified bit
* in channel is set.
*
* @param[in] notified - pointer to the notified variable
*/
static inline void wait_for_notified(atomic_int *notified)
{
unsigned int flags;
do {
flags = metal_irq_save_disable();
if (!atomic_flag_test_and_set(notified)) {
metal_irq_restore_enable(flags);
break;
}
wait_for_interrupt();
metal_irq_restore_enable(flags);
} while(1);
}
/**
* @brief print_demo() - print demo string
*
* @param[in] name - demo name
*/
static inline void print_demo(char *name)
{
LPRINTF("====== libmetal demo: %s ======\n", name);
}
#endif /* __COMMON_H__ */
@@ -0,0 +1,218 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* ipi_latency_demod.c
* This is the remote side of the IPI latency measurement demo.
* This demo does the follwing steps:
*
* 1. Open the shared memory device.
* 1. Open the TTC timer device.
* 2. Open the IPI device.
* 3. Register IPI interrupt handler.
* 6. When it receives IPI interrupt, the IPI interrupt handler to stop
* the RPU to APU TTC counter.
* 7. Check the shared memory to see if demo is on. If the demo is on,
* reest the RPU to APU TTC counter and kick IPI to notify the remote.
* 8. If the shared memory indicates the demo is off, cleanup resource:
* disable IPI interrupt and deregister the IPI interrupt handler.
*/
#include <unistd.h>
#include <metal/atomic.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/irq.h>
#include "common.h"
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
#define TTC_CLK_FREQ_HZ 100000000
/* Shared memory offset */
#define SHM_DEMO_CNTRL_OFFSET 0x0
#define DEMO_STATUS_IDLE 0x0
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
struct channel_s {
struct metal_io_region *ipi_io; /* IPI metal i/o region */
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
struct metal_io_region *ttc_io; /* TTC metal i/o region */
uint32_t ipi_mask; /* RPU IPI mask */
atomic_int remote_nkicked; /* 0 - kicked from remote */
};
/**
* @brief reset_timer() - function to reset TTC counter
* Set the RST bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void reset_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_RST_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief stop_timer() - function to stop TTC counter
* Set the disable bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void stop_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_DIS_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief ipi_irq_handler() - IPI interrupt handler
* It will clear the notified flag to mark it's got an IPI interrupt.
* It will stop the RPU->APU timer and will clear the notified
* flag to mark it's got an IPI interrupt
*
* @param[in] vect_id - IPI interrupt vector ID
* @param[in/out] priv - communication channel data for this application.
*
* @return - If the IPI interrupt is triggered by its remote, it returns
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
* not the interrupt it expected.
*
*/
static int ipi_irq_handler (int vect_id, void *priv)
{
struct channel_s *ch = (struct channel_s *)priv;
uint32_t val;
(void)vect_id;
if (ch) {
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
if (val & ch->ipi_mask) {
/* stop RPU -> APU timer */
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
ch->ipi_mask);
atomic_flag_clear(&ch->remote_nkicked);
return METAL_IRQ_HANDLED;
}
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief measure_ipi_latencyd() - measure IPI latency with libmetal
* Loop until APU tells RPU to stop via shared memory.
* In loop, wait for interrupt (interrupt handler stops APU to
* RPU TTC counter). Then reset count on RPU to APU TTC counter
* and kick IPI to notify APU.
*
* @param[in] ch - channel information
* @return - 0 on success, error code if failure.
*/
static int measure_ipi_latencyd(struct channel_s *ch)
{
LPRINTF("Starting IPI latency demo\r\n");
while(1) {
wait_for_notified(&ch->remote_nkicked);
if (metal_io_read32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET) ==
DEMO_STATUS_START) {
/* Reset RPU to APU TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
/* Kick IPI to notify the remote */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
ch->ipi_mask);
} else {
break;
}
}
return 0;
}
int ipi_latency_demod()
{
struct channel_s ch;
int ipi_irq;
int ret = 0;
print_demo("IPI latency");
memset(&ch, 0, sizeof(ch));
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
ch.shm_io = metal_device_io_region(shm_dev, 0);
if (!ch.shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Get TTC IO region */
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
if (!ch.ttc_io) {
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
ret = -ENODEV;
goto out;
}
/* Get IPI device IO region */
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ch.ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
ch.ipi_mask = IPI_MASK;
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
/* initialize remote_nkicked */
atomic_init(&ch.remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = measure_ipi_latencyd(&ch);
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler */
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
out:
return ret;
}
@@ -0,0 +1,282 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* ipi_shmem_demo.c - shared memory with IPI demo
* This demo will:
*
* 1. Get the shared memory device I/O region.
* 2. Get the IPI device I/O region.
* 3. Register IPI interrupt handler.
* 4. Wait for remote IPI notification to receive message.
* 5. When message is received, check if it is shutdown message.
* 6. If it is shutdown message, do cleanup, otherwise, echo it back to the
* shared buffer.
* 7. Kick IPI to notify there is a message written to the shared memory
* if it echos back the message.
* 8. Repeat 4.
* 9. Clean up: disable IPI interrupt, deregister the IPI interrupt handler.
*
* Here is the Shared memory structure of this demo:
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
* |0x04 - 0x07 | number of APU to RPU buffers consumed by RPU |
* |0x08 - 0x1FFC | address array for shared buffers from APU to RPU |
* |0x2000 - 0x2003 | number of RPU to APU buffers available to APU |
* |0x2004 - 0x2007 | number of RPU to APU buffers consumed by APU |
* |0x2008 - 0x3FFC | address array for shared buffers from RPU to APU |
* |0x04000 - 0x103FFC | APU to RPU buffers |
* |0x104000 - 0x203FFC | RPU to APU buffers |
*/
#include <sys/types.h>
#include <metal/sys.h>
#include <metal/io.h>
#include <metal/alloc.h>
#include <metal/device.h>
#include <metal/irq.h>
#include <metal/errno.h>
#include "common.h"
#define BUF_SIZE_MAX 512
#define SHUTDOWN "shutdown"
/* Shared memory offsets */
#define SHM_DESC_OFFSET_RX 0x0
#define SHM_BUFF_OFFSET_RX 0x04000
#define SHM_DESC_OFFSET_TX 0x02000
#define SHM_BUFF_OFFSET_TX 0x104000
/* Shared memory descriptors offset */
#define SHM_DESC_AVAIL_OFFSET 0x00
#define SHM_DESC_USED_OFFSET 0x04
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x08
#define PKGS_TOTAL 1024
#define BUF_SIZE_MAX 512
#define SHUTDOWN "shutdown"
struct msg_hdr_s {
uint32_t index;
uint32_t len;
};
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
1 - not-kicked */
static int ipi_irq_handler (int vect_id, void *priv)
{
(void)vect_id;
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
uint32_t ipi_mask = IPI_MASK;
uint64_t val = 1;
if (!ipi_io)
return METAL_IRQ_NOT_HANDLED;
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
if (val & ipi_mask) {
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
atomic_flag_clear(&remote_nkicked);
return METAL_IRQ_HANDLED;
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief ipi_shmem_echod() - shared memory IPI demo
* This task will:
* * Wait for IPI interrupt from the remote
* * Once it received the interrupt, copy the content from
* the ping buffer to the pong buffer.
* * Update the shared memory descriptor for the new available
* pong buffer.
* * Trigger IPI to notifty the remote.
* @param[in] ipi_io - IPI metal i/o region
* @param[in] shm_io - shared memory metal i/o region
* @return - return 0 on success, otherwise return error number indicating
* type of error.
*/
static int ipi_shmem_echod(struct metal_io_region *ipi_io,
struct metal_io_region *shm_io)
{
int ret = 0;
uint32_t rx_count, rx_avail;
unsigned long tx_avail_offset, rx_avail_offset;
unsigned long rx_used_offset;
unsigned long tx_addr_offset, rx_addr_offset;
unsigned long tx_data_offset, rx_data_offset;
void *lbuf = NULL;
struct msg_hdr_s *msg_hdr;
uint32_t ipi_mask = IPI_MASK;
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
if (!lbuf) {
LPERROR("Failed to allocate local buffer for msg.\n");
ret = -ENOMEM;
goto out;
}
/* Clear shared memory */
metal_io_block_set(shm_io, 0, 0, metal_io_region_size(shm_io));
/* Set tx/rx buffer address offset */
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
rx_used_offset = SHM_DESC_OFFSET_RX + SHM_DESC_USED_OFFSET;
tx_addr_offset = SHM_DESC_OFFSET_TX + SHM_DESC_ADDR_ARRAY_OFFSET;
rx_addr_offset = SHM_DESC_OFFSET_RX + SHM_DESC_ADDR_ARRAY_OFFSET;
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
LPRINTF("Wait for echo test to start.\n");
rx_count = 0;
while (1) {
wait_for_notified(&remote_nkicked);
rx_avail = metal_io_read32(shm_io, rx_avail_offset);
while(rx_count != rx_avail) {
uint32_t buf_phy_addr_32;
/* Received ping from the other side */
/* Get the buffer location from the shared memory
* rx address array.
*/
buf_phy_addr_32 = metal_io_read32(shm_io,
rx_addr_offset);
rx_data_offset = metal_io_phys_to_offset(shm_io,
(metal_phys_addr_t)buf_phy_addr_32);
if (rx_data_offset == METAL_BAD_OFFSET) {
LPERROR("[%u]failed to get rx offset: 0x%x, 0x%lx.\n",
rx_count, buf_phy_addr_32,
metal_io_phys(shm_io, rx_addr_offset));
ret = -EINVAL;
goto out;
}
rx_addr_offset += sizeof(buf_phy_addr_32);
/* Read message header from shared memory */
metal_io_block_read(shm_io, rx_data_offset, lbuf,
sizeof(struct msg_hdr_s));
msg_hdr = (struct msg_hdr_s *)lbuf;
/* Check if the message header is valid */
if (msg_hdr->len > (BUF_SIZE_MAX - sizeof(*msg_hdr))) {
LPERROR("wrong msg: length invalid: %u, %u.\n",
BUF_SIZE_MAX - sizeof(*msg_hdr),
msg_hdr->len);
ret = -EINVAL;
goto out;
}
rx_data_offset += sizeof(*msg_hdr);
/* Read message */
metal_io_block_read(shm_io,
rx_data_offset,
lbuf + sizeof(*msg_hdr), msg_hdr->len);
rx_data_offset += msg_hdr->len;
rx_count++;
/* increase rx used count to indicate it has consumed
* the received data */
metal_io_write32(shm_io, rx_used_offset, rx_count);
/* Check if the it is the shutdown message */
if (msg_hdr->len == strlen(SHUTDOWN) &&
!strncmp(SHUTDOWN,
(lbuf + sizeof(struct msg_hdr_s)),
strlen(SHUTDOWN))) {
LPRINTF("Received shutdown message\n");
goto out;
}
/* Copy the message back to the other end */
metal_io_block_write(shm_io, tx_data_offset, msg_hdr,
sizeof(struct msg_hdr_s) + msg_hdr->len);
/* Write to the address array to tell the other end
* the buffer address.
*/
buf_phy_addr_32 = (uint32_t)metal_io_phys(shm_io,
tx_data_offset);
metal_io_write32(shm_io, tx_addr_offset,
buf_phy_addr_32);
tx_data_offset += sizeof(struct msg_hdr_s) +
msg_hdr->len;
tx_addr_offset += sizeof(uint32_t);
/* Increase number of available buffers */
metal_io_write32(shm_io, tx_avail_offset, rx_count);
/* Kick IPI to notify data is in shared buffer */
metal_io_write32(ipi_io, IPI_TRIG_OFFSET,
ipi_mask);
}
}
out:
LPRINTF("IPI with shared memory demo finished with exit code: %i.\n",
ret);
if (lbuf)
metal_free_memory(lbuf);
return ret;
}
int ipi_shmem_demod()
{
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
int ipi_irq;
int ret = 0;
print_demo("IPI and shared memory");
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
shm_io = metal_device_io_region(shm_dev, 0);
if (!shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Get IPI device IO region */
if (!ipi_dev) {
ret = -ENODEV;
goto out;
}
ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
/* initialize remote_nkicked */
atomic_init(&remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = ipi_shmem_echod(ipi_io, shm_io);
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler */
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
out:
return ret;
}
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/***************************************************************************
* libmetal_amp_demo.c
*
* This application shows how to use IPI to trigger interrupt and how to
* setup shared memory with libmetal API for communication between processors.
*
* This app does the following:
* 1. Initialize the platform hardware such as UART, GIC.
* 2. Connect the IPI interrupt.
* 3. Register IPI device, shared memory descriptor device and shared memory
* device with libmetal in the intialization.
* 4. In the main application it does the following,
* * open the registered libmetal devices: IPI device, shared memory
* descriptor device and shared memory device.
* * Map the shared memory descriptor as non-cached memory.
* * Map the shared memory as non-cached memory. If you do not map the
* shared memory as non-cached memory, make sure you flush the cache,
* before you notify the remote.
* 7. Register the IPI interrupt handler with libmetal.
* 8. Run the atomic demo task ipi_task_shm_atomicd():
* * Wait for the IPI interrupt from the remote.
* * Once it receives the interrupt, it does atomic add by 1 to the
* first 32bit of the shared memory descriptor location by 1000 times.
* * It will then notify the remote after the calucation.
* * As the remote side also does 1000 times add after it has notified
* this end. The remote side will check if the result is 2000, if not,
* it will error.
* 9. Run the shared memory echo demo task ipi_task_echod()
* * Wait for the IPI interrupt from the other end.
* * If an IPI interrupt is received, copy the message to the current
* available RPU to APU buffer, increase the available buffer indicator,
* and trigger IPI to notify the remote.
* * If "shutdown" message is received, cleanup the libmetal source.
*/
#include <unistd.h>
#include <metal/atomic.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/irq.h>
#include "common.h"
/**
* @brief main function of the demo application.
* Here are the steps for the main function:
* * Setup libmetal resources
* * Run the IPI with shared memory demo.
* * Run the shared memory demo.
* * Run the atomic across shared memory demo.
* * Run the ipi latency demo.
* * Run the shared memory latency demo.
* * Run the shared memory throughput demo.
* * Cleanup libmetal resources
* Report if any of the above demos failed.
* @return 0 - succeeded, non-zero for failures.
*/
int main(void)
{
int ret;
ret = sys_init();
if (ret) {
LPERROR("Failed to initialize system.\n");
return ret;
}
ret = shmem_demod();
if (ret){
LPERROR("shared memory demo failed.\n");
return ret;
}
ret = atomic_shmem_demod();
if (ret){
LPERROR("shared memory atomic demo failed.\n");
return ret;
}
ret = ipi_shmem_demod();
if (ret){
LPERROR("shared memory atomic demo failed.\n");
return ret;
}
ret = ipi_latency_demod();
if (ret){
LPERROR("IPI latency demo failed.\n");
return ret;
}
ret = shmem_latency_demod();
if (ret){
LPERROR("shared memory latency demo failed.\n");
return ret;
}
ret = shmem_throughput_demod();
if (ret){
LPERROR("shared memory thoughput demo failed.\n");
return ret;
}
sys_cleanup();
return ret;
}
@@ -0,0 +1,317 @@
/******************************************************************************
*
* Copyright (C) 2017 Xilinx, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of Xilinx nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x2000;
_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x4000;
_ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024;
_SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048;
_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024;
_FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024;
_UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024;
/* Define Memories in the system */
MEMORY
{
psu_r5_atcm_MEM_0 : ORIGIN = 0x0, LENGTH = 0x10000
psu_r5_btcm_MEM_0 : ORIGIN = 0x20000, LENGTH = 0x10000
psu_r5_ddr_0_MEM_0 : ORIGIN = 0x3ed00000, LENGTH = 0x80000
}
/* Specify the default entry point to the program */
ENTRY(_boot)
/* Define the sections, and where they are mapped in memory */
SECTIONS
{
.vectors : {
KEEP (*(.vectors))
*(.boot)
} > psu_r5_atcm_MEM_0
.text : {
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
*(.plt)
*(.gnu_warning)
*(.gcc_execpt_table)
*(.glue_7)
*(.glue_7t)
*(.vfp11_veneer)
*(.ARM.extab)
*(.gnu.linkonce.armextab.*)
} > psu_r5_atcm_MEM_0
.init : {
KEEP (*(.init))
} > psu_r5_btcm_MEM_0
.fini : {
KEEP (*(.fini))
} > psu_r5_btcm_MEM_0
.interp : {
KEEP (*(.interp))
} > psu_r5_btcm_MEM_0
.note-ABI-tag : {
KEEP (*(.note-ABI-tag))
} > psu_r5_btcm_MEM_0
.rodata : {
__rodata_start = .;
*(.rodata)
*(.rodata.*)
*(.gnu.linkonce.r.*)
__rodata_end = .;
} > psu_r5_btcm_MEM_0
.rodata1 : {
__rodata1_start = .;
*(.rodata1)
*(.rodata1.*)
__rodata1_end = .;
} > psu_r5_btcm_MEM_0
.sdata2 : {
__sdata2_start = .;
*(.sdata2)
*(.sdata2.*)
*(.gnu.linkonce.s2.*)
__sdata2_end = .;
} > psu_r5_btcm_MEM_0
.sbss2 : {
__sbss2_start = .;
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
__sbss2_end = .;
} > psu_r5_btcm_MEM_0
.data : {
__data_start = .;
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
*(.jcr)
*(.got)
*(.got.plt)
__data_end = .;
} > psu_r5_btcm_MEM_0
.data1 : {
__data1_start = .;
*(.data1)
*(.data1.*)
__data1_end = .;
} > psu_r5_btcm_MEM_0
.got : {
*(.got)
} > psu_r5_btcm_MEM_0
.ctors : {
__CTOR_LIST__ = .;
___CTORS_LIST___ = .;
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
__CTOR_END__ = .;
___CTORS_END___ = .;
} > psu_r5_btcm_MEM_0
.dtors : {
__DTOR_LIST__ = .;
___DTORS_LIST___ = .;
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
__DTOR_END__ = .;
___DTORS_END___ = .;
} > psu_r5_btcm_MEM_0
.fixup : {
__fixup_start = .;
*(.fixup)
__fixup_end = .;
} > psu_r5_btcm_MEM_0
.eh_frame : {
*(.eh_frame)
} > psu_r5_btcm_MEM_0
.eh_framehdr : {
__eh_framehdr_start = .;
*(.eh_framehdr)
__eh_framehdr_end = .;
} > psu_r5_btcm_MEM_0
.gcc_except_table : {
*(.gcc_except_table)
} > psu_r5_btcm_MEM_0
.mmu_tbl (ALIGN(16384)) : {
__mmu_tbl_start = .;
*(.mmu_tbl)
__mmu_tbl_end = .;
} > psu_r5_btcm_MEM_0
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx*)
*(.gnu.linkonce.armexidix.*.*)
__exidx_end = .;
} > psu_r5_btcm_MEM_0
.preinit_array : {
__preinit_array_start = .;
KEEP (*(SORT(.preinit_array.*)))
KEEP (*(.preinit_array))
__preinit_array_end = .;
} > psu_r5_btcm_MEM_0
.init_array : {
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
} > psu_r5_btcm_MEM_0
.fini_array : {
__fini_array_start = .;
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array))
__fini_array_end = .;
} > psu_r5_btcm_MEM_0
.ARM.attributes : {
__ARM.attributes_start = .;
*(.ARM.attributes)
__ARM.attributes_end = .;
} > psu_r5_btcm_MEM_0
.sdata : {
__sdata_start = .;
*(.sdata)
*(.sdata.*)
*(.gnu.linkonce.s.*)
__sdata_end = .;
} > psu_r5_btcm_MEM_0
.sbss (NOLOAD) : {
__sbss_start = .;
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
__sbss_end = .;
} > psu_r5_btcm_MEM_0
.tdata : {
__tdata_start = .;
*(.tdata)
*(.tdata.*)
*(.gnu.linkonce.td.*)
__tdata_end = .;
} > psu_r5_btcm_MEM_0
.tbss : {
__tbss_start = .;
*(.tbss)
*(.tbss.*)
*(.gnu.linkonce.tb.*)
__tbss_end = .;
} > psu_r5_btcm_MEM_0
.bss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > psu_r5_btcm_MEM_0
_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 );
_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 );
/* Generate Stack and Heap definitions */
.heap (NOLOAD) : {
. = ALIGN(16);
_heap = .;
HeapBase = .;
_heap_start = .;
. += _HEAP_SIZE;
_heap_end = .;
HeapLimit = .;
} > psu_r5_btcm_MEM_0
.stack (NOLOAD) : {
. = ALIGN(16);
_stack_end = .;
. += _STACK_SIZE;
_stack = .;
__stack = _stack;
. = ALIGN(16);
_irq_stack_end = .;
. += _IRQ_STACK_SIZE;
__irq_stack = .;
_supervisor_stack_end = .;
. += _SUPERVISOR_STACK_SIZE;
. = ALIGN(16);
__supervisor_stack = .;
_abort_stack_end = .;
. += _ABORT_STACK_SIZE;
. = ALIGN(16);
__abort_stack = .;
_fiq_stack_end = .;
. += _FIQ_STACK_SIZE;
. = ALIGN(16);
__fiq_stack = .;
_undef_stack_end = .;
. += _UNDEF_STACK_SIZE;
. = ALIGN(16);
__undef_stack = .;
} > psu_r5_btcm_MEM_0
_end = .;
}
@@ -0,0 +1,6 @@
#ifndef __PLATFORM_CONFIG_H_
#define __PLATFORM_CONFIG_H_
#define STDOUT_IS_PSU_UART
#define UART_DEVICE_ID 0
#endif
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* atomic_shmem_demod.c - Shared memory atomic operation demo
* This task will:
* 1. Get the shared memory device I/O region.
* 2. Get the IPI device I/O region.
* 3. Register IPI interrupt handler.
* 4. Wait for the APU to kick IPI to start the demo
* 5. Once notification is received, start atomic add by
* 1 for 5000 times over the shared memory
* 6. Trigger IPI to notify the remote it has finished calculation.
* 7. Clean up: Disable IPI interrupt, deregister the IPI interrupt handler.
*/
#include <metal/shmem.h>
#include <metal/atomic.h>
#include <metal/device.h>
#include <metal/io.h>
#include <sys/time.h>
#include <stdio.h>
#include "common.h"
#include "sys_init.h"
#define ATOMIC_INT_OFFSET 0x0 /* shared memory offset for atomic operation */
#define ITERATIONS 5000
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
1 - not-kicked */
static int ipi_irq_handler (int vect_id, void *priv)
{
(void)vect_id;
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
uint32_t ipi_mask = IPI_MASK;
uint64_t val = 1;
if (!ipi_io)
return METAL_IRQ_NOT_HANDLED;
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
if (val & ipi_mask) {
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
atomic_flag_clear(&remote_nkicked);
return METAL_IRQ_HANDLED;
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief atomic_add_shmemd() - Shared memory atomic operation demo
* This task will:
* * Wait for the remote to write to shared memory.
* * Once it receives the notification via polling, start atomic add by
* 1 for 5000 times to first 32 bits of memory in the shared memory
* which is pointed to by shm_io.
* * Write to shared mem to notify the remote once it finishes
* calculation.
*
* @param[in] ipi_io - IPI metal i/o region
* @param[in] shm_io - shared memory metal i/o region
* @return - If setup failed, return the corresponding error number. Otherwise
* return 0 on success.
*/
int atomic_add_shmemd(struct metal_io_region *ipi_io,
struct metal_io_region *shm_io)
{
atomic_int *shm_int;
uint32_t ipi_mask = IPI_MASK;
int i;
LPRINTF("Starting atomic add on shared memory demo.\n");
shm_int = (atomic_int *)metal_io_virt(shm_io,
ATOMIC_INT_OFFSET);
/* Wait for notification from the remote to start the demo */
wait_for_notified(&remote_nkicked);
/* Do atomic add over the shared memory */
for (i = 0; i < ITERATIONS; i++)
atomic_fetch_add(shm_int, 1);
/* Write to IPI trigger register to notify the remote it has finished
* the atomic operation. */
metal_io_write32(ipi_io, IPI_TRIG_OFFSET, ipi_mask);
LPRINTF("Shared memory with atomics test finished\n");
return 0;
}
int atomic_shmem_demod()
{
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
int ipi_irq;
int ret = 0;
print_demo("atomic operation over shared memory");
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
shm_io = metal_device_io_region(shm_dev, 0);
if (!shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Get IPI device IO region */
if (!ipi_dev) {
ret = -ENODEV;
goto out;
}
ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
/* initialize remote_nkicked */
atomic_init(&remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = atomic_add_shmemd(ipi_io, shm_io);
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler */
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
out:
return ret;
}
@@ -0,0 +1,168 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* shmem_demod.c
* This demo demonstrates the use of shared mem. between the APU and RPU.
* This demo does so via the following steps:
*
* 1. Get the shared memory device I/O region.
* 2. Clear the demo control value in shared memory.
* 3. Check the demo control value in the shared memory to wait for APU
* to start the demo.
* 4. Once the demo control value indicates the demo starts, it polls on
* RX available value to see if there is new RX message available.
* 5. If there is a new RX message available, it reads the message from
* the shared memory
* 6. It echos back the message to the shared memory
* 7. It increases the TX available value in the shared memory to notify
* the other end there is a message available to read.
* 8. Check if the demo control value and the RX available values to see
* if demo finishes and if there is new RX data available.
*
* Here is the Shared memory structure of this demo:
* |0 | 4Bytes | DEMO control status shows if demo starts or not |
* |0x04 | 4Bytes | number of APU to RPU buffers available to RPU |
* |0x08 | 4Bytes | number of APU to RPU buffers consumed by RPU |
* |0x0c | 4Bytes | number of RPU to APU buffers available to APU |
* |0x10 | 4Bytes | number of RPU to APU buffers consumed by APU |
* |0x14 | 1KBytes | APU to RPU buffer |
* ... ...
* |0x800 | 1KBytes | RPU to APU buffer |
*/
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <metal/sys.h>
#include <metal/device.h>
#include <metal/io.h>
#include <metal/alloc.h>
#include "common.h"
/* Shared memory offsets */
#define SHM_DEMO_CNTRL_OFFSET 0x0
#define SHM_RX_AVAIL_OFFSET 0x04
#define SHM_RX_USED_OFFSET 0x08
#define SHM_TX_AVAIL_OFFSET 0x0C
#define SHM_TX_USED_OFFSET 0x10
#define SHM_RX_BUFFER_OFFSET 0x14
#define SHM_TX_BUFFER_OFFSET 0x800
#define SHM_BUFFER_SIZE 0x400
#define DEMO_STATUS_IDLE 0x0
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
struct msg_hdr_s {
uint32_t index;
uint32_t len;
};
/**
* @brief shmem_echod() - Show use of shared memory with libmetal.
* Wait for message from APU. Once received, read and echo it back.
*
* @param[in] shm_io - metal i/o region of the shared memory
* @return - return 0 on success, otherwise return error number indicating
* type of error
*/
static int shmem_echod(struct metal_io_region *shm_io)
{
void *data = NULL;
struct msg_hdr_s *msg_hdr;
unsigned int rx_count = 0;
unsigned int len;
int ret = 0;
/* clear demo status value */
metal_io_write32(shm_io, SHM_DEMO_CNTRL_OFFSET, 0);
/* allocate memory for receiving data */
data = metal_allocate_memory(SHM_BUFFER_SIZE);
if (!data) {
LPERROR("Failed to allocate memory.\r\n");
return -1;
}
LPRINTF("Wait for shared memory demo to start.\r\n");
while (metal_io_read32(shm_io, SHM_DEMO_CNTRL_OFFSET) !=
DEMO_STATUS_START);
LPRINTF("Demo has started.\r\n");
/* wait for message is available */
while(metal_io_read32(shm_io, SHM_DEMO_CNTRL_OFFSET) ==
DEMO_STATUS_START) {
if (metal_io_read32(shm_io, SHM_RX_AVAIL_OFFSET)
== rx_count)
continue;
/* Message is available, read the message header */
ret = metal_io_block_read(shm_io, SHM_RX_BUFFER_OFFSET,
data, sizeof(struct msg_hdr_s));
if (ret < 0){
LPERROR("Unable to metal_io_block_read()\n");
return ret;
}
msg_hdr = (struct msg_hdr_s *)data;
/* Get the length of the data, if the data length is
* too large, truncate it. */
len = msg_hdr->len;
if (msg_hdr->len >
(SHM_BUFFER_SIZE - sizeof(*msg_hdr))) {
LPERROR("Input message is too long %u.\n",
msg_hdr->len);
len = SHM_BUFFER_SIZE - sizeof(*msg_hdr);
}
/* Read the message data */
ret = metal_io_block_read(shm_io,
SHM_RX_BUFFER_OFFSET + sizeof(*msg_hdr),
data + sizeof(*msg_hdr), len);
rx_count++;
ret = metal_io_block_write(shm_io,
SHM_TX_BUFFER_OFFSET,
(void*)data, sizeof(*msg_hdr) + len);
if (ret < 0){
LPERROR("Unable to metal_io_block_write()\n");
return ret;
}
/* increase TX available value to notify the other end
* there is data ready to read. */
metal_io_write32(shm_io, SHM_TX_AVAIL_OFFSET, rx_count);
}
metal_free_memory(data);
LPRINTF("Shared memory test finished\r\n");
return 0;
}
int shmem_demod()
{
struct metal_io_region *io = NULL;
int ret = 0;
print_demo("shared memory");
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
io = metal_device_io_region(shm_dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
ret = shmem_echod(io);
out:
return ret;
}
@@ -0,0 +1,265 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* shmem_latency_demod.c
* This is the remote side of the IPI latency measurement demo.
* This demo does the follwing steps:
*
* 1. Get the shared memory device libmetal I/O region.
* 1. Get the TTC timer device libemtal I/O region.
* 2. Get IPI device libmetal I/O region and the IPI interrupt vector.
* 3. Register IPI interrupt handler.
* 6. When it receives IPI interrupt, the IPI interrupt handler marked the
* remote has kicked.
* 7. Check the shared memory to see if demo is on. If the demo is on,
* copy data from the shared memory to local memory, stop the APU to RPU
* timer. Reset the RPU to APU TTC counter, copy data from local memory
* to shared memory, kick IPI to notify the remote.
* 8. If the shared memory indicates the demo is off, cleanup resource:
* disable IPI interrupt and deregister the IPI interrupt handler.
*/
#include <unistd.h>
#include <metal/atomic.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/irq.h>
#include "common.h"
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
#define TTC_CLK_FREQ_HZ 100000000
/* Shared memory offset */
#define SHM_DEMO_CNTRL_OFFSET 0x0 /* Shared memory for the demo status */
#define SHM_BUFF_OFFSET_RX 0x1000 /* Shared memory RX buffer start offset */
#define SHM_BUFF_OFFSET_TX 0x2000 /* Shared memory TX buffer start offset */
#define DEMO_STATUS_IDLE 0x0
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
#define BUF_SIZE_MAX 4096
struct channel_s {
struct metal_io_region *ipi_io; /* IPI metal i/o region */
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
struct metal_io_region *ttc_io; /* TTC metal i/o region */
uint32_t ipi_mask; /* RPU IPI mask */
atomic_int remote_nkicked; /* 0 - kicked from remote */
};
struct msg_hdr_s {
uint32_t index;
uint32_t len;
};
/**
* @brief reset_timer() - function to reset TTC counter
* Set the RST bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void reset_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_RST_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief stop_timer() - function to stop TTC counter
* Set the disable bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void stop_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_DIS_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief ipi_irq_handler() - IPI interrupt handler
* It will clear the notified flag to mark it's got an IPI interrupt.
* It will stop the RPU->APU timer and will clear the notified
* flag to mark it's got an IPI interrupt
*
* @param[in] vect_id - IPI interrupt vector ID
* @param[in/out] priv - communication channel data for this application.
*
* @return - If the IPI interrupt is triggered by its remote, it returns
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
* not the interrupt it expected.
*
*/
static int ipi_irq_handler (int vect_id, void *priv)
{
struct channel_s *ch = (struct channel_s *)priv;
uint32_t val;
(void)vect_id;
if (ch) {
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
if (val & ch->ipi_mask) {
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
ch->ipi_mask);
atomic_flag_clear(&ch->remote_nkicked);
return METAL_IRQ_HANDLED;
}
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief measure_shmem_latencyd() - measure shmem latency with libmetal
* Loop until APU tells RPU to stop via shared memory.
* In loop, wait for interrupt (interrupt handler stops APU to
* RPU TTC counter). Then reset count on RPU to APU TTC counter
* and kick IPI to notify APU.
*
* @param[in] ch - channel information
* @return - 0 on success, error code if failure.
*/
static int measure_shmem_latencyd(struct channel_s *ch)
{
void *lbuf = NULL;
struct msg_hdr_s *msg_hdr;
int ret = 0;
/* allocate memory for receiving data */
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
if (!lbuf) {
LPERROR("Failed to allocate memory.\r\n");
return -1;
}
LPRINTF("Starting IPI latency demo\r\n");
while(1) {
wait_for_notified(&ch->remote_nkicked);
if (metal_io_read32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET) ==
DEMO_STATUS_START) {
/* Read message header from shared memory */
metal_io_block_read(ch->shm_io, SHM_BUFF_OFFSET_RX,
lbuf, sizeof(struct msg_hdr_s));
msg_hdr = (struct msg_hdr_s *)lbuf;
/* Check if the message header is valid */
if (msg_hdr->len > (BUF_SIZE_MAX - sizeof(*msg_hdr))) {
LPERROR("wrong msg: length invalid: %u, %u.\n",
BUF_SIZE_MAX - sizeof(*msg_hdr),
msg_hdr->len);
ret = -EINVAL;
goto out;
}
/* Read message */
metal_io_block_read(ch->shm_io,
SHM_BUFF_OFFSET_RX + sizeof(*msg_hdr),
lbuf + sizeof(*msg_hdr), msg_hdr->len);
/* Stop APU to RPU TTC counter */
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
/* Reset RPU to APU TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
/* Copy the message back to the other end */
metal_io_block_write(ch->shm_io, SHM_BUFF_OFFSET_TX,
msg_hdr,
sizeof(*msg_hdr) + msg_hdr->len);
/* Kick IPI to notify the remote */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
ch->ipi_mask);
} else {
break;
}
}
out:
metal_free_memory(lbuf);
return ret;
}
int shmem_latency_demod()
{
struct channel_s ch;
int ipi_irq;
int ret = 0;
print_demo("shared memory latency");
memset(&ch, 0, sizeof(ch));
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
ch.shm_io = metal_device_io_region(shm_dev, 0);
if (!ch.shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Get TTC IO region */
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
if (!ch.ttc_io) {
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
ret = -ENODEV;
goto out;
}
/* Get IPI device IO region */
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ch.ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
ch.ipi_mask = IPI_MASK;
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
/* initialize remote_nkicked */
atomic_init(&ch.remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = measure_shmem_latencyd(&ch);
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler */
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
out:
return ret;
}
@@ -0,0 +1,355 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* shmem_throughput_demo_task.c
* This is the remote side of the shared memory throughput demo.
* This demo does the following steps:
*
* 1. Get the shared memory device libmetal I/O region.
* 1. Get the TTC timer device libemtal I/O region.
* 2. Get IPI device libmetal I/O region and the IPI interrupt vector.
* 3. Register IPI interrupt handler.
* 6. Download throughput measurement:
* Start TTC RPU counter, wait for IPI kick, check if data is available,
* if yes, read as much data as possible from shared memory. It will
* iterates untill 1000 packages have been received, stop TTC RPU counter
* and kick IPI to notify the remote. Repeat for different package size.
* 7. Upload throughput measurement:
* Start TTC RPU counter, write data to shared memory and kick IPI to
* notify remote. It will iterate for 1000 times, stop TTC RPU counter.
* wait for APU IPI kick to know APU has finished receiving packages.
* Kick IPI to notify it TTC RPU conter value is ready to read.
* Repeat for different package size.
* 8. Cleanup resource:
* disable IPI interrupt and deregister the IPI interrupt handler.
*
* Here is the Shared memory structure of this demo:
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
* |0x04 - 0x1FFFFF | address array for shared buffers from APU to RPU |
* |0x200000 - 0x200004 | number of RPU to APU buffers available to APU |
* |0x200004 - 0x3FFFFF | address array for shared buffers from RPU to APU |
* |0x400000 - 0x7FFFFF | APU to RPU buffers |
* |0x800000 - 0xAFFFFF | RPU to APU buffers |
*/
#include <unistd.h>
#include <metal/atomic.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/irq.h>
#include <metal/alloc.h>
#include "common.h"
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
/* Shared memory offsets */
#define SHM_DESC_OFFSET_RX 0x0
#define SHM_BUFF_OFFSET_RX 0x400000
#define SHM_DESC_OFFSET_TX 0x200000
#define SHM_BUFF_OFFSET_TX 0x800000
/* Shared memory descriptors offset */
#define SHM_DESC_AVAIL_OFFSET 0x00
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x04
#define BUF_SIZE_MAX 4096
#define PKG_SIZE_MAX 1024
#define PKG_SIZE_MIN 16
#define TOTAL_DATA_SIZE (1024 * 4096)
struct channel_s {
struct metal_io_region *ipi_io; /* IPI metal i/o region */
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
struct metal_io_region *ttc_io; /* TTC metal i/o region */
uint32_t ipi_mask; /* RPU IPI mask */
atomic_int remote_nkicked; /* 0 - kicked from remote */
};
/**
* @brief reset_timer() - function to reset TTC counter
* Set the RST bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void reset_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_RST_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief stop_timer() - function to stop TTC counter
* Set the disable bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void stop_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_DIS_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief ipi_irq_handler() - IPI interrupt handler
* It will clear the notified flag to mark it's got an IPI interrupt.
* It will stop the RPU->APU timer and will clear the notified
* flag to mark it's got an IPI interrupt
*
* @param[in] vect_id - IPI interrupt vector ID
* @param[in/out] priv - communication channel data for this application.
*
* @return - If the IPI interrupt is triggered by its remote, it returns
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
* not the interrupt it expected.
*
*/
static int ipi_irq_handler (int vect_id, void *priv)
{
struct channel_s *ch = (struct channel_s *)priv;
uint32_t val;
(void)vect_id;
if (ch) {
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
if (val & ch->ipi_mask) {
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
ch->ipi_mask);
atomic_flag_clear(&ch->remote_nkicked);
return METAL_IRQ_HANDLED;
}
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief measure_shmem_throughputd() - measure shmem throughpput with libmetal
* - Download throughput measurement:
* Start TTC RPU counter, wait for IPI kick, check if data is
* available, if yes, read as much data as possible from shared
* memory. It will iterates untill 1000 packages have been received,
* stop TTC RPU counter and kick IPI to notify the remote. Repeat
* for different package size.
* - Upload throughput measurement:
* Start TTC RPU counter, write data to shared memory and kick IPI
* to notify remote. It will iterate for 1000 times, stop TTC RPU
* counter.Wait for APU IPI kick to know APU has received all the
* packages. Kick IPI to notify it TTC RPU conter value is ready to
* read. Repeat for different package size.
*
* @param[in] ch - channel information
* @return - 0 on success, error code if failure.
*/
static int measure_shmem_throughputd(struct channel_s *ch)
{
void *lbuf = NULL;
int ret = 0;
size_t s;
uint32_t rx_count, rx_avail, tx_count, iterations;
unsigned long tx_avail_offset, rx_avail_offset;
unsigned long tx_addr_offset, rx_addr_offset;
unsigned long tx_data_offset, rx_data_offset;
uint32_t buf_phy_addr_32;
/* allocate memory for receiving data */
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
if (!lbuf) {
LPERROR("Failed to allocate memory.\r\n");
return -1;
}
memset(lbuf, 0xA, BUF_SIZE_MAX);
/* Clear shared memory */
metal_io_block_set(ch->shm_io, 0, 0, metal_io_region_size(ch->shm_io));
LPRINTF("Starting shared mem throughput demo\n");
/* for each data size, measure block receive throughput */
for (s = PKG_SIZE_MIN; s <= PKG_SIZE_MAX; s <<= 1) {
rx_count = 0;
iterations = TOTAL_DATA_SIZE / s;
/* Set rx buffer address offset */
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
rx_addr_offset = SHM_DESC_OFFSET_RX +
SHM_DESC_ADDR_ARRAY_OFFSET;
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
wait_for_notified(&ch->remote_nkicked);
/* Data has arrived, seasure start. Reset RPU TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
while (1) {
rx_avail = metal_io_read32(ch->shm_io, rx_avail_offset);
while(rx_count != rx_avail) {
/* Get the buffer location from the shared
* memory rx address array.
*/
buf_phy_addr_32 = metal_io_read32(ch->shm_io,
rx_addr_offset);
rx_data_offset = metal_io_phys_to_offset(
ch->shm_io,
(metal_phys_addr_t)buf_phy_addr_32);
if (rx_data_offset == METAL_BAD_OFFSET) {
LPERROR(
"[%u]failed to get rx offset: 0x%x, 0x%lx.\n",
rx_count, buf_phy_addr_32,
metal_io_phys(ch->shm_io,
rx_addr_offset));
ret = -EINVAL;
goto out;
}
rx_addr_offset += sizeof(buf_phy_addr_32);
/* Read data from shared memory */
metal_io_block_read(ch->shm_io, rx_data_offset,
lbuf, s);
rx_count++;
}
if (rx_count < iterations)
/* Need to wait for more data */
wait_for_notified(&ch->remote_nkicked);
else
break;
}
/* Stop RPU TTC counter */
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
/* Clear remote kicked flag -- 0 is kicked */
atomic_init(&ch->remote_nkicked, 1);
/* Kick IPI to notify RPU TTC counter value is ready */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
}
/* for each data size, measure send throughput */
for (s = PKG_SIZE_MIN; s <= PKG_SIZE_MAX; s <<= 1) {
tx_count = 0;
iterations = TOTAL_DATA_SIZE / s;
/* Set tx buffer address offset */
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
tx_addr_offset = SHM_DESC_OFFSET_TX +
SHM_DESC_ADDR_ARRAY_OFFSET;
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
/* Wait for APU to signal it is ready for the measurement */
wait_for_notified(&ch->remote_nkicked);
/* Data has arrived, seasure start. Reset RPU TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
while (tx_count < iterations) {
/* Write data to the shared memory*/
metal_io_block_write(ch->shm_io, tx_data_offset,
lbuf, s);
/* Write to the address array to tell the other end
* the buffer address.
*/
buf_phy_addr_32 = (uint32_t)metal_io_phys(ch->shm_io,
tx_data_offset);
metal_io_write32(ch->shm_io, tx_addr_offset,
buf_phy_addr_32);
tx_data_offset += s;
tx_addr_offset += sizeof(buf_phy_addr_32);
/* Increase number of available buffers */
tx_count++;
metal_io_write32(ch->shm_io, tx_avail_offset, tx_count);
/* Kick IPI to notify remote data is ready in the
* shared memory */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
ch->ipi_mask);
}
/* Stop RPU TTC counter */
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
/* Wait for IPI kick to know when the remote is ready
* to read the TTC counter value */
wait_for_notified(&ch->remote_nkicked);
/* Kick IPI to notify RPU TTC counter value is ready */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
}
out:
if (lbuf)
metal_free_memory(lbuf);
return ret;
}
int shmem_throughput_demod()
{
struct channel_s ch;
int ipi_irq;
int ret = 0;
print_demo("shared memory throughput");
memset(&ch, 0, sizeof(ch));
/* Get shared memory device IO region */
if (!shm_dev) {
ret = -ENODEV;
goto out;
}
ch.shm_io = metal_device_io_region(shm_dev, 0);
if (!ch.shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Get TTC IO region */
ch.ttc_io = metal_device_io_region(ttc_dev, 0);
if (!ch.ttc_io) {
LPERROR("Failed to map io region for %s.\n", ttc_dev->name);
ret = -ENODEV;
goto out;
}
/* Get IPI device IO region */
ch.ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ch.ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
ch.ipi_mask = IPI_MASK;
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, &ch);
/* initialize remote_nkicked */
atomic_init(&ch.remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = measure_shmem_throughputd(&ch);
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler */
metal_irq_unregister(ipi_irq, 0, ipi_dev, &ch);
out:
return ret;
}
@@ -0,0 +1,360 @@
/******************************************************************************
*
* Copyright (C) 2017 Xilinx, Inc. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
******************************************************************************/
#include <xparameters.h>
#include <xil_cache.h>
#include <xil_exception.h>
#include <xstatus.h>
#include <xscugic.h>
#include <xreg_cortexr5.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/sys.h>
#include <metal/irq.h>
#include "platform_config.h"
#include "common.h"
#ifdef STDOUT_IS_16550
#include <xuartns550_l.h>
#define UART_BAUD 9600
#endif
#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
#define IPI_IRQ_VECT_ID 65
#define SHM_BASE_ADDR 0x3ED80000
#define TTC0_BASE_ADDR 0xFF110000
#define IPI_BASE_ADDR 0xFF310000
/* Default generic I/O region page shift */
/* Each I/O region can contain multiple pages.
* In baremetal system, the memory mapping is flat, there is no
* virtual memory.
* We can assume there is only one page in the whole baremetal system.
*/
#define DEFAULT_PAGE_SHIFT (-1UL)
#define DEFAULT_PAGE_MASK (-1UL)
static XScuGic xInterruptController;
const metal_phys_addr_t metal_phys[] = {
IPI_BASE_ADDR, /**< base IPI address */
SHM_BASE_ADDR, /**< shared memory base address */
TTC0_BASE_ADDR, /**< base TTC0 address */
};
/* Define metal devices table for IPI, shared memory and TTC devices.
* Linux system uses device tree to describe devices. Unlike Linux,
* there is no standard device abstraction for baremetal system, we
* uses libmetal devices structure to describe the devices we used in
* the example.
* The IPI, shared memory and TTC devices are memory mapped
* devices. For this type of devices, it is required to provide
* accessible memory mapped regions, and interrupt information.
* In baremetal system, the memory mapping is flat. As you can see
* in the table before, we set the virtual address "virt" the same
* as the physical address.
*/
static struct metal_device metal_dev_table[] = {
{
/* IPI device */
.name = IPI_DEV_NAME,
.bus = NULL,
.num_regions = 1,
.regions = {
{
.virt = (void *)IPI_BASE_ADDR,
.physmap = &metal_phys[0],
.size = 0x1000,
.page_shift = DEFAULT_PAGE_SHIFT,
.page_mask = DEFAULT_PAGE_MASK,
.mem_flags = DEVICE_NONSHARED | PRIV_RW_USER_RW,
.ops = {NULL},
}
},
.node = {NULL},
.irq_num = 1,
.irq_info = (void *)IPI_IRQ_VECT_ID,
},
{
/* Shared memory management device */
.name = SHM_DEV_NAME,
.bus = NULL,
.num_regions = 1,
.regions = {
{
.virt = (void *)SHM_BASE_ADDR,
.physmap = &metal_phys[1],
.size = 0x1000000,
.page_shift = DEFAULT_PAGE_SHIFT,
.page_mask = DEFAULT_PAGE_MASK,
.mem_flags = NORM_SHARED_NCACHE |
PRIV_RW_USER_RW,
.ops = {NULL},
}
},
.node = {NULL},
.irq_num = 0,
.irq_info = NULL,
},
{
/* ttc0 */
.name = TTC_DEV_NAME,
.bus = NULL,
.num_regions = 1,
.regions = {
{
.virt = (void *)TTC0_BASE_ADDR ,
.physmap = &metal_phys[2],
.size = 0x1000,
.page_shift = DEFAULT_PAGE_SHIFT,
.page_mask = DEFAULT_PAGE_MASK,
.mem_flags = DEVICE_NONSHARED | PRIV_RW_USER_RW,
.ops = {NULL},
}
},
.node = {NULL},
.irq_num = 0,
.irq_info = NULL,
},
};
/**
* Extern global variables
*/
struct metal_device *ipi_dev = NULL;
struct metal_device *shm_dev = NULL;
struct metal_device *ttc_dev = NULL;
/**
* @brief enable_caches() - Enable caches
*/
void enable_caches()
{
#ifdef __MICROBLAZE__
#ifdef XPAR_MICROBLAZE_USE_ICACHE
Xil_ICacheEnable();
#endif
#ifdef XPAR_MICROBLAZE_USE_DCACHE
Xil_DCacheEnable();
#endif
#endif
}
/**
* @brief disable_caches() - Disable caches
*/
void disable_caches()
{
Xil_DCacheDisable();
Xil_ICacheDisable();
}
/**
* @brief init_uart() - Initialize UARTs
*/
void init_uart()
{
#ifdef STDOUT_IS_16550
XUartNs550_SetBaud(STDOUT_BASEADDR, XPAR_XUARTNS550_CLOCK_HZ,
UART_BAUD);
XUartNs550_SetLineControlReg(STDOUT_BASEADDR, XUN_LCR_8_DATA_BITS);
#endif
/* Bootrom/BSP configures PS7/PSU UART to 115200 bps */
}
/**
* @brief init_irq() - Initialize GIC and connect IPI interrupt
* This function will initialize the GIC and connect the IPI
* interrupt.
*
* @return 0 - succeeded, non-0 for failures
*/
int init_irq()
{
int ret = 0;
XScuGic_Config *IntcConfig; /* The configuration parameters of
* the interrupt controller */
Xil_ExceptionDisable();
/*
* Initialize the interrupt controller driver
*/
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return (int)XST_FAILURE;
}
ret = XScuGic_CfgInitialize(&xInterruptController, IntcConfig,
IntcConfig->CpuBaseAddress);
if (ret != XST_SUCCESS) {
return (int)XST_FAILURE;
}
/*
* Register the interrupt handler to the hardware interrupt handling
* logic in the ARM processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
&xInterruptController);
Xil_ExceptionEnable();
/* Connect IPI Interrupt ID with libmetal ISR */
XScuGic_Connect(&xInterruptController, IPI_IRQ_VECT_ID,
(Xil_ExceptionHandler)metal_irq_isr,
(void *)IPI_IRQ_VECT_ID);
XScuGic_Enable(&xInterruptController, IPI_IRQ_VECT_ID);
return 0;
}
/**
* @brief platform_register_metal_device() - Statically Register libmetal
* devices.
* This function registers the IPI, shared memory and
* TTC devices to the libmetal generic bus.
* Libmetal uses bus structure to group the devices. Before you can
* access the device with libmetal device operation, you will need to
* register the device to a libmetal supported bus.
* For non-Linux system, libmetal only supports "generic" bus, which is
* used to manage the memory mapped devices.
*
* @return 0 - succeeded, non-zero for failures.
*/
int platform_register_metal_device(void)
{
unsigned int i;
int ret;
struct metal_device *dev;
for (i = 0; i < sizeof(metal_dev_table)/sizeof(struct metal_device);
i++) {
dev = &metal_dev_table[i];
xil_printf("registering: %d, name=%s\n", i, dev->name);
ret = metal_register_generic_device(dev);
if (ret)
return ret;
}
return 0;
}
/**
* @brief open_metal_devices() - Open registered libmetal devices.
* This function opens all the registered libmetal devices.
*
* @return 0 - succeeded, non-zero for failures.
*/
int open_metal_devices(void)
{
int ret;
/* Open shared memory device */
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &shm_dev);
if (ret) {
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
goto out;
}
/* Open IPI device */
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &ipi_dev);
if (ret) {
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
goto out;
}
/* Open TTC device */
ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &ttc_dev);
if (ret) {
LPERROR("Failed to open device %s.\n", TTC_DEV_NAME);
goto out;
}
out:
return ret;
}
/**
* @brief close_metal_devices() - close libmetal devices
* This function closes all the libmetal devices which have
* been opened.
*
*/
void close_metal_devices(void)
{
/* Close shared memory device */
if (shm_dev)
metal_device_close(shm_dev);
/* Close IPI device */
if (ipi_dev)
metal_device_close(ipi_dev);
/* Close TTC device */
if (ttc_dev)
metal_device_close(ttc_dev);
}
/**
* @brief sys_init() - Register libmetal devices.
* This function register the libmetal generic bus, and then
* register the IPI, shared memory descriptor and shared memory
* devices to the libmetal generic bus.
*
* @return 0 - succeeded, non-zero for failures.
*/
int sys_init()
{
struct metal_init_params metal_param = METAL_INIT_DEFAULTS;
int ret;
enable_caches();
init_uart();
if (init_irq()) {
LPERROR("Failed to initialize interrupt\n");
}
/* Initialize libmetal environment */
metal_init(&metal_param);
/* Register libmetal devices */
ret = platform_register_metal_device();
if (ret) {
LPERROR("%s: failed to register devices: %d\n", __func__, ret);
return ret;
}
/* Open libmetal devices which have been registered */
ret = open_metal_devices();
if (ret) {
LPERROR("%s: failed to open devices: %d\n", __func__, ret);
return ret;
}
return 0;
}
/**
* @brief sys_cleanup() - system cleanup
* This function finish the libmetal environment
* and disable caches.
*
* @return 0 - succeeded, non-zero for failures.
*/
void sys_cleanup()
{
/* Close libmetal devices which have been opened */
close_metal_devices();
/* Finish libmetal environment */
metal_finish();
disable_caches();
}
@@ -0,0 +1,17 @@
/******************************************************************************
*
* Copyright (C) 2017 Xilinx, Inc. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
******************************************************************************/
#ifndef __SYS_INIT_H__
#define __SYS_INIT_H__
#include "platform_config.h"
int sys_init();
void sys_cleanup();
#endif /* __SYS_INIT_H__ */
@@ -0,0 +1,4 @@
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
add_subdirectory(${PROJECT_MACHINE})
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
@@ -0,0 +1 @@
add_subdirectory(zynqmp_amp_demo)
@@ -0,0 +1,44 @@
collector_list (_list PROJECT_INC_DIRS)
include_directories (${_list} ${CMAKE_CURRENT_SOURCE_DIR})
collector_list (_list PROJECT_LIB_DIRS)
link_directories (${_list})
collector_list (_deps PROJECT_LIB_DEPS)
set (_src_common ${CMAKE_CURRENT_SOURCE_DIR}/sys_init.c)
foreach (_app libmetal_amp_demo libmetal_amp_demod)
set (_src ${CMAKE_CURRENT_SOURCE_DIR}/${_app}.c)
list(APPEND _src ${_src_common})
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/shmem_demo.c)
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/shmem_atomic_demo.c)
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/ipi_shmem_demo.c)
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/ipi_latency_demo.c)
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/shmem_latency_demo.c)
list(APPEND _src ${CMAKE_CURRENT_SOURCE_DIR}/shmem_throughput_demo.c)
if (WITH_SHARED_LIB)
add_executable (${_app}-share ${_src})
if (PROJECT_EC_FLAGS)
string(REPLACE " " ";" _ec_flgs ${PROJECT_EC_FLAGS})
target_compile_options (${_app}-share PUBLIC ${_ec_flgs})
endif (PROJECT_EC_FLAGS)
target_link_libraries (${_app}-share ${PROJECT_NAME}-shared ${_deps})
install (TARGETS ${_app}-share RUNTIME DESTINATION bin)
add_dependencies (${_app}-share ${PROJECT_NAME}-shared)
endif (WITH_SHARED_LIB)
if (WITH_STATIC_LIB)
if (${PROJECT_SYSTEM} STREQUAL "linux")
add_executable (${_app}-static ${_src})
if (PROJECT_EC_FLAGS)
string(REPLACE " " ";" _ec_flgs ${PROJECT_EC_FLAGS})
target_compile_options (${_app}-static PUBLIC ${_ec_flgs})
endif (PROJECT_EC_FLAGS)
target_link_libraries (${_app}-static ${PROJECT_NAME}-static ${_deps})
install (TARGETS ${_app}-static RUNTIME DESTINATION bin)
endif (${PROJECT_SYSTEM} STREQUAL "linux")
endif (WITH_STATIC_LIB)
endforeach (_app)
# vim: expandtab:ts=2:sw=2:smartindent
@@ -0,0 +1,198 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __COMMON_H__
#define __COMMON_H__
#include <sys/types.h>
#include <metal/irq.h>
#include <metal/atomic.h>
#include <metal/cpu.h>
#include <stdio.h>
#define BUS_NAME "platform"
#define IPI_DEV_NAME "ff340000.ipi"
#define SHM_DEV_NAME "3ed80000.shm"
#define TTC_DEV_NAME "ff110000.timer"
/* Apply this snippet to the device tree in an overlay so that
* Linux userspace can see and use TTC0:
* &TTC0 {
* compatible = "ttc0_libmetal_demo";
* status = "okay";
* };
*/
/* IPI registers offset */
#define IPI_TRIG_OFFSET 0x0 /* IPI trigger reg offset */
#define IPI_OBS_OFFSET 0x4 /* IPI observation reg offset */
#define IPI_ISR_OFFSET 0x10 /* IPI interrupt status reg offset */
#define IPI_IMR_OFFSET 0x14 /* IPI interrupt mask reg offset */
#define IPI_IER_OFFSET 0x18 /* IPI interrupt enable reg offset */
#define IPI_IDR_OFFSET 0x1C /* IPI interrup disable reg offset */
#define IPI_MASK 0x100 /* IPI mask for kick from RPU. */
/* TTC counter offsets */
#define XTTCPS_CLK_CNTRL_OFFSET 0x0 /* TTC counter clock control reg offset */
#define XTTCPS_CNT_CNTRL_OFFSET 0xC /* TTC counter control reg offset */
#define XTTCPS_CNT_VAL_OFFSET 0x18 /* TTC counter val reg offset */
#define XTTCPS_CNT_OFFSET(ID) ((ID) == 1 ? 0 : 1 << (ID)) /* TTC counter offset
ID is from 1 to 3 */
/* TTC counter control masks */
#define XTTCPS_CNT_CNTRL_RST_MASK 0x10U /* TTC counter control reset mask */
#define XTTCPS_CNT_CNTRL_DIS_MASK 0x01U /* TTC counter control disable mask */
#define LPRINTF(format, ...) \
printf("CLIENT> " format, ##__VA_ARGS__)
#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__)
/**
* @brief shmem_demo() - Show use of shared memory with Libmetal.
* For NUM_TIMES times, send message to RPU and notify RPU by writing to
* share mem that RPU is polling. Once detected, RPU will then similarly
* write message and notify APU and the APU will then verify the
* response. If the message does not match expected response, record
* error. Afterwards, report test result and clean up.
* Notes:
* * The RPU will repeatedly wait for shared mem. from APU until APU
* notifies remote by changing the KEEP_GOING value in shared memory.
*
* @return - return 0 on success, otherwise return error number indicating
* type of error
*/
int shmem_demo();
/**
* @brief ipi_shmem_demo() - shared memory IPI demo
* This task will:
* * Get the timestamp and put it into the ping shared memory
* * Update the shared memory descriptor for the new available
* ping buffer.
* * Trigger IPI to notifty the remote.
* * Repeat the above steps until it sends out all the packages.
* * Monitor IPI interrupt, verify every received package.
* * After all the packages are received, it sends out shutdown
* message to the remote.
*
* @return - return 0 on success, otherwise return error number indicating
* type of error.
*/
int ipi_shmem_demo();
/**
* @brief atomic_shmem_demo() - Shared memory atomic operation demo
* This task will:
* - Write to shared memory to notify the remote to start atomic add
* on the shared memory descriptor memory for 1000 times.
* - Start atomic add by 1 for 1000 times to first 32 bits of memory
* in the shared memory location at 3ed00000 which is
* pointed to by shm_io.
* - Wait for the remote to write to shared memory
* - Once it received the polling kick from the remote, it will check
* if the value stored in the shared memory for the atomic add is
* 2000.
* - It will print if the atomic add test has passed or not.
*
* @param[in] channel- hold shared mem. device
* @return - If setup failed, return the corresponding error number. Otherwise
* return 0 on success.
*/
int atomic_shmem_demo();
/**
* @brief ipi_latency_demo() - Show performance of IPI with Libmetal.
* For NUM_TIMES times, repeatedly send an IPI from APU and then detect
* this IPI from RPU and measure the latency. Similarly, measure the
* latency from RPU to APU. Each iteration, record this latency and
* after the loop has finished, report the total latency in nanseconds.
* Notes:
* * The RPU will repeatedly wait for IPI from APU until APU notifies
* remote by changing the KEEPGOING value in shared memory.
* * To further ensure the accuracy of the readings a different thread
* (i.e. the IRQ handler) will stop the timer measuring RPU to APU
* latency.
*
* @return - 0 on success, error code if failure.
*/
int ipi_latency_demo();
/**
* @brief shmem_latency_demo_demo() - Show performance of shared memory
* For 8, 512, and 1024 bytes, measure latency from block write to block
* read on remote side in shared memory. For each size, find average
* latency by running NUM_TIMES times and reporting the average latency
* for both APU block write to RPU block read as well as RPU block write
* to APU block read.
*
* @return - 0 on success, error code if failure.
*/
int shmem_latency_demo();
/**
* @brief shmem_throughput_demo_demo() - Show performance of shared memory
* Record average throughput for APU block read, write, RPU block read
* and write for sizes 1/2KB, 1KB and 2KB. For each size, run 1000 times
* each operation and record average.
*
* @return - 0 on success, error code if failure.
*/
int shmem_throughput_demo();
/**
* @breif wait_for_notified() - Loop until notified bit in channel is set.
*
* @param[in] notified - pointer to the notified variable
*/
static inline void wait_for_notified(atomic_int *notified)
{
unsigned int flags;
do {
flags = metal_irq_save_disable();
if (!atomic_flag_test_and_set(notified)) {
metal_irq_restore_enable(flags);
break;
}
metal_cpu_yield();
metal_irq_restore_enable(flags);
} while(1);
}
/**
* @breif dump_buffer() - print hex value of each byte in the buffer
*
* @param[in] buf - pointer to the buffer
* @param[in] len - len of the buffer
*/
static inline void dump_buffer(void *buf, unsigned int len)
{
unsigned int i;
unsigned char *tmp = (unsigned char *)buf;
for (i = 0; i < len; i++) {
printf(" %02x", *(tmp++));
if (!(i % 20))
printf("\n");
}
printf("\n");
}
/**
* @brief print_demo() - print demo string
*
* @param[in] name - demo name
*/
static inline void print_demo(char *name)
{
LPRINTF("****** libmetal demo: %s ******\n", name);
}
#endif /* __COMMON_H__ */
@@ -0,0 +1,295 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* ipi_latency_demo.c
* This demo measures the IPI latency between the APU and RPU.
* This demo does the follwing steps:
*
* 1. Get the shared memory device I/O region.
* 1. Get the TTC timer device I/O region.
* 2. Get the IPI device I/O region.
* 3. Register IPI interrupt handler.
* 4. Write to shared memory to indicate demo starts
* 5. Reset the APU to RPU TTC counter and then kick IPI to notify the
* remote.
* 6. When it receives IPI interrupt, the IPI interrupt handler to stop
* the RPU to APU TTC counter.
* 7. Accumulate APU to RPU and RPU to APU counter values.
* 8. Repeat step 5, 6 and 7 for 1000 times
* 9. Write shared memory to indicate RPU about demo finishes and kick
* IPI to notify.
* 10. Clean up: disable IPI interrupt, deregister the IPI interrupt handler.
*/
#include <unistd.h>
#include <stdio.h>
#include <metal/errno.h>
#include <sys/types.h>
#include <metal/atomic.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/irq.h>
#include "common.h"
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
#define TTC_CLK_FREQ_HZ 100000000
#define NS_PER_SEC 1000000000
/* Shared memory offset */
#define SHM_DEMO_CNTRL_OFFSET 0x0
#define DEMO_STATUS_IDLE 0x0
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
#define ITERATIONS 1000
struct channel_s {
struct metal_device *ipi_dev; /* IPI metal device */
struct metal_io_region *ipi_io; /* IPI metal i/o region */
struct metal_device *shm_dev; /* Shared memory metal device */
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
struct metal_device *ttc_dev; /* TTC metal device */
struct metal_io_region *ttc_io; /* TTC metal i/o region */
uint32_t ipi_mask; /* RPU IPI mask */
atomic_int remote_nkicked; /* 0 - kicked from remote */
};
/**
* @brief read_timer() - return TTC counter value
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter ID
*/
static inline uint32_t read_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
unsigned long offset = XTTCPS_CNT_VAL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
return metal_io_read32(ttc_io, offset);
}
/**
* @brief reset_timer() - function to reset TTC counter
* Set the RST bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void reset_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_RST_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief stop_timer() - function to stop TTC counter
* Set the disable bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void stop_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_DIS_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief ipi_irq_handler() - IPI interrupt handler
* It will clear the notified flag to mark it's got an IPI interrupt.
* It will stop the RPU->APU timer and will clear the notified
* flag to mark it's got an IPI interrupt
*
* @param[in] vect_id - IPI interrupt vector ID
* @param[in/out] priv - communication channel data for this application.
*
* @return - If the IPI interrupt is triggered by its remote, it returns
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
* not the interrupt it expected.
*
*/
static int ipi_irq_handler (int vect_id, void *priv)
{
struct channel_s *ch = (struct channel_s *)priv;
uint32_t val;
(void)vect_id;
if (ch) {
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
if (val & ch->ipi_mask) {
/* stop RPU -> APU timer */
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
ch->ipi_mask);
atomic_flag_clear(&ch->remote_nkicked);
return METAL_IRQ_HANDLED;
}
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief measure_ipi_latency() - Measure latency of IPI
* Repeatedly kick IPI to notify the remote and then wait for IPI kick
* from RPU and measure the latency. Similarly, measure the latency
* from RPU to APU. Each iteration, record this latency and after the
* loop has finished, report the total latency in nanseconds.
* Notes:
* - RPU will repeatedly wait for IPI from APU until APU
* notifies remote demo has finished by setting the value in the
* shared memory.
*
* @param[in] ch - channel information, which contains the IPI i/o region,
* shared memory i/o region and the ttc timer i/o region.
* @return - 0 on success, error code if failure.
*/
static int measure_ipi_latency(struct channel_s *ch)
{
uint32_t apu_to_rpu_sum = 0, rpu_to_apu_sum = 0;
int i;
LPRINTF("Starting IPI latency task\n");
/* write to shared memory to indicate demo has started */
metal_io_write32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET, DEMO_STATUS_START);
for ( i = 1; i <= ITERATIONS; i++) {
/* Reset TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
/* Kick IPI to notify the remote */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
/* irq handler stops timer for rpu->apu irq */
wait_for_notified(&ch->remote_nkicked);
apu_to_rpu_sum += read_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
rpu_to_apu_sum += read_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
}
/* write to shared memory to indicate demo has finished */
metal_io_write32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET, 0);
/* Kick IPI to notify the remote */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
/* report avg latencies */
LPRINTF("IPI latency result with %i iterations:\n", ITERATIONS);
LPRINTF("APU to RPU average latency: %u ns \n",
apu_to_rpu_sum / ITERATIONS * NS_PER_SEC / TTC_CLK_FREQ_HZ );
LPRINTF("RPU to APU average latency: %u ns \n",
rpu_to_apu_sum / ITERATIONS * NS_PER_SEC / TTC_CLK_FREQ_HZ );
LPRINTF("Finished IPI latency task\n");
return 0;
}
int ipi_latency_demo()
{
struct metal_device *dev;
struct metal_io_region *io;
struct channel_s ch;
int ipi_irq;
int ret = 0;
print_demo("IPI latency");
memset(&ch, 0, sizeof(ch));
/* Open shared memory device */
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &dev);
if (ret) {
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
goto out;
}
/* Get shared memory device IO region */
io = metal_device_io_region(dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", dev->name);
ret = -ENODEV;
goto out;
}
ch.shm_dev = dev;
ch.shm_io = io;
/* Open TTC device */
ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &dev);
if (ret) {
LPERROR("Failed to open device %s.\n", TTC_DEV_NAME);
goto out;
}
/* Get TTC IO region */
io = metal_device_io_region(dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", dev->name);
ret = -ENODEV;
goto out;
}
ch.ttc_dev = dev;
ch.ttc_io = io;
/* Open IPI device */
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &dev);
if (ret) {
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
goto out;
}
/* Get IPI device IO region */
io = metal_device_io_region(dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", dev->name);
ret = -ENODEV;
goto out;
}
ch.ipi_dev = dev;
ch.ipi_io = io;
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ch.ipi_dev->irq_info;
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
/* initialize remote_nkicked */
atomic_init(&ch.remote_nkicked, 1);
ch.ipi_mask = IPI_MASK;
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ch.ipi_dev, &ch);
/* Enable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = measure_ipi_latency(&ch);
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler by setting the handler to 0 */
metal_irq_unregister(ipi_irq, 0, ch.ipi_dev, &ch);
out:
if (ch.ttc_dev)
metal_device_close(ch.ttc_dev);
if (ch.shm_dev)
metal_device_close(ch.shm_dev);
if (ch.ipi_dev)
metal_device_close(ch.ipi_dev);
return ret;
}
@@ -0,0 +1,391 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* ipi_shmem_demo.c - shared memory with IPI demo
* This demo will:
* 1. Open the shared memory device.
* 2. Open the IPI device.
* 3. Register IPI interrupt handler.
* 4. Write message to the shared memory.
* 5. Kick IPI to notify there is a message written to the shared memory
* 6. Wait until the remote has kicked the IPI to notify the remote
* has echoed back the message.
* 7. Read the message from shared memory.
* 8. Verify the message
* 9. Repeat step 4 to 8 for 100 times.
* 10. Clean up: deregister the IPI interrupt handler, close the IPI device
* , close the shared memory device.
*
* Here is the Shared memory structure of this demo:
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
* |0x04 - 0x07 | number of APU to RPU buffers consumed by RPU |
* |0x08 - 0x1FFC | address array for shared buffers from APU to RPU |
* |0x2000 - 0x2003 | number of RPU to APU buffers available to APU |
* |0x2004 - 0x2007 | number of RPU to APU buffers consumed by APU |
* |0x2008 - 0x3FFC | address array for shared buffers from RPU to APU |
* |0x04000 - 0x103FFC | APU to RPU buffers |
* |0x104000 - 0x203FFC | RPU to APU buffers |
*/
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <metal/sys.h>
#include <metal/io.h>
#include <metal/alloc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <metal/device.h>
#include <metal/irq.h>
#include <metal/errno.h>
#include "common.h"
/* Shared memory offsets */
#define SHM_DESC_OFFSET_TX 0x0
#define SHM_BUFF_OFFSET_TX 0x04000
#define SHM_DESC_OFFSET_RX 0x02000
#define SHM_BUFF_OFFSET_RX 0x104000
/* Shared memory descriptors offset */
#define SHM_DESC_AVAIL_OFFSET 0x00
#define SHM_DESC_USED_OFFSET 0x04
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x08
#define PKGS_TOTAL 1024
#define BUF_SIZE_MAX 512
#define SHUTDOWN "shutdown"
#define NS_PER_S (1000 * 1000 * 1000)
struct msg_hdr_s {
uint32_t index;
uint32_t len;
};
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
1 - not-kicked */
/**
* @breif get_timestamp() - Get the timestamp
* IT gets the timestamp and return nanoseconds.
*
* @return nano seconds.
*/
static unsigned long long get_timestamp (void)
{
unsigned long long t = 0;
struct timespec tp;
int r;
r = clock_gettime(CLOCK_MONOTONIC, &tp);
if (r == -1) {
LPERROR("Bad clock_gettime!\n");
return t;
} else {
t = tp.tv_sec * (NS_PER_S);
t += tp.tv_nsec;
}
return t;
}
static int ipi_irq_handler (int vect_id, void *priv)
{
(void)vect_id;
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
uint32_t ipi_mask = IPI_MASK;
uint64_t val = 1;
if (!ipi_io)
return METAL_IRQ_NOT_HANDLED;
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
if (val & ipi_mask) {
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
atomic_flag_clear(&remote_nkicked);
return METAL_IRQ_HANDLED;
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief ipi_shmem_echo() - shared memory IPI demo
* This task will:
* * Get the timestamp and put it into the ping shared memory
* * Update the shared memory descriptor for the new available
* ping buffer.
* * Trigger IPI to notifty the remote.
* * Repeat the above steps until it sends out all the packages.
* * Monitor IPI interrupt, verify every received package.
* * After all the packages are received, it sends out shutdown
* message to the remote.
*
* @param[in] ipi_io - IPI metal i/o region
* @param[in] shm_io - shared memory metal i/o region
* @return - return 0 on success, otherwise return error number indicating
* type of error.
*/
static int ipi_shmem_echo(struct metal_io_region *ipi_io,
struct metal_io_region *shm_io)
{
int ret;
uint32_t i;
uint32_t rx_avail;
unsigned long tx_avail_offset, rx_avail_offset;
unsigned long rx_used_offset;
unsigned long tx_addr_offset, rx_addr_offset;
unsigned long tx_data_offset, rx_data_offset;
unsigned long long tstart, tend;
long long tdiff;
long long tdiff_avg_s = 0, tdiff_avg_ns = 0;
void *txbuf = NULL, *rxbuf = NULL, *tmpptr;
struct msg_hdr_s *msg_hdr;
uint32_t ipi_mask = IPI_MASK;
uint32_t tx_phy_addr_32;
txbuf = metal_allocate_memory(BUF_SIZE_MAX);
if (!txbuf) {
LPERROR("Failed to allocate local tx buffer for msg.\n");
ret = -ENOMEM;
goto out;
}
rxbuf = metal_allocate_memory(BUF_SIZE_MAX);
if (!rxbuf) {
LPERROR("Failed to allocate local rx buffer for msg.\n");
ret = -ENOMEM;
goto out;
}
/* Clear shared memory */
metal_io_block_set(shm_io, 0, 0, metal_io_region_size(shm_io));
/* Set tx/rx buffer address offset */
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
rx_used_offset = SHM_DESC_OFFSET_RX + SHM_DESC_USED_OFFSET;
tx_addr_offset = SHM_DESC_OFFSET_TX + SHM_DESC_ADDR_ARRAY_OFFSET;
rx_addr_offset = SHM_DESC_OFFSET_RX + SHM_DESC_ADDR_ARRAY_OFFSET;
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
LPRINTF("Start echo flood testing....\n");
LPRINTF("Sending msgs to the remote.\n");
for (i = 0; i < PKGS_TOTAL; i++) {
/* Construct a message to send */
tmpptr = txbuf;
msg_hdr = tmpptr;
msg_hdr->index = i;
msg_hdr->len = sizeof(tstart);
tmpptr += sizeof(struct msg_hdr_s);
tstart = get_timestamp();
*(unsigned long long *)tmpptr = tstart;
/* copy message to shared buffer */
metal_io_block_write(shm_io, tx_data_offset, msg_hdr,
sizeof(struct msg_hdr_s) + msg_hdr->len);
/* Write to the address array to tell the other end
* the buffer address.
*/
tx_phy_addr_32 = (uint32_t)metal_io_phys(shm_io,
tx_data_offset);
metal_io_write32(shm_io, tx_addr_offset, tx_phy_addr_32);
tx_data_offset += sizeof(struct msg_hdr_s) + msg_hdr->len;
tx_addr_offset += sizeof(uint32_t);
/* Increase number of available buffers */
metal_io_write32(shm_io, tx_avail_offset, (i + 1));
/* Kick IPI to notify data has been put to shared buffer */
metal_io_write32(ipi_io, IPI_TRIG_OFFSET, ipi_mask);
}
LPRINTF("Waiting for messages to echo back and verify.\n");
i = 0;
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
while (i != PKGS_TOTAL) {
wait_for_notified(&remote_nkicked);
rx_avail = metal_io_read32(shm_io, rx_avail_offset);
while (i != rx_avail) {
uint32_t rx_phy_addr_32;
/* Received pong from the other side */
/* Get the buffer location from the shared memory
* rx address array.
*/
rx_phy_addr_32 = metal_io_read32(shm_io,
rx_addr_offset);
rx_data_offset = metal_io_phys_to_offset(shm_io,
(metal_phys_addr_t)rx_phy_addr_32);
if (rx_data_offset == METAL_BAD_OFFSET) {
LPERROR("failed to get rx [%d] offset: 0x%x.\n",
i, rx_phy_addr_32);
ret = -EINVAL;
goto out;
}
rx_addr_offset += sizeof(rx_phy_addr_32);
/* Read message header from shared memory */
metal_io_block_read(shm_io, rx_data_offset, rxbuf,
sizeof(struct msg_hdr_s));
msg_hdr = (struct msg_hdr_s *)rxbuf;
/* Check if the message header is valid */
if (msg_hdr->index != (uint32_t)i) {
LPERROR("wrong msg: expected: %d, actual: %d\n",
i, msg_hdr->index);
ret = -EINVAL;
goto out;
}
if (msg_hdr->len != sizeof(tstart)) {
LPERROR("wrong msg: length invalid: %lu, %u.\n",
sizeof(tstart), msg_hdr->len);
ret = -EINVAL;
goto out;
}
/* Read message */
rx_data_offset += sizeof(*msg_hdr);
metal_io_block_read(shm_io,
rx_data_offset,
rxbuf + sizeof(*msg_hdr), msg_hdr->len);
rx_data_offset += msg_hdr->len;
/* increase rx used count to indicate it has consumed
* the received data */
metal_io_write32(shm_io, rx_used_offset, (i + 1));
/* Verify message */
/* Get tx message previously sent*/
metal_io_block_read(shm_io, tx_data_offset, txbuf,
sizeof(*msg_hdr) + sizeof(tstart));
tx_data_offset += sizeof(*msg_hdr) + sizeof(tstart);
/* Compare the received message and the sent message */
ret = memcmp(rxbuf, txbuf,
sizeof(*msg_hdr) + sizeof(tstart));
if (ret) {
LPERROR("data[%u] verification failed.\n", i);
LPRINTF("Expected:");
dump_buffer(txbuf,
sizeof(*msg_hdr) + sizeof(tstart));
LPRINTF("Actual:");
dump_buffer(rxbuf,
sizeof(*msg_hdr) + sizeof(tstart));
ret = -EINVAL;
goto out;
}
i++;
}
}
tend = get_timestamp();
tdiff = tend - tstart;
/* Send shutdown message */
tmpptr = txbuf;
msg_hdr = tmpptr;
msg_hdr->index = i;
msg_hdr->len = strlen(SHUTDOWN);
tmpptr += sizeof(struct msg_hdr_s);
sprintf(tmpptr, SHUTDOWN);
/* copy message to shared buffer */
metal_io_block_write(shm_io,
tx_data_offset,
msg_hdr,
sizeof(struct msg_hdr_s) + msg_hdr->len);
tx_phy_addr_32 = (uint32_t)metal_io_phys(shm_io,
tx_data_offset);
metal_io_write32(shm_io, tx_addr_offset, tx_phy_addr_32);
metal_io_write32(shm_io, tx_avail_offset, PKGS_TOTAL + 1);
LPRINTF("Kick remote to notify shutdown message sent...\n");
metal_io_write32(ipi_io, IPI_TRIG_OFFSET, ipi_mask);
tdiff /= PKGS_TOTAL;
tdiff_avg_s = tdiff / NS_PER_S;
tdiff_avg_ns = tdiff % NS_PER_S;
LPRINTF("Total packages: %d, time_avg = %lds, %ldns\n",
i, (long int)tdiff_avg_s, (long int)tdiff_avg_ns);
ret = 0;
out:
if (txbuf)
metal_free_memory(txbuf);
if (rxbuf)
metal_free_memory(rxbuf);
return ret;
}
int ipi_shmem_demo()
{
struct metal_device *ipi_dev = NULL, *shm_dev = NULL;
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
int ipi_irq;
int ret = 0;
print_demo("IPI and shared memory");
/* Open shared memory device */
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &shm_dev);
if (ret) {
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
goto out;
}
/* Get shared memory device IO region */
shm_io = metal_device_io_region(shm_dev, 0);
if (!shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Open IPI device */
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &ipi_dev);
if (ret) {
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
goto out;
}
/* Get IPI device IO region */
ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
/* initialize remote_nkicked */
atomic_init(&remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = ipi_shmem_echo(ipi_io, shm_io);
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler by setting the handler to 0 */
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
out:
if (shm_dev)
metal_device_close(shm_dev);
if (ipi_dev)
metal_device_close(ipi_dev);
return ret;
}
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* libmetal_amp_demo.c
*
* This application shows how to use IPI to trigger interrupt and how to
* setup shared memory with libmetal API for communication between processors.
*
* This app does the following:
* 1. Run the shared memory echo demo task ipi_shmem_task()
* * Write message to the APU to RPU shared buffer.
* * Update the APU to RPU shared memory available index.
* * Trigger IPI to the remote.
* * Repeat the above 3 sub steps until it sends all the packages.
* * Wait for IPI to receive all the packages
* * If "shutdown" message is received, cleanup the libmetal source.
* 2. Run shared memory demo with shmem_task().
* * Open shared memory device.
* * For 1000 times, communicate between local and remote processes
* using shared memory and polling via shared memory.
* * Cleanup shared memory device.
* 3. Run the atomic demo task atomic_shmem_task():
* * Trigger the IPI to the remote, the remote will then start doing atomic
* add calculation.
* * Start atomic add by 1 for 1000 times to the first 32bit of the shared
* memory descriptor location.
* * Once it receives the IPI interrupt, it will check if the value stored
* in the shared memory descriptor location is 2000. If yes, the atomic
* across the shared memory passed, otherwise, it failed.
* 4. Demonstrate IPI latency with ipi_latency_demo_task()
* * Open IPI and timer devices.
* * For 1000 times, record APU to RPU IPI latency and RPU to APU
* latency. Then report average time for each direction.
* * Cleanup libmetal resources
* 5. Demonstrate shared memory latency with shmem_latency_demo_task()
* * Open shared memory and timer devices.
* * For 1000 times, record APU to RPU shared memory latency and RPU to APU
* latency for 8 bytes, 1/2K and 1K. Then report average time for each
* direction.
* * Cleanup libmetal resources
* 6. Demonstrate shared memory throughput with shmem_throughput_demo_task()
* * Open shared memory, IPI and timer devices.
* * For 1000 times, record APU block read and write times. Notify remote
* to run test, then similarly record RPU block read and write times for
* 1/2KB, 1KB and 2KB. Then report average throughput for each data size
* and operation.
* * Cleanup libmetal resources
*/
#include <stdio.h>
#include <unistd.h>
#include <metal/io.h>
#include <metal/device.h>
#include "common.h"
#include "sys_init.h"
/**
* @brief main function of the demo application.
* Here are the steps for the main function:
* * initialize libmetal environment
* * Run the IPI with shared memory demo.
* * Run the shared memory demo.
* * Run the atomic across shared memory demo.
* * Run the ipi latency demo.
* * Run the shared memory latency demo.
* * Run the shared memory throughput demo.
* * Cleanup libmetal environment
* Report if any of the above tasks failed.
* @return 0 - succeeded, non-zero for failures.
*/
int main(void)
{
int ret;
ret = sys_init();
if (ret) {
LPERROR("Failed to initialize system.\n");
return ret;
}
ret = shmem_demo();
if (ret) {
LPERROR("shared memory demo failed.\n");
goto out;
}
/* sleep, to give time for RPU to clean up the last demo. */
sleep(1);
ret = atomic_shmem_demo();
if (ret) {
LPERROR("shared memory atomic demo failed.\n");
goto out;
}
/* sleep, to give time for RPU to clean up the last demo. */
sleep(1);
ret = ipi_shmem_demo();
if (ret) {
LPERROR("IPI and shared memory demo failed.\n");
goto out;
}
/* sleep, to give time for RPU to clean up the last demo. */
sleep(1);
ret = ipi_latency_demo();
if (ret) {
LPERROR("IPI latency demo failed.\n");
goto out;
}
/* sleep, to give time for RPU to clean up the last demo. */
sleep(1);
ret = shmem_latency_demo();
if (ret) {
LPERROR("shared memory latency demo failed.\n");
goto out;
}
/* sleep, to give time for RPU to clean up the last demo. */
sleep(1);
ret = shmem_throughput_demo();
if (ret) {
LPERROR("shared memory throughput demo failed.\n");
goto out;
}
out:
sys_cleanup();
return ret;
}
@@ -0,0 +1,378 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <unistd.h>
#include <metal/sys.h>
#include <metal/device.h>
#include <metal/irq.h>
#include <metal/atomic.h>
#include <metal/cpu.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "sys_init.h"
#define IPI_TRIG_OFFSET 0x0
#define IPI_OBS_OFFSET 0x4
#define IPI_ISR_OFFSET 0x10
#define IPI_IMR_OFFSET 0x14
#define IPI_IER_OFFSET 0x18
#define IPI_IDR_OFFSET 0x1C
#define IPI_MASK 0x1000000
#define IPI_DEV_NAME "ff310000.ipi"
#define SHM0_DESC_DEV_NAME "3ed00000.shm_desc"
#define SHM1_DESC_DEV_NAME "3ed10000.shm_desc"
#define SHM_DEV_NAME "3ed20000.shm"
#define BUS_NAME "platform"
#define D0_SHM_OFFSET 0x00000
#define D1_SHM_OFFSET 0x20000
#define BUF_SIZE_MAX 512
#define SHUTDOWN "shutdown"
#define LPRINTF(format, ...) \
printf("SERVER> " format, ##__VA_ARGS__)
struct shm_mg_s {
uint32_t avails;
uint32_t used;
};
typedef uint64_t shm_addr_t;
struct msg_hdr_s {
uint32_t index;
int32_t len;
};
struct channel_s {
struct metal_device *ipi_dev;
struct metal_io_region *ipi_io;
unsigned int ipi_mask;
struct metal_device *shm0_desc_dev;
struct metal_io_region *shm0_desc_io;
struct metal_device *shm1_desc_dev;
struct metal_io_region *shm1_desc_io;
struct metal_device *shm_dev;
struct metal_io_region *shm_io;
atomic_int notified;
unsigned long d0_start_offset;
unsigned long d1_start_offset;
};
static struct channel_s ch0;
extern int system_init();
extern int run_comm_task(void *task, void *arg);
extern void wait_for_interrupt(void);
static int ipi_irq_isr (int vect_id, void *priv)
{
(void)vect_id;
struct channel_s *ch = (struct channel_s *)priv;
uint64_t val = 1;
if (!ch)
return METAL_IRQ_NOT_HANDLED;
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
if (val & ch->ipi_mask) {
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET, ch->ipi_mask);
atomic_flag_clear(&ch->notified);
return METAL_IRQ_HANDLED;
}
return METAL_IRQ_NOT_HANDLED;
}
static int ipi_task_shm_atomicd(void *arg)
{
struct channel_s *ch = (struct channel_s *)arg;
atomic_int *shm_int;
unsigned int flags;
int i;
shm_int = (atomic_int *)metal_io_virt(ch->shm0_desc_io, 0);
LPRINTF("Wait for atomic test to start.\n");
while (1) {
do {
flags = metal_irq_save_disable();
if (!atomic_flag_test_and_set(&ch->notified)) {
metal_irq_restore_enable(flags);
break;
}
wait_for_interrupt();
metal_irq_restore_enable(flags);
} while(1);
for (i = 0; i < 1000; i++)
atomic_fetch_add(shm_int, 1);
//*((unsigned int volatile *)shm_int) += 1;
/* memory barrier */
atomic_thread_fence(memory_order_acq_rel);
/* Send the message */
LPRINTF("SENDING message...\n");
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
break;
}
return 0;
}
static int ipi_task_echod(void *arg)
{
struct channel_s *ch = (struct channel_s *)arg;
struct shm_mg_s *shm0_mg, *shm1_mg;
shm_addr_t *shm0_addr_array, *shm1_addr_array;
struct msg_hdr_s *msg_hdr;
unsigned int flags;
void *d0, *d1, *lbuf;
metal_phys_addr_t d0_pa;
int len;
shm0_mg = (struct shm_mg_s *)metal_io_virt(ch->shm0_desc_io, 0);
shm1_mg = (struct shm_mg_s *)metal_io_virt(ch->shm1_desc_io, 0);
shm0_addr_array = (void *)shm0_mg + sizeof(struct shm_mg_s);
shm1_addr_array = (void *)shm1_mg + sizeof(struct shm_mg_s);
d1 = metal_io_virt(ch->shm_io, ch->d1_start_offset);
lbuf = malloc(BUF_SIZE_MAX);
if (!lbuf) {
LPRINTF("ERROR: Failed to allocate local buffer for msg.\n");
return -1;
}
LPRINTF("Wait for echo test to start.\n");
while (1) {
do {
flags = metal_irq_save_disable();
if (!atomic_flag_test_and_set(&ch->notified)) {
metal_irq_restore_enable(flags);
break;
}
wait_for_interrupt();
metal_irq_restore_enable(flags);
} while(1);
atomic_thread_fence(memory_order_acq_rel);
while(shm0_mg->used != shm0_mg->avails) {
d0_pa = (metal_phys_addr_t)shm0_addr_array[shm0_mg->used];
d0 = metal_io_phys_to_virt(ch->shm_io, d0_pa);
if (!d0) {
LPRINTF("ERROR: failed to get rx addr:0x%lx.\n",
d0_pa);
goto out;
}
/* Copy msg header from shared buf to local mem */
len = metal_io_block_read(ch->shm_io,
metal_io_virt_to_offset(ch->shm_io, d0),
lbuf, sizeof(struct msg_hdr_s));
if (len < (int)sizeof(struct msg_hdr_s)) {
LPRINTF("ERROR: failed to get msg header.\n");
goto out;
}
msg_hdr = lbuf;
if (msg_hdr->len < 0) {
LPRINTF("ERROR: wrong msg length: %d.\n",
(int)msg_hdr->len);
goto out;
} else {
/* copy msg data from shared buf to local mem */
d0 += sizeof(struct msg_hdr_s);
len = metal_io_block_read(ch->shm_io,
metal_io_virt_to_offset(ch->shm_io, d0),
lbuf + sizeof(struct msg_hdr_s),
msg_hdr->len);
#if DEBUG
LPRINTF("received: %d, %d\n",
(int)msg_hdr->index, (int)msg_hdr->len);
#endif
/* Check if the it is the shutdown message */
if (!strncmp((lbuf + sizeof(struct msg_hdr_s)),
SHUTDOWN, sizeof(SHUTDOWN))) {
LPRINTF("Received shutdown message\n");
goto out;
}
}
/* Copy the message back to the other end */
metal_io_block_write(ch->shm_io,
metal_io_virt_to_offset(ch->shm_io, d1),
lbuf,
sizeof(struct msg_hdr_s) + msg_hdr->len);
/* Update the d1 address */
shm1_addr_array[shm1_mg->avails] =
(uint64_t)metal_io_virt_to_phys(
ch->shm_io, d1);
d1 += (sizeof(struct msg_hdr_s) + msg_hdr->len);
shm0_mg->used++;
shm1_mg->avails++;
/* memory barrier */
atomic_thread_fence(memory_order_acq_rel);
/* Send the message */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
ch->ipi_mask);
}
}
out:
free(lbuf);
return 0;
}
int main(void)
{
struct metal_device *device;
struct metal_io_region *io;
int irq;
uint32_t val;
int ret = 0;
ret = sys_init();
if (ret) {
LPRINTF("ERROR: Failed to initialize system\n");
return -1;
}
memset(&ch0, 0, sizeof(ch0));
atomic_store(&ch0.notified, 1);
/* Open IPI device */
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &device);
if (ret) {
LPRINTF("ERROR: Failed to open device %s.\n", IPI_DEV_NAME);
goto out;
}
/* Map IPI device IO region */
io = metal_device_io_region(device, 0);
if (!io) {
LPRINTF("ERROR: Failed to map io regio for %s.\n",
device->name);
metal_device_close(device);
ret = -ENODEV;
goto out;
}
/* Store the IPI device and I/O region */
ch0.ipi_dev = device;
ch0.ipi_io = io;
/* Open shared memory0 descriptor device */
ret = metal_device_open(BUS_NAME, SHM0_DESC_DEV_NAME, &device);
if (ret) {
LPRINTF("ERROR: Failed to open device %s.\n",
SHM0_DESC_DEV_NAME);
goto out;
}
/* Map shared memory0 descriptor device IO region */
io = metal_device_io_region(device, 0);
if (!io) {
LPRINTF("ERROR: Failed to map io regio for %s.\n",
device->name);
metal_device_close(device);
ret = -ENODEV;
goto out;
}
/* Store the shared memory0 descriptor device and I/O region */
ch0.shm0_desc_dev = device;
ch0.shm0_desc_io = io;
/* Open shared memory1 descriptor device */
ret = metal_device_open(BUS_NAME, SHM1_DESC_DEV_NAME, &device);
if (ret) {
LPRINTF("ERROR: Failed to open device %s.\n",
SHM1_DESC_DEV_NAME);
goto out;
}
/* Map shared memory1 descriptor device IO region */
io = metal_device_io_region(device, 0);
if (!io) {
LPRINTF("ERROR: Failed to map io regio for %s.\n",
device->name);
metal_device_close(device);
ret = -ENODEV;
goto out;
}
/* Store the shared memory0 descriptor device and I/O region */
ch0.shm1_desc_dev = device;
ch0.shm1_desc_io = io;
/* Open shared memory device */
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &device);
if (ret) {
LPRINTF("ERROR: Failed to open device %s.\n", SHM_DEV_NAME);
goto out;
}
/* Map shared memory device IO region */
io = metal_device_io_region(device, 0);
if (!io) {
LPRINTF("ERROR: Failed to map io regio for %s.\n",
device->name);
metal_device_close(device);
ret = -ENODEV;
goto out;
}
/* Store the shared memory device and I/O region */
ch0.shm_dev = device;
ch0.shm_io = io;
ch0.d1_start_offset = D1_SHM_OFFSET;
/* Get interrupt ID from IPI metal device */
irq = (intptr_t)ch0.ipi_dev->irq_info;
if (irq < 0) {
LPRINTF("ERROR: Failed to request interrupt for %s.\n",
device->name);
ret = -EINVAL;
goto out;
}
ch0.ipi_mask = IPI_MASK;
LPRINTF("Try to register IPI interrupt.\n");
ret = metal_irq_register(irq, ipi_irq_isr, ch0.ipi_dev, &ch0);
LPRINTF("registered IPI interrupt.\n");
if (ret)
goto out;
/* Enable interrupt */
metal_io_write32(ch0.ipi_io, IPI_IER_OFFSET, ch0.ipi_mask);
val = metal_io_read32(ch0.ipi_io, IPI_IMR_OFFSET);
if (val & ch0.ipi_mask) {
LPRINTF("ERROR: Failed to enable IPI interrupt.\n");
ret = -1;
goto out;
}
LPRINTF("enabled IPI interrupt.\n");
ret = ipi_task_shm_atomicd((void *)&ch0);
if (ret) {
LPRINTF("ERROR: Failed to run shared memory atomic task.\n");
goto out;
}
ret = ipi_task_echod((void*)&ch0);
if (ret)
LPRINTF("ERROR: Failed to run IPI communication task.\n");
out:
if (ch0.ipi_dev)
metal_device_close(ch0.ipi_dev);
if (ch0.shm0_desc_dev)
metal_device_close(ch0.shm0_desc_dev);
if (ch0.shm1_desc_dev)
metal_device_close(ch0.shm1_desc_dev);
if (ch0.shm_dev)
metal_device_close(ch0.shm_dev);
sys_cleanup();
return ret;
}
@@ -0,0 +1,181 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* atomic_shmem_demod.c - Shared memory atomic operation demo
* This demo will:
*
* 1. Open the shared memory device.
* 2. Open the IPI device.
* 3. Register IPI interrupt handler.
* 4. Kick IPI to notify the other end to start the demo
* 5. Start atomic add by 1 for 5000 times over the shared memory
* 6. Wait for remote IPI kick to know when the remote has finished the demo.
* 7. Verify the result. As two sides both have done 5000 times of adding 1,
* check if the end result is 5000*2.
* 8. Clean up: deregister the IPI interrupt handler, close the IPI device
* , close the shared memory device.
*/
#include <unistd.h>
#include <stdio.h>
#include <metal/atomic.h>
#include <metal/cpu.h>
#include <metal/io.h>
#include <sys/types.h>
#include <metal/device.h>
#include <metal/irq.h>
#include <metal/errno.h>
#include <time.h>
#include "common.h"
#define ATOMIC_INT_OFFSET 0x0 /* shared memory offset for atomic operation */
#define ITERATIONS 5000
static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
1 - not-kicked */
static int ipi_irq_handler (int vect_id, void *priv)
{
(void)vect_id;
struct metal_io_region *ipi_io = (struct metal_io_region *)priv;
uint32_t ipi_mask = IPI_MASK;
uint64_t val = 1;
if (!ipi_io)
return METAL_IRQ_NOT_HANDLED;
val = metal_io_read32(ipi_io, IPI_ISR_OFFSET);
if (val & ipi_mask) {
metal_io_write32(ipi_io, IPI_ISR_OFFSET, ipi_mask);
atomic_flag_clear(&remote_nkicked);
return METAL_IRQ_HANDLED;
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief atomic_add_shmem() - Shared memory atomic operation demo
* This task will:
* * Write to shared memory to notify the remote to start atomic add on
* the shared memory for 1000 times.
* * Start atomic add by 1 for 5000 times to first 32 bits of memory in
* the shared memory which is pointed to by shm_io.
* * Wait for the remote to write to shared memory
* * Once it received the polling kick from the remote, it will check
* if the value stored in the shared memory is the same as the
* expected.
* * It will print if the atomic add test has passed or not.
* @param[in] ipi_io - IPI metal i/o region
* @param[in] shm_io - shared memory metal i/o region
* @return - If setup failed, return the corresponding error number. Otherwise
* return 0 on success.
*/
static int atomic_add_shmem(struct metal_io_region *ipi_io,
struct metal_io_region *shm_io)
{
int i, ret;
atomic_int *shm_int;
uint32_t ipi_mask = IPI_MASK;
LPRINTF("Starting atomic shared memory task.\n");
/* Initialize the shared memory on which we run the atomic add */
shm_int = (atomic_int *)metal_io_virt(shm_io,
ATOMIC_INT_OFFSET);
atomic_store(shm_int, 0);
/* Kick the remote to notify demo starts. */
metal_io_write32(ipi_io, IPI_TRIG_OFFSET, ipi_mask);
/* Do atomic add over the shared memory */
for (i = 0; i < ITERATIONS; i++) {
atomic_fetch_add(shm_int, 1);
}
/* Wait for kick from RPU to know when RPU finishes the demo */
wait_for_notified(&remote_nkicked);
if (atomic_load(shm_int) == (ITERATIONS << 1 )) {
LPRINTF("shm atomic demo PASSED!\n");
ret = 0;
} else {
LPRINTF("shm atomic demo FAILED. expected: %u, actual: %u\n",
(unsigned int)(ITERATIONS << 1), atomic_load(shm_int));
ret = -1;
}
return ret;
}
int atomic_shmem_demo()
{
struct metal_device *ipi_dev = NULL, *shm_dev = NULL;
struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
int ipi_irq;
int ret = 0;
print_demo("atomic operation over shared memory");
/* Open shared memory device */
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &shm_dev);
if (ret) {
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
goto out;
}
/* Get shared memory device IO region */
shm_io = metal_device_io_region(shm_dev, 0);
if (!shm_io) {
LPERROR("Failed to map io region for %s.\n", shm_dev->name);
ret = -ENODEV;
goto out;
}
/* Open IPI device */
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &ipi_dev);
if (ret) {
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
goto out;
}
/* Get IPI device IO region */
ipi_io = metal_device_io_region(ipi_dev, 0);
if (!ipi_io) {
LPERROR("Failed to map io region for %s.\n", ipi_dev->name);
ret = -ENODEV;
goto out;
}
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ipi_dev->irq_info;
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ipi_io, IPI_ISR_OFFSET, IPI_MASK);
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ipi_dev, ipi_io);
/* initialize remote_nkicked */
atomic_init(&remote_nkicked, 1);
/* Enable IPI interrupt */
metal_io_write32(ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = atomic_add_shmem(ipi_io, shm_io);
/* disable IPI interrupt */
metal_io_write32(ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler by setting the handler to 0 */
metal_irq_unregister(ipi_irq, 0, ipi_dev, ipi_io);
out:
if (shm_dev)
metal_device_close(shm_dev);
if (ipi_dev)
metal_device_close(ipi_dev);
return ret;
}
@@ -0,0 +1,196 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* shmem_demo.c
* This demo demonstrates the use of shared mem. between the APU and RPU.
* This demo does so via the following steps:
*
* 1. Open the shared memory device.
* 2. Clear the demo control TX/RX available values in shared memory.
* 3. APU set demo control in shared memory to notify RPU demo has started
* 4. APU will write message to the shared memory.
* 5. APU will increase TX avail values in the shared memory to notify RPU
* there is a message ready to read.
* 6. APU will poll the RX avail value in th shared memory to see if RPU
* has echoed back the message into the shared memory.
* 7. When APU knows there is new RX message available, it will read the
* RX message from the shared memory.
* 8. APU will verify the message to see if it matches the one it has sent.
* 9. Close the shared memory device.
*
* Here is the Shared memory structure of this demo:
* |0 | 4Bytes | DEMO control status shows if demo starts or not |
* |0x04 | 4Bytes | number of APU to RPU buffers available to RPU |
* |0x08 | 4Bytes | number of APU to RPU buffers consumed by RPU |
* |0x0c | 4Bytes | number of RPU to APU buffers available to APU |
* |0x10 | 4Bytes | number of RPU to APU buffers consumed by APU |
* |0x14 | 1KBytes | APU to RPU buffer |
* ... ...
* |0x800 | 1KBytes | RPU to APU buffer |
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <metal/errno.h>
#include <string.h>
#include <metal/io.h>
#include <metal/alloc.h>
#include <metal/device.h>
#include "common.h"
/* Shared memory offsets */
#define SHM_DEMO_CNTRL_OFFSET 0x0
#define SHM_TX_AVAIL_OFFSET 0x04
#define SHM_RX_AVAIL_OFFSET 0x0C
#define SHM_TX_BUFFER_OFFSET 0x14
#define SHM_RX_BUFFER_OFFSET 0x800
#define SHM_BUFFER_SIZE 0x400
#define DEMO_STATUS_IDLE 0x0
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
#define TEST_MSG "Hello World - libmetal shared memory demo"
struct msg_hdr_s {
uint32_t index;
uint32_t len;
};
/**
* @brief shmem_task() - Show use of shared memory with Libmetal.
* Write message to RPU. RPU will then read and echo
* back. Confirm if echoed message is identical.
* If messages differ, report error.
*
* Steps:
* 1. Clear demo control and TX/RX avaiable values
*
* @param[in] shm_io - metal i/o region of the shared memory
* @return - return 0 on success, otherwise return error number indicating
* type of error
*/
int shmem_echo(struct metal_io_region *shm_io)
{
void *tx_data = NULL;
void *rx_data = NULL;
unsigned int tx_count = 0;
unsigned int rx_count = 0;
struct msg_hdr_s *msg_hdr;
unsigned int data_len;
int ret;
LPRINTF("Setting up shared memory demo.\n");
/* clear demo status value */
metal_io_write32(shm_io, SHM_DEMO_CNTRL_OFFSET, 0);
/* Clear TX/RX avail */
metal_io_write32(shm_io, SHM_TX_AVAIL_OFFSET, 0);
metal_io_write32(shm_io, SHM_RX_AVAIL_OFFSET, 0);
LPRINTF("Starting shared memory demo.\n");
/* Notify the remote the demo starts */
metal_io_write32(shm_io, SHM_DEMO_CNTRL_OFFSET, DEMO_STATUS_START);
/* preparing data to send */
data_len = sizeof(struct msg_hdr_s) + strlen(TEST_MSG) + 1;
tx_data = metal_allocate_memory(data_len);
if (!tx_data) {
LPERROR("Failed to allocate memory.\n");
ret = -1;
goto out;
}
msg_hdr = (struct msg_hdr_s *)tx_data;
msg_hdr->index = tx_count;
msg_hdr->len = strlen(TEST_MSG) + 1;
sprintf(tx_data + sizeof(*msg_hdr), TEST_MSG);
LPRINTF("Sending message: %s\n",
(char *)(tx_data + sizeof(*msg_hdr)));
/* write data to the shared memory*/
ret = metal_io_block_write(shm_io, SHM_TX_BUFFER_OFFSET,
tx_data, data_len);
if (ret < 0){
LPERROR("Unable to metal_io_block_write()\n");
goto out;
}
/* Increase number of buffers available to notify the remote */
tx_count++;
metal_io_write32(shm_io, SHM_TX_AVAIL_OFFSET, tx_count);
/* wait for remote to echo back the data */
while (metal_io_read32(shm_io, SHM_RX_AVAIL_OFFSET) == rx_count);
rx_count++;
/* New RX data is available, allocate buffer to received data */
rx_data = metal_allocate_memory(data_len);
if (!rx_data) {
LPERROR("Failed to allocate memory\n");
ret = -1;
goto out;
}
/* read data from the shared memory*/
metal_io_block_read(shm_io, SHM_RX_BUFFER_OFFSET,
rx_data, data_len);
if (ret < 0){
LPERROR("Unable to metal_io_block_read()\n");
goto out;
}
/* verify the received data */
ret = memcmp(tx_data, rx_data, data_len);
if (ret) {
LPERROR("Received data verification failed.\n");
LPRINTF("Expected:");
dump_buffer(tx_data, data_len);
LPRINTF("Actual:");
dump_buffer(rx_data, data_len);
} else {
LPRINTF("Message Received: %s\n",
(char *)(rx_data + sizeof(*msg_hdr)));
}
/* Notify the remote the demo has finished. */
metal_io_write32(shm_io, SHM_DEMO_CNTRL_OFFSET, DEMO_STATUS_IDLE);
out:
if (tx_data)
metal_free_memory(tx_data);
if (rx_data)
metal_free_memory(rx_data);
LPRINTF("Shared memory demo: %s.\n", ret ? "Failed": "Passed" );
return ret;
}
int shmem_demo()
{
struct metal_device *device = NULL;
struct metal_io_region *io = NULL;
int ret = 0;
print_demo("shared memory");
/* Open the shared memory device */
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &device);
if (ret) {
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
return ret;
}
/* get shared memory device IO region */
io = metal_device_io_region(device, 0);
if (!io) {
LPERROR("Failed to get io region for %s.\n", device->name);
ret = -ENODEV;
goto out;
}
/* Run the demo */
ret = shmem_echo(io);
out:
if (device)
metal_device_close(device);
return ret;
}
@@ -0,0 +1,353 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* shmem_latency_demo.c
* This demo demonstrates the shared mem. latency between the APU and RPU.
* This demo does so via the following steps:
*
* 1. Get the shared memory device I/O region.
* 1. Get the TTC timer device I/O region.
* 2. Get the IPI device I/O region.
* 3. Register IPI interrupt handler.
* 4. Write to shared memory to indicate demo starts
* 5. Reset the APU to RPU TTC counter, write data to the shared memory, then
* kick IPI to notify the remote.
* 6. When it receives IPI interrupt, the IPI interrupt handler marks the
* remote has kicked.
* 7. Accumulate APU to RPU and RPU to APU counter values.
* 8. Repeat step 5, 6 and 7 for 1000 times
* 9. Write shared memory to indicate RPU about demo finishes and kick
* IPI to notify.
* 10. Clean up: disable IPI interrupt, deregister the IPI interrupt handler.
*/
#include <unistd.h>
#include <metal/errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <metal/atomic.h>
#include <metal/cpu.h>
#include <metal/alloc.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/irq.h>
#include "common.h"
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
#define TTC_CLK_FREQ_HZ 100000000
#define NS_PER_SEC 1000000000
/* Shared memory offset */
#define SHM_DEMO_CNTRL_OFFSET 0x0 /* Shared memory for the demo status */
#define SHM_BUFF_OFFSET_TX 0x1000 /* Shared memory TX buffer start offset */
#define SHM_BUFF_OFFSET_RX 0x2000 /* Shared memory RX buffer start offset */
#define DEMO_STATUS_IDLE 0x0
#define DEMO_STATUS_START 0x1 /* Status value to indicate demo start */
#define ITERATIONS 1000
#define BUF_SIZE_MAX 4096
#define PKG_SIZE_MIN 16
#define PKG_SIZE_MAX 1024
struct channel_s {
struct metal_device *ipi_dev; /* IPI metal device */
struct metal_io_region *ipi_io; /* IPI metal i/o region */
struct metal_device *shm_dev; /* Shared memory metal device */
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
struct metal_device *ttc_dev; /* TTC metal device */
struct metal_io_region *ttc_io; /* TTC metal i/o region */
uint32_t ipi_mask; /* RPU IPI mask */
atomic_int remote_nkicked; /* 0 - kicked from remote */
};
struct msg_hdr_s {
uint32_t index;
uint32_t len;
};
/**
* @brief read_timer() - return TTC counter value
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter ID
*/
static inline uint32_t read_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
unsigned long offset = XTTCPS_CNT_VAL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
return metal_io_read32(ttc_io, offset);
}
/**
* @brief reset_timer() - function to reset TTC counter
* Set the RST bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void reset_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_RST_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief stop_timer() - function to stop TTC counter
* Set the disable bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void stop_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_DIS_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief ipi_irq_handler() - IPI interrupt handler
* It will clear the notified flag to mark it's got an IPI interrupt.
* It will stop the RPU->APU timer and will clear the notified
* flag to mark it's got an IPI interrupt
*
* @param[in] vect_id - IPI interrupt vector ID
* @param[in/out] priv - communication channel data for this application.
*
* @return - If the IPI interrupt is triggered by its remote, it returns
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
* not the interrupt it expected.
*
*/
static int ipi_irq_handler (int vect_id, void *priv)
{
struct channel_s *ch = (struct channel_s *)priv;
uint32_t val;
(void)vect_id;
if (ch) {
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
if (val & ch->ipi_mask) {
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
ch->ipi_mask);
atomic_flag_clear(&ch->remote_nkicked);
return METAL_IRQ_HANDLED;
}
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief measure_shmem_latency() - Measure latency of using shared memory
* and IPI with libmetal.
* Repeatedly send a message to RPU and then detect IPI from RPU
* and measure the latency. Similarly, measure the latency from RPU
* to APU. Each iteration, record this latency and after the loop
* has finished, report the total latency in nanseconds.
* Notes:
* - RPU will repeatedly wait for IPI from APU until APU
* notifies remote demo has finished by setting the value in the
* shared memory.
*
* @param[in] ch - channel information, which contains the IPI i/o region,
* shared memory i/o region and the ttc timer i/o region.
* @return - 0 on success, error code if failure.
*/
static int measure_shmem_latency(struct channel_s *ch)
{
uint32_t apu_to_rpu_sum = 0, rpu_to_apu_sum = 0;
int i;
size_t s;
struct msg_hdr_s *msg_hdr;
void *lbuf;
int ret;
LPRINTF("Starting IPI latency task\n");
/* allocate memory for receiving data */
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
if (!lbuf) {
LPERROR("Failed to allocate memory.\r\n");
return -1;
}
memset(lbuf, 0xA, BUF_SIZE_MAX);
/* write to shared memory to indicate demo has started */
metal_io_write32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET, DEMO_STATUS_START);
for (s = PKG_SIZE_MIN; s <= PKG_SIZE_MAX; s <<= 1) {
for (i = 1; i <= ITERATIONS; i++) {
/* Reset TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
/* prepare data */
msg_hdr = lbuf;
msg_hdr->index = i;
msg_hdr->len = s - sizeof(*msg_hdr);
/* Copy data to the shared memory */
ret = metal_io_block_write(ch->shm_io,
SHM_BUFF_OFFSET_TX, lbuf, s);
if ((size_t)ret != s) {
LPERROR("Write shm failure: %lu,%lu\n",
s, (size_t)ret);
ret = -1;
goto out;
}
/* Kick IPI to notify the remote */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
ch->ipi_mask);
/* irq handler stops timer for rpu->apu irq */
wait_for_notified(&ch->remote_nkicked);
/* Read message */
metal_io_block_read(ch->shm_io,
SHM_BUFF_OFFSET_RX,
lbuf, s);
msg_hdr = lbuf;
if (msg_hdr->len != (s - sizeof(*msg_hdr))) {
LPERROR("Read shm failure: %lu,%lu\n",
s, msg_hdr->len + sizeof(*msg_hdr));
ret = -1;
goto out;
}
/* Stop RPU to APU TTC counter */
stop_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
apu_to_rpu_sum += read_timer(ch->ttc_io,
TTC_CNT_APU_TO_RPU);
rpu_to_apu_sum += read_timer(ch->ttc_io,
TTC_CNT_RPU_TO_APU);
}
/* report avg latencies */
LPRINTF("package size %lu latency result:\n", s);
LPRINTF("APU to RPU average latency: %u ns \n",
apu_to_rpu_sum / ITERATIONS * NS_PER_SEC / TTC_CLK_FREQ_HZ );
LPRINTF("RPU to APU average latency: %u ns \n",
rpu_to_apu_sum / ITERATIONS * NS_PER_SEC / TTC_CLK_FREQ_HZ );
}
/* write to shared memory to indicate demo has finished */
metal_io_write32(ch->shm_io, SHM_DEMO_CNTRL_OFFSET, 0);
/* Kick IPI to notify the remote */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
LPRINTF("Finished shared memory latency task\n");
out:
metal_free_memory(lbuf);
return 0;
}
int shmem_latency_demo()
{
struct metal_device *dev;
struct metal_io_region *io;
struct channel_s ch;
int ipi_irq;
int ret = 0;
print_demo("shared memory latency");
memset(&ch, 0, sizeof(ch));
/* Open shared memory device */
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &dev);
if (ret) {
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
goto out;
}
/* Get shared memory device IO region */
io = metal_device_io_region(dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", dev->name);
ret = -ENODEV;
goto out;
}
ch.shm_dev = dev;
ch.shm_io = io;
/* Open TTC device */
ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &dev);
if (ret) {
LPERROR("Failed to open device %s.\n", TTC_DEV_NAME);
goto out;
}
/* Get TTC IO region */
io = metal_device_io_region(dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", dev->name);
ret = -ENODEV;
goto out;
}
ch.ttc_dev = dev;
ch.ttc_io = io;
/* Open IPI device */
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &dev);
if (ret) {
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
goto out;
}
/* Get IPI device IO region */
io = metal_device_io_region(dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", dev->name);
ret = -ENODEV;
goto out;
}
ch.ipi_dev = dev;
ch.ipi_io = io;
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ch.ipi_dev->irq_info;
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
/* initialize remote_nkicked */
atomic_init(&ch.remote_nkicked, 1);
ch.ipi_mask = IPI_MASK;
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ch.ipi_dev, &ch);
/* Enable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = measure_shmem_latency(&ch);
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler by setting the handler to 0 */
metal_irq_unregister(ipi_irq, 0, ch.ipi_dev, &ch);
out:
if (ch.ttc_dev)
metal_device_close(ch.ttc_dev);
if (ch.shm_dev)
metal_device_close(ch.shm_dev);
if (ch.ipi_dev)
metal_device_close(ch.ipi_dev);
return ret;
}
@@ -0,0 +1,469 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*****************************************************************************
* shmem_throughput_demo_task.c
* This is the remote side of the shared memory throughput demo.
* This demo does the following steps:
*
* 1. Get the shared memory device libmetal I/O region.
* 1. Get the TTC timer device libemtal I/O region.
* 2. Get IPI device libmetal I/O region and the IPI interrupt vector.
* 3. Register IPI interrupt handler.
* 6. Upload throughput measurement:
* Start TTC APU counter, write data to shared memory and kick IPI to
* notify remote. It will iterate for 1000 times, stop TTC APU counter.
* Wait for RPU IPI kick to know RPU has finished receiving packages
* and RPU TX counter is ready to read. Read the APU TX and RPU RX
* counter values and save them. Repeat for different package sizes.
* After this measurement, kick IPI to notify the remote, the
* measurement has finished.
* 7. Download throughput measurement:
* Start TTC APU counter, wait for IPI kick, check if data is available,
* if yes, read as much data as possible from shared memory. It will
* iterates untill 1000 packages have been received, stop TTC APU counter.
* Wait for RPU IPI kick so that APU can get the TTC RPU TX counter
* value. Kick IPI to notify the remote it has read the TTCi counter.
* Repeat for different package size.
* 8. Cleanup resource:
* disable IPI interrupt and deregister the IPI interrupt handler.
*
* Here is the Shared memory structure of this demo:
* |0x0 - 0x03 | number of APU to RPU buffers available to RPU |
* |0x04 - 0x1FFFFF | address array for shared buffers from APU to RPU |
* |0x200000 - 0x200004 | number of RPU to APU buffers available to APU |
* |0x200004 - 0x3FFFFF | address array for shared buffers from RPU to APU |
* |0x400000 - 0x7FFFFF | APU to RPU buffers |
* |0x800000 - 0xAFFFFF | RPU to APU buffers |
*/
#include <unistd.h>
#include <error.h>
#include <metal/atomic.h>
#include <metal/io.h>
#include <metal/device.h>
#include <metal/alloc.h>
#include <metal/irq.h>
#include "common.h"
#define TTC_CNT_APU_TO_RPU 2 /* APU to RPU TTC counter ID */
#define TTC_CNT_RPU_TO_APU 3 /* RPU to APU TTC counter ID */
#define TTC_CLK_FREQ_HZ 100000000
#define NS_PER_SEC 1000000000
/* Shared memory offsets */
#define SHM_DESC_OFFSET_TX 0x0
#define SHM_BUFF_OFFSET_TX 0x400000
#define SHM_DESC_OFFSET_RX 0x200000
#define SHM_BUFF_OFFSET_RX 0x800000
/* Shared memory descriptors offset */
#define SHM_DESC_AVAIL_OFFSET 0x00
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x04
#define ITERATIONS 1000
#define BUF_SIZE_MAX 4096
#define PKG_SIZE_MAX 1024
#define PKG_SIZE_MIN 16
#define TOTAL_DATA_SIZE (1024 * 4096)
#define MB (1024 * 1024) /* Mega Bytes */
struct channel_s {
struct metal_device *ipi_dev; /* IPI metal device */
struct metal_io_region *ipi_io; /* IPI metal i/o region */
struct metal_device *shm_dev; /* Shared memory metal device */
struct metal_io_region *shm_io; /* Shared memory metal i/o region */
struct metal_device *ttc_dev; /* TTC metal device */
struct metal_io_region *ttc_io; /* TTC metal i/o region */
uint32_t ipi_mask; /* RPU IPI mask */
atomic_int remote_nkicked; /* 0 - kicked from remote */
};
/**
* @brief read_timer() - return TTC counter value
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter ID
*/
static inline uint32_t read_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
unsigned long offset = XTTCPS_CNT_VAL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
return metal_io_read32(ttc_io, offset);
}
/**
* @brief reset_timer() - function to reset TTC counter
* Set the RST bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void reset_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_RST_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief stop_timer() - function to stop TTC counter
* Set the disable bit in the Count Control Reg.
*
* @param[in] ttc_io - TTC timer i/o region
* @param[in] cnt_id - counter id
*/
static inline void stop_timer(struct metal_io_region *ttc_io,
unsigned long cnt_id)
{
uint32_t val;
unsigned long offset = XTTCPS_CNT_CNTRL_OFFSET +
XTTCPS_CNT_OFFSET(cnt_id);
val = XTTCPS_CNT_CNTRL_DIS_MASK;
metal_io_write32(ttc_io, offset, val);
}
/**
* @brief ipi_irq_handler() - IPI interrupt handler
* It will clear the notified flag to mark it's got an IPI interrupt.
* It will stop the RPU->APU timer and will clear the notified
* flag to mark it's got an IPI interrupt
*
* @param[in] vect_id - IPI interrupt vector ID
* @param[in/out] priv - communication channel data for this application.
*
* @return - If the IPI interrupt is triggered by its remote, it returns
* METAL_IRQ_HANDLED. It returns METAL_IRQ_NOT_HANDLED, if it is
* not the interrupt it expected.
*
*/
static int ipi_irq_handler (int vect_id, void *priv)
{
struct channel_s *ch = (struct channel_s *)priv;
uint32_t val;
(void)vect_id;
if (ch) {
val = metal_io_read32(ch->ipi_io, IPI_ISR_OFFSET);
if (val & ch->ipi_mask) {
metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET,
ch->ipi_mask);
atomic_flag_clear(&ch->remote_nkicked);
return METAL_IRQ_HANDLED;
}
}
return METAL_IRQ_NOT_HANDLED;
}
/**
* @brief measure_shmem_throughput() - Show throughput of using shared memory.
* - Upload throughput measurement:
* Start TTC APU counter, write data to shared memory and kick IPI to
* notify remote. It will iterate for 1000 times, stop TTC APU
* counter. Wait for RPU IPI kick to know RPU has finished receiving
* packages and RPU TX counter is ready to read. Read the APU TX and
* RPU RX counter values and save them. Repeat for different package
* sizes. After this measurement, kick IPI to notify the remote, the
* measurement has finished.
* - Download throughput measurement:
* Start TTC APU counter, wait for IPI kick, check if data is
* available, if yes, read as much data as possible from shared
* memory. It will iterates untill 1000 packages have been received,
* stop TTC APU counter. Wait for RPU IPI kick so that APU can get
* the TTC RPU TX counter value. Kick IPI to notify the remote it
* has read the TTCi counter. Repeat for different package size.
*
* @param[in] ch - channel information, which contains the IPI i/o region,
* shared memory i/o region and the ttc timer i/o region.
* @return - 0 on success, error code if failure.
*/
static int measure_shmem_throughput(struct channel_s* ch)
{
void *lbuf = NULL;
int ret = 0;
size_t s, i;
uint32_t rx_count, rx_avail, tx_count, iterations;
unsigned long tx_avail_offset, rx_avail_offset;
unsigned long tx_addr_offset, rx_addr_offset;
unsigned long tx_data_offset, rx_data_offset;
uint32_t buf_phy_addr_32;
uint32_t *apu_tx_count = NULL;
uint32_t *apu_rx_count = NULL;
uint32_t *rpu_tx_count = NULL;
uint32_t *rpu_rx_count = NULL;
/* allocate memory for receiving data */
lbuf = metal_allocate_memory(BUF_SIZE_MAX);
if (!lbuf) {
LPERROR("Failed to allocate memory.\r\n");
return -ENOMEM;
}
memset(lbuf, 0xA, BUF_SIZE_MAX);
/* allocate memory for saving counter values */
for (s = PKG_SIZE_MIN, i = 0; s <= PKG_SIZE_MAX; s <<=1, i++);
apu_tx_count = metal_allocate_memory(i * sizeof(uint32_t));
apu_rx_count = metal_allocate_memory(i * sizeof(uint32_t));
rpu_tx_count = metal_allocate_memory(i * sizeof(uint32_t));
rpu_rx_count = metal_allocate_memory(i * sizeof(uint32_t));
if (!apu_tx_count || !apu_rx_count || !rpu_tx_count || !rpu_rx_count) {
LPERROR("Failed to allocate memory.\r\n");
ret = -ENOMEM;
goto out;
}
/* Clear shared memory */
metal_io_block_set(ch->shm_io, 0, 0, metal_io_region_size(ch->shm_io));
LPRINTF("Starting shared mem throughput demo\n");
/* for each data size, measure send throughput */
for (s = PKG_SIZE_MIN, i = 0; s <= PKG_SIZE_MAX; s <<= 1, i++) {
tx_count = 0;
iterations = TOTAL_DATA_SIZE / s;
/* Set tx buffer address offset */
tx_avail_offset = SHM_DESC_OFFSET_TX + SHM_DESC_AVAIL_OFFSET;
tx_addr_offset = SHM_DESC_OFFSET_TX +
SHM_DESC_ADDR_ARRAY_OFFSET;
tx_data_offset = SHM_DESC_OFFSET_TX + SHM_BUFF_OFFSET_TX;
/* Reset APU TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
while (tx_count < iterations) {
/* Write data to the shared memory*/
metal_io_block_write(ch->shm_io, tx_data_offset,
lbuf, s);
/* Write to the address array to tell the other end
* the buffer address.
*/
buf_phy_addr_32 = (uint32_t)metal_io_phys(ch->shm_io,
tx_data_offset);
metal_io_write32(ch->shm_io, tx_addr_offset,
buf_phy_addr_32);
tx_data_offset += s;
tx_addr_offset += sizeof(buf_phy_addr_32);
/* Increase number of available buffers */
tx_count++;
metal_io_write32(ch->shm_io, tx_avail_offset,
tx_count);
/* Kick IPI to notify RPU data is ready in
* the shared memory */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET,
ch->ipi_mask);
}
/* Stop RPU TTC counter */
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
/* Wait for RPU to signal RPU RX TTC counter is ready to
* read */
wait_for_notified(&ch->remote_nkicked);
/* Read TTC counter values */
apu_tx_count[i] = read_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
rpu_rx_count[i] = read_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
}
/* Kick IPI to notify RPU that APU has read the RPU RX TTC counter
* value */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
/* for each data size, meaasure block read throughput */
for (s = PKG_SIZE_MIN, i = 0; s <= PKG_SIZE_MAX; s <<= 1, i++) {
rx_count = 0;
iterations = TOTAL_DATA_SIZE / s;
/* Set rx buffer address offset */
rx_avail_offset = SHM_DESC_OFFSET_RX + SHM_DESC_AVAIL_OFFSET;
rx_addr_offset = SHM_DESC_OFFSET_RX +
SHM_DESC_ADDR_ARRAY_OFFSET;
rx_data_offset = SHM_DESC_OFFSET_RX + SHM_BUFF_OFFSET_RX;
wait_for_notified(&ch->remote_nkicked);
/* Data has arrived, seasure start. Reset RPU TTC counter */
reset_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
while (1) {
rx_avail = metal_io_read32(ch->shm_io, rx_avail_offset);
while(rx_count != rx_avail) {
/* Get the buffer location from the shared
* memory rx address array.
*/
buf_phy_addr_32 = metal_io_read32(ch->shm_io,
rx_addr_offset);
rx_data_offset = metal_io_phys_to_offset(
ch->shm_io,
(metal_phys_addr_t)buf_phy_addr_32);
if (rx_data_offset == METAL_BAD_OFFSET) {
LPERROR(
"[%u]failed to get rx offset: 0x%x, 0x%lx.\n",
rx_count, buf_phy_addr_32,
metal_io_phys(ch->shm_io,
rx_addr_offset));
ret = -EINVAL;
goto out;
}
rx_addr_offset += sizeof(buf_phy_addr_32);
/* Read data from shared memory */
metal_io_block_read(ch->shm_io, rx_data_offset,
lbuf, s);
rx_count++;
}
if (rx_count < iterations)
/* Need to wait for more data */
wait_for_notified(&ch->remote_nkicked);
else
break;
}
/* Stop RPU TTC counter */
stop_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
/* Clear remote kicked flag -- 0 is kicked */
atomic_init(&ch->remote_nkicked, 1);
/* Kick IPI to notify remote it is ready to read data */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
/* Wait for RPU to signal RPU TX TTC counter is ready to
* read */
wait_for_notified(&ch->remote_nkicked);
/* Read TTC counter values */
apu_rx_count[i] = read_timer(ch->ttc_io, TTC_CNT_APU_TO_RPU);
rpu_tx_count[i] = read_timer(ch->ttc_io, TTC_CNT_RPU_TO_APU);
/* Kick IPI to notify RPU APU has read the RPU TX TTC counter
* value */
metal_io_write32(ch->ipi_io, IPI_TRIG_OFFSET, ch->ipi_mask);
}
/* Print the measurement result */
for (s = PKG_SIZE_MIN, i = 0; s <= PKG_SIZE_MAX; s <<= 1, i++) {
LPRINTF("Shared memory throughput of pkg size %lu : \n", s);
LPRINTF(" APU send: %x, %lu MB/s\n", apu_tx_count[i],
s * iterations * TTC_CLK_FREQ_HZ / apu_tx_count[i] / MB);
LPRINTF(" APU receive: %x, %lu MB/s\n", apu_rx_count[i],
s * iterations * TTC_CLK_FREQ_HZ / apu_rx_count[i] / MB);
LPRINTF(" RPU send: %x, %lu MB/s\n", rpu_tx_count[i],
s * iterations * TTC_CLK_FREQ_HZ / rpu_tx_count[i] / MB);
LPRINTF(" RPU receive: %x, %lu MB/s\n", rpu_rx_count[i],
s * iterations * TTC_CLK_FREQ_HZ / rpu_rx_count[i] / MB);
}
LPRINTF("Finished shared memory throughput\n");
out:
if (lbuf)
metal_free_memory(lbuf);
if (apu_tx_count)
metal_free_memory(apu_tx_count);
if (apu_rx_count)
metal_free_memory(apu_rx_count);
if (rpu_tx_count)
metal_free_memory(rpu_tx_count);
if (rpu_rx_count)
metal_free_memory(rpu_rx_count);
return ret;
}
int shmem_throughput_demo()
{
struct metal_device *dev;
struct metal_io_region *io;
struct channel_s ch;
int ipi_irq;
int ret = 0;
print_demo("shared memory throughput");
memset(&ch, 0, sizeof(ch));
/* Open shared memory device */
ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &dev);
if (ret) {
LPERROR("Failed to open device %s.\n", SHM_DEV_NAME);
goto out;
}
/* Get shared memory device IO region */
io = metal_device_io_region(dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", dev->name);
ret = -ENODEV;
goto out;
}
ch.shm_dev = dev;
ch.shm_io = io;
/* Open TTC device */
ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &dev);
if (ret) {
LPERROR("Failed to open device %s.\n", TTC_DEV_NAME);
goto out;
}
/* Get TTC IO region */
io = metal_device_io_region(dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", dev->name);
ret = -ENODEV;
goto out;
}
ch.ttc_dev = dev;
ch.ttc_io = io;
/* Open IPI device */
ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &dev);
if (ret) {
LPERROR("Failed to open device %s.\n", IPI_DEV_NAME);
goto out;
}
/* Get IPI device IO region */
io = metal_device_io_region(dev, 0);
if (!io) {
LPERROR("Failed to map io region for %s.\n", dev->name);
ret = -ENODEV;
goto out;
}
ch.ipi_dev = dev;
ch.ipi_io = io;
/* Get the IPI IRQ from the opened IPI device */
ipi_irq = (intptr_t)ch.ipi_dev->irq_info;
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* clear old IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_ISR_OFFSET, IPI_MASK);
/* initialize remote_nkicked */
atomic_init(&ch.remote_nkicked, 1);
ch.ipi_mask = IPI_MASK;
/* Register IPI irq handler */
metal_irq_register(ipi_irq, ipi_irq_handler, ch.ipi_dev, &ch);
/* Enable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IER_OFFSET, IPI_MASK);
/* Run atomic operation demo */
ret = measure_shmem_throughput(&ch);
/* disable IPI interrupt */
metal_io_write32(ch.ipi_io, IPI_IDR_OFFSET, IPI_MASK);
/* unregister IPI irq handler by setting the handler to 0 */
metal_irq_unregister(ipi_irq, 0, ch.ipi_dev, &ch);
out:
if (ch.ttc_dev)
metal_device_close(ch.ttc_dev);
if (ch.shm_dev)
metal_device_close(ch.shm_dev);
if (ch.ipi_dev)
metal_device_close(ch.ipi_dev);
return ret;
}
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <unistd.h>
#include <metal/sys.h>
#include "common.h"
int sys_init()
{
struct metal_init_params init_param = METAL_INIT_DEFAULTS;
int ret;
ret = metal_init(&init_param);
if (ret)
LPERROR("Failed to initialize libmetal\n");
return ret;
}
void sys_cleanup()
{
metal_finish();
}
void wait_for_interrupt(void) {
return;
}
@@ -0,0 +1,16 @@
/******************************************************************************
*
* Copyright (C) 2017 Xilinx, Inc. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
******************************************************************************/
#ifndef __SYS_INIT_H__
#define __SYS_INIT_H__
int sys_init();
void sys_cleanup();
#endif /* __SYS_INIT_H__ */