建立工程,成功创建两个虚拟串口
This commit is contained in:
@@ -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__ */
|
||||
Reference in New Issue
Block a user