forked from stm32-rs/stm32h7xx-hal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mdma.rs
223 lines (176 loc) · 6.73 KB
/
mdma.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
//! Example of Memory to Memory Transfer with the Master DMA (MDMA)
#![allow(clippy::transmute_ptr_to_ptr)]
#![deny(warnings)]
#![no_main]
#![no_std]
use core::{mem, mem::MaybeUninit};
use cortex_m_rt::entry;
#[macro_use]
mod utilities;
use stm32h7xx_hal::{pac, prelude::*};
use stm32h7xx_hal::dma::{
mdma::{MdmaConfig, MdmaIncrement, MdmaSize, StreamsTuple},
traits::Direction,
MemoryToMemory, Transfer,
};
use log::info;
// The MDMA can interact with SRAM banks as well as the TCM. For this example,
// we place the source in the AXI SRAM.
//
// The runtime does not initialise AXI SRAM banks.
#[link_section = ".axisram.buffers"]
static mut SOURCE_BUFFER: MaybeUninit<[u32; 200]> = MaybeUninit::uninit();
#[entry]
fn main() -> ! {
utilities::logger::init();
let dp = pac::Peripherals::take().unwrap();
// Constrain and Freeze power
info!("Setup PWR... ");
let pwr = dp.PWR.constrain();
let pwrcfg = example_power!(pwr).freeze();
// Constrain and Freeze clock
info!("Setup RCC... ");
let rcc = dp.RCC.constrain();
let ccdr = rcc
.sys_ck(100.MHz())
.hclk(50.MHz())
.freeze(pwrcfg, &dp.SYSCFG);
info!("");
info!("stm32h7xx-hal example - Memory to TCM with Master DMA");
info!("");
// Initialise the source buffer without taking any references to
// uninitialisated memory
let source_buffer: &'static mut [u32; 200] = {
let buf: &mut [MaybeUninit<u32>; 200] =
unsafe { mem::transmute(&mut SOURCE_BUFFER) };
for value in buf.iter_mut() {
unsafe {
value.as_mut_ptr().write(0x11223344u32);
}
}
unsafe { mem::transmute(buf) }
};
//
// Example 1: Memory to TCM
//
// Target buffer on the stack
let mut target_buffer: [u32; 200] = [0; 200];
// Setup DMA
let streams = StreamsTuple::new(dp.MDMA, ccdr.peripheral.MDMA);
let config = MdmaConfig::default()
// increment by one element per element
.destination_increment(MdmaIncrement::Increment)
.source_increment(MdmaIncrement::Increment);
let mut transfer: Transfer<_, _, MemoryToMemory<u32>, _, _> = {
// Extend the lifetime of our data on the stack. We assert that it lives
// as long as this transfer
let target: &'static mut [u32; 200] =
unsafe { mem::transmute(&mut target_buffer) };
Transfer::init_master(
streams.0,
MemoryToMemory::new(),
target, // Dest: TCM (stack)
Some(source_buffer), // Source: AXISRAM
config,
)
};
// Block of 800 bytes, MDMA checks other streams every 128 bytes
assert_eq!(transfer.get_block_length(), 800);
assert_eq!(transfer.get_buffer_length(), 128);
// Start block
transfer.start(|_| {});
// Wait for transfer to complete
while !transfer.get_transfer_complete_flag() {}
// Decompose the stream to get the source buffer back
let (stream, _mem2mem, _target, source_buffer_opt) = transfer.free();
let source_buffer = source_buffer_opt.unwrap();
for a in target_buffer.iter() {
assert_eq!(*a, 0x11223344);
}
info!("Example 1: Memory to TCM DMA completed successfully");
//
// Example 2: Memory to TCM with endianess and offset
//
// Reset source buffer
*source_buffer = [0xAABBCCDD; 200];
// New target buffer on the stack
let mut target_buffer: [u32; 20] = [0; 20];
let config = MdmaConfig::default()
.source_increment(MdmaIncrement::Increment)
.destination_increment(MdmaIncrement::IncrementWithOffset(
MdmaSize::DoubleWord,
))
.half_word_endianness_exchange(true);
let mut transfer: Transfer<_, _, MemoryToMemory<u32>, _, _> = {
let target: &'static mut [u32; 20] =
unsafe { mem::transmute(&mut target_buffer) };
// Note that our source and destination buffers now have different types
// (they are arrays with different lengths). We pass slices instead, and
// the HAL takes the length into account.
Transfer::init_master(
stream,
MemoryToMemory::new(),
&mut target[..], // Dest: TCM (stack)
Some(&mut source_buffer[..]), // Source: AXISRAM
config,
)
};
// Block length is limited to the minimum number of bytes that are valid for
// both buffers. For this configuration, it is only possible to write 40
// bytes (10 words) to the target buffer before reaching the end.
assert_eq!(transfer.get_block_length(), 40);
transfer.start(|_| {});
// Wait for transfer to complete
while !transfer.get_transfer_complete_flag() {}
assert_eq!(
target_buffer,
[
0xCCDDAABB, 0, 0xCCDDAABB, 0, 0xCCDDAABB, 0, 0xCCDDAABB, 0,
0xCCDDAABB, 0, 0xCCDDAABB, 0, 0xCCDDAABB, 0, 0xCCDDAABB, 0,
0xCCDDAABB, 0, 0xCCDDAABB, 0,
]
);
info!(
"Example 2: Memory to TCM DMA with endianess and offset completed successfully"
);
//
// Example 3: TCM to TCM with offset
//
let mut source_buffer_tcm = [1u8, 2];
// unsafe: we must ensure source_buffer_tcm lives long enough
let source_buffer: &'static mut [u8] =
unsafe { mem::transmute(&mut source_buffer_tcm[..]) };
let mut target_buffer = [0u8; 17];
let config = MdmaConfig::default().destination_increment(
MdmaIncrement::IncrementWithOffset(MdmaSize::DoubleWord),
);
let mut transfer: Transfer<_, _, MemoryToMemory<u8>, _, _> = {
// Be very careful when using an unsafe transmute in the init call like
// this because the target_buffer type will be transmuted to the source
// type. In this case it's ok at the source_buffer is a slice [u8]. But
// if both source and target types were arrays, length of the target
// array would be lost.
Transfer::init_master(
streams.1,
MemoryToMemory::new(),
unsafe { mem::transmute(&mut target_buffer[..]) }, // Dest: TCM (stack)
Some(source_buffer), // Source: TCM (stack)
config,
)
};
transfer.start(|_| {});
// Wait for transfer to complete
while !transfer.get_transfer_complete_flag() {}
// Value returned by `get_block_length` decrements during the transfer,
// reaching zero at the end
assert_eq!(transfer.get_block_length(), 0);
assert_eq!(source_buffer_tcm, [1, 2]);
assert_eq!(
target_buffer,
[1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]
);
info!("Example 3: TCM to TCM DMA with offset completed successfully");
loop {
cortex_m::asm::nop()
}
}