Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ struct channel_s {
void *machine_ctx; /* Platform- or OS-private channel state */
uint32_t ipi_mask; /* RPU IPI mask */
int irq_vector_id; /* IRQ number. */
uint32_t desc0_size; /* host to remote descriptor region size */
uint32_t desc1_size; /* remote to host descriptor region size */
uint32_t shm_payload_size; /* shared payload buffer size */
};

#endif /* __IRQ_SHMEM_DEMO_H__ */
152 changes: 117 additions & 35 deletions examples/libmetal/demos/irq_shmem_demo/host/irq_shmem_demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
* amd_linux_userspace/README.md.
*/

#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <unistd.h>
#include <metal/sys.h>
#include <metal/io.h>
#include <metal/alloc.h>
Expand All @@ -37,30 +38,11 @@
#include "common.h"
#include "platform_init.h"

/* Shared memory offsets */
#define SHM_DESC_OFFSET_TX 0x0
#define SHM_BUFF_OFFSET_TX 0x04000
#define SHM_DESC_OFFSET_RX 0x02000
#define SHM_BUFF_OFFSET_RX 0x104000

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

/* Descriptor regions for each direction. */
/* Note that H_TO_R_ is host to remote and R_TO_H_ is vice versa. */
#define H_TO_R_DESC_ADDR_START SHM_DESC_ADDR_ARRAY_OFFSET
#define H_TO_R_DESC_ADDR_END SHM0_DESC_SIZE
#define R_TO_H_DESC_ADDR_START SHM_DESC_ADDR_ARRAY_OFFSET
#define R_TO_H_DESC_ADDR_END SHM1_DESC_SIZE

/* Split of the data / payload area for each direction */
#define H_TO_R_PAYLOAD_START SHM_PAYLOAD_RX_OFFSET
#define H_TO_R_PAYLOAD_END (SHM_PAYLOAD_RX_OFFSET + SHM_PAYLOAD_HALF_SIZE)
#define R_TO_H_PAYLOAD_START SHM_PAYLOAD_TX_OFFSET
#define R_TO_H_PAYLOAD_END (SHM_PAYLOAD_TX_OFFSET + SHM_PAYLOAD_HALF_SIZE)

#define PKGS_TOTAL 1024

#define BUF_SIZE_MAX 512
Expand All @@ -73,6 +55,83 @@ struct msg_hdr_s {
uint32_t len;
};

enum {
OPT_SHM_DEV = 1000,
OPT_DESC0_DEV,
OPT_DESC1_DEV,
OPT_IPI_DEV,
OPT_TTC_DEV,
OPT_IPI_MASK_PROP,
};

static void usage(const char *prog, const struct app_platform_options *options)
{
fprintf(stderr,
"Usage: %s [options]\n"
" --shm-dev <name> Shared-memory UIO name (default: %s)\n"
" --desc0-dev <name> Host-to-remote descriptor UIO name (default: %s)\n"
" --desc1-dev <name> Remote-to-host descriptor UIO name (default: %s)\n"
" --ipi-dev <name> IPI UIO name (default: %s)\n"
" --ttc-dev <name> Timer UIO name (default: %s)\n"
" --ipi-mask-prop <name> IPI remote-mask DT property (default: %s)\n"
" -h, --help Show this help text\n",
prog, options->shm_dev_name, options->desc0_dev_name,
options->desc1_dev_name, options->ipi_dev_name,
options->ttc_dev_name, options->ipi_remote_mask_property);
}

static int parse_args(int argc, char **argv,
struct app_platform_options *options)
{
static const struct option long_options[] = {
{ "shm-dev", required_argument, NULL, OPT_SHM_DEV },
{ "desc0-dev", required_argument, NULL, OPT_DESC0_DEV },
{ "desc1-dev", required_argument, NULL, OPT_DESC1_DEV },
{ "ipi-dev", required_argument, NULL, OPT_IPI_DEV },
{ "ttc-dev", required_argument, NULL, OPT_TTC_DEV },
{ "ipi-mask-prop", required_argument, NULL, OPT_IPI_MASK_PROP },
{ "help", no_argument, NULL, 'h' },
{ 0, 0, 0, 0 }
};
int c;

while ((c = getopt_long(argc, argv, "h", long_options, NULL)) != -1) {
switch (c) {
case OPT_SHM_DEV:
options->shm_dev_name = optarg;
break;
case OPT_DESC0_DEV:
options->desc0_dev_name = optarg;
break;
case OPT_DESC1_DEV:
options->desc1_dev_name = optarg;
break;
case OPT_IPI_DEV:
options->ipi_dev_name = optarg;
break;
case OPT_TTC_DEV:
options->ttc_dev_name = optarg;
break;
case OPT_IPI_MASK_PROP:
options->ipi_remote_mask_property = optarg;
break;
case 'h':
usage(argv[0], options);
return 1;
default:
usage(argv[0], options);
return -EINVAL;
}
}

if (optind != argc) {
usage(argv[0], options);
return -EINVAL;
}

return 0;
}

