Skip to main content

Software-Defined Device

Software Defined Device (SDD) is a framework that enables an application developer to write DPU applications interacting with the DPU’s on-board hardware accelerators. SDD provides APIs to emulate custom PCI devices entirely in software, running on the embedded CPU cores in the DPU SoC. In other words, the devices are software defined, instead of being fixed in hardware like on an FPGA or ASIC.

Using device emulation APIs, you can emulate a vast range of PCI devices. This includes NVMe disks, VirtIO devices (block, net, FS, and more), and even custom, experimental devices using new or existing interfaces.

Example

Here is an example of using SDD APIs to create a virtio block device.

This example demonstrates how to:

  1. Create an emulated device
  2. Configure vendor/device IDs for a virtio-blk device
  3. Configure BAR space and register callbacks
  4. Start the device, process operations, and teardown
example_sdd_virtio_blk.cc
#include <libmango.h>

/* Red Hat vendor ID */
#define VIRTIO_VENDOR_ID 0x1AF4
/* VirtIO Block device ID */
#define VIRTIO_BLK_DEV_ID 0x1001

/* VirtIO register offsets (simplified for example) */
#define VIRTIO_PCI_COMMON_DF 0x04 /* Device feature */
#define VIRTIO_PCI_COMMON_GF 0x0C /* Guest feature */
#define VIRTIO_PCI_COMMON_Q_SELECT 0x16 /* Queue select */
#define VIRTIO_PCI_COMMON_STATUS 0x14 /* Device status */
#define VIRTIO_PCI_COMMON_Q_DESCLO 0x20 /* Queue descriptor low address */
#define VIRTIO_PCI_COMMON_Q_DESCHI 0x24 /* Queue descriptor high address */

/* Doorbell register base offset (in BAR 1) */
#define VIRTIO_DOORBELL_BASE 0x00

/* Request completion function for sending MSI-X interrupts */
void req_complete(mango_sdd_dev_h dev, uint32_t queue_id) {
printf("Request completed for queue %u, sending MSI-X interrupt\n", queue_id);

/* Use queue ID as MSI-X vector number */
int status = mango_sdd_msix_inject(dev, queue_id);
if (status != 0) {
fprintf(stderr, "Failed to inject MSI-X interrupt: %d\n", status);
}
}

/* Main BAR I/O callback function for control registers */
uint64_t bar_io_callback(const mango_sdd_bar_io_t *io_event, void *user_data) {
uint64_t value = 0;

if (io_event->is_write) {
/* Handle write operation */
printf("BAR Write: BAR %u, Offset 0x%lx, Value 0x%lx, Size %u\n",
io_event->bar_idx, io_event->offset, io_event->value,
1 << io_event->size);

/* Process different register writes */
switch (io_event->offset) {
case VIRTIO_PCI_COMMON_GF:
printf("Driver features write: 0x%lx\n", io_event->value);
break;

case VIRTIO_PCI_COMMON_Q_SELECT:
printf("Queue selection write: %lu\n", io_event->value);
break;

case VIRTIO_PCI_COMMON_STATUS:
printf("Device status write: 0x%lx\n", io_event->value);
break;

default:
printf("Unhandled register write at offset 0x%lx\n", io_event->offset);
break;
}
/* Return value is ignored for writes */
return 0;
} else {
/* Handle read operation */
printf("BAR Read: BAR %u, Offset 0x%lx, Size %u\n",
io_event->bar_idx, io_event->offset,
1 << io_event->size);

/* Process different register reads */
switch (io_event->offset) {
case VIRTIO_PCI_COMMON_DF:
printf("Reading device features\n");
/* memcpy features to value */
break;

case VIRTIO_PCI_COMMON_STATUS:
printf("Reading device status\n");
/* memcpy status to value */
break;

default:
printf("Unhandled register read at offset 0x%lx\n", io_event->offset);
break;
}

printf("Returning value: 0x%lx\n", value);
return value;
}
}

