aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/flash
diff options
context:
space:
mode:
authorRasmus Melchior Jacobsen <[email protected]>2023-03-25 16:04:45 +0100
committerRasmus Melchior Jacobsen <[email protected]>2023-03-25 16:04:45 +0100
commitbc69eb596e2496a5eb0cf1252ada12f2710aaff2 (patch)
treed2a4c233d439cba49e2512fce076155b30d20d3c /embassy-stm32/src/flash
parent245147634bfbdcd325eea20be19286708bb29c9f (diff)
Add is_eraseable_range and split write into consecutive parts
Diffstat (limited to 'embassy-stm32/src/flash')
-rw-r--r--embassy-stm32/src/flash/f3.rs55
-rw-r--r--embassy-stm32/src/flash/f4.rs74
-rw-r--r--embassy-stm32/src/flash/f7.rs70
-rw-r--r--embassy-stm32/src/flash/h7.rs76
-rw-r--r--embassy-stm32/src/flash/l.rs54
-rw-r--r--embassy-stm32/src/flash/mod.rs85
6 files changed, 221 insertions, 193 deletions
diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs
index b24dfb4a7..99ac1a153 100644
--- a/embassy-stm32/src/flash/f3.rs
+++ b/embassy-stm32/src/flash/f3.rs
@@ -1,13 +1,13 @@
1use core::convert::TryInto; 1use core::convert::TryInto;
2use core::mem::size_of;
3use core::ptr::write_volatile; 2use core::ptr::write_volatile;
4 3
5use super::FlashRegion; 4use atomic_polyfill::{fence, Ordering};
5
6use super::{FlashRegion, BANK1, WRITE_SIZE};
6use crate::flash::Error; 7use crate::flash::Error;
7use crate::pac; 8use crate::pac;
8 9
9pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; 10const ERASE_SIZE: usize = BANK1::ERASE_SIZE;
10pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1::ERASE_SIZE;
11 11
12pub(crate) unsafe fn lock() { 12pub(crate) unsafe fn lock() {
13 pac::FLASH.cr().modify(|w| w.set_lock(true)); 13 pac::FLASH.cr().modify(|w| w.set_lock(true));
@@ -18,33 +18,35 @@ pub(crate) unsafe fn unlock() {
18 pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); 18 pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB));
19} 19}
20 20
21pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { 21pub(crate) unsafe fn begin_write() {
22 pac::FLASH.cr().write(|w| w.set_pg(true)); 22 assert_eq!(0, WRITE_SIZE % 2);
23 23
24 let ret = { 24 pac::FLASH.cr().write(|w| w.set_pg(true));
25 let mut ret: Result<(), Error> = Ok(()); 25}
26 let mut address = first_address;
27 let chunks = buf.chunks_exact(size_of::<u16>());
28 assert!(chunks.remainder().is_empty());
29 for chunk in chunks {
30 write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap()));
31 address += chunk.len() as u32;
32
33 ret = blocking_wait_ready();
34 if ret.is_err() {
35 break;
36 }
37 }
38 ret
39 };
40 26
27pub(crate) unsafe fn end_write() {
41 pac::FLASH.cr().write(|w| w.set_pg(false)); 28 pac::FLASH.cr().write(|w| w.set_pg(false));
29}
42 30
43 ret 31pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
32 let mut address = start_address;
33 for chunk in buf.chunks(2) {
34 write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap()));
35 address += chunk.len() as u32;
36
37 // prevents parallelism errors
38 fence(Ordering::SeqCst);
39 }
40
41 blocking_wait_ready()
44} 42}
45 43
46pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { 44pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool {
47 for page in (from_address..to_address).step_by(MAX_ERASE_SIZE) { 45 start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0
46}
47
48pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> {
49 for page in (start_address..end_address).step_by(ERASE_SIZE) {
48 pac::FLASH.cr().modify(|w| { 50 pac::FLASH.cr().modify(|w| {
49 w.set_per(true); 51 w.set_per(true);
50 }); 52 });
@@ -71,7 +73,6 @@ pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Resul
71 return ret; 73 return ret;
72 } 74 }
73 } 75 }
74
75 Ok(()) 76 Ok(())
76} 77}
77 78
@@ -89,7 +90,7 @@ pub(crate) unsafe fn clear_all_err() {
89 }); 90 });
90} 91}
91 92
92pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { 93unsafe fn blocking_wait_ready() -> Result<(), Error> {
93 loop { 94 loop {
94 let sr = pac::FLASH.sr().read(); 95 let sr = pac::FLASH.sr().read();
95 96
diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs
index 0d9d405ba..7428fd572 100644
--- a/embassy-stm32/src/flash/f4.rs
+++ b/embassy-stm32/src/flash/f4.rs
@@ -1,23 +1,19 @@
1use core::convert::TryInto; 1use core::convert::TryInto;
2use core::mem::size_of;
3use core::ptr::write_volatile; 2use core::ptr::write_volatile;
4use core::sync::atomic::{fence, Ordering}; 3use core::sync::atomic::{fence, Ordering};
5 4
6use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; 5use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET};
7 6
8use super::{FlashRegion, FLASH_SIZE}; 7use super::{FLASH_SIZE, WRITE_SIZE};
9use crate::flash::Error; 8use crate::flash::Error;
10use crate::pac; 9use crate::pac;
11 10
12pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; 11fn is_dual_bank() -> bool {
13pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE;
14
15unsafe fn is_dual_bank() -> bool {
16 match FLASH_SIZE / 1024 { 12 match FLASH_SIZE / 1024 {
17 // 1 MB devices depend on configuration 13 // 1 MB devices depend on configuration
18 1024 => { 14 1024 => {
19 if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) { 15 if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) {
20 pac::FLASH.optcr().read().db1m() 16 unsafe { pac::FLASH.optcr().read().db1m() }
21 } else { 17 } else {
22 false 18 false
23 } 19 }
@@ -38,49 +34,53 @@ pub(crate) unsafe fn unlock() {
38 pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); 34 pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
39} 35}
40 36
41pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { 37pub(crate) unsafe fn begin_write() {
38 assert_eq!(0, WRITE_SIZE % 4);
39
42 pac::FLASH.cr().write(|w| { 40 pac::FLASH.cr().write(|w| {
43 w.set_pg(true); 41 w.set_pg(true);
44 w.set_psize(pac::flash::vals::Psize::PSIZE32); 42 w.set_psize(pac::flash::vals::Psize::PSIZE32);
45 }); 43 });
44}
46 45
47 let ret = { 46pub(crate) unsafe fn end_write() {
48 let mut ret: Result<(), Error> = Ok(()); 47 pac::FLASH.cr().write(|w| w.set_pg(false));
49 let mut address = first_address; 48}
50 for chunk in buf.chunks(MAX_WRITE_SIZE) {
51 let vals = chunk.chunks_exact(size_of::<u32>());
52 assert!(vals.remainder().is_empty());
53 for val in vals {
54 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
55 address += val.len() as u32;
56
57 // prevents parallelism errors
58 fence(Ordering::SeqCst);
59 }
60 49
61 ret = blocking_wait_ready(); 50pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
62 if ret.is_err() { 51 let mut address = start_address;
63 break; 52 for val in buf.chunks(4) {
64 } 53 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
65 } 54 address += val.len() as u32;
66 ret
67 };
68 55
69 pac::FLASH.cr().write(|w| w.set_pg(false)); 56 // prevents parallelism errors
57 fence(Ordering::SeqCst);
58 }
70 59
71 ret 60 blocking_wait_ready()
72} 61}
73 62
74pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { 63pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool {
75 let mut addr = from_address;
76 let dual_bank = is_dual_bank(); 64 let dual_bank = is_dual_bank();
65 let mut address = start_address;
66 while address < end_address {
67 let sector = get_sector(address, dual_bank, FLASH_SIZE as u32);
68 if sector.start != address {
69 return false;
70 }
71 address += sector.size;
72 }
73 address == end_address
74}
77 75
78 while addr < to_address { 76pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> {
79 let sector = get_sector(addr, dual_bank, FLASH_SIZE as u32); 77 let dual_bank = is_dual_bank();
78 let mut address = start_address;
79 while address < end_address {
80 let sector = get_sector(address, dual_bank, FLASH_SIZE as u32);
80 erase_sector(sector.index)?; 81 erase_sector(sector.index)?;
81 addr += sector.size; 82 address += sector.size;
82 } 83 }
83
84 Ok(()) 84 Ok(())
85} 85}
86 86
@@ -116,7 +116,7 @@ pub(crate) unsafe fn clear_all_err() {
116 }); 116 });
117} 117}
118 118
119pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { 119unsafe fn blocking_wait_ready() -> Result<(), Error> {
120 loop { 120 loop {
121 let sr = pac::FLASH.sr().read(); 121 let sr = pac::FLASH.sr().read();
122 122
diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs
index 8b8076e0c..16b684580 100644
--- a/embassy-stm32/src/flash/f7.rs
+++ b/embassy-stm32/src/flash/f7.rs
@@ -1,17 +1,13 @@
1use core::convert::TryInto; 1use core::convert::TryInto;
2use core::mem::size_of;
3use core::ptr::write_volatile; 2use core::ptr::write_volatile;
4use core::sync::atomic::{fence, Ordering}; 3use core::sync::atomic::{fence, Ordering};
5 4
6use embassy_hal_common::stm32::flash::f7::get_sector; 5use embassy_hal_common::stm32::flash::f7::get_sector;
7 6
8use super::FlashRegion; 7use super::WRITE_SIZE;
9use crate::flash::Error; 8use crate::flash::Error;
10use crate::pac; 9use crate::pac;
11 10
12pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE;
13pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE;
14
15pub(crate) unsafe fn lock() { 11pub(crate) unsafe fn lock() {
16 pac::FLASH.cr().modify(|w| w.set_lock(true)); 12 pac::FLASH.cr().modify(|w| w.set_lock(true));
17} 13}
@@ -21,49 +17,51 @@ pub(crate) unsafe fn unlock() {
21 pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); 17 pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
22} 18}
23 19
24pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { 20pub(crate) unsafe fn begin_write() {
21 assert_eq!(0, WRITE_SIZE % 4);
22
25 pac::FLASH.cr().write(|w| { 23 pac::FLASH.cr().write(|w| {
26 w.set_pg(true); 24 w.set_pg(true);
27 w.set_psize(pac::flash::vals::Psize::PSIZE32); 25 w.set_psize(pac::flash::vals::Psize::PSIZE32);
28 }); 26 });
27}
29 28
30 let ret = { 29pub(crate) unsafe fn end_write() {
31 let mut ret: Result<(), Error> = Ok(()); 30 pac::FLASH.cr().write(|w| w.set_pg(false));
32 let mut address = first_address; 31}
33 for chunk in buf.chunks(MAX_WRITE_SIZE) {
34 let vals = chunk.chunks_exact(size_of::<u32>());
35 assert!(vals.remainder().is_empty());
36 for val in vals {
37 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
38 address += val.len() as u32;
39
40 // prevents parallelism errors
41 fence(Ordering::SeqCst);
42 }
43 32
44 ret = blocking_wait_ready(); 33pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
45 if ret.is_err() { 34 let mut address = start_address;
46 break; 35 for val in buf.chunks(4) {
47 } 36 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
48 } 37 address += val.len() as u32;
49 ret
50 };
51 38
52 pac::FLASH.cr().write(|w| w.set_pg(false)); 39 // prevents parallelism errors
40 fence(Ordering::SeqCst);
41 }
53 42
54 ret 43 blocking_wait_ready()
55} 44}
56 45
57pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { 46pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool {
58 let start_sector = get_sector(from_address); 47 let mut address = start_address;
59 let end_sector = get_sector(to_address); 48 while address < end_address {
60 for sector in start_sector.index..end_sector.index { 49 let sector = get_sector(address);
61 let ret = erase_sector(sector as u8); 50 if sector.start != address {
62 if ret.is_err() { 51 return false;
63 return ret;
64 } 52 }
53 address += sector.size;
65 } 54 }
55 address == end_address
56}
66 57
58pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> {
59 let mut address = start_address;
60 while address < end_address {
61 let sector = get_sector(address);
62 erase_sector(sector.index)?;
63 address += sector.size;
64 }
67 Ok(()) 65 Ok(())
68} 66}
69 67
@@ -106,7 +104,7 @@ pub(crate) unsafe fn clear_all_err() {
106 }); 104 });
107} 105}
108 106
109pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { 107unsafe fn blocking_wait_ready() -> Result<(), Error> {
110 loop { 108 loop {
111 let sr = pac::FLASH.sr().read(); 109 let sr = pac::FLASH.sr().read();
112 110
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs
index 6ab8e7b74..21a9e45df 100644
--- a/embassy-stm32/src/flash/h7.rs
+++ b/embassy-stm32/src/flash/h7.rs
@@ -1,15 +1,13 @@
1use core::convert::TryInto; 1use core::convert::TryInto;
2use core::mem::size_of;
3use core::ptr::write_volatile; 2use core::ptr::write_volatile;
4 3
5use super::{FlashRegion, FLASH_SIZE}; 4use atomic_polyfill::{fence, Ordering};
5
6use super::{FlashRegion, FLASH_SIZE, WRITE_SIZE};
6use crate::flash::Error; 7use crate::flash::Error;
7use crate::pac; 8use crate::pac;
8 9
9const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE;
10const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; 10const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE;
11pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE;
12pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE;
13const SECOND_BANK_OFFSET: usize = 0x0010_0000; 11const SECOND_BANK_OFFSET: usize = 0x0010_0000;
14 12
15const fn is_dual_bank() -> bool { 13const fn is_dual_bank() -> bool {
@@ -33,59 +31,60 @@ pub(crate) unsafe fn unlock() {
33 } 31 }
34} 32}
35 33
36pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { 34pub(crate) unsafe fn begin_write() {
37 let bank = if !is_dual_bank() || (first_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { 35 assert_eq!(0, WRITE_SIZE % 4);
36}
37
38pub(crate) unsafe fn end_write() {}
39
40pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
41 // We cannot have the write setup sequence in begin_write as it depends on the address
42 let bank = if !is_dual_bank() || (start_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 {
38 pac::FLASH.bank(0) 43 pac::FLASH.bank(0)
39 } else { 44 } else {
40 pac::FLASH.bank(1) 45 pac::FLASH.bank(1)
41 }; 46 };
42
43 bank.cr().write(|w| { 47 bank.cr().write(|w| {
44 w.set_pg(true); 48 w.set_pg(true);
45 w.set_psize(2); // 32 bits at once 49 w.set_psize(2); // 32 bits at once
46 }); 50 });
47
48 cortex_m::asm::isb(); 51 cortex_m::asm::isb();
49 cortex_m::asm::dsb(); 52 cortex_m::asm::dsb();
50 core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); 53 fence(Ordering::SeqCst);
51 54
52 let ret = { 55 let mut res = None;
53 let mut ret: Result<(), Error> = Ok(()); 56 let mut address = start_address;
54 let mut address = first_address; 57 for val in buf.chunks(4) {
55 'outer: for chunk in buf.chunks(WRITE_SIZE) { 58 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
56 let vals = chunk.chunks_exact(size_of::<u32>()); 59 address += val.len() as u32;
57 assert!(vals.remainder().is_empty()); 60
58 for val in vals { 61 res = Some(blocking_wait_ready(bank));
59 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); 62 bank.sr().modify(|w| {
60 address += val.len() as u32; 63 if w.eop() {
61 64 w.set_eop(true);
62 ret = blocking_wait_ready(bank);
63 bank.sr().modify(|w| {
64 if w.eop() {
65 w.set_eop(true);
66 }
67 });
68 if ret.is_err() {
69 break 'outer;
70 }
71 } 65 }
66 });
67 if res.unwrap().is_err() {
68 break;
72 } 69 }
73 ret 70 }
74 };
75 71
76 bank.cr().write(|w| w.set_pg(false)); 72 bank.cr().write(|w| w.set_pg(false));
77 73
78 cortex_m::asm::isb(); 74 cortex_m::asm::isb();
79 cortex_m::asm::dsb(); 75 cortex_m::asm::dsb();
80 core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); 76 fence(Ordering::SeqCst);
81 77
82 ret 78 res.unwrap()
83} 79}
84 80
85pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { 81pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool {
86 let start_sector = (from - super::FLASH_BASE as u32) / ERASE_SIZE as u32; 82 start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0
87 let end_sector = (to - super::FLASH_BASE as u32) / ERASE_SIZE as u32; 83}
88 84
85pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> {
86 let start_sector = (start_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32;
87 let end_sector = (end_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32;
89 for sector in start_sector..end_sector { 88 for sector in start_sector..end_sector {
90 let bank = if sector >= 8 { 1 } else { 0 }; 89 let bank = if sector >= 8 { 1 } else { 0 };
91 let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); 90 let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8);
@@ -93,7 +92,6 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
93 return ret; 92 return ret;
94 } 93 }
95 } 94 }
96
97 Ok(()) 95 Ok(())
98} 96}
99 97
@@ -157,7 +155,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) {
157 }); 155 });
158} 156}
159 157
160pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { 158unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
161 loop { 159 loop {
162 let sr = bank.sr().read(); 160 let sr = bank.sr().read();
163 161
diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs
index 9ab732b8b..57989625c 100644
--- a/embassy-stm32/src/flash/l.rs
+++ b/embassy-stm32/src/flash/l.rs
@@ -1,14 +1,12 @@
1use core::convert::TryInto;
2use core::ptr::write_volatile; 1use core::ptr::write_volatile;
3 2
4use super::FlashRegion; 3use atomic_polyfill::{fence, Ordering};
4
5use super::{FlashRegion, WRITE_SIZE};
5use crate::flash::Error; 6use crate::flash::Error;
6use crate::pac; 7use crate::pac;
7 8
8const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE;
9const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; 9const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE;
10pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE;
11pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE;
12 10
13pub(crate) unsafe fn lock() { 11pub(crate) unsafe fn lock() {
14 #[cfg(any(flash_wl, flash_wb, flash_l4))] 12 #[cfg(any(flash_wl, flash_wb, flash_l4))]
@@ -39,35 +37,37 @@ pub(crate) unsafe fn unlock() {
39 } 37 }
40} 38}
41 39
42pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { 40pub(crate) unsafe fn begin_write() {
41 assert_eq!(0, WRITE_SIZE % 4);
42
43 #[cfg(any(flash_wl, flash_wb, flash_l4))] 43 #[cfg(any(flash_wl, flash_wb, flash_l4))]
44 pac::FLASH.cr().write(|w| w.set_pg(true)); 44 pac::FLASH.cr().write(|w| w.set_pg(true));
45}
45 46
46 let ret = { 47pub(crate) unsafe fn end_write() {
47 let mut ret: Result<(), Error> = Ok(());
48 let mut address = first_address;
49 for chunk in buf.chunks(WRITE_SIZE) {
50 for val in chunk.chunks(4) {
51 write_volatile(address as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap()));
52 address += val.len() as u32;
53 }
54
55 ret = blocking_wait_ready();
56 if ret.is_err() {
57 break;
58 }
59 }
60 ret
61 };
62
63 #[cfg(any(flash_wl, flash_wb, flash_l4))] 48 #[cfg(any(flash_wl, flash_wb, flash_l4))]
64 pac::FLASH.cr().write(|w| w.set_pg(false)); 49 pac::FLASH.cr().write(|w| w.set_pg(false));
50}
51
52pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
53 let mut address = start_address;
54 for val in buf.chunks(4) {
55 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
56 address += val.len() as u32;
57
58 // prevents parallelism errors
59 fence(Ordering::SeqCst);
60 }
61
62 blocking_wait_ready()
63}
65 64
66 ret 65pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool {
66 start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0
67} 67}
68 68
69pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { 69pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> {
70 for page in (from_address..to_address).step_by(ERASE_SIZE) { 70 for page in (start_address..end_address).step_by(ERASE_SIZE) {
71 #[cfg(any(flash_l0, flash_l1))] 71 #[cfg(any(flash_l0, flash_l1))]
72 { 72 {
73 pac::FLASH.pecr().modify(|w| { 73 pac::FLASH.pecr().modify(|w| {
@@ -155,7 +155,7 @@ pub(crate) unsafe fn clear_all_err() {
155 }); 155 });
156} 156}
157 157
158pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { 158unsafe fn blocking_wait_ready() -> Result<(), Error> {
159 loop { 159 loop {
160 let sr = pac::FLASH.sr().read(); 160 let sr = pac::FLASH.sr().read();
161 161
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index b6cecfdb3..c704909ac 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -1,8 +1,10 @@
1use embassy_hal_common::{into_ref, PeripheralRef}; 1use embassy_hal_common::{into_ref, PeripheralRef};
2use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
3use embassy_sync::mutex::{Mutex, MutexGuard};
2use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; 4use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
3 5
4pub use crate::_generated::flash_regions::*; 6pub use crate::_generated::flash_regions::*;
5pub use crate::pac::{FLASH_BASE, FLASH_SIZE}; 7pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
6use crate::peripherals::FLASH; 8use crate::peripherals::FLASH;
7use crate::Peripheral; 9use crate::Peripheral;
8 10
@@ -17,6 +19,8 @@ pub struct Flash<'d> {
17 _inner: PeripheralRef<'d, FLASH>, 19 _inner: PeripheralRef<'d, FLASH>,
18} 20}
19 21
22static REGION_LOCK: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(());
23
20impl<'d> Flash<'d> { 24impl<'d> Flash<'d> {
21 pub fn new(p: impl Peripheral<P = FLASH> + 'd) -> Self { 25 pub fn new(p: impl Peripheral<P = FLASH> + 'd) -> Self {
22 into_ref!(p); 26 into_ref!(p);
@@ -33,7 +37,6 @@ impl<'d> Flash<'d> {
33 } 37 }
34 38
35 let first_address = FLASH_BASE as u32 + offset; 39 let first_address = FLASH_BASE as u32 + offset;
36
37 let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; 40 let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) };
38 bytes.copy_from_slice(flash_data); 41 bytes.copy_from_slice(flash_data);
39 Ok(()) 42 Ok(())
@@ -43,39 +46,56 @@ impl<'d> Flash<'d> {
43 if offset as usize + buf.len() > FLASH_SIZE { 46 if offset as usize + buf.len() > FLASH_SIZE {
44 return Err(Error::Size); 47 return Err(Error::Size);
45 } 48 }
46 if offset as usize % family::MAX_WRITE_SIZE != 0 || buf.len() as usize % family::MAX_WRITE_SIZE != 0 { 49 if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 {
47 return Err(Error::Unaligned); 50 return Err(Error::Unaligned);
48 } 51 }
49 52
50 let first_address = FLASH_BASE as u32 + offset; 53 let start_address = FLASH_BASE as u32 + offset;
51 trace!("Writing {} bytes at 0x{:x}", buf.len(), first_address); 54 trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address);
55
56 // No need to take lock here as we only have one mut flash reference.
52 57
53 unsafe { 58 unsafe {
54 family::clear_all_err(); 59 family::clear_all_err();
55
56 family::unlock(); 60 family::unlock();
57 let res = family::blocking_write(first_address, buf); 61 let res = Flash::blocking_write_all(start_address, buf);
58 family::lock(); 62 family::lock();
59 res 63 res
60 } 64 }
61 } 65 }
62 66
67 unsafe fn blocking_write_all(start_address: u32, buf: &[u8]) -> Result<(), Error> {
68 family::begin_write();
69 let mut address = start_address;
70 for chunk in buf.chunks(WRITE_SIZE) {
71 let res = unsafe { family::blocking_write(address, chunk.try_into().unwrap()) };
72 if res.is_err() {
73 family::end_write();
74 return res;
75 }
76 address += WRITE_SIZE as u32;
77 }
78
79 family::end_write();
80 Ok(())
81 }
82
63 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { 83 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
64 if to < from || to as usize > FLASH_SIZE { 84 if to < from || to as usize > FLASH_SIZE {
65 return Err(Error::Size); 85 return Err(Error::Size);
66 } 86 }
67 if (from as usize % family::MAX_ERASE_SIZE) != 0 || (to as usize % family::MAX_ERASE_SIZE) != 0 { 87
88 let start_address = FLASH_BASE as u32 + from;
89 let end_address = FLASH_BASE as u32 + to;
90 if !family::is_eraseable_range(start_address, end_address) {
68 return Err(Error::Unaligned); 91 return Err(Error::Unaligned);
69 } 92 }
70 93 trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address);
71 let from_address = FLASH_BASE as u32 + from;
72 let to_address = FLASH_BASE as u32 + to;
73 94
74 unsafe { 95 unsafe {
75 family::clear_all_err(); 96 family::clear_all_err();
76
77 family::unlock(); 97 family::unlock();
78 let res = family::blocking_erase(from_address, to_address); 98 let res = family::blocking_erase(start_address, end_address);
79 family::lock(); 99 family::lock();
80 res 100 res
81 } 101 }
@@ -101,7 +121,6 @@ pub trait FlashRegion {
101 } 121 }
102 122
103 let first_address = Self::BASE as u32 + offset; 123 let first_address = Self::BASE as u32 + offset;
104
105 let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; 124 let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) };
106 bytes.copy_from_slice(flash_data); 125 bytes.copy_from_slice(flash_data);
107 Ok(()) 126 Ok(())
@@ -115,17 +134,19 @@ pub trait FlashRegion {
115 return Err(Error::Unaligned); 134 return Err(Error::Unaligned);
116 } 135 }
117 136
118 let first_address = Self::BASE as u32 + offset; 137 let start_address = Self::BASE as u32 + offset;
119 trace!("Writing {} bytes from 0x{:x}", buf.len(), first_address); 138 trace!("Writing {} bytes from 0x{:x}", buf.len(), start_address);
120 139
121 critical_section::with(|_| unsafe { 140 // Protect agains simultaneous write/erase to multiple regions.
122 family::clear_all_err(); 141 let _guard = take_lock_spin();
123 142
143 unsafe {
144 family::clear_all_err();
124 family::unlock(); 145 family::unlock();
125 let res = family::blocking_write(first_address, buf); 146 let res = Flash::blocking_write_all(start_address, buf);
126 family::lock(); 147 family::lock();
127 res 148 res
128 }) 149 }
129 } 150 }
130 151
131 fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { 152 fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
@@ -136,18 +157,28 @@ pub trait FlashRegion {
136 return Err(Error::Unaligned); 157 return Err(Error::Unaligned);
137 } 158 }
138 159
139 let from_address = Self::BASE as u32 + from; 160 let start_address = Self::BASE as u32 + from;
140 let to_address = Self::BASE as u32 + to; 161 let end_address = Self::BASE as u32 + to;
141 trace!("Erasing from 0x{:x} to 0x{:x}", from_address, to_address); 162 trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address);
142 163
143 critical_section::with(|_| unsafe { 164 // Protect agains simultaneous write/erase to multiple regions.
144 family::clear_all_err(); 165 let _guard = take_lock_spin();
145 166
167 unsafe {
168 family::clear_all_err();
146 family::unlock(); 169 family::unlock();
147 let res = family::blocking_erase(from_address, to_address); 170 let res = family::blocking_erase(start_address, end_address);
148 family::lock(); 171 family::lock();
149 res 172 res
150 }) 173 }
174 }
175}
176
177fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> {
178 loop {
179 if let Ok(guard) = REGION_LOCK.try_lock() {
180 return guard;
181 }
151 } 182 }
152} 183}
153 184