diff options
| author | James Munns <[email protected]> | 2025-12-09 18:33:01 +0100 |
|---|---|---|
| committer | James Munns <[email protected]> | 2025-12-09 18:33:01 +0100 |
| commit | a75aa12d928d1ba0c1759052a652426ab965f739 (patch) | |
| tree | 4365146214d6387a035514416c690479a3ab9f79 /embassy-mcxa/src | |
| parent | 3e7de3a5d81e32e77aeb5232e5a7f512ce39db0e (diff) | |
Add error state for DMA transfers
Diffstat (limited to 'embassy-mcxa/src')
| -rw-r--r-- | embassy-mcxa/src/dma.rs | 160 |
1 files changed, 154 insertions, 6 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)] | ||
| 1834 | pub struct TransferErrorRaw(u8); | ||
| 1835 | |||
| 1836 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 1837 | #[derive(Copy, Clone, Debug)] | ||
| 1838 | pub struct TransferErrorRawIter(u8); | ||
| 1839 | |||
| 1840 | impl 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 | |||
| 1913 | impl 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))] | ||
| 1938 | pub 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 | |||
| 1823 | impl<'a> Unpin for Transfer<'a> {} | 1963 | impl<'a> Unpin for Transfer<'a> {} |
| 1824 | 1964 | ||
| 1825 | impl<'a> Future for Transfer<'a> { | 1965 | impl<'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 | } |