/* Doorbell callback */
uint64_t doorbell_callback(const mango_sdd_bar_io_t *io_event, void *user_data) {
if (io_event->is_write) {
/* Handle doorbell write (queue notification) */
printf("DOORBELL Write: Offset 0x%lx, Value 0x%lx\n",
io_event->offset, io_event->value);

/* Extract queue index from doorbell write */
uint32_t queue_idx = (uint32_t)(io_event->offset / sizeof(uint32_t));

/* Handle queue notification */
printf("Queue %u notification received\n", queue_idx);

/*
* Process queue operations:
* 1. Check if the queue has pending requests
* 2. Process requests
* 3. Send completion notifications when done
*
* Once request processing is complete, call req_complete:
* req_complete(g_dev_handle, queue_id);
*/
} else {
/* Handle doorbell read (typically not used) */
printf("DOORBELL Read: Offset 0x%lx\n", io_event->offset);
}

return 0; /* Return value ignored for doorbells */
}

int main(int argc, char *argv[]) {
mango_sdd_dev_h dev_handle;

/* Step 1: Create an emulated device */
dev_handle = mango_sdd_dev_open("virtio-blk0");

/* Step 2: Configure the device with Red Hat vendor ID and virtio-blk device ID */
mango_sdd_dev_set_vendor_id(dev_handle, VIRTIO_VENDOR_ID);
mango_sdd_dev_set_device_id(dev_handle, VIRTIO_BLK_DEV_ID);

/* Step 3: Configure BAR 0 for control registers */
mango_sdd_configure_bar(dev_handle, 0, 4096,
MANGO_SDD_BAR_TYPE_REGULAR, 0);

/* Step 4: Configure BAR 1 for doorbell notifications */
mango_sdd_configure_bar(dev_handle, 1, 4096,
MANGO_SDD_BAR_TYPE_DOORBELL, 0);

/* Step 5: Configure BAR 2 for MSI-X table */
mango_sdd_configure_bar(dev_handle, 2, 4096,
MANGO_SDD_BAR_TYPE_MSIX, 0);

/* Step 6: Configure BAR 3 for MSI-X PBA (Pending Bit Array) */
mango_sdd_configure_bar(dev_handle, 3, 4096,
MANGO_SDD_BAR_TYPE_MSIX_PBA, 0);

/* Step 7: Register callback for main control registers (BAR 0) */
mango_sdd_register_bar_io_cb(dev_handle, 0, bar_io_callback, NULL);

/* Step 8: Register separate callback for doorbell operations (BAR 1) */
mango_sdd_register_bar_io_cb(dev_handle, 1, doorbell_callback, NULL);

/* Configure device with 16 MSI-X interrupts */
mango_sdd_dev_set_msix_vectors(dev_handle, 16);

/* Step 9: Start the device */
mango_sdd_start(dev_handle);

...

/* Step 8: Teardown the device */
mango_sdd_stop(dev_handle);
mango_sdd_dev_close(dev_handle);

return 0;
}

Datatype

mango_sdd_dev_h

typedef void * mango_sdd_dev_h;

A handle of mango software-defined device.

mango_sdd_queue_h

typedef void * mango_sdd_queue_h;

A handle of mango software-defined device queue.

mango_sdd_dev_state_e

typedef enum _mango_sdd_dev_state_e mango_sdd_dev_state_e;

Represents the possible states of an emulated device.

Values

MANGO_SDD_DEV_STATE_INITIALIZINGThe device is being initialized and configured
MANGO_SDD_DEV_STATE_RUNNINGThe device is running and can accept requests
MANGO_SDD_DEV_STATE_STOPPINGThe device is in the process of stopping, pending the completion of outstanding operations
MANGO_SDD_DEV_STATE_STOPPEDThe device is stopped and cannot process any more requests

mango_sdd_bar_type_e

typedef enum _mango_sdd_bar_type_e mango_sdd_bar_type_e;

Represents the possible types of BAR memory.

Values