/**
* @brief wait_for_notified() - Loop until notified bit in channel is set.
*
Expand Down Expand Up @@ -137,6 +196,8 @@ static int irq_shmem_echo(struct channel_s *ch)
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 h_to_r_desc_addr_end, h_to_r_payload_start;
uint32_t h_to_r_payload_end, r_to_h_payload_start;
void *txbuf = NULL, *rxbuf = NULL, *tmpptr;
long long tdiff_avg_s = 0, tdiff_avg_ns = 0;
unsigned long long tstart, tend;
Expand All @@ -163,23 +224,31 @@ static int irq_shmem_echo(struct channel_s *ch)

if (!ch || !ch->shm_io || !ch->host_to_remote_desc_io ||
!ch->remote_to_host_desc_io || !ch->ipi_io) {
return -EINVAL;
ret = -EINVAL;
goto out;
}

h_to_r_desc_addr_end = ch->desc0_size;
h_to_r_payload_start = 0;
h_to_r_payload_end = ch->shm_payload_size / 2;
r_to_h_payload_start = ch->shm_payload_size / 2;

/* Clear shared memory and descriptors */
ret = metal_io_block_set(ch->shm_io, 0, 0, SHM_PAYLOAD_SIZE);
ret = metal_io_block_set(ch->shm_io, 0, 0, ch->shm_payload_size);
if (ret < 0) {
metal_err("HOST: Failed to clear payload area.\n");
goto out;
}

ret = metal_io_block_set(ch->host_to_remote_desc_io, 0, 0, SHM0_DESC_SIZE);
ret = metal_io_block_set(ch->host_to_remote_desc_io, 0, 0,
ch->desc0_size);
if (ret < 0) {
metal_err("HOST: Failed to clear host to remote descriptor area.\n");
goto out;
}

ret = metal_io_block_set(ch->remote_to_host_desc_io, 0, 0, SHM1_DESC_SIZE);
ret = metal_io_block_set(ch->remote_to_host_desc_io, 0, 0,
ch->desc1_size);
if (ret < 0) {
metal_err("HOST: Failed to clear remote to host descriptor area.\n");
goto out;
Expand All @@ -189,10 +258,10 @@ static int irq_shmem_echo(struct channel_s *ch)
tx_avail_offset = SHM_DESC_AVAIL_OFFSET;
rx_avail_offset = SHM_DESC_AVAIL_OFFSET;
rx_used_offset = SHM_DESC_USED_OFFSET;
tx_addr_offset = H_TO_R_DESC_ADDR_START;
rx_addr_offset = R_TO_H_DESC_ADDR_START;
tx_data_offset = H_TO_R_PAYLOAD_START;
rx_data_offset = R_TO_H_PAYLOAD_START;
tx_addr_offset = SHM_DESC_ADDR_ARRAY_OFFSET;
rx_addr_offset = SHM_DESC_ADDR_ARRAY_OFFSET;
tx_data_offset = h_to_r_payload_start;
rx_data_offset = r_to_h_payload_start;

metal_info("HOST: Start echo flood testing....\n");
metal_info("HOST: Sending msgs to the remote.\n");
Expand Down Expand Up @@ -225,11 +294,12 @@ static int irq_shmem_echo(struct channel_s *ch)
goto out;
}

metal_io_write32(desc_host_to_remote, tx_addr_offset, tx_phy_addr_32);
metal_io_write32(desc_host_to_remote, tx_addr_offset,
tx_phy_addr_32);
tx_data_offset += sizeof(struct msg_hdr_s) + msg_hdr->len;
tx_addr_offset += sizeof(uint32_t);
if (tx_addr_offset >= H_TO_R_DESC_ADDR_END)
tx_addr_offset = H_TO_R_DESC_ADDR_START;
if (tx_addr_offset >= h_to_r_desc_addr_end)
tx_addr_offset = SHM_DESC_ADDR_ARRAY_OFFSET;

/* Increase number of available buffers */
metal_io_write32(desc_host_to_remote, tx_avail_offset, (i + 1));
Expand All @@ -238,7 +308,7 @@ static int irq_shmem_echo(struct channel_s *ch)
}
metal_info("HOST: Waiting for messages to echo back and verify.\n");
i = 0;
tx_data_offset = H_TO_R_PAYLOAD_START;
tx_data_offset = h_to_r_payload_start;

while (i != PKGS_TOTAL) {

Expand Down Expand Up @@ -314,8 +384,11 @@ static int irq_shmem_echo(struct channel_s *ch)
}

tx_data_offset += sizeof(*msg_hdr) + sizeof(tstart);
if (tx_data_offset >= h_to_r_payload_end)
tx_data_offset = h_to_r_payload_start;
/* Compare the received message and the sent message */
ret = memcmp(rxbuf, txbuf, sizeof(*msg_hdr) + sizeof(tstart));
ret = memcmp(rxbuf, txbuf,
sizeof(*msg_hdr) + sizeof(tstart));
if (ret) {
metal_err("HOST: data[%u] verification failed.\n", i);
metal_info("HOST: Expected:");
Expand Down Expand Up @@ -375,16 +448,25 @@ static int irq_shmem_echo(struct channel_s *ch)
return ret;
}

