Skip to content
1 change: 1 addition & 0 deletions examples/libmetal/demos/irq_shmem_demo/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
collect (PROJECT_INC_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/common")
add_subdirectory(${ROLE})
30 changes: 30 additions & 0 deletions examples/libmetal/demos/irq_shmem_demo/common/irq_shmem_demo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#ifndef __IRQ_SHMEM_DEMO_H__
#define __IRQ_SHMEM_DEMO_H__

#include <stdint.h>

#include <metal/io.h>

/*
* Common transport state shared by the host and remote implementations of the
* IRQ shared-memory demo. Platform- or OS-private wait state is kept behind
* machine_ctx to avoid duplicating this layout in each machine header.
*/
struct channel_s {
struct metal_io_region *host_to_remote_desc_io; /* host to remote descriptors */
struct metal_io_region *remote_to_host_desc_io; /* remote to host descriptors */
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 */
void *machine_ctx; /* Platform- or OS-private channel state */
uint32_t ipi_mask; /* RPU IPI mask */
int irq_vector_id; /* IRQ number. */
};

#endif /* __IRQ_SHMEM_DEMO_H__ */
8 changes: 6 additions & 2 deletions examples/libmetal/demos/irq_shmem_demo/host/irq_shmem_demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ static inline void dump_buffer(void *buf, unsigned int len)
*/
static int irq_shmem_echo(struct channel_s *ch)
{
struct channel_machine_ctx_s *machine = channel_machine_ctx(ch);
struct metal_io_region *desc_host_to_remote = ch->host_to_remote_desc_io;
struct metal_io_region *desc_remote_to_host = ch->remote_to_host_desc_io;
struct metal_io_region *payload_io = ch->shm_io;
Expand Down Expand Up @@ -241,7 +242,7 @@ static int irq_shmem_echo(struct channel_s *ch)

while (i != PKGS_TOTAL) {

wait_for_notified(&ch->remote_nkicked);
wait_for_notified(&machine->remote_nkicked);

rx_avail = metal_io_read32(desc_remote_to_host, rx_avail_offset);
while (i != rx_avail) {
Expand Down Expand Up @@ -376,7 +377,10 @@ static int irq_shmem_echo(struct channel_s *ch)

int main(void)
{
struct channel_s ch_s;
struct channel_machine_ctx_s ch_machine_s = {0};
struct channel_s ch_s = {
.machine_ctx = &ch_machine_s,
};
int ret = 0;

/* platform_init will set the OS agnostic channel information */
Expand Down
72 changes: 25 additions & 47 deletions examples/libmetal/demos/irq_shmem_demo/remote/irq_shmem_demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,19 @@
* Shared-memory partitioning details are documented in machine/remote/amd_rpu/
* README.md.
*/
#include "common.h"

#include <stdbool.h>

#include "common.h"

#define BUF_SIZE_MAX 512
#define SHUTDOWN "shutdown"

/* Shared memory offsets */
#define SHM0_DESC_OFFSET 0x0
#define SHM1_DESC_OFFSET SHM0_DESC_SIZE
#define SHM_PAYLOAD_OFFSET (SHM0_DESC_SIZE + SHM1_DESC_SIZE)

/* Shared memory descriptors offset */
#define SHM_DESC_AVAIL_OFFSET 0x00
#define SHM_DESC_USED_OFFSET 0x04
#define SHM_DESC_ADDR_ARRAY_OFFSET 0x08

/* Descriptor 0 (Host to Remote) resides at SHM0_DESC_OFFSET.
* Descriptor 1 (Remote to Host) resides at SHM1_DESC_OFFSET.
* The payload carveout begins at SHM_PAYLOAD_OFFSET and is split evenly
* between RX (lower half) and TX (upper half).
*
* Note that H_TO_R_ is host to remote and R_TO_H_ is vice versa.
*/
#define SHM_DESC_OFFSET_H_TO_R SHM0_DESC_OFFSET
#define SHM_DESC_OFFSET_R_TO_H SHM1_DESC_OFFSET
#define SHM_PAYLOAD_H_TO_R SHM_PAYLOAD_RX_OFFSET
#define SHM_PAYLOAD_R_TO_H SHM_PAYLOAD_TX_OFFSET

#define H_TO_R_DESC_ADDR_START \
(SHM_DESC_OFFSET_H_TO_R + SHM_DESC_ADDR_ARRAY_OFFSET)
#define H_TO_R_DESC_ADDR_END \
(SHM_DESC_OFFSET_H_TO_R + SHM0_DESC_SIZE)
#define R_TO_H_DESC_ADDR_START \
(SHM_DESC_OFFSET_R_TO_H + SHM_DESC_ADDR_ARRAY_OFFSET)
#define R_TO_H_DESC_ADDR_END \
(SHM_DESC_OFFSET_R_TO_H + SHM1_DESC_SIZE)

#define H_TO_R_PAYLOAD_START SHM_PAYLOAD_H_TO_R
#define H_TO_R_PAYLOAD_END (SHM_PAYLOAD_H_TO_R + SHM_PAYLOAD_HALF_SIZE)
#define R_TO_H_PAYLOAD_START SHM_PAYLOAD_R_TO_H
#define R_TO_H_PAYLOAD_END (SHM_PAYLOAD_R_TO_H + SHM_PAYLOAD_HALF_SIZE)
#define SHM_DESC_ADDR_START SHM_DESC_ADDR_ARRAY_OFFSET
#define PKGS_TOTAL 1024

/**
Expand All @@ -70,10 +40,12 @@
*/
int demo(void *arg)
{
unsigned long tx_data_offset, rx_data_offset, rx_used_offset;
unsigned long tx_avail_offset, rx_avail_offset;
struct channel_machine_ctx_s ch_machine_s = {0};
unsigned long tx_data_offset, rx_data_offset;
unsigned long tx_addr_offset, rx_addr_offset;
struct channel_s ch_s = {0x0};
struct channel_s ch_s = {
.machine_ctx = &ch_machine_s,
};
struct channel_s *ch = &ch_s;
bool platform_ready = false;
uint32_t rx_count, rx_avail;
Expand Down Expand Up @@ -108,32 +80,35 @@ int demo(void *arg)
}

/* Set tx/rx buffer address offset */
tx_addr_offset = R_TO_H_DESC_ADDR_START;
rx_addr_offset = H_TO_R_DESC_ADDR_START;
tx_data_offset = R_TO_H_PAYLOAD_START;
tx_addr_offset = SHM_DESC_ADDR_START;
rx_addr_offset = SHM_DESC_ADDR_START;
tx_data_offset = SHM_PAYLOAD_TX_OFFSET;

metal_info("REMOTE: Wait for echo test to start.\n");
rx_count = 0;
while (1) {
system_suspend(ch);

rx_avail = metal_io_read32(ch->shm_io,
SHM_DESC_OFFSET_H_TO_R +
rx_avail = metal_io_read32(ch->host_to_remote_desc_io,
SHM_DESC_AVAIL_OFFSET);
while (rx_count != rx_avail) {
uint32_t buf_phy_addr;
/* Get the buffer location from the rx addr array. */
buf_phy_addr = metal_io_read32(ch->shm_io, rx_addr_offset);
buf_phy_addr = metal_io_read32(ch->host_to_remote_desc_io,
rx_addr_offset);
rx_data_offset = metal_io_phys_to_offset(ch->shm_io,
(metal_phys_addr_t)buf_phy_addr);
if (rx_data_offset == METAL_BAD_OFFSET) {
metal_err("REMOTE: [%u]failed to get rx offset: 0x%x, 0x%lx.\n",
rx_count, buf_phy_addr,
metal_io_phys(ch->shm_io, rx_addr_offset));
metal_io_phys(ch->host_to_remote_desc_io,
rx_addr_offset));
ret = -EINVAL;
goto out;
}
rx_addr_offset += sizeof(buf_phy_addr);
if (rx_addr_offset >= SHM0_DESC_SIZE)
rx_addr_offset = SHM_DESC_ADDR_START;

/* Read message header from shared memory */
ret = metal_io_block_read(ch->shm_io, rx_data_offset, lbuf,
Expand Down Expand Up @@ -168,8 +143,8 @@ int demo(void *arg)
payload = (char *)lbuf + sizeof(*msg_hdr);
rx_count++;
/* Increase rx used count to indicate received data was used. */
metal_io_write32(ch->shm_io,
SHM_DESC_OFFSET_H_TO_R + SHM_DESC_USED_OFFSET,
metal_io_write32(ch->host_to_remote_desc_io,
SHM_DESC_USED_OFFSET,
rx_count);

/* Check if it is the shutdown message. */
Expand Down Expand Up @@ -199,13 +174,16 @@ int demo(void *arg)
goto out;
}

metal_io_write32(ch->shm_io, tx_addr_offset, buf_phy_addr);
metal_io_write32(ch->remote_to_host_desc_io,
tx_addr_offset, buf_phy_addr);
tx_data_offset += sizeof(struct msg_hdr_s) + msg_hdr->len;
tx_addr_offset += sizeof(uint32_t);
if (tx_addr_offset >= SHM1_DESC_SIZE)
tx_addr_offset = SHM_DESC_ADDR_START;

/* Increase number of available buffers. */
metal_io_write32(ch->shm_io,
SHM_DESC_OFFSET_R_TO_H + SHM_DESC_AVAIL_OFFSET,
metal_io_write32(ch->remote_to_host_desc_io,
SHM_DESC_AVAIL_OFFSET,
rx_count);

/* Kick IRQ to notify data is in shared buffer. */
Expand Down
18 changes: 10 additions & 8 deletions examples/libmetal/machine/host/amd_linux_userspace/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <stdio.h>

#include "irq_shmem_demo.h"
#include "config.h"

/*
Expand Down Expand Up @@ -113,17 +114,18 @@ static inline void update_stat(struct metal_stat *pst, uint64_t val)
pst->st_max = val;
}

struct channel_s {
struct metal_io_region *host_to_remote_desc_io; /* host to remote descriptors */
struct metal_io_region *remote_to_host_desc_io; /* remote to host descriptors */
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 */
struct channel_machine_ctx_s {
atomic_flag remote_nkicked; /* IRQ kick flag */
uint32_t ipi_mask; /* RPU IPI mask */
int irq_vector_id; /* IRQ number. */
};

static inline struct channel_machine_ctx_s *channel_machine_ctx(struct channel_s *ch)
{
metal_assert(ch);
metal_assert(ch->machine_ctx);

return (struct channel_machine_ctx_s *)ch->machine_ctx;
}

/**
* @ AMD RPU port for IRQ notification
* @param[in] irq_io - IO region used for IRQ kick
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ int open_metal_devices(void)
static int irq_isr(int vect_id, void *priv)
{
struct channel_s *ch = (struct channel_s *)priv;
struct channel_machine_ctx_s *machine = channel_machine_ctx(ch);
struct metal_io_region *ipi_io = ch->ipi_io;
uint32_t ipi_mask = IPI_MASK;
uint64_t val = 1;
Expand All @@ -105,7 +106,7 @@ static int irq_isr(int vect_id, void *priv)
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(&ch->remote_nkicked);
atomic_flag_clear(&machine->remote_nkicked);
return METAL_IRQ_HANDLED;
}
return METAL_IRQ_NOT_HANDLED;
Expand All @@ -114,19 +115,18 @@ static int irq_isr(int vect_id, void *priv)
int platform_init(struct channel_s *ch)
{
struct metal_init_params init_param = METAL_INIT_DEFAULTS;
struct channel_machine_ctx_s *machine = channel_machine_ctx(ch);
int ret;

metal_assert(ch);

ret = metal_init(&init_param);
if (ret) {
metal_err("HOST: Failed to initialize libmetal\n");
return ret;
}

/* initialize remote_nkicked */
ch->remote_nkicked = (atomic_flag)ATOMIC_FLAG_INIT;
atomic_flag_test_and_set(&ch->remote_nkicked);
machine->remote_nkicked = (atomic_flag)ATOMIC_FLAG_INIT;
atomic_flag_test_and_set(&machine->remote_nkicked);

ret = open_metal_devices();
if (ret) {
Expand Down Expand Up @@ -195,9 +195,7 @@ void platform_cleanup(struct channel_s *ch)
/* unregister IPI irq handler by setting the handler to 0 */
metal_irq_disable(ch->irq_vector_id);
metal_irq_unregister(ch->irq_vector_id);

metal_irq_unregister(ch->irq_vector_id);
memset(&ch, 0, sizeof(ch));
memset(ch, 0, sizeof(*ch));

/* Close libmetal devices which have been opened */
close_metal_devices();
Expand Down
4 changes: 3 additions & 1 deletion examples/libmetal/machine/remote/amd_rpu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ add_subdirectory (system)
set (_elf_name ${DEMO})

set (source_dirs ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/system/${PROJECT_SYSTEM})
collector_list (_inc_dirs PROJECT_INC_DIRS)
link_directories(${source_dirs})
include_directories(${source_dirs})
include_directories(${source_dirs} ${_inc_dirs})

collector_list (_deps PROJECT_LIB_DEPS)

Expand Down Expand Up @@ -60,5 +61,6 @@ target_link_libraries(${_elf_name}.elf PRIVATE
-Wl,--start-group ${_deps} -Wl,--end-group)

target_compile_definitions(${_elf_name}.elf PUBLIC ${USER_COMPILE_DEFINITIONS})
target_include_directories(${_elf_name}.elf PUBLIC ${_inc_dirs})
target_include_directories(${_elf_name}.elf PUBLIC ${USER_INCLUDE_DIRECTORIES})
install (TARGETS ${_elf_name}.elf RUNTIME DESTINATION bin)
4 changes: 4 additions & 0 deletions examples/libmetal/machine/remote/amd_rpu/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
/* This provides IPI_DEV_NAME, IPI_MASK, and carveout definitions */
#include "config.h"

/* Cortex-R52 DDR range used by the Versal Net MPU workaround. */
#define R52_LOW_DDR_ADDR 0x0U
#define R52_HIGH_DDR_ADDR 0x7FFFFFFFU

#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 */

Expand Down
Loading