MANGO_SDD_BAR_TYPE_MEMORYStandard memory BAR. Direct memory access for the device
MANGO_SDD_BAR_TYPE_IOI/O space BAR
MANGO_SDD_BAR_TYPE_MSIXMSI-X table memory. BAR memory that contains the MSI-X interrupt table
MANGO_SDD_BAR_TYPE_MSIX_PBAMSI-X Pending Bit Array memory

mango_sdd_bar_write_size_e

typedef enum _mango_bar_write_size mango_sdd_bar_write_size_e;

Defines the size of a BAR write.

Values

MANGO_SDD_BAR_WRITE_8BITAn 8-bit (1 byte) write
MANGO_SDD_BAR_WRITE_16BITA 16-bit (2 byte) write
MANGO_SDD_BAR_WRITE_32BITA 32-bit (4 byte) write
MANGO_SDD_BAR_WRITE_64BITA 64-bit (8 byte) write

mango_sdd_bar_io_t

typedef struct _mango_sdd_bar_io mango_sdd_bar_io_t;

A structure representing a BAR I/O operation (read or write).

Members

is_writetrue for write operations, false for read operations
bar_idxThe index of the BAR on which the operation occurred
offsetThe offset within the BAR where the operation occurred
valueFor writes: The value that was written. For reads: not used
sizeThe size of the operation

mango_sdd_bar_io_cb

typedef uint64_t(*)(const mango_sdd_bar_io_t *io_event, void *user_data) mango_sdd_bar_io_cb;

The function pointer type for a unified BAR I/O callback. The callback can handle both read and write operations based on the is_write flag. For read operations (is_write=false), the return value is used as the value returned to the host. For write operations (is_write=true), the return value is ignored.

mango_sdd_device_reset_type_e

typedef enum _mango_device_reset_type mango_sdd_device_reset_type_e;

Defines the types of device resets.

Values

MANGO_SDD_DEV_RESET_FLRFunction Level Reset

mango_sdd_device_reset_cb

typedef void(*)(mango_sdd_dev_h dev_handle, mango_sdd_device_reset_type_e reset_type, void *user_data) mango_sdd_device_reset_cb;

The function pointer type for a device reset callback.

mango_sdd_queue_direction_e

typedef enum _mango_sdd_queue_direction mango_sdd_queue_direction_e;

Defines the direction of data flow for a queue.

Values

MANGO_SDD_QUEUE_DIRECTION_H2CHost-to-Card direction. Data flows from host memory to the DPU
MANGO_SDD_QUEUE_DIRECTION_C2HCard-to-Host direction. Data flows from the DPU to host memory

mango_sdd_queue_type_e

typedef enum _mango_sdd_queue_type mango_sdd_queue_type_e;

Defines the type and behavior of a queue.

Values

MANGO_SDD_QUEUE_TYPE_RINGRing buffer queue. A circular buffer with head and tail pointers
MANGO_SDD_QUEUE_TYPE_SUBMISSIONSubmission queue. Optimized for receiving work items from the host
MANGO_SDD_QUEUE_TYPE_COMPLETIONCompletion queue. Optimized for sending completion notifications to the host

mango_sdd_queue_state_e

typedef enum _mango_sdd_queue_state mango_sdd_queue_state_e;

Represents the possible states of a queue.

Values

MANGO_SDD_QUEUE_STATE_CREATEDQueue is created and ready for operations but not yet started
MANGO_SDD_QUEUE_STATE_ACTIVEQueue is actively processing data transfers
MANGO_SDD_QUEUE_STATE_STOPPEDQueue is stopped and not processing transfers

mango_sdd_queue_item_cb

typedef void(*)(mango_sdd_queue_h queue_handle, uint32_t index, uint64_t dpu_addr, int result, void *user_data) mango_sdd_queue_item_cb;

The function pointer type for a queue item callback.

mango_sdd_queue_config_t

typedef struct _mango_sdd_queue_config mango_sdd_queue_config_t;

Configuration structure for creating a queue.

Members

