Skip to content

Commit

Permalink
support psram in DmaTxBuf
Browse files Browse the repository at this point in the history
  • Loading branch information
liebman committed Sep 15, 2024
1 parent 562c891 commit 0812219
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 10 deletions.
104 changes: 95 additions & 9 deletions esp-hal/src/dma/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,16 @@ impl Debug for DmaDescriptorFlags {
}
}

#[cfg(feature = "defmt")]
impl defmt::Format for DmaDescriptorFlags {
fn format(&self, fmt: defmt::Formatter<'_>) {
defmt::write!(fmt, "DmaDescriptorFlags {{ size: {}, length: {}, suc_eof: {}, owner: {} }}", self.size(), self.length(), self.suc_eof(), if self.owner() { "DMA" } else { "CPU" });
}
}

/// A DMA transfer descriptor.
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DmaDescriptor {
pub(crate) flags: DmaDescriptorFlags,
pub(crate) buffer: *mut u8,
Expand Down Expand Up @@ -284,7 +292,11 @@ use enumset::{EnumSet, EnumSetType};
pub use self::gdma::*;
#[cfg(pdma)]
pub use self::pdma::*;
use crate::{interrupt::InterruptHandler, soc::is_slice_in_dram, Mode};
use crate::{
interrupt::InterruptHandler,
soc::{is_slice_in_dram, is_slice_in_psram},
Mode,
};

#[cfg(gdma)]
mod gdma;
Expand Down Expand Up @@ -929,6 +941,16 @@ pub enum DmaExtMemBKSize {
Size64 = 2,
}

impl From<DmaBufBlkSize> for DmaExtMemBKSize {
fn from(size: DmaBufBlkSize) -> Self {
match size {
DmaBufBlkSize::Size16 => DmaExtMemBKSize::Size16,
DmaBufBlkSize::Size32 => DmaExtMemBKSize::Size32,
DmaBufBlkSize::Size64 => DmaExtMemBKSize::Size64,
}
}
}

pub(crate) struct TxCircularState {
write_offset: usize,
write_descr_ptr: *mut DmaDescriptor,
Expand Down Expand Up @@ -1362,7 +1384,6 @@ where
if des.buffer as usize % alignment != 0 && des.size() % alignment != 0 {
return Err(DmaError::InvalidAlignment);
}
// TODO: make this optional?
crate::soc::cache_invalidate_addr(des.buffer as u32, des.size() as u32);
}
}
Expand Down Expand Up @@ -1679,6 +1700,7 @@ where
peri: DmaPeripheral,
chain: &DescriptorChain,
) -> Result<(), DmaError> {
// TODO: based on the ESP32-S3 TRM teh alignment check is not needed for TX!
// for esp32s3 we check each descriptor buffer that points to psram for
// alignment and writeback the cache for that buffer
#[cfg(esp32s3)]
Expand All @@ -1704,7 +1726,11 @@ where
buffer: &mut BUF,
) -> Result<(), DmaError> {
let preparation = buffer.prepare();

#[cfg(esp32s3)]
if let Some(block_size) = preparation.block_size {
self.set_ext_mem_block_size(block_size.into());
}
// TODO: Get burst mode from DmaBuf.
self.tx_impl
.prepare_transfer_without_start(preparation.start, peri)
}
Expand Down Expand Up @@ -1942,6 +1968,8 @@ where
/// Holds all the information needed to configure a DMA channel for a transfer.
pub struct Preparation {
start: *mut DmaDescriptor,
/// block size for PSRAM transfers (TODO: enable burst mode for non external memory?)
block_size: Option<DmaBufBlkSize>,
// burst_mode, alignment, check_owner, etc.
}

Expand Down Expand Up @@ -1989,17 +2017,37 @@ pub enum DmaBufError {
InsufficientDescriptors,
/// Descriptors or buffers are not located in a supported memory region
UnsupportedMemoryRegion,
/// Buffer is not aligned to the required size
InvalidAlignment,
/// Invalid chunk size: must be > 0 and <= 4092
InvalidChunkSize,
}

/// DMA buffer allignments
#[cfg(all(esp32s3, psram))]
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DmaBufBlkSize {
/// 16 bytes
Size16 = 16,
/// 32 bytes
Size32 = 32,
/// 64 bytes
Size64 = 64,
}

/// DMA transmit buffer
///
/// This is a contiguous buffer linked together by DMA descriptors of length
/// 4092. It can only be used for transmitting data to a peripheral's FIFO.
/// 4092 at most. It can only be used for transmitting data to a peripheral's FIFO.
/// See [DmaRxBuf] for receiving data.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DmaTxBuf {
descriptors: &'static mut [DmaDescriptor],
buffer: &'static mut [u8],
chunk_size: usize,
block_size: Option<DmaBufBlkSize>,
}

