Skip to content

Commit 6b2a791

Browse files
committed
Extend Flash API to support writing half-pages
This commit includes C code and a binary blob. Please refer to the comments in the commit on why this is necessary.
1 parent 94a9983 commit 6b2a791

File tree

8 files changed

+138
-1
lines changed

8 files changed

+138
-1
lines changed

build.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::env;
2-
use std::fs::File;
2+
use std::fs::{self, File};
33
use std::io::Write;
44
use std::path::PathBuf;
55

@@ -48,4 +48,23 @@ fn main() {
4848
}
4949

5050
println!("cargo:rerun-if-changed=build.rs");
51+
52+
53+
// Copy the binary blog required by the Flash API somewhere the linker can
54+
// find it, and tell Cargo to link it.
55+
56+
let blob_name = "flash";
57+
let blob_file = format!("lib{}.a", blob_name);
58+
let blob_path = format!("flash-code/{}", blob_file);
59+
60+
fs::copy(
61+
&blob_path,
62+
out.join(format!("{}", blob_file)),
63+
)
64+
.expect("Failed to copy binary blog for Flash API");
65+
66+
println!("cargo:rustc-link-lib=static={}", blob_name);
67+
println!("cargo:rustc-link-search={}", out.display());
68+
69+
println!("cargo:rerun-if-changed={}", blob_path);
5170
}

examples/flash.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ fn main() -> ! {
5959
assert_eq!(word, 0);
6060
}
6161

62+
let words = [
63+
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
64+
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
65+
];
66+
67+
flash.write_flash_half_page(address, &words)
68+
.expect("Failed to write Flash half-page");
69+
70+
for (i, &expected) in words.iter().enumerate() {
71+
let actual = unsafe { *address.offset(i as isize) };
72+
assert_eq!(expected, actual);
73+
}
74+
6275
// Blink LED to indicate we haven't panicked.
6376
loop {
6477
led.set_high().unwrap();

flash-code/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# C compiler output
2+
/*.o

flash-code/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C code used by the FLASH API to write half-pages. Contains both the source code, as well as a precompiled binary.
2+
3+
The binary can be compiled using `compile.sh` in this directory. See `src/flash.rs` for an explanation of why this is necessary.

flash-code/compile.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/sh
2+
3+
# Compiles `flash.c` using the GCC toolchain. `build.rs` makes sure that the
4+
# resulting library is linked.
5+
#
6+
# Since requiring all users of this HAL to have the GCC toolchain installed
7+
# would be inconvenient for many, the resulting library is checked into the
8+
# repository. After every change to `flash.c`, you need to call this script
9+
# manually and commit the updated binary.
10+
11+
arm-none-eabi-gcc -march=armv6s-m -ffreestanding -O2 -c flash.c &&
12+
arm-none-eabi-ar r libflash.a flash.o

flash-code/flash.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <stdint.h>
2+
3+
4+
// Called from src/flash.rs. See comment there for an explanation of why a C
5+
// function is necessary.
6+
__attribute__((section(".data")))
7+
void write_half_page(uint32_t *address, uint32_t *words) {
8+
uint32_t i = 0;
9+
while (i < 16) {
10+
*(address + i) = *(words + i);
11+
i++;
12+
}
13+
}

flash-code/libflash.a

896 Bytes
Binary file not shown.

src/flash.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,59 @@ impl FLASH {
160160
})
161161
}
162162

163+
/// Writes a half-page (16 words) of Flash memory
164+
///
165+
/// The memory written to must have been erased before, otherwise this
166+
/// method will return an error.
167+
///
168+
/// # Panics
169+
///
170+
/// This method will panic, unless all of the following is true:
171+
/// - `address` points to Flash memory
172+
/// - `address` is aligned to a half-page boundary (16 words, 64 bytes)
173+
/// - `words` has a length of 16
174+
pub fn write_flash_half_page(&mut self, address: *mut u32, words: &[u32])
175+
-> Result
176+
{
177+
self.unlock(|self_| {
178+
let memory = self_.verify_address(address);
179+
180+
if !memory.is_flash() {
181+
panic!("Address does not point to Flash memory");
182+
}
183+
if address as u32 & 0x3f != 0 {
184+
panic!("Address is not aligned to half-page boundary");
185+
}
186+
if words.len() != 16 {
187+
panic!("`words` is not exactly a half-page of memory");
188+
}
189+
190+
// Wait, while the memory interface is busy.
191+
while self_.flash.sr.read().bsy().is_active() {}
192+
193+
// Enable write operation
194+
self_.flash.pecr.modify(|_, w| {
195+
// Half-page programming mode
196+
w.fprg().set_bit();
197+
// Required for mass operations in Flash memory
198+
w.prog().set_bit();
199+
200+
w
201+
});
202+
203+
// Safe, because we've verified the valididty of `address` and the
204+
// length `words`.
205+
unsafe { write_half_page(address, words.as_ptr()); }
206+
207+
// Wait for operation to complete
208+
while self_.flash.sr.read().bsy().is_active() {}
209+
210+
self_.check_errors()
211+
212+
// No need to reset PECR flags, that's done by `unlock`.
213+
})
214+
}
215+
163216
fn unlock(&mut self, f: impl FnOnce(&mut Self) -> Result) -> Result {
164217
// Unlock everything that needs unlocking
165218
self.flash.pekeyr.write(|w| w.pekeyr().bits(0x89ABCDEF));
@@ -261,6 +314,28 @@ pub fn flash_size_in_kb() -> u32 {
261314
}
262315

263316

317+
extern {
318+
/// Writes a half-page at the given address
319+
///
320+
/// Unfortunately this function had to be implemented in C. No access to
321+
/// Flash memory is allowed after the first word has been written, and that
322+
/// includes execution of code that is located in Flash. This means the
323+
/// function that writes the half-page has to be executed from memory, and
324+
/// is not allowed to call any functions that are not located in memory.
325+
///
326+
/// Unfortunately I found this impossible to achieve in Rust. I can write
327+
/// a Rust function that is located in RAM, using `#[link_section=".data"]`,
328+
/// but I failed to write any useful Rust code that doesn't include function
329+
/// calls to _something_ that is outside of my control, as so much of Rust's
330+
/// functionality is defined in terms of function calls.
331+
///
332+
/// I ended up writing it in C, as that was the only solution I could come
333+
/// up with that will run on the stable channel (is nightly is acceptable,
334+
/// we could use a Rust function with inline assembly).
335+
fn write_half_page(address: *mut u32, words: *const u32);
336+
}
337+
338+
264339
#[derive(Copy, Clone, Eq, PartialEq)]
265340
enum Memory {
266341
Flash,

0 commit comments

Comments
 (0)