host_addrHost memory address for the queue
dpu_addrDPU memory address for the queue
queue_sizeNumber of elements the queue can hold
element_sizeSize of each queue element in bytes
directionDirection of data flow for this queue
typeType and behavior of the queue
alignmentRequired memory alignment in bytes
callbackCallback function to be invoked after DMA operations complete
user_dataUser-defined pointer passed to every callback invocation
flagsAdditional queue configuration flags

Functions

mango_sdd_create_device

mango_status_e mango_sdd_create_device(const char *name,
mango_sdd_dev_h *dev_handle)

Creates a new emulated device.

Parameters

  • in name Name of the device to create.
  • out dev_handle Pointer to store the handle to the created device.

Returns 0 on success, or a negative error code on failure.

mango_sdd_destroy_device

mango_status_e mango_sdd_destroy_device(mango_sdd_dev_h dev_handle)

Destroys an emulated device.

Parameters

  • in dev_handle Handle to the device to destroy.

Returns 0 on success, or a negative error code on failure.

mango_sdd_start_device

mango_status_e mango_sdd_start_device(mango_sdd_dev_h dev_handle)

Starts an emulated device.

Parameters

  • in dev_handle Handle to the device to start.

Returns 0 on success, or a negative error code on failure.

mango_sdd_stop_device

mango_status_e mango_sdd_stop_device(mango_sdd_dev_h dev_handle)

Stops an emulated device.

Parameters

  • in dev_handle Handle to the device to stop.

Returns 0 on success, or a negative error code on failure.

mango_sdd_get_device_state

mango_status_e mango_sdd_get_device_state(mango_sdd_dev_h dev_handle,
mango_sdd_dev_state_e *state)

Gets the current state of an emulated device.

Parameters

  • in dev_handle Handle to the device.
  • out state Pointer to store the device state.

Returns 0 on success, or a negative error code on failure.

mango_sdd_set_device_property

mango_status_e mango_sdd_set_device_property(mango_sdd_dev_h dev_handle,
const char *key,
const char *value)

Sets a device property value.

Parameters

  • in dev_handle Handle to the device.
  • in key Property name.
  • in value Property value.

Returns 0 on success, or a negative error code on failure.

mango_sdd_get_device_property

mango_status_e mango_sdd_get_device_property(mango_sdd_dev_h dev_handle,
const char *key,
char *value,
size_t *value_size)

Gets a device property value.

Parameters

  • in dev_handle Handle to the device.
  • in key Property name.
  • out value Pointer to store the property value.
  • inout value_size Size of the value buffer on input, size of the actual value on output.

Returns 0 on success, or a negative error code on failure.

mango_sdd_configure_bar

mango_status_e mango_sdd_configure_bar(mango_sdd_dev_h dev_handle,
uint8_t bar_idx,
size_t bar_size,
mango_sdd_bar_type_e bar_type,
uint32_t flags)

Configures a BAR for an emulated device.

Parameters

  • in dev_handle Handle to the device.
  • in bar_idx Index of the BAR to configure.
  • in bar_size Size of the BAR in bytes.
  • in bar_type Type of the BAR.
  • in flags Additional configuration flags.

Returns 0 on success, or a negative error code on failure.

mango_sdd_register_bar_io_cb

mango_status_e mango_sdd_register_bar_io_cb(mango_sdd_dev_h dev_handle,
uint8_t bar_idx,
mango_sdd_bar_io_cb io_cb,
void *user_data)

Registers a unified callback function for all BAR I/O operations (both reads and writes).

Parameters

  • in dev_handle Handle to the device.
  • in bar_idx Index of the BAR.
  • in io_cb Callback function for I/O operations.
  • in user_data User-defined pointer to pass to the callback.

Returns 0 on success, or a negative error code on failure.

mango_sdd_register_reset_cb

mango_status_e mango_sdd_register_reset_cb(mango_sdd_dev_h dev_handle,
mango_sdd_device_reset_cb reset_cb,
void *user_data)

Registers a callback function for device reset events.

