Skip to content

Commit

Permalink
User space interface for DMA and Mailbox
Browse files Browse the repository at this point in the history
  • Loading branch information
mlvisaya committed Jan 3, 2025
1 parent 8a7b2b3 commit 60b23bd
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ members = [
"romtime",
"runtime",
"runtime/apps/example",
"runtime/apps/apis/dma",
"runtime/apps/apis/mailbox",
"runtime/apps/libtock/apis/interface/buttons",
"runtime/apps/libtock/apis/interface/buzzer",
"runtime/apps/libtock/apis/interface/console",
Expand Down
12 changes: 12 additions & 0 deletions runtime/apps/apis/dma/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Licensed under the Apache-2.0 license

[package]
name = "dma-api"
version.workspace = true
authors.workspace = true
edition.workspace = true

[dependencies]
libtock = { path = "../../libtock" }
libtock_platform = { path = "../../libtock/platform" }
libtockasync = { path = "../../libtockasync" }
98 changes: 98 additions & 0 deletions runtime/apps/apis/dma/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Licensed under the Apache-2.0 license

//! # AsyncDMA: A DMA Interface for AXI Source to AXI Destination Transfers
//!
//! This library provides an abstraction for performing asynchronous Direct Memory Access (DMA)
//! transfers between AXI source and AXI destination addresses.
use libtock_platform::{ErrorCode, Syscalls};
use libtockasync::TockSubscribe;

/// DMA interface.
pub struct AsyncDMA<const DRIVER_NUM: u32, S: Syscalls>(S);

/// Configuration parameters for a DMA transfer.
#[derive(Debug, Copy, Clone)]
pub struct DMATransaction {
/// Number of bytes to transfer.
pub byte_count: usize,
/// Source address for the transfer.
pub src_addr: u64,
/// Destination address for the transfer.
pub dest_addr: u64,
}

impl<const DRIVER_NUM: u32, S: Syscalls> AsyncDMA<DRIVER_NUM, S> {
/// Do a DMA transfer.
///
/// This method executes a DMA transfer based on the provided `DMATransaction` configuration.
///
/// # Arguments
/// * `transaction` - A `DMATransaction` struct containing the transfer details.
///
/// # Returns
/// * `Ok(())` if the transfer starts successfully.
/// * `Err(ErrorCode)` if the transfer fails.
pub async fn xfer(transaction: DMATransaction) -> Result<(), ErrorCode> {
Self::setup(transaction).await?;

let async_start = TockSubscribe::subscribe::<S>(DRIVER_NUM, dma_subscribe::XFER_DONE);
S::command(DRIVER_NUM, dma_cmd::START, 0, 0).to_result::<(), ErrorCode>()?;
async_start.await.map(|_| ())
}

async fn setup(config: DMATransaction) -> Result<(), ErrorCode> {
let async_configure =
TockSubscribe::subscribe::<S>(DRIVER_NUM, dma_subscribe::SET_BYTE_XFER_COUNT_DONE);
S::command(
DRIVER_NUM,
dma_cmd::SET_BYTE_XFER_COUNT,
config.byte_count as u32,
0,
)
.to_result::<(), ErrorCode>()?;
async_configure.await.map(|_| ())?;

let async_src = TockSubscribe::subscribe::<S>(DRIVER_NUM, dma_subscribe::SET_SRC_DONE);
S::command(
DRIVER_NUM,
dma_cmd::SET_SRC_ADDR,
(config.src_addr & 0xFFFF_FFFF) as u32,
(config.src_addr >> 32) as u32,
)
.to_result::<(), ErrorCode>()?;
async_src.await.map(|_| ())?;

let async_dest = TockSubscribe::subscribe::<S>(DRIVER_NUM, dma_subscribe::SET_DEST_DONE);
S::command(
DRIVER_NUM,
dma_cmd::SET_DEST_ADDR,
(config.dest_addr & 0xFFFF_FFFF) as u32,
(config.dest_addr >> 32) as u32,
)
.to_result::<(), ErrorCode>()?;
async_dest.await.map(|_| ())?;

Ok(())
}
}

// -----------------------------------------------------------------------------
// Command IDs and DMA-specific constants
// -----------------------------------------------------------------------------

/// Command IDs used by the DMA interface.
mod dma_cmd {
pub const SET_BYTE_XFER_COUNT: u32 = 0;
pub const SET_SRC_ADDR: u32 = 1;
pub const SET_DEST_ADDR: u32 = 2;
pub const START: u32 = 3;
}

/// Subscription IDs for asynchronous notifications.
mod dma_subscribe {
pub const SET_BYTE_XFER_COUNT_DONE: u32 = 0;
pub const SET_SRC_DONE: u32 = 1;
pub const SET_DEST_DONE: u32 = 2;
pub const XFER_DONE: u32 = 3;
}
12 changes: 12 additions & 0 deletions runtime/apps/apis/mailbox/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Licensed under the Apache-2.0 license

[package]
name = "mailbox-api"
version.workspace = true
authors.workspace = true
edition.workspace = true

[dependencies]
libtock = { path = "../../libtock" }
libtock_platform = { path = "../../libtock/platform" }
libtockasync = { path = "../../libtockasync" }
106 changes: 106 additions & 0 deletions runtime/apps/apis/mailbox/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Licensed under the Apache-2.0 license

//! # AsyncMailbox: Mailbox Interface
use libtock_platform::allow_ro::{AllowRo, Config as ReadOnlyConfig};
use libtock_platform::allow_rw::{AllowRw, Config as ReadWriteConfig};
use libtock_platform::{share, DefaultConfig, ErrorCode, Syscalls};
use libtockasync::TockSubscribe;
/// Mailbox interface user interface.
///
/// # Generics
/// - `DRIVER_NUM`: The driver number for the mailbox interface.
/// - `S`: The syscall implementation.
/// - `W`: Configuration for writable buffers (response).
/// - `R`: Configuration for read-only buffers (input), defaults to `DefaultConfig`.
pub struct AsyncMailbox<
const DRIVER_NUM: u32,
S: Syscalls,
W: ReadWriteConfig,
R: ReadOnlyConfig = DefaultConfig,
>(S, W, R);

impl<const DRIVER_NUM: u32, S: Syscalls, W: ReadWriteConfig, R: ReadOnlyConfig>
AsyncMailbox<DRIVER_NUM, S, W, R>
{
/// Executes a mailbox command and returns the response.
///
/// This method sends a mailbox command to the kernel, then waits
/// asynchronously for the command to complete. The response buffer is filled with
/// the result from the kernel.
///
/// # Arguments
/// - `command`: The mailbox command ID to execute.
/// - `input_data`: A read-only buffer containing the mailbox command parameters.
/// - `response_buffer`: A writable buffer to store the response data.
///
/// # Returns
/// - `Ok(usize)` on success, containing the number of bytes written to the response buffer.
/// - `Err(ErrorCode)` if the command fails.
///
/// # Example
/// ```rust
/// let mailbox = AsyncMailbox::<DRIVER_NUM, S, W>::new(...);
/// let mut response = [0u8; 128];
/// mailbox.execute_command(1, &[1, 2, 3], &mut response).await?;
/// ```
pub async fn execute(
command: u32,
input_data: &[u8],
response_buffer: &mut [u8],
) -> Result<usize, ErrorCode> {
share::scope::<
(
AllowRo<_, DRIVER_NUM, { mailbox_buffer::INPUT }>,
AllowRw<_, DRIVER_NUM, { mailbox_buffer::RESPONSE }>,
),
_,
_,
>(|handle| {
let (allow_ro, allow_rw) = handle.split();

// Share the input buffer (read-only)
S::allow_ro::<R, DRIVER_NUM, { mailbox_buffer::INPUT }>(allow_ro, input_data)?;

// Share the response buffer (read-write)
S::allow_rw::<W, DRIVER_NUM, { mailbox_buffer::RESPONSE }>(allow_rw, response_buffer)?;

// Subscribe to the asynchronous notification for when the command is processed
let async_command =
TockSubscribe::subscribe::<S>(DRIVER_NUM, mailbox_subscribe::COMMAND_DONE);

// Issue the command to the kernel
S::command(DRIVER_NUM, mailbox_cmd::EXECUTE_COMMAND, command, 0)
.to_result::<(), ErrorCode>()?;

// Return the subscription for further processing
Ok(async_command)
})?
.await
.map(|res| res.0 as usize)
}
}

// -----------------------------------------------------------------------------
// Command IDs and Mailbox-specific constants
// -----------------------------------------------------------------------------

/// Command IDs for mailbox operations.
mod mailbox_cmd {
/// Execute a command with input and response buffers.
pub const EXECUTE_COMMAND: u32 = 1;
}

/// Buffer IDs for mailbox read/write operations.
mod mailbox_buffer {
/// Buffer ID for the input buffer (read-only).
pub const INPUT: u32 = 0;
/// Buffer ID for the response buffer (read-write).
pub const RESPONSE: u32 = 1;
}

/// Subscription IDs for asynchronous mailbox events.
mod mailbox_subscribe {
/// Subscription ID for the `COMMAND_DONE` event.
pub const COMMAND_DONE: u32 = 1;
}

0 comments on commit 60b23bd

Please sign in to comment.