aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-mcxa/src/dma.rs423
-rw-r--r--examples/mcxa/src/bin/dma_mem_to_mem.rs75
2 files changed, 169 insertions, 329 deletions
diff --git a/embassy-mcxa/src/dma.rs b/embassy-mcxa/src/dma.rs
index d563c2e29..b3ea94dbf 100644
--- a/embassy-mcxa/src/dma.rs
+++ b/embassy-mcxa/src/dma.rs
@@ -581,7 +581,6 @@ pub struct DmaChannel<C: Channel> {
581// - [`write_to_peripheral()`](DmaChannel::write_to_peripheral) - Memory-to-peripheral with custom eDMA TCD block 581// - [`write_to_peripheral()`](DmaChannel::write_to_peripheral) - Memory-to-peripheral with custom eDMA TCD block
582// - [`read_from_peripheral()`](DmaChannel::read_from_peripheral) - Peripheral-to-memory with custom eDMA TCD block 582// - [`read_from_peripheral()`](DmaChannel::read_from_peripheral) - Peripheral-to-memory with custom eDMA TCD block
583// - [`mem_to_mem()`](DmaChannel::mem_to_mem) - Memory-to-memory using default eDMA TCD block 583// - [`mem_to_mem()`](DmaChannel::mem_to_mem) - Memory-to-memory using default eDMA TCD block
584// - [`transfer_mem_to_mem()`](DmaChannel::transfer_mem_to_mem) - Memory-to-memory with custom eDMA TCD block
585// 584//
586// The `Transfer` manages the DMA lifecycle automatically: 585// The `Transfer` manages the DMA lifecycle automatically:
587// - Enables channel request 586// - Enables channel request
@@ -673,6 +672,87 @@ impl<C: Channel> DmaChannel<C> {
673 edma.tcd(C::INDEX) 672 edma.tcd(C::INDEX)
674 } 673 }
675 674
675 fn clear_tcd(t: &'static pac::edma_0_tcd0::Tcd) {
676 // Full TCD reset following NXP SDK pattern (EDMA_TcdResetExt).
677 // Reset ALL TCD registers to 0 to clear any stale configuration from
678 // previous transfers. This is critical when reusing a channel.
679 t.tcd_saddr().write(|w| unsafe { w.saddr().bits(0) });
680 t.tcd_soff().write(|w| unsafe { w.soff().bits(0) });
681 t.tcd_attr().write(|w| unsafe { w.bits(0) });
682 t.tcd_nbytes_mloffno().write(|w| unsafe { w.nbytes().bits(0) });
683 t.tcd_slast_sda().write(|w| unsafe { w.slast_sda().bits(0) });
684 t.tcd_daddr().write(|w| unsafe { w.daddr().bits(0) });
685 t.tcd_doff().write(|w| unsafe { w.doff().bits(0) });
686 t.tcd_citer_elinkno().write(|w| unsafe { w.bits(0) });
687 t.tcd_dlast_sga().write(|w| unsafe { w.dlast_sga().bits(0) });
688 t.tcd_csr().write(|w| unsafe { w.bits(0) }); // Clear CSR completely
689 t.tcd_biter_elinkno().write(|w| unsafe { w.bits(0) });
690 }
691
692 #[inline]
693 fn set_major_loop_ct_elinkno(t: &'static pac::edma_0_tcd0::Tcd, count: u16) {
694 t.tcd_biter_elinkno().write(|w| unsafe { w.biter().bits(count) });
695 t.tcd_citer_elinkno().write(|w| unsafe { w.citer().bits(count) });
696 }
697
698 #[inline]
699 fn set_minor_loop_ct_no_offsets(t: &'static pac::edma_0_tcd0::Tcd, count: u32) {
700 t.tcd_nbytes_mloffno().write(|w| unsafe { w.nbytes().bits(count) });
701 }
702
703 #[inline]
704 fn set_no_final_adjustments(t: &'static pac::edma_0_tcd0::Tcd) {
705 // No source/dest adjustment after major loop
706 t.tcd_slast_sda().write(|w| unsafe { w.slast_sda().bits(0) });
707 t.tcd_dlast_sga().write(|w| unsafe { w.dlast_sga().bits(0) });
708 }
709
710 #[inline]
711 fn set_source_ptr<T>(t: &'static pac::edma_0_tcd0::Tcd, p: *const T) {
712 t.tcd_saddr().write(|w| unsafe { w.saddr().bits(p as u32) });
713 }
714
715 #[inline]
716 fn set_source_increment(t: &'static pac::edma_0_tcd0::Tcd, sz: WordSize) {
717 t.tcd_soff().write(|w| unsafe { w.soff().bits(sz.bytes() as u16) });
718 }
719
720 #[inline]
721 fn set_source_fixed(t: &'static pac::edma_0_tcd0::Tcd) {
722 t.tcd_soff().write(|w| unsafe { w.soff().bits(0) });
723 }
724
725 #[inline]
726 fn set_dest_ptr<T>(t: &'static pac::edma_0_tcd0::Tcd, p: *mut T) {
727 t.tcd_daddr().write(|w| unsafe { w.daddr().bits(p as u32) });
728 }
729
730 #[inline]
731 fn set_dest_increment(t: &'static pac::edma_0_tcd0::Tcd, sz: WordSize) {
732 t.tcd_doff().write(|w| unsafe { w.doff().bits(sz.bytes() as u16) });
733 }
734
735 #[inline]
736 fn set_dest_fixed(t: &'static pac::edma_0_tcd0::Tcd) {
737 t.tcd_doff().write(|w| unsafe { w.doff().bits(0) });
738 }
739
740 #[inline]
741 fn set_even_transfer_size(t: &'static pac::edma_0_tcd0::Tcd, sz: WordSize) {
742 let hw_size = sz.to_hw_size();
743 t.tcd_attr().write(|w| unsafe { w.ssize().bits(hw_size).dsize().bits(hw_size) } );
744 }
745
746 #[inline]
747 fn reset_channel_state(t: &'static pac::edma_0_tcd0::Tcd) {
748 // CSR: Resets to all zeroes (disabled), "done" is cleared by writing 1
749 t.ch_csr().write(|w| w.done().clear_bit_by_one());
750 // ES: Resets to all zeroes (disabled), "err" is cleared by writing 1
751 t.ch_es().write(|w| w.err().clear_bit_by_one());
752 // INT: Resets to all zeroes (disabled), "int" is cleared by writing 1
753 t.ch_int().write(|w| w.int().clear_bit_by_one());
754 }
755
676 /// Start an async transfer. 756 /// Start an async transfer.
677 /// 757 ///
678 /// The channel must already be configured. This enables the channel 758 /// The channel must already be configured. This enables the channel
@@ -716,32 +796,7 @@ impl<C: Channel> DmaChannel<C> {
716 /// 796 ///
717 /// The source and destination buffers must remain valid for the 797 /// The source and destination buffers must remain valid for the
718 /// duration of the transfer. 798 /// duration of the transfer.
719 pub unsafe fn mem_to_mem<W: Word>(&self, src: &[W], dst: &mut [W], options: TransferOptions) -> Transfer<'_> { 799 pub fn mem_to_mem<W: Word>(&self, src: &[W], dst: &mut [W], options: TransferOptions) -> Transfer<'_> {
720 self.transfer_mem_to_mem(src, dst, options)
721 }
722
723 /// Perform a memory-to-memory DMA transfer.
724 ///
725 /// This is a type-safe wrapper that uses the `Word` trait to determine
726 /// the correct transfer width automatically.
727 ///
728 /// # Arguments
729 ///
730 /// * `edma` - Reference to the eDMA TCD register block
731 /// * `src` - Source buffer
732 /// * `dst` - Destination buffer (must be at least as large as src)
733 /// * `options` - Transfer configuration options
734 ///
735 /// # Safety
736 ///
737 /// The source and destination buffers must remain valid for the
738 /// duration of the transfer.
739 pub unsafe fn transfer_mem_to_mem<W: Word>(
740 &self,
741 src: &[W],
742 dst: &mut [W],
743 options: TransferOptions,
744 ) -> Transfer<'_> {
745 assert!(!src.is_empty()); 800 assert!(!src.is_empty());
746 assert!(dst.len() >= src.len()); 801 assert!(dst.len() >= src.len());
747 assert!(src.len() <= 0x7fff); 802 assert!(src.len() <= 0x7fff);
@@ -752,36 +807,12 @@ impl<C: Channel> DmaChannel<C> {
752 let t = self.tcd(); 807 let t = self.tcd();
753 808
754 // Reset channel state - clear DONE, disable requests, clear errors 809 // Reset channel state - clear DONE, disable requests, clear errors
755 t.ch_csr().write(|w| { 810 Self::reset_channel_state(t);
756 w.erq()
757 .disable()
758 .earq()
759 .disable()
760 .eei()
761 .no_error()
762 .done()
763 .clear_bit_by_one()
764 });
765 t.ch_es().write(|w| w.err().clear_bit_by_one());
766 t.ch_int().write(|w| w.int().clear_bit_by_one());
767 811
768 // Memory barrier to ensure channel state is fully reset before touching TCD 812 // Memory barrier to ensure channel state is fully reset before touching TCD
769 cortex_m::asm::dsb(); 813 cortex_m::asm::dsb();
770 814
771 // Full TCD reset following NXP SDK pattern (EDMA_TcdResetExt). 815 Self::clear_tcd(t);
772 // Reset ALL TCD registers to 0 to clear any stale configuration from
773 // previous transfers. This is critical when reusing a channel.
774 t.tcd_saddr().write(|w| w.saddr().bits(0));
775 t.tcd_soff().write(|w| w.soff().bits(0));
776 t.tcd_attr().write(|w| w.bits(0));
777 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(0));
778 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0));
779 t.tcd_daddr().write(|w| w.daddr().bits(0));
780 t.tcd_doff().write(|w| w.doff().bits(0));
781 t.tcd_citer_elinkno().write(|w| w.bits(0));
782 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0));
783 t.tcd_csr().write(|w| w.bits(0)); // Clear CSR completely
784 t.tcd_biter_elinkno().write(|w| w.bits(0));
785 816
786 // Memory barrier after TCD reset 817 // Memory barrier after TCD reset
787 cortex_m::asm::dsb(); 818 cortex_m::asm::dsb();
@@ -792,28 +823,25 @@ impl<C: Channel> DmaChannel<C> {
792 // Now configure the new transfer 823 // Now configure the new transfer
793 824
794 // Source address and increment 825 // Source address and increment
795 t.tcd_saddr().write(|w| w.saddr().bits(src.as_ptr() as u32)); 826 Self::set_source_ptr(t, src.as_ptr());
796 t.tcd_soff().write(|w| w.soff().bits(size.bytes() as u16)); 827 Self::set_source_increment(t, size);
797 828
798 // Destination address and increment 829 // Destination address and increment
799 t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); 830 Self::set_dest_ptr(t, dst.as_mut_ptr());
800 t.tcd_doff().write(|w| w.doff().bits(size.bytes() as u16)); 831 Self::set_dest_increment(t, size);
801 832
802 // Transfer attributes (size) 833 // Transfer attributes (size)
803 let hw_size = size.to_hw_size(); 834 Self::set_even_transfer_size(t, size);
804 t.tcd_attr().write(|w| w.ssize().bits(hw_size).dsize().bits(hw_size));
805 835
806 // Minor loop: transfer all bytes in one minor loop 836 // Minor loop: transfer all bytes in one minor loop
807 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(byte_count)); 837 Self::set_minor_loop_ct_no_offsets(t, byte_count);
808 838
809 // No source/dest adjustment after major loop 839 // No source/dest adjustment after major loop
810 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); 840 Self::set_no_final_adjustments(t);
811 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0));
812 841
813 // Major loop count = 1 (single major loop) 842 // Major loop count = 1 (single major loop)
814 // Write BITER first, then CITER (CITER must match BITER at start) 843 // Write BITER first, then CITER (CITER must match BITER at start)
815 t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); 844 Self::set_major_loop_ct_elinkno(t, 1);
816 t.tcd_citer_elinkno().write(|w| w.citer().bits(1));
817 845
818 // Memory barrier before setting START 846 // Memory barrier before setting START
819 cortex_m::asm::dsb(); 847 cortex_m::asm::dsb();
@@ -862,10 +890,7 @@ impl<C: Channel> DmaChannel<C> {
862 /// // buffer is now filled with 0xDEADBEEF 890 /// // buffer is now filled with 0xDEADBEEF
863 /// ``` 891 /// ```
864 /// 892 ///
865 /// # Safety 893 pub fn memset<W: Word>(&self, pattern: &W, dst: &mut [W], options: TransferOptions) -> Transfer<'_> {
866 ///
867 /// - The pattern and destination buffer must remain valid for the duration of the transfer.
868 pub unsafe fn memset<W: Word>(&self, pattern: &W, dst: &mut [W], options: TransferOptions) -> Transfer<'_> {
869 assert!(!dst.is_empty()); 894 assert!(!dst.is_empty());
870 assert!(dst.len() <= 0x7fff); 895 assert!(dst.len() <= 0x7fff);
871 896
@@ -877,36 +902,12 @@ impl<C: Channel> DmaChannel<C> {
877 let t = self.tcd(); 902 let t = self.tcd();
878 903
879 // Reset channel state - clear DONE, disable requests, clear errors 904 // Reset channel state - clear DONE, disable requests, clear errors
880 t.ch_csr().write(|w| { 905 Self::reset_channel_state(t);
881 w.erq()
882 .disable()
883 .earq()
884 .disable()
885 .eei()
886 .no_error()
887 .done()
888 .clear_bit_by_one()
889 });
890 t.ch_es().write(|w| w.err().clear_bit_by_one());
891 t.ch_int().write(|w| w.int().clear_bit_by_one());
892 906
893 // Memory barrier to ensure channel state is fully reset before touching TCD 907 // Memory barrier to ensure channel state is fully reset before touching TCD
894 cortex_m::asm::dsb(); 908 cortex_m::asm::dsb();
895 909
896 // Full TCD reset following NXP SDK pattern (EDMA_TcdResetExt). 910 Self::clear_tcd(t);
897 // Reset ALL TCD registers to 0 to clear any stale configuration from
898 // previous transfers. This is critical when reusing a channel.
899 t.tcd_saddr().write(|w| w.saddr().bits(0));
900 t.tcd_soff().write(|w| w.soff().bits(0));
901 t.tcd_attr().write(|w| w.bits(0));
902 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(0));
903 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0));
904 t.tcd_daddr().write(|w| w.daddr().bits(0));
905 t.tcd_doff().write(|w| w.doff().bits(0));
906 t.tcd_citer_elinkno().write(|w| w.bits(0));
907 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0));
908 t.tcd_csr().write(|w| w.bits(0)); // Clear CSR completely
909 t.tcd_biter_elinkno().write(|w| w.bits(0));
910 911
911 // Memory barrier after TCD reset 912 // Memory barrier after TCD reset
912 cortex_m::asm::dsb(); 913 cortex_m::asm::dsb();
@@ -923,29 +924,26 @@ impl<C: Channel> DmaChannel<C> {
923 // START triggers. 924 // START triggers.
924 925
925 // Source: pattern address, fixed (soff=0) 926 // Source: pattern address, fixed (soff=0)
926 t.tcd_saddr().write(|w| w.saddr().bits(pattern as *const W as u32)); 927 Self::set_source_ptr(t, pattern);
927 t.tcd_soff().write(|w| w.soff().bits(0)); // Fixed source - reads pattern repeatedly 928 Self::set_source_fixed(t);
928 929
929 // Destination: memory buffer, incrementing by word size 930 // Destination: memory buffer, incrementing by word size
930 t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); 931 Self::set_dest_ptr(t, dst.as_mut_ptr());
931 t.tcd_doff().write(|w| w.doff().bits(byte_size as u16)); 932 Self::set_dest_increment(t, size);
932 933
933 // Transfer attributes - source and dest are same word size 934 // Transfer attributes - source and dest are same word size
934 let hw_size = size.to_hw_size(); 935 Self::set_even_transfer_size(t, size);
935 t.tcd_attr().write(|w| w.ssize().bits(hw_size).dsize().bits(hw_size));
936 936
937 // Minor loop: transfer ALL bytes in one minor loop (like mem_to_mem) 937 // Minor loop: transfer ALL bytes in one minor loop (like mem_to_mem)
938 // This allows the entire transfer to complete with a single START trigger 938 // This allows the entire transfer to complete with a single START trigger
939 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(total_bytes)); 939 Self::set_minor_loop_ct_no_offsets(t, total_bytes);
940 940
941 // No address adjustment after major loop 941 // No address adjustment after major loop
942 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); 942 Self::set_no_final_adjustments(t);
943 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0));
944 943
945 // Major loop count = 1 (single major loop, all data in minor loop) 944 // Major loop count = 1 (single major loop, all data in minor loop)
946 // Write BITER first, then CITER (CITER must match BITER at start) 945 // Write BITER first, then CITER (CITER must match BITER at start)
947 t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); 946 Self::set_major_loop_ct_elinkno(t, 1);
948 t.tcd_citer_elinkno().write(|w| w.citer().bits(1));
949 947
950 // Memory barrier before setting START 948 // Memory barrier before setting START
951 cortex_m::asm::dsb(); 949 cortex_m::asm::dsb();
@@ -1066,42 +1064,28 @@ impl<C: Channel> DmaChannel<C> {
1066 let t = self.tcd(); 1064 let t = self.tcd();
1067 1065
1068 // Reset channel state 1066 // Reset channel state
1069 t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); 1067 Self::reset_channel_state(t);
1070 t.ch_es().write(|w| w.bits(0));
1071 t.ch_int().write(|w| w.int().clear_bit_by_one());
1072 1068
1073 // Addresses 1069 // Addresses
1074 t.tcd_saddr().write(|w| w.saddr().bits(buf.as_ptr() as u32)); 1070 Self::set_source_ptr(t, buf.as_ptr());
1075 t.tcd_daddr().write(|w| w.daddr().bits(peri_addr as u32)); 1071 t.tcd_daddr().write(|w| w.daddr().bits(peri_addr as u32));
1076 1072
1077 // Offsets: Source increments, Dest fixed 1073 // Offsets: Source increments, Dest fixed
1078 t.tcd_soff().write(|w| w.soff().bits(byte_size as u16)); 1074 Self::set_source_increment(t, size);
1079 t.tcd_doff().write(|w| w.doff().bits(0)); 1075 Self::set_dest_fixed(t);
1080 1076
1081 // Attributes: set size and explicitly disable modulo 1077 // Attributes: set size and explicitly disable modulo
1082 let hw_size = size.to_hw_size(); 1078 Self::set_even_transfer_size(t, size);
1083 t.tcd_attr().write(|w| {
1084 w.ssize()
1085 .bits(hw_size)
1086 .dsize()
1087 .bits(hw_size)
1088 .smod()
1089 .disable()
1090 .dmod()
1091 .bits(0)
1092 });
1093 1079
1094 // Minor loop: transfer one word per request (match old: only set nbytes) 1080 // Minor loop: transfer one word per request (match old: only set nbytes)
1095 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(byte_size as u32)); 1081 Self::set_minor_loop_ct_no_offsets(t, byte_size as u32);
1096 1082
1097 // No final adjustments 1083 // No final adjustments
1098 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); 1084 Self::set_no_final_adjustments(t);
1099 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0));
1100 1085
1101 // Major loop count = number of words 1086 // Major loop count = number of words
1102 let count = buf.len() as u16; 1087 let count = buf.len() as u16;
1103 t.tcd_citer_elinkno().write(|w| w.citer().bits(count).elink().disable()); 1088 Self::set_major_loop_ct_elinkno(t, count);
1104 t.tcd_biter_elinkno().write(|w| w.biter().bits(count).elink().disable());
1105 1089
1106 // CSR: interrupt on major loop complete and auto-clear ERQ 1090 // CSR: interrupt on major loop complete and auto-clear ERQ
1107 t.tcd_csr().write(|w| { 1091 t.tcd_csr().write(|w| {
@@ -1232,60 +1216,28 @@ impl<C: Channel> DmaChannel<C> {
1232 let t = self.tcd(); 1216 let t = self.tcd();
1233 1217
1234 // Reset channel control/error/interrupt state 1218 // Reset channel control/error/interrupt state
1235 t.ch_csr().write(|w| { 1219 Self::reset_channel_state(t);
1236 w.erq()
1237 .disable()
1238 .earq()
1239 .disable()
1240 .eei()
1241 .no_error()
1242 .ebw()
1243 .disable()
1244 .done()
1245 .clear_bit_by_one()
1246 });
1247 t.ch_es().write(|w| w.bits(0));
1248 t.ch_int().write(|w| w.int().clear_bit_by_one());
1249 1220
1250 // Source: peripheral register, fixed 1221 // Source: peripheral register, fixed
1251 t.tcd_saddr().write(|w| w.saddr().bits(peri_addr as u32)); 1222 Self::set_source_ptr(t, peri_addr);
1252 t.tcd_soff().write(|w| w.soff().bits(0)); // No increment 1223 Self::set_source_fixed(t);
1253 1224
1254 // Destination: memory buffer, incrementing 1225 // Destination: memory buffer, incrementing
1255 t.tcd_daddr().write(|w| w.daddr().bits(buf.as_mut_ptr() as u32)); 1226 Self::set_dest_ptr(t, buf.as_mut_ptr());
1256 t.tcd_doff().write(|w| w.doff().bits(byte_size as u16)); 1227 Self::set_dest_increment(t, size);
1257 1228
1258 // Transfer attributes: set size and explicitly disable modulo 1229 // Transfer attributes: set size and explicitly disable modulo
1259 let hw_size = size.to_hw_size(); 1230 Self::set_even_transfer_size(t, size);
1260 t.tcd_attr().write(|w| {
1261 w.ssize()
1262 .bits(hw_size)
1263 .dsize()
1264 .bits(hw_size)
1265 .smod()
1266 .disable()
1267 .dmod()
1268 .bits(0)
1269 });
1270 1231
1271 // Minor loop: transfer one word per request, no offsets 1232 // Minor loop: transfer one word per request, no offsets
1272 t.tcd_nbytes_mloffno().write(|w| { 1233 Self::set_minor_loop_ct_no_offsets(t, byte_size as u32);
1273 w.nbytes()
1274 .bits(byte_size as u32)
1275 .dmloe()
1276 .offset_not_applied()
1277 .smloe()
1278 .offset_not_applied()
1279 });
1280 1234
1281 // Major loop count = number of words 1235 // Major loop count = number of words
1282 let count = buf.len() as u16; 1236 let count = buf.len() as u16;
1283 t.tcd_citer_elinkno().write(|w| w.citer().bits(count).elink().disable()); 1237 Self::set_major_loop_ct_elinkno(t, count);
1284 t.tcd_biter_elinkno().write(|w| w.biter().bits(count).elink().disable());
1285 1238
1286 // No address adjustment after major loop 1239 // No address adjustment after major loop
1287 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); 1240 Self::set_no_final_adjustments(t);
1288 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0));
1289 1241
1290 // Control/status: interrupt on major complete, auto-clear ERQ when done 1242 // Control/status: interrupt on major complete, auto-clear ERQ when done
1291 t.tcd_csr().write(|w| { 1243 t.tcd_csr().write(|w| {
@@ -1356,42 +1308,28 @@ impl<C: Channel> DmaChannel<C> {
1356 let t = self.tcd(); 1308 let t = self.tcd();
1357 1309
1358 // Reset channel state 1310 // Reset channel state
1359 t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); 1311 Self::reset_channel_state(t);
1360 t.ch_es().write(|w| w.bits(0));
1361 t.ch_int().write(|w| w.int().clear_bit_by_one());
1362 1312
1363 // Addresses 1313 // Addresses
1364 t.tcd_saddr().write(|w| w.saddr().bits(buf.as_ptr() as u32)); 1314 Self::set_source_ptr(t, buf.as_ptr());
1365 t.tcd_daddr().write(|w| w.daddr().bits(peri_addr as u32)); 1315 Self::set_dest_ptr(t, peri_addr);
1366 1316
1367 // Offsets: Source increments, Dest fixed 1317 // Offsets: Source increments, Dest fixed
1368 t.tcd_soff().write(|w| w.soff().bits(byte_size as u16)); 1318 Self::set_source_increment(t, size);
1369 t.tcd_doff().write(|w| w.doff().bits(0)); 1319 Self::set_dest_fixed(t);
1370 1320
1371 // Attributes: set size and explicitly disable modulo 1321 // Attributes: set size and explicitly disable modulo
1372 let hw_size = size.to_hw_size(); 1322 Self::set_even_transfer_size(t, size);
1373 t.tcd_attr().write(|w| {
1374 w.ssize()
1375 .bits(hw_size)
1376 .dsize()
1377 .bits(hw_size)
1378 .smod()
1379 .disable()
1380 .dmod()
1381 .bits(0)
1382 });
1383 1323
1384 // Minor loop: transfer one word per request 1324 // Minor loop: transfer one word per request
1385 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(byte_size as u32)); 1325 Self::set_minor_loop_ct_no_offsets(t, byte_size as u32);
1386 1326
1387 // No final adjustments 1327 // No final adjustments
1388 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); 1328 Self::set_no_final_adjustments(t);
1389 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0));
1390 1329
1391 // Major loop count = number of words 1330 // Major loop count = number of words
1392 let count = buf.len() as u16; 1331 let count = buf.len() as u16;
1393 t.tcd_citer_elinkno().write(|w| w.citer().bits(count).elink().disable()); 1332 Self::set_major_loop_ct_elinkno(t, count);
1394 t.tcd_biter_elinkno().write(|w| w.biter().bits(count).elink().disable());
1395 1333
1396 // CSR: optional interrupt on major loop complete and auto-clear ERQ 1334 // CSR: optional interrupt on major loop complete and auto-clear ERQ
1397 t.tcd_csr().write(|w| { 1335 t.tcd_csr().write(|w| {
@@ -1456,53 +1394,28 @@ impl<C: Channel> DmaChannel<C> {
1456 let t = self.tcd(); 1394 let t = self.tcd();
1457 1395
1458 // Reset channel control/error/interrupt state 1396 // Reset channel control/error/interrupt state
1459 t.ch_csr().write(|w| { 1397 Self::reset_channel_state(t);
1460 w.erq()
1461 .disable()
1462 .earq()
1463 .disable()
1464 .eei()
1465 .no_error()
1466 .ebw()
1467 .disable()
1468 .done()
1469 .clear_bit_by_one()
1470 });
1471 t.ch_es().write(|w| w.bits(0));
1472 t.ch_int().write(|w| w.int().clear_bit_by_one());
1473 1398
1474 // Source: peripheral register, fixed 1399 // Source: peripheral register, fixed
1475 t.tcd_saddr().write(|w| w.saddr().bits(peri_addr as u32)); 1400 Self::set_source_ptr(t, peri_addr);
1476 t.tcd_soff().write(|w| w.soff().bits(0)); 1401 Self::set_source_fixed(t);
1477 1402
1478 // Destination: memory buffer, incrementing 1403 // Destination: memory buffer, incrementing
1479 t.tcd_daddr().write(|w| w.daddr().bits(buf.as_mut_ptr() as u32)); 1404 Self::set_dest_ptr(t, buf.as_mut_ptr());
1480 t.tcd_doff().write(|w| w.doff().bits(byte_size as u16)); 1405 Self::set_dest_increment(t, size);
1481 1406
1482 // Attributes: set size and explicitly disable modulo 1407 // Attributes: set size and explicitly disable modulo
1483 let hw_size = size.to_hw_size(); 1408 Self::set_even_transfer_size(t, size);
1484 t.tcd_attr().write(|w| {
1485 w.ssize()
1486 .bits(hw_size)
1487 .dsize()
1488 .bits(hw_size)
1489 .smod()
1490 .disable()
1491 .dmod()
1492 .bits(0)
1493 });
1494 1409
1495 // Minor loop: transfer one word per request 1410 // Minor loop: transfer one word per request
1496 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(byte_size as u32)); 1411 Self::set_minor_loop_ct_no_offsets(t, byte_size as u32);
1497 1412
1498 // No final adjustments 1413 // No final adjustments
1499 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); 1414 Self::set_no_final_adjustments(t);
1500 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0));
1501 1415
1502 // Major loop count = number of words 1416 // Major loop count = number of words
1503 let count = buf.len() as u16; 1417 let count = buf.len() as u16;
1504 t.tcd_citer_elinkno().write(|w| w.citer().bits(count).elink().disable()); 1418 Self::set_major_loop_ct_elinkno(t, count);
1505 t.tcd_biter_elinkno().write(|w| w.biter().bits(count).elink().disable());
1506 1419
1507 // CSR: optional interrupt on major loop complete and auto-clear ERQ 1420 // CSR: optional interrupt on major loop complete and auto-clear ERQ
1508 t.tcd_csr().write(|w| { 1421 t.tcd_csr().write(|w| {
@@ -2233,56 +2146,25 @@ impl<C: Channel> DmaChannel<C> {
2233 let t = self.tcd(); 2146 let t = self.tcd();
2234 2147
2235 // Reset channel state 2148 // Reset channel state
2236 t.ch_csr().write(|w| { 2149 Self::reset_channel_state(t);
2237 w.erq()
2238 .disable()
2239 .earq()
2240 .disable()
2241 .eei()
2242 .no_error()
2243 .ebw()
2244 .disable()
2245 .done()
2246 .clear_bit_by_one()
2247 });
2248 t.ch_es().write(|w| w.bits(0));
2249 t.ch_int().write(|w| w.int().clear_bit_by_one());
2250 2150
2251 // Source: peripheral register, fixed 2151 // Source: peripheral register, fixed
2252 t.tcd_saddr().write(|w| w.saddr().bits(peri_addr as u32)); 2152 Self::set_source_ptr(t, peri_addr);
2253 t.tcd_soff().write(|w| w.soff().bits(0)); // No increment 2153 Self::set_source_fixed(t);
2254 2154
2255 // Destination: memory buffer, incrementing 2155 // Destination: memory buffer, incrementing
2256 t.tcd_daddr().write(|w| w.daddr().bits(buf.as_mut_ptr() as u32)); 2156 Self::set_dest_ptr(t, buf.as_mut_ptr());
2257 t.tcd_doff().write(|w| w.doff().bits(byte_size as u16)); 2157 Self::set_dest_increment(t, size);
2258 2158
2259 // Transfer attributes 2159 // Transfer attributes
2260 let hw_size = size.to_hw_size(); 2160 Self::set_even_transfer_size(t, size);
2261 t.tcd_attr().write(|w| {
2262 w.ssize()
2263 .bits(hw_size)
2264 .dsize()
2265 .bits(hw_size)
2266 .smod()
2267 .disable()
2268 .dmod()
2269 .bits(0)
2270 });
2271 2161
2272 // Minor loop: transfer one word per request 2162 // Minor loop: transfer one word per request
2273 t.tcd_nbytes_mloffno().write(|w| { 2163 Self::set_minor_loop_ct_no_offsets(t, byte_size as u32);
2274 w.nbytes()
2275 .bits(byte_size as u32)
2276 .dmloe()
2277 .offset_not_applied()
2278 .smloe()
2279 .offset_not_applied()
2280 });
2281 2164
2282 // Major loop count = buffer size 2165 // Major loop count = buffer size
2283 let count = buf.len() as u16; 2166 let count = buf.len() as u16;
2284 t.tcd_citer_elinkno().write(|w| w.citer().bits(count).elink().disable()); 2167 Self::set_major_loop_ct_elinkno(t, count);
2285 t.tcd_biter_elinkno().write(|w| w.biter().bits(count).elink().disable());
2286 2168
2287 // After major loop: reset destination to buffer start (circular) 2169 // After major loop: reset destination to buffer start (circular)
2288 let buf_bytes = (buf.len() * byte_size) as i32; 2170 let buf_bytes = (buf.len() * byte_size) as i32;
@@ -2475,18 +2357,7 @@ impl<W: Word> ScatterGatherBuilder<W> {
2475 2357
2476 // Reset channel state - clear DONE, disable requests, clear errors 2358 // Reset channel state - clear DONE, disable requests, clear errors
2477 // This ensures the channel is in a clean state before loading the TCD 2359 // This ensures the channel is in a clean state before loading the TCD
2478 t.ch_csr().write(|w| { 2360 Self::reset_channel_state(t);
2479 w.erq()
2480 .disable()
2481 .earq()
2482 .disable()
2483 .eei()
2484 .no_error()
2485 .done()
2486 .clear_bit_by_one()
2487 });
2488 t.ch_es().write(|w| w.err().clear_bit_by_one());
2489 t.ch_int().write(|w| w.int().clear_bit_by_one());
2490 2361
2491 // Memory barrier to ensure channel state is reset before loading TCD 2362 // Memory barrier to ensure channel state is reset before loading TCD
2492 cortex_m::asm::dsb(); 2363 cortex_m::asm::dsb();
diff --git a/examples/mcxa/src/bin/dma_mem_to_mem.rs b/examples/mcxa/src/bin/dma_mem_to_mem.rs
index 149e37326..b78745464 100644
--- a/examples/mcxa/src/bin/dma_mem_to_mem.rs
+++ b/examples/mcxa/src/bin/dma_mem_to_mem.rs
@@ -18,6 +18,7 @@ use embassy_mcxa::clocks::config::Div8;
18use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, TransferOptions}; 18use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, TransferOptions};
19use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; 19use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
20use embassy_mcxa::{bind_interrupts, pac}; 20use embassy_mcxa::{bind_interrupts, pac};
21use static_cell::ConstStaticCell;
21use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; 22use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
22use core::fmt::Write as _; 23use core::fmt::Write as _;
23 24
@@ -29,14 +30,14 @@ bind_interrupts!(struct Irqs {
29const BUFFER_LENGTH: usize = 4; 30const BUFFER_LENGTH: usize = 4;
30 31
31// Buffers in RAM (static mut is automatically placed in .bss/.data) 32// Buffers in RAM (static mut is automatically placed in .bss/.data)
32static mut SRC_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; 33static SRC_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([1, 2, 3, 4]);
33static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; 34static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);
34static mut MEMSET_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; 35static MEMSET_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);
35 36
36/// Helper to print a buffer as [v1, v2, v3, v4] to UART 37/// Helper to print a buffer as [v1, v2, v3, v4] to UART
37/// Takes a raw pointer to avoid warnings about shared references to mutable statics 38/// Takes a raw pointer to avoid warnings about shared references to mutable statics
38fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const [u32; BUFFER_LENGTH]) { 39fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: &[u32; BUFFER_LENGTH]) {
39 write!(tx, "{:?}", unsafe { &*buf_ptr }).ok(); 40 write!(tx, "{:?}", buf_ptr).ok();
40} 41}
41 42
42#[embassy_executor::main] 43#[embassy_executor::main]
@@ -70,18 +71,16 @@ async fn main(_spawner: Spawner) {
70 tx.blocking_write(b"EDMA memory to memory example begin.\r\n\r\n") 71 tx.blocking_write(b"EDMA memory to memory example begin.\r\n\r\n")
71 .unwrap(); 72 .unwrap();
72 73
73 // Initialize buffers 74 let src = SRC_BUFFER.take();
74 unsafe { 75 let dst = DEST_BUFFER.take();
75 SRC_BUFFER = [1, 2, 3, 4]; 76 let mst = MEMSET_BUFFER.take();
76 DEST_BUFFER = [0; BUFFER_LENGTH];
77 }
78 77
79 tx.blocking_write(b"Source Buffer: ").unwrap(); 78 tx.blocking_write(b"Source Buffer: ").unwrap();
80 print_buffer(&mut tx, &raw const SRC_BUFFER); 79 print_buffer(&mut tx, src);
81 tx.blocking_write(b"\r\n").unwrap(); 80 tx.blocking_write(b"\r\n").unwrap();
82 81
83 tx.blocking_write(b"Destination Buffer (before): ").unwrap(); 82 tx.blocking_write(b"Destination Buffer (before): ").unwrap();
84 print_buffer(&mut tx, &raw const DEST_BUFFER); 83 print_buffer(&mut tx, dst);
85 tx.blocking_write(b"\r\n").unwrap(); 84 tx.blocking_write(b"\r\n").unwrap();
86 85
87 tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") 86 tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n")
@@ -106,30 +105,17 @@ async fn main(_spawner: Spawner) {
106 // Using async `.await` - the executor can run other tasks while waiting! 105 // Using async `.await` - the executor can run other tasks while waiting!
107 106
108 // Perform type-safe memory-to-memory transfer using Embassy-style async API 107 // Perform type-safe memory-to-memory transfer using Embassy-style async API
109 unsafe { 108 // Using async `.await` - the executor can run other tasks while waiting!
110 let src = &*core::ptr::addr_of!(SRC_BUFFER); 109 let transfer = dma_ch0.mem_to_mem(src, dst, options);
111 let dst = &mut *core::ptr::addr_of_mut!(DEST_BUFFER); 110 transfer.await;
112
113 // Using async `.await` - the executor can run other tasks while waiting!
114 let transfer = dma_ch0.mem_to_mem(src, dst, options);
115 transfer.await;
116 }
117 111
118 tx.blocking_write(b"DMA mem-to-mem transfer complete!\r\n\r\n").unwrap(); 112 tx.blocking_write(b"DMA mem-to-mem transfer complete!\r\n\r\n").unwrap();
119 tx.blocking_write(b"Destination Buffer (after): ").unwrap(); 113 tx.blocking_write(b"Destination Buffer (after): ").unwrap();
120 print_buffer(&mut tx, &raw const DEST_BUFFER); 114 print_buffer(&mut tx, dst);
121 tx.blocking_write(b"\r\n").unwrap(); 115 tx.blocking_write(b"\r\n").unwrap();
122 116
123 // Verify data 117 // Verify data
124 let mut mismatch = false; 118 let mut mismatch = src != dst;
125 unsafe {
126 for i in 0..BUFFER_LENGTH {
127 if SRC_BUFFER[i] != DEST_BUFFER[i] {
128 mismatch = true;
129 break;
130 }
131 }
132 }
133 119
134 if mismatch { 120 if mismatch {
135 tx.blocking_write(b"FAIL: mem_to_mem mismatch!\r\n").unwrap(); 121 tx.blocking_write(b"FAIL: mem_to_mem mismatch!\r\n").unwrap();
@@ -152,37 +138,24 @@ async fn main(_spawner: Spawner) {
152 .unwrap(); 138 .unwrap();
153 139
154 tx.blocking_write(b"Memset Buffer (before): ").unwrap(); 140 tx.blocking_write(b"Memset Buffer (before): ").unwrap();
155 print_buffer(&mut tx, &raw const MEMSET_BUFFER); 141 print_buffer(&mut tx, mst);
156 tx.blocking_write(b"\r\n").unwrap(); 142 tx.blocking_write(b"\r\n").unwrap();
157 143
158 // Fill buffer with a pattern value using DMA memset 144 // Fill buffer with a pattern value using DMA memset
159 let pattern: u32 = 0xDEADBEEF; 145 let pattern: u32 = 0xDEADBEEF;
160 tx.blocking_write(b"Filling with pattern 0xDEADBEEF...\r\n").unwrap(); 146 tx.blocking_write(b"Filling with pattern 0xDEADBEEF...\r\n").unwrap();
161 147
162 unsafe { 148 // Using blocking_wait() for demonstration - also shows non-async usage
163 let dst = &mut *core::ptr::addr_of_mut!(MEMSET_BUFFER); 149 let transfer = dma_ch0.memset(&pattern, mst, options);
164 150 transfer.blocking_wait();
165 // Using blocking_wait() for demonstration - also shows non-async usage
166 let transfer = dma_ch0.memset(&pattern, dst, options);
167 transfer.blocking_wait();
168 }
169 151
170 tx.blocking_write(b"DMA memset complete!\r\n\r\n").unwrap(); 152 tx.blocking_write(b"DMA memset complete!\r\n\r\n").unwrap();
171 tx.blocking_write(b"Memset Buffer (after): ").unwrap(); 153 tx.blocking_write(b"Memset Buffer (after): ").unwrap();
172 print_buffer(&mut tx, &raw const MEMSET_BUFFER); 154 print_buffer(&mut tx, mst);
173 tx.blocking_write(b"\r\n").unwrap(); 155 tx.blocking_write(b"\r\n").unwrap();
174 156
175 // Verify memset result 157 // Verify memset result
176 let mut memset_ok = true; 158 let memset_ok = mst.iter().all(|&v| v == pattern);
177 unsafe {
178 #[allow(clippy::needless_range_loop)]
179 for i in 0..BUFFER_LENGTH {
180 if MEMSET_BUFFER[i] != pattern {
181 memset_ok = false;
182 break;
183 }
184 }
185 }
186 159
187 if !memset_ok { 160 if !memset_ok {
188 tx.blocking_write(b"FAIL: memset mismatch!\r\n").unwrap(); 161 tx.blocking_write(b"FAIL: memset mismatch!\r\n").unwrap();
@@ -193,8 +166,4 @@ async fn main(_spawner: Spawner) {
193 } 166 }
194 167
195 tx.blocking_write(b"=== All DMA tests complete ===\r\n").unwrap(); 168 tx.blocking_write(b"=== All DMA tests complete ===\r\n").unwrap();
196
197 loop {
198 cortex_m::asm::wfe();
199 }
200} 169}