Skip to content

Commit 3f7a156

Browse files
authored
Using flash memory to store user-defined types
1 parent 8725a3d commit 3f7a156

File tree

4 files changed

+173
-17
lines changed

4 files changed

+173
-17
lines changed

Cargo.lock

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hal/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ embedded-hal = { version = "0.2.6", features = ["unproven"] }
2828
embedded-time = { version = "0.12", optional = true }
2929
nb = "1"
3030
num-traits = { version = "0.2", default-features = false }
31+
num-integer = { version = "0.1", default-features = false }
3132
paste = "1"
3233
rand_core = "0.6"
3334
stm32wl = { version = "0.14", default-features = false }

hal/src/flash.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use crate::pac;
44
use core::{ops::Range, ptr::write_volatile};
5+
use num_integer::Integer;
56

67
/// Starting address of the flash memory.
78
pub const FLASH_START: usize = 0x0800_0000;
@@ -403,6 +404,82 @@ impl<'a> Flash<'a> {
403404
ret
404405
}
405406

407+
/// Program a user-defined type.
408+
///
409+
/// # Safety
410+
///
411+
/// 1. Do not write to flash memory that is being used for your code.
412+
/// 2. The destination address must be within the flash memory region.
413+
/// 3. The `from` and `to` pointers must be aligned to `u64`.
414+
/// Use `#[repr(align(8))]` to align your stucture.
415+
#[allow(unused_unsafe)]
416+
pub unsafe fn standard_program_generic<T>(
417+
&mut self,
418+
from: *const T,
419+
to: *mut T,
420+
) -> Result<(), Error> {
421+
let size: isize = core::mem::size_of::<T>() as isize;
422+
if size == 0 {
423+
return Ok(());
424+
}
425+
426+
let sr: u32 = self.sr();
427+
if sr & flags::BSY != 0 {
428+
return Err(Error::Busy);
429+
}
430+
if sr & flags::PESD != 0 {
431+
return Err(Error::Suspend);
432+
}
433+
434+
self.clear_all_err();
435+
436+
c1_c2!(
437+
self.flash.cr.modify(|_, w| w.pg().set_bit()),
438+
self.flash.c2cr.modify(|_, w| w.pg().set_bit())
439+
);
440+
441+
// Calculate the index of the last double word
442+
let last_double_word_idx: isize = size.div_ceil(&8) - 1;
443+
444+
// Write the type as double words and return the number of bytes written
445+
let written_bytes: isize = (0..last_double_word_idx).fold(0, |acc, n| {
446+
unsafe {
447+
write_volatile(
448+
(to as *mut u64).offset(n),
449+
(from as *const u64).offset(n).read(),
450+
)
451+
};
452+
acc + 8
453+
});
454+
455+
// Determine how many bytes are left to write
456+
let bytes_left: isize = size - written_bytes;
457+
458+
// Append the left over bytes to a double word,
459+
// the last few bytes can look random in flash memory since Rust uses memory alignment to make accessing faster.
460+
let last_double_word: u64 = (0..bytes_left).fold(0, |dw, n| {
461+
let byte: u8 = (from as *const u8).offset(written_bytes + n).read();
462+
dw | u64::from(byte) << (8 * n)
463+
});
464+
465+
// Write the last double word
466+
unsafe {
467+
write_volatile(
468+
(to as *mut u64).offset(last_double_word_idx),
469+
(&last_double_word as *const u64).read(),
470+
)
471+
};
472+
473+
let ret: Result<(), Error> = self.wait_for_not_busy();
474+
475+
c1_c2!(
476+
self.flash.cr.modify(|_, w| w.pg().clear_bit()),
477+
self.flash.c2cr.modify(|_, w| w.pg().clear_bit())
478+
);
479+
480+
ret
481+
}
482+
406483
/// Program 256 bytes.
407484
///
408485
/// # Safety

testsuite/src/flash.rs

Lines changed: 90 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use nucleo_wl55jc_bsp::hal::{
1414
};
1515
use panic_probe as _;
1616
use rand::Rng as RngTrait;
17+
use static_assertions as sa;
1718

