aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2025-12-09 18:33:01 +0100
committerJames Munns <[email protected]>2025-12-09 18:33:01 +0100
commita75aa12d928d1ba0c1759052a652426ab965f739 (patch)
tree4365146214d6387a035514416c690479a3ab9f79
parent3e7de3a5d81e32e77aeb5232e5a7f512ce39db0e (diff)
Add error state for DMA transfers
-rw-r--r--embassy-mcxa/src/dma.rs160
-rw-r--r--examples/mcxa/src/bin/dma_mem_to_mem.rs2
-rw-r--r--examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs4
3 files changed, 157 insertions, 9 deletions
diff --git a/embassy-mcxa/src/dma.rs b/embassy-mcxa/src/dma.rs
index ffe816469..e31cf5907 100644
--- a/embassy-mcxa/src/dma.rs
+++ b/embassy-mcxa/src/dma.rs
@@ -1774,7 +1774,7 @@ impl<'a> Transfer<'a> {
1774 /// 1774 ///
1775 /// The transfer must be configured with `TransferOptions::half_transfer_interrupt = true` 1775 /// The transfer must be configured with `TransferOptions::half_transfer_interrupt = true`
1776 /// for this method to work correctly. 1776 /// for this method to work correctly.
1777 pub async fn wait_half(&mut self) -> bool { 1777 pub async fn wait_half(&mut self) -> Result<bool, TransferErrorRaw> {
1778 use core::future::poll_fn; 1778 use core::future::poll_fn;
1779 1779
1780 poll_fn(|cx| { 1780 poll_fn(|cx| {
@@ -1783,19 +1783,27 @@ impl<'a> Transfer<'a> {
1783 // Register the half-transfer waker 1783 // Register the half-transfer waker
1784 state.half_waker.register(cx.waker()); 1784 state.half_waker.register(cx.waker());
1785 1785
1786 // Check if we're past the half-way point 1786 // Check if there's an error
1787 let t = self.channel.tcd(); 1787 let t = self.channel.tcd();
1788 let es = t.ch_es().read();
1789 if es.err().is_error() {
1790 // Currently, all error fields are in the lowest 8 bits, as-casting truncates
1791 let errs = es.bits() as u8;
1792 return Poll::Ready(Err(TransferErrorRaw(errs)))
1793 }
1794
1795 // Check if we're past the half-way point
1788 let biter = t.tcd_biter_elinkno().read().biter().bits(); 1796 let biter = t.tcd_biter_elinkno().read().biter().bits();
1789 let citer = t.tcd_citer_elinkno().read().citer().bits(); 1797 let citer = t.tcd_citer_elinkno().read().citer().bits();
1790 let half_point = biter / 2; 1798 let half_point = biter / 2;
1791 1799
1792 if self.channel.is_done() { 1800 if self.channel.is_done() {
1793 // Transfer completed before half-transfer 1801 // Transfer completed before half-transfer
1794 Poll::Ready(false) 1802 Poll::Ready(Ok(false))
1795 } else if citer <= half_point { 1803 } else if citer <= half_point {
1796 // We're past the half-way point 1804 // We're past the half-way point
1797 fence(Ordering::SeqCst); 1805 fence(Ordering::SeqCst);
1798 Poll::Ready(true) 1806 Poll::Ready(Ok(true))
1799 } else { 1807 } else {
1800 Poll::Pending 1808 Poll::Pending
1801 } 1809 }
@@ -1820,10 +1828,142 @@ impl<'a> Transfer<'a> {
1820 } 1828 }
1821} 1829}
1822 1830
1831/// Raw transfer error bits. Can be queried or all errors can be iterated over
1832#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1833#[derive(Copy, Clone, Debug)]
1834pub struct TransferErrorRaw(u8);
1835
1836#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1837#[derive(Copy, Clone, Debug)]
1838pub struct TransferErrorRawIter(u8);
1839
1840impl TransferErrorRaw {
1841 const MAP: &[(u8, TransferError)] = &[
1842 (1 << 0, TransferError::DestinationBus),
1843 (1 << 1, TransferError::SourceBus),
1844 (1 << 2, TransferError::ScatterGatherConfiguration),
1845 (1 << 3, TransferError::NbytesCiterConfiguration),
1846 (1 << 4, TransferError::DestinationOffset),
1847 (1 << 5, TransferError::DestinationAddress),
1848 (1 << 6, TransferError::SourceOffset),
1849 (1 << 7, TransferError::SourceAddress),
1850 ];
1851
1852 /// Convert to an iterator of contained errors
1853 pub fn err_iter(self) -> TransferErrorRawIter {
1854 TransferErrorRawIter(self.0)
1855 }
1856
1857 /// Destination Bus Error
1858 #[inline]
1859 pub fn has_destination_bus_err(&self) -> bool {
1860 (self.0 & (1 << 0)) != 0
1861 }
1862
1863 /// Source Bus Error
1864 #[inline]
1865 pub fn has_source_bus_err(&self) -> bool {
1866 (self.0 & (1 << 1)) != 0
1867 }
1868
1869 /// Indicates that `TCDn_DLAST_SGA` is not on a 32-byte boundary. This field is
1870 /// checked at the beginning of a scatter/gather operation after major loop completion
1871 /// if `TCDn_CSR[ESG]` is enabled.
1872 #[inline]
1873 pub fn has_scatter_gather_configuration_err(&self) -> bool {
1874 (self.0 & (1 << 2)) != 0
1875 }
1876
1877 /// This error indicates that one of the following has occurred:
1878 ///
1879 /// * `TCDn_NBYTES` is not a multiple of `TCDn_ATTR[SSIZE]` and `TCDn_ATTR[DSIZE]`
1880 /// * `TCDn_CITER[CITER]` is equal to zero
1881 /// * `TCDn_CITER[ELINK]` is not equal to `TCDn_BITER[ELINK]`
1882 #[inline]
1883 pub fn has_nbytes_citer_configuration_err(&self) -> bool {
1884 (self.0 & (1 << 3)) != 0
1885 }
1886
1887 /// `TCDn_DOFF` is inconsistent with `TCDn_ATTR[DSIZE]`.
1888 #[inline]
1889 pub fn has_destination_offset_err(&self) -> bool {
1890 (self.0 & (1 << 4)) != 0
1891 }
1892
1893 /// `TCDn_DADDR` is inconsistent with `TCDn_ATTR[DSIZE]`.
1894 #[inline]
1895 pub fn has_destination_address_err(&self) -> bool {
1896 (self.0 & (1 << 5)) != 0
1897 }
1898
1899 /// `TCDn_SOFF` is inconsistent with `TCDn_ATTR[SSIZE]`.
1900 #[inline]
1901 pub fn has_source_offset_err(&self) -> bool {
1902 (self.0 & (1 << 6)) != 0
1903 }
1904
1905 /// `TCDn_SADDR` is inconsistent with `TCDn_ATTR[SSIZE]`
1906 #[inline]
1907 pub fn has_source_address_err(&self) -> bool {
1908 (self.0 & (1 << 7)) != 0
1909 }
1910
1911}
1912
1913impl Iterator for TransferErrorRawIter {
1914 type Item = TransferError;
1915
1916 fn next(&mut self) -> Option<Self::Item> {
1917 if self.0 == 0 {
1918 return None;
1919 }
1920
1921 for (mask, var) in TransferErrorRaw::MAP {
1922 // If the bit is set...
1923 if self.0 | mask != 0 {
1924 // clear the bit
1925 self.0 &= !mask;
1926 // and return the answer
1927 return Some(*var);
1928 }
1929 }
1930
1931 // Shouldn't happen, but oh well.
1932 None
1933 }
1934}
1935
1936#[derive(Copy, Clone, Debug)]
1937#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1938pub enum TransferError {
1939 /// `TCDn_SADDR` is inconsistent with `TCDn_ATTR[SSIZE]`
1940 SourceAddress,
1941 /// `TCDn_SOFF` is inconsistent with `TCDn_ATTR[SSIZE]`.
1942 SourceOffset,
1943 /// `TCDn_DADDR` is inconsistent with `TCDn_ATTR[DSIZE]`.
1944 DestinationAddress,
1945 /// `TCDn_DOFF` is inconsistent with `TCDn_ATTR[DSIZE]`.
1946 DestinationOffset,
1947 /// This error indicates that one of the following has occurred:
1948 ///
1949 /// * `TCDn_NBYTES` is not a multiple of `TCDn_ATTR[SSIZE]` and `TCDn_ATTR[DSIZE]`
1950 /// * `TCDn_CITER[CITER]` is equal to zero
1951 /// * `TCDn_CITER[ELINK]` is not equal to `TCDn_BITER[ELINK]`
1952 NbytesCiterConfiguration,
1953 /// Indicates that `TCDn_DLAST_SGA` is not on a 32-byte boundary. This field is
1954 /// checked at the beginning of a scatter/gather operation after major loop completion
1955 /// if `TCDn_CSR[ESG]` is enabled.
1956 ScatterGatherConfiguration,
1957 /// Source Bus Error
1958 SourceBus,
1959 /// Destination Bus Error
1960 DestinationBus,
1961}
1962
1823impl<'a> Unpin for Transfer<'a> {} 1963impl<'a> Unpin for Transfer<'a> {}
1824 1964
1825impl<'a> Future for Transfer<'a> { 1965impl<'a> Future for Transfer<'a> {
1826 type Output = (); 1966 type Output = Result<(), TransferErrorRaw>;
1827 1967
1828 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { 1968 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1829 let state = &STATES[self.channel.index]; 1969 let state = &STATES[self.channel.index];
@@ -1836,7 +1976,15 @@ impl<'a> Future for Transfer<'a> {
1836 if done { 1976 if done {
1837 // Ensure all DMA writes are visible before returning 1977 // Ensure all DMA writes are visible before returning
1838 fence(Ordering::SeqCst); 1978 fence(Ordering::SeqCst);
1839 Poll::Ready(()) 1979
1980 let es = self.channel.tcd().ch_es().read();
1981 if es.err().is_error() {
1982 // Currently, all error fields are in the lowest 8 bits, as-casting truncates
1983 let errs = es.bits() as u8;
1984 Poll::Ready(Err(TransferErrorRaw(errs)))
1985 } else {
1986 Poll::Ready(Ok(()))
1987 }
1840 } else { 1988 } else {
1841 Poll::Pending 1989 Poll::Pending
1842 } 1990 }
diff --git a/examples/mcxa/src/bin/dma_mem_to_mem.rs b/examples/mcxa/src/bin/dma_mem_to_mem.rs
index a9ee9ffdf..b38baccb5 100644
--- a/examples/mcxa/src/bin/dma_mem_to_mem.rs
+++ b/examples/mcxa/src/bin/dma_mem_to_mem.rs
@@ -71,7 +71,7 @@ async fn main(_spawner: Spawner) {
71 // Perform type-safe memory-to-memory transfer using Embassy-style async API 71 // Perform type-safe memory-to-memory transfer using Embassy-style async API
72 // Using async `.await` - the executor can run other tasks while waiting! 72 // Using async `.await` - the executor can run other tasks while waiting!
73 let transfer = dma_ch0.mem_to_mem(src, dst, options).unwrap(); 73 let transfer = dma_ch0.mem_to_mem(src, dst, options).unwrap();
74 transfer.await; 74 transfer.await.unwrap();
75 75
76 defmt::info!("DMA mem-to-mem transfer complete!"); 76 defmt::info!("DMA mem-to-mem transfer complete!");
77 defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); 77 defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice());
diff --git a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs
index 1e16e60ba..80df40449 100644
--- a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs
+++ b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs
@@ -221,13 +221,13 @@ async fn main(_spawner: Spawner) {
221 221
222 // Wait for half-transfer (first 4 elements) 222 // Wait for half-transfer (first 4 elements)
223 defmt::info!("Waiting for first half..."); 223 defmt::info!("Waiting for first half...");
224 let _ok = transfer.wait_half().await; 224 let _ok = transfer.wait_half().await.unwrap();
225 225
226 defmt::info!("Half-transfer complete!"); 226 defmt::info!("Half-transfer complete!");
227 227
228 // Wait for complete transfer 228 // Wait for complete transfer
229 defmt::info!("Waiting for second half..."); 229 defmt::info!("Waiting for second half...");
230 transfer.await; 230 transfer.await.unwrap();
231 231
232 defmt::info!("Transfer complete! Full DST2: {=[?]}", dst2.as_slice()); 232 defmt::info!("Transfer complete! Full DST2: {=[?]}", dst2.as_slice());
233 233