Parameters

  • in dev_handle Handle to the device.
  • in reset_cb Callback function for reset operations.
  • in user_data User-defined pointer to pass to the callback.

Returns 0 on success, or a negative error code on failure.

mango_sdd_create_queue

mango_status_e mango_sdd_create_queue(mango_sdd_dev_h dev_handle,
const mango_sdd_queue_config_t *config,
mango_sdd_queue_h *queue_handle)

Creates a queue for an emulated device.

Parameters

  • in dev_handle Handle to the device.
  • in config Configuration parameters for the queue.
  • out queue_handle Pointer to store the handle to the created queue.

Returns 0 on success, or a negative error code on failure.

mango_sdd_destroy_queue

mango_status_e mango_sdd_destroy_queue(mango_sdd_queue_h queue_handle)

Destroys a queue.

Parameters

  • in queue_handle Handle to the queue to destroy.

Returns 0 on success, or a negative error code on failure.

mango_sdd_start_queue

mango_status_e mango_sdd_start_queue(mango_sdd_queue_h queue_handle)

Starts a queue's operation.

Parameters

  • in queue_handle Handle to the queue to start.

Returns 0 on success, or a negative error code on failure.

mango_sdd_stop_queue

mango_status_e mango_sdd_stop_queue(mango_sdd_queue_h queue_handle)

Stops a queue's operation.

Parameters

  • in queue_handle Handle to the queue to stop.

Returns 0 on success, or a negative error code on failure.

mango_sdd_get_queue_state

mango_status_e mango_sdd_get_queue_state(mango_sdd_queue_h queue_handle,
mango_sdd_queue_state_e *state)

Gets the current state of a queue.

Parameters

  • in queue_handle Handle to the queue.
  • out state Pointer to store the queue state.

Returns 0 on success, or a negative error code on failure.

mango_sdd_process_queue_item

mango_status_e mango_sdd_process_queue_item(mango_sdd_queue_h queue_handle,
uint32_t index)

Processes a single item in the queue.

Parameters

  • in queue_handle Handle to the queue.
  • in index Index of the item to process.

Returns 0 on success, or a negative error code on failure.

mango_sdd_process_queue

mango_status_e mango_sdd_process_queue(mango_sdd_queue_h queue_handle,
uint32_t max_items,
uint32_t *processed_items)

Processes all available items in the queue.

Parameters

  • in queue_handle Handle to the queue.
  • in max_items Maximum number of items to process (0 for all available).
  • out processed_items Pointer to store the number of items processed.

Returns 0 on success, or a negative error code on failure.

mango_sdd_get_queue_count

mango_status_e mango_sdd_get_queue_count(mango_sdd_queue_h queue_handle,
uint32_t *count)

Gets the number of items available in the queue.

Parameters

  • in queue_handle Handle to the queue.
  • out count Pointer to store the number of available items.

Returns 0 on success, or a negative error code on failure.

mango_sdd_get_queue_device

mango_status_e mango_sdd_get_queue_device(mango_sdd_queue_h queue_handle,
mango_sdd_dev_h *dev_handle)

Gets the parent device of a queue.

Parameters

  • in queue_handle Handle to the queue.
  • out dev_handle Pointer to store the device handle.

Returns 0 on success, or a negative error code on failure.

mango_sdd_set_queue_property

mango_status_e mango_sdd_set_queue_property(mango_sdd_queue_h queue_handle,
const char *key,
const char *value)

Sets a queue property value.

Parameters

  • in queue_handle Handle to the queue.
  • in key Property name.
  • in value Property value.

Returns 0 on success, or a negative error code on failure.

mango_sdd_get_queue_property

mango_status_e mango_sdd_get_queue_property(mango_sdd_queue_h queue_handle,
const char *key,
char *value,
size_t *value_size)

Gets a queue property value.

Parameters

  • in queue_handle Handle to the queue.
  • in key Property name.
  • out value Pointer to store the property value.
  • inout value_size Size of the value buffer on input, size of the actual value on output.

Returns 0 on success, or a negative error code on failure.