1819
const FREQ: u32 = 48_000_000;
1920
const CYC_PER_MICRO: u32 = FREQ / 1000 / 1000;
@@ -39,6 +40,9 @@ mod tests {
3940
struct TestArgs {
4041
flash: pac::FLASH,
4142
page: Page,
43+
// address to use for testing
44+
// incremented by the test after the address is programmed
45+
addr: usize,
4246
rng: Rng,
4347
}
4448

@@ -71,6 +75,7 @@ mod tests {
7175
TestArgs {
7276
flash: dp.FLASH,
7377
page,
78+
addr: page.addr(),
7479
rng,
7580
}
7681
}
@@ -123,7 +128,7 @@ mod tests {
123128
let mut flash: Flash = Flash::unlock(&mut ta.flash);
124129

125130
let start: u32 = DWT::get_cycle_count();
126-
unwrap!(unsafe { flash.fast_program(BUF.as_ptr(), ta.page.addr() as *mut u64) });
131+
unwrap!(unsafe { flash.fast_program(BUF.as_ptr(), ta.addr as *mut u64) });
127132
let end: u32 = DWT::get_cycle_count();
128133
let elapsed: u32 = end.wrapping_sub(start);
129134

@@ -134,35 +139,31 @@ mod tests {
134139

135140
for (idx, &dw) in unsafe { BUF }.iter().enumerate() {
136141
let expected: u64 = unsafe {
137-
(ta.page.addr() as *const u64)
142+
(ta.addr as *const u64)
138143
.offset(unwrap!(idx.try_into()))
139144
.read_volatile()
140145
};
141146
defmt::assert_eq!(dw, expected);
142147
}
148+
149+
// increment address by program size
150+
ta.addr += 256;
143151
}
144152

145153
#[test]
146154
fn standard_program(ta: &mut TestArgs) {
147-
let addr: usize = {
148-
let unaligned_addr: usize = ta
149-
.rng
150-
.gen_range(ta.page.addr() + 256..ta.page.addr() + Page::SIZE - 1);
151-
unaligned_addr - (unaligned_addr % size_of::<u64>())
152-
};
153-
154155
let data: u64 = ta.rng.gen_range(1..u64::MAX - 1);
155156
defmt::assert_ne!(data, u64::MAX);
156157
defmt::assert_ne!(data, 0);
157158

158-
defmt::info!("Writing {:#016X} to {:#08X}", data, addr);
159+
defmt::info!("Writing {:#016X} to {:#08X}", data, ta.addr);
159160

160-
defmt::assert_eq!(unsafe { read_volatile(addr as *const u64) }, u64::MAX);
161+
defmt::assert_eq!(unsafe { read_volatile(ta.addr as *const u64) }, u64::MAX);
161162

162163
let mut flash: Flash = Flash::unlock(&mut ta.flash);
163164

164165
let start: u32 = DWT::get_cycle_count();
165-
unwrap!(unsafe { flash.standard_program(&data, addr as *mut u64) });
166+
unwrap!(unsafe { flash.standard_program(&data, ta.addr as *mut u64) });
166167
let end: u32 = DWT::get_cycle_count();
167168
let elapsed: u32 = end.wrapping_sub(start);
168169

@@ -171,6 +172,82 @@ mod tests {
171172
elapsed / CYC_PER_MICRO
172173
);
173174

174-
defmt::assert_eq!(unsafe { read_volatile(addr as *const u64) }, data);
175+
defmt::assert_eq!(unsafe { read_volatile(ta.addr as *const u64) }, data);
176+
177+
// increment address by program size
178+
ta.addr += size_of::<u64>();
179+
}
180+
181+
#[test]
182+
fn standard_program_generic_zero_size(ta: &mut TestArgs) {
183+
type ZeroSizeType = ();
184+
185+
sa::assert_eq_size!(ZeroSizeType, [u8; 0]);
186+
187+
let my_zero_size_type: ZeroSizeType = ();
188+
189+
let mut flash: Flash = Flash::unlock(&mut ta.flash);
190+
191+
// check flash is erased
192+
defmt::assert_eq!(unsafe { read_volatile(ta.addr as *const u64) }, u64::MAX);
193+
194+
unwrap!(unsafe {
195+
flash.standard_program_generic(&my_zero_size_type, ta.addr as *mut ZeroSizeType)
196+
});
197+
198+
// check flash was not modified
199+
defmt::assert_eq!(unsafe { read_volatile(ta.addr as *const u64) }, u64::MAX);
200+
}
201+
202+
#[test]
203+
fn standard_program_generic(ta: &mut TestArgs) {
204+
#[derive(defmt::Format, PartialEq, Eq)]
205+
struct Keys {
206+
eui: [u8; 8],
207+
key: [u8; 16],
208+
}
209+
210+
#[derive(defmt::Format, PartialEq, Eq)]
211+
#[repr(align(8))]
212+
struct TestStruct {
213+
connected: bool,
214+
keys: Keys,
215+
framecount_down: u32,
216+
framecount_up: u32,
217+
}
218+
219+
sa::assert_eq_align!(TestStruct, u64);
220+
221+
let data = TestStruct {
222+
connected: false,
223+
keys: Keys {
224+
eui: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
225+
key: [
226+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05,
227+
0x06, 0x07, 0x08,
228+
],
229+
},
230+
framecount_up: 12,
231+
framecount_down: 120,
232+
};
233+
234+
defmt::info!("Writing {} to {:#08X}", data, ta.addr);
235+
236+
let mut flash: Flash = Flash::unlock(&mut ta.flash);
237+
238+
let start: u32 = DWT::get_cycle_count();
239+
unwrap!(unsafe { flash.standard_program_generic(&data, ta.addr as *mut TestStruct) });
240+
let end: u32 = DWT::get_cycle_count();
241+
let elapsed: u32 = end.wrapping_sub(start);
242+
243+
let size = core::mem::size_of::<TestStruct>();
244+
245+
defmt::info!(
246+
"{}B program duration: {=u32:us} seconds",
247+
size,
248+
elapsed / CYC_PER_MICRO
249+
);
250+
251+
defmt::assert_eq!(unsafe { read_volatile(ta.addr as *const TestStruct) }, data);
175252
}
176253
}

0 commit comments

Comments
 (0)