int main(void)
int main(int argc, char **argv)
{
struct channel_machine_ctx_s ch_machine_s = {0};
struct app_platform_options platform_options;
struct channel_s ch_s = {
.machine_ctx = &ch_machine_s,
};
int ret = 0;

platform_get_default_options(&platform_options);
ret = parse_args(argc, argv, &platform_options);
if (ret) {
if (ret > 0)
return 0;
return ret;
}

/* platform_init will set the OS agnostic channel information */
ret = platform_init(&ch_s);
ret = platform_init(&ch_s, &platform_options);
if (ret) {
metal_err("HOST: Failed to initialize system.\n");
return ret;
Expand Down
11 changes: 0 additions & 11 deletions examples/libmetal/machine/host/amd_linux_userspace/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
# Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
include (CheckIncludeFiles)
include (CheckCSourceCompiles)
include (${APPS_ROOT_DIR}/../legacy_apps/cmake/options.cmake)
include (${APPS_ROOT_DIR}/../legacy_apps/cmake/collect.cmake)

get_property (DEMO_CFG_FILE GLOBAL PROPERTY DEMO_CFG_FILE)
if (EXISTS ${DEMO_CFG_FILE})
include(${DEMO_CFG_FILE})
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in"
"${CMAKE_CURRENT_SOURCE_DIR}/config.h")
message("WARNING: DEMO_CFG_FILE ${DEMO_CFG_FILE} passed in at configure time. Using this to configure demo.")
endif()

collect (APP_COMMON_SOURCES platform_init.c)
collect (APP_INC_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")
Expand Down
36 changes: 23 additions & 13 deletions examples/libmetal/machine/host/amd_linux_userspace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ messages.
- Signals completion via IPI and then disables the interrupt and releases the mapped devices.

## Prerequisites
- Linux kernel exposes the shared memory carveouts and descriptor UIOs
(`9868000.shm`, `9860000.shm_desc`, `9864000.shm_desc` by default; adjust to
the platform-specific devices/offsets), along with IPI and TTC peripherals,
to userspace with permissions suitable for the demo binary.
- Linux kernel exposes the shared memory carveouts and descriptor UIOs to
userspace with stable logical names:
`libmetal-data`, `libmetal-desc0`, `libmetal-desc1`, `libmetal-ipi`, and
`libmetal-timer`.
- The host IPI UIO node carries a `libmetal,ipi-remote-mask` device-tree
property so the demo can discover the platform-specific interrupt bit at
runtime.
- libmetal (and dependent libraries) installed on the host system, as well as
the `metal_xlnx_extension` library when required by the platform glue.
- The project toolchain file defines the correct platform macro (for example,
`-DPLATFORM_ZYNQMP`) so `common.h` selects the matching peripheral map.
- Remote firmware is already loaded and waiting for interrupts before the host
demo starts.

Expand All @@ -51,22 +52,31 @@ The static executable is emitted at

## Run
1. Start the remote firmware so it sits in the notification loop.
2. Launch the host binary (root/sudo may be required for IPI device access):
2. Confirm the expected logical UIO names are visible:
```bash
cat /sys/class/uio/uio*/name
```
3. Launch the host binary (root/sudo may be required for IPI device access):
```bash
./irq_shmem_demo-static
```
3. Observe the console output for packet progress and the final average
Override individual logical names when the platform uses different UIO
aliases:
```bash
./irq_shmem_demo-static --shm-dev my-data --desc0-dev my-desc0 \
--desc1-dev my-desc1 --ipi-dev my-ipi --ttc-dev my-timer
```
4. Observe the console output for packet progress and the final average
round-trip latency.

## [Shared Memory Layout](../../../demos/irq_shmem_demo/README.md#shared-memory-layout)
Shared buffer map used by both sides of the demo.

## Troubleshooting
- **Hangs waiting for notification**: ensure the IPI mask configured in
`common.h` (or overridden via the optional demo config file) matches the
remote firmware and that the host process can write to the IPI device.
- **Hangs waiting for notification**: ensure the host IPI UIO node exposes
`libmetal,ipi-remote-mask`, that the host process can write to the IPI device,
and that the remote firmware uses the matching interrupt bit.
- **Shared-memory access errors**: confirm the UIO entries expose the expected
descriptor/payload ranges (`0x09860000` base) with read/write permissions for
the demo user.
descriptor and payload regions with read/write permissions for the demo user.
- **Mismatched payloads**: verify both sides agree on descriptor offsets and
the `PKGS_TOTAL` value compiled into each binary.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#ifndef __COMMON_H__
#define __COMMON_H__

#include <stdint.h>
#include <sys/types.h>

#include <metal/atomic.h>
Expand All @@ -17,7 +18,6 @@
#include <stdio.h>

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

/*
* Apply this snippet to the device tree in an overlay so that Linux userspace can
Expand Down
27 changes: 0 additions & 27 deletions examples/libmetal/machine/host/amd_linux_userspace/config.h.in

This file was deleted.

Loading