Skip to content

Commit cf7c76d

Browse files
committed
Reuse buffer preparation code
1 parent 5317fe5 commit cf7c76d

File tree

2 files changed

+93
-64
lines changed

2 files changed

+93
-64
lines changed

esp-hal/src/dma/buffers.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,15 @@ pub struct Preparation {
4242

4343
/// Block size for PSRAM transfers.
4444
///
45-
/// The implementation of the buffer must provide a block size if the data
46-
/// is in PSRAM.
45+
/// If the buffer is in PSRAM, the implementation must ensure the following:
46+
///
47+
/// - The implementation of the buffer must provide a non-`None` block size.
48+
/// - For [`TransferDirection::In`] transfers, the implementation of the
49+
/// buffer must invalidate the cache that contains the buffer before the
50+
/// DMA starts.
51+
/// - For [`TransferDirection::Out`] transfers, the implementation of the
52+
/// buffer must write back the cache that contains the buffer before the
53+
/// DMA starts.
4754
#[cfg(esp32s3)]
4855
pub external_memory_block_size: Option<DmaBufBlkSize>,
4956

@@ -52,9 +59,10 @@ pub struct Preparation {
5259
/// The implementation of the buffer must ensure that burst mode is only
5360
/// enabled when alignment requirements are met.
5461
///
55-
/// There are no additional alignment requirements for TX burst transfers,
56-
/// but RX transfers require all descriptors to have buffer pointers and
57-
/// sizes that are a multiple of 4 (word aligned).
62+
/// There are no additional alignment requirements for
63+
/// [`TransferDirection::Out`] burst transfers, but
64+
/// [`TransferDirection::In`] transfers require all descriptors to have
65+
/// buffer pointers and sizes that are a multiple of 4 (word aligned).
5866
pub burst_transfer: BurstTransfer,
5967

6068
/// Configures the "check owner" feature of the DMA channel.

esp-hal/src/dma/mod.rs

Lines changed: 80 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,6 +1756,27 @@ where
17561756
pub fn set_priority(&mut self, priority: DmaPriority) {
17571757
self.rx_impl.set_priority(priority);
17581758
}
1759+
1760+
fn do_prepare(
1761+
&mut self,
1762+
preparation: Preparation,
1763+
peri: DmaPeripheral,
1764+
) -> Result<(), DmaError> {
1765+
debug_assert_eq!(preparation.direction, TransferDirection::In);
1766+
1767+
self.rx_impl.set_burst_mode(preparation.burst_transfer);
1768+
self.rx_impl.set_descr_burst_mode(true);
1769+
self.rx_impl.set_check_owner(preparation.check_owner);
1770+
1771+
compiler_fence(core::sync::atomic::Ordering::SeqCst);
1772+
1773+
self.rx_impl.clear_all();
1774+
self.rx_impl.reset();
1775+
self.rx_impl.set_link_addr(preparation.start as u32);
1776+
self.rx_impl.set_peripheral(peri as u8);
1777+
1778+
Ok(())
1779+
}
17591780
}
17601781

17611782
impl<M, CH> crate::private::Sealed for ChannelRx<'_, M, CH>
@@ -1770,24 +1791,18 @@ where
17701791
M: Mode,
17711792
CH: DmaChannel,
17721793
{
1794+
// TODO: used by I2S, which should be rewritten to use the Preparation-based
1795+
// API.
17731796
unsafe fn prepare_transfer_without_start(
17741797
&mut self,
17751798
peri: DmaPeripheral,
17761799
chain: &DescriptorChain,
17771800
) -> Result<(), DmaError> {
1778-
// if self.burst_mode
1779-
// && chain
1780-
// .descriptors
1781-
// .iter()
1782-
// .any(|d| d.len() % 4 != 0 || d.buffer as u32 % 4 != 0)
1783-
//{
1784-
// return Err(DmaError::InvalidAlignment);
1785-
//}
1786-
1787-
// for esp32s3 we check each descriptor buffer that points to psram for
1788-
// alignment and invalidate the cache for that buffer
1801+
// For ESP32-S3 we check each descriptor buffer that points to PSRAM for
1802+
// alignment and invalidate the cache for that buffer.
17891803
// NOTE: for RX the `buffer` and `size` need to be aligned but the `len` does
17901804
// not. TRM section 3.4.9
1805+
// Note that DmaBuffer implementations are required to do this for us.
17911806
#[cfg(esp32s3)]
17921807
for des in chain.descriptors.iter() {
17931808
// we are forcing the DMA alignment to the cache line size
@@ -1802,14 +1817,17 @@ where
18021817
}
18031818
}
18041819

1805-
compiler_fence(core::sync::atomic::Ordering::SeqCst);
1806-
1807-
self.rx_impl.clear_all();
1808-
self.rx_impl.reset();
1809-
self.rx_impl.set_link_addr(chain.first() as u32);
1810-
self.rx_impl.set_peripheral(peri as u8);
1811-
1812-
Ok(())
1820+
self.do_prepare(
1821+
Preparation {
1822+
start: chain.first().cast_mut(),
1823+
#[cfg(esp32s3)]
1824+
external_memory_block_size: None,
1825+
direction: TransferDirection::In,
1826+
burst_transfer: BurstTransfer::Disabled,
1827+
check_owner: Some(false),
1828+
},
1829+
peri,
1830+
)
18131831
}
18141832

18151833
unsafe fn prepare_transfer<BUF: DmaRxBuffer>(
@@ -1819,20 +1837,7 @@ where
18191837
) -> Result<(), DmaError> {
18201838
let preparation = buffer.prepare();
18211839

1822-
debug_assert_eq!(preparation.direction, TransferDirection::In);
1823-
1824-
self.rx_impl.set_burst_mode(preparation.burst_transfer);
1825-
self.rx_impl.set_descr_burst_mode(true);
1826-
self.rx_impl.set_check_owner(preparation.check_owner);
1827-
1828-
compiler_fence(core::sync::atomic::Ordering::SeqCst);
1829-
1830-
self.rx_impl.clear_all();
1831-
self.rx_impl.reset();
1832-
self.rx_impl.set_link_addr(preparation.start as u32);
1833-
self.rx_impl.set_peripheral(peri as u8);
1834-
1835-
Ok(())
1840+
self.do_prepare(preparation, peri)
18361841
}
18371842

18381843
fn start_transfer(&mut self) -> Result<(), DmaError> {
@@ -2041,6 +2046,32 @@ where
20412046
pub fn set_priority(&mut self, priority: DmaPriority) {
20422047
self.tx_impl.set_priority(priority);
20432048
}
2049+
2050+
fn do_prepare(
2051+
&mut self,
2052+
preparation: Preparation,
2053+
peri: DmaPeripheral,
2054+
) -> Result<(), DmaError> {
2055+
debug_assert_eq!(preparation.direction, TransferDirection::Out);
2056+
2057+
#[cfg(esp32s3)]
2058+
if let Some(block_size) = preparation.external_memory_block_size {
2059+
self.set_ext_mem_block_size(block_size.into());
2060+
}
2061+
2062+
self.tx_impl.set_burst_mode(preparation.burst_transfer);
2063+
self.tx_impl.set_descr_burst_mode(true);
2064+
self.tx_impl.set_check_owner(preparation.check_owner);
2065+
2066+
compiler_fence(core::sync::atomic::Ordering::SeqCst);
2067+
2068+
self.tx_impl.clear_all();
2069+
self.tx_impl.reset();
2070+
self.tx_impl.set_link_addr(preparation.start as u32);
2071+
self.tx_impl.set_peripheral(peri as u8);
2072+
2073+
Ok(())
2074+
}
20442075
}
20452076

20462077
impl<M, CH> crate::private::Sealed for ChannelTx<'_, M, CH>
@@ -2055,6 +2086,8 @@ where
20552086
M: Mode,
20562087
CH: DmaChannel,
20572088
{
2089+
// TODO: used by I2S, which should be rewritten to use the Preparation-based
2090+
// API.
20582091
unsafe fn prepare_transfer_without_start(
20592092
&mut self,
20602093
peri: DmaPeripheral,
@@ -2063,7 +2096,8 @@ where
20632096
// Based on the ESP32-S3 TRM the alignment check is not needed for TX
20642097

20652098
// For esp32s3 we check each descriptor buffer that points to PSRAM for
2066-
// alignment and writeback the cache for that buffer
2099+
// alignment and writeback the cache for that buffer.
2100+
// Note that DmaBuffer implementations are required to do this for us.
20672101
#[cfg(esp32s3)]
20682102
for des in chain.descriptors.iter() {
20692103
// we are forcing the DMA alignment to the cache line size
@@ -2078,12 +2112,17 @@ where
20782112
}
20792113
}
20802114

2081-
compiler_fence(core::sync::atomic::Ordering::SeqCst);
2082-
2083-
self.tx_impl.clear_all();
2084-
self.tx_impl.reset();
2085-
self.tx_impl.set_link_addr(chain.first() as u32);
2086-
self.tx_impl.set_peripheral(peri as u8);
2115+
self.do_prepare(
2116+
Preparation {
2117+
start: chain.first().cast_mut(),
2118+
#[cfg(esp32s3)]
2119+
external_memory_block_size: None,
2120+
direction: TransferDirection::Out,
2121+
burst_transfer: BurstTransfer::Disabled,
2122+
check_owner: Some(false),
2123+
},
2124+
peri,
2125+
)?;
20872126

20882127
// enable descriptor write back in circular mode
20892128
self.tx_impl
@@ -2099,25 +2138,7 @@ where
20992138
) -> Result<(), DmaError> {
21002139
let preparation = buffer.prepare();
21012140

2102-
debug_assert_eq!(preparation.direction, TransferDirection::Out);
2103-
2104-
#[cfg(esp32s3)]
2105-
if let Some(block_size) = preparation.external_memory_block_size {
2106-
self.set_ext_mem_block_size(block_size.into());
2107-
}
2108-
2109-
self.tx_impl.set_burst_mode(preparation.burst_transfer);
2110-
self.tx_impl.set_descr_burst_mode(true);
2111-
self.tx_impl.set_check_owner(preparation.check_owner);
2112-
2113-
compiler_fence(core::sync::atomic::Ordering::SeqCst);
2114-
2115-
self.tx_impl.clear_all();
2116-
self.tx_impl.reset();
2117-
self.tx_impl.set_link_addr(preparation.start as u32);
2118-
self.tx_impl.set_peripheral(peri as u8);
2119-
2120-
Ok(())
2141+
self.do_prepare(preparation, peri)
21212142
}
21222143

21232144
fn start_transfer(&mut self) -> Result<(), DmaError> {

0 commit comments

Comments
 (0)