impl DmaTxBuf {
Expand All @@ -2009,23 +2057,50 @@ impl DmaTxBuf {
/// Each descriptor can handle 4092 bytes worth of buffer.
///
/// Both the descriptors and buffer must be in DMA-capable memory.
/// Only DRAM is supported.
/// Only DRAM is supported for descriptors.
pub fn new(
descriptors: &'static mut [DmaDescriptor],
buffer: &'static mut [u8],
) -> Result<Self, DmaBufError> {
let min_descriptors = buffer.len().div_ceil(CHUNK_SIZE);
Self::new_with_chunk_size(descriptors, buffer, CHUNK_SIZE, None)
}

/// Creates a new [DmaTxBuf] from some descriptors and a buffer.
///
/// There must be enough descriptors for the provided buffer.
/// Each descriptor can handle at most 4092 bytes worth of buffer.
/// The chunk size must be greater than 0 and less than or equal to 4092.
/// Optionally, a block size can be provided for PSRAM & Burst transfers.
///
/// Both the descriptors and buffer must be in DMA-capable memory.
/// Only DRAM is supported for descriptors.
pub fn new_with_chunk_size(
descriptors: &'static mut [DmaDescriptor],
buffer: &'static mut [u8],
chunk_size: usize,
block_size: Option<DmaBufBlkSize>,
) -> Result<Self, DmaBufError> {
if chunk_size == 0 || chunk_size > 4092 {
return Err(DmaBufError::InvalidChunkSize);
}
let min_descriptors = buffer.len().div_ceil(chunk_size);
if descriptors.len() < min_descriptors {
return Err(DmaBufError::InsufficientDescriptors);
}

if !is_slice_in_dram(descriptors) || !is_slice_in_dram(buffer) {
// descriptors are required to be in DRAM
if !is_slice_in_dram(descriptors) {
return Err(DmaBufError::UnsupportedMemoryRegion);
}

// buffer can be either DRAM or PSRAM (if supported)
if !is_slice_in_dram(buffer) && !is_slice_in_psram(buffer) {
return Err(DmaBufError::UnsupportedMemoryRegion);
}

// Setup size and buffer pointer as these will not change for the remainder of
// this object's lifetime
let chunk_iter = descriptors.iter_mut().zip(buffer.chunks_mut(CHUNK_SIZE));
let chunk_iter = descriptors.iter_mut().zip(buffer.chunks_mut(chunk_size));
for (desc, chunk) in chunk_iter {
desc.set_size(chunk.len());
desc.buffer = chunk.as_mut_ptr();
Expand All @@ -2034,6 +2109,8 @@ impl DmaTxBuf {
let mut buf = Self {
descriptors,
buffer,
chunk_size,
block_size,
};
buf.set_length(buf.capacity());

Expand Down Expand Up @@ -2072,7 +2149,7 @@ impl DmaTxBuf {
assert!(len <= self.buffer.len());

// Get the minimum number of descriptors needed for this length of data.
let descriptor_count = len.div_ceil(CHUNK_SIZE).max(1);
let descriptor_count = len.div_ceil(self.chunk_size).max(1);
let required_descriptors = &mut self.descriptors[0..descriptor_count];

// Link up the relevant descriptors.
Expand Down Expand Up @@ -2133,8 +2210,14 @@ impl DmaTxBuffer for DmaTxBuf {
}
}

#[cfg(esp32s3)]
if crate::soc::is_valid_psram_address(self.buffer.as_ptr() as u32) {
unsafe {crate::soc::cache_writeback_addr(self.buffer.as_ptr() as u32, self.buffer.len() as u32)};
}

Preparation {
start: self.descriptors.as_mut_ptr(),
block_size: self.block_size,
}
}

Expand Down Expand Up @@ -2371,6 +2454,7 @@ impl DmaRxBuffer for DmaRxBuf {

Preparation {
start: self.descriptors.as_mut_ptr(),
block_size: None,
}
}

Expand Down Expand Up @@ -2563,6 +2647,7 @@ impl DmaTxBuffer for DmaRxTxBuf {

Preparation {
start: self.tx_descriptors.as_mut_ptr(),
block_size: None, // TODO: support block size!
}
}

Expand Down Expand Up @@ -2592,6 +2677,7 @@ impl DmaRxBuffer for DmaRxTxBuf {

Preparation {
start: self.rx_descriptors.as_mut_ptr(),
block_size: None, // TODO: support block size!
}
}

Expand Down
7 changes: 7 additions & 0 deletions esp-hal/src/soc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ pub(crate) fn is_valid_psram_address(address: u32) -> bool {
false
}

#[allow(unused)]
pub(crate) fn is_slice_in_psram<T>(slice: &[T]) -> bool {
let start = slice.as_ptr() as u32;
let end = start + slice.len() as u32;
is_valid_psram_address(start) && is_valid_psram_address(end)
}

#[allow(unused)]
pub(crate) fn is_valid_memory_address(address: u32) -> bool {
is_valid_ram_address(address) || is_valid_psram_address(address)
Expand Down
8 changes: 7 additions & 1 deletion hil-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ harness = false
name = "spi_half_duplex_write"
harness = false

[[test]]
name = "spi_half_duplex_write_psram"
harness = false

[[test]]
name = "systimer"
harness = false
Expand Down Expand Up @@ -187,6 +191,7 @@ embedded-hal = "1.0.0"
embedded-hal-02 = { version = "0.2.7", package = "embedded-hal", features = ["unproven"] }
embedded-hal-async = "1.0.0"
embedded-hal-nb = { version = "1.0.0", optional = true }
esp-alloc = { path = "../esp-alloc", optional = true }
esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "panic-handler", "defmt", "semihosting"] }
esp-hal = { path = "../esp-hal", features = ["defmt", "digest"], optional = true }
esp-hal-embassy = { path = "../esp-hal-embassy", optional = true }
Expand Down Expand Up @@ -215,7 +220,7 @@ esp-metadata = { version = "0.3.0", path = "../esp-metadata" }
[features]
default = ["embassy"]

defmt = ["dep:defmt-rtt"]
defmt = ["dep:defmt-rtt", "embedded-test/defmt"]

# Device support (required!):
esp32 = [
Expand Down Expand Up @@ -252,6 +257,7 @@ generic-queue = [
integrated-timers = [
"esp-hal-embassy/integrated-timers",
]
psram = ["esp-hal/opsram-2m", "dep:esp-alloc"]

# https://doc.rust-lang.org/cargo/reference/profiles.html#test
# Test and bench profiles inherit from dev and release respectively.
Expand Down
Loading

0 comments on commit 0812219

Please sign in to comment.