bootloader: Add the flash module.
authorTilman Sauerbeck <tilman@code-monkey.de>
Tue, 25 Jun 2019 04:06:30 +0000 (06:06 +0200)
committerTilman Sauerbeck <tilman@code-monkey.de>
Mon, 30 Dec 2019 11:27:58 +0000 (12:27 +0100)
Offers functions to erase a flash sector, program a flash sector and
verify a flash sector's contents.

Note that we don't clear the flash controller's cache by writing to
MCM_PLACR after erasing or programming sectors since it doesn't seem
to be required.

SConscript.target
src/bootloader/flash.rs [new file with mode: 0644]
src/bootloader/main.rs

index 6dd2f413d66fa60b2684d3c648c21b1ec11bdda1..39f0fad7f47209c419840a02ce7bdbafa2bfbd12 100644 (file)
@@ -47,6 +47,7 @@ SConscript('SConscript.libcommon', exports='env', duplicate=0)
 
 libbootloader_source_files = [
     'src/bootloader/main.rs', # Must be listed first (see below).
+    'src/bootloader/flash.rs',
 ]
 
 libbootloader = env.Rustc('libbootloader.a', libbootloader_source_files[0])
diff --git a/src/bootloader/flash.rs b/src/bootloader/flash.rs
new file mode 100644 (file)
index 0000000..f6c00d9
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2019 Tilman Sauerbeck (tilman at code-monkey de)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+use common::register;
+
+type Reg8 = register::Register<u8>;
+
+const FTFA_BASE: u32 = 0x40020000;
+
+const FTFA_FSTAT : u32 = FTFA_BASE + 0x00;
+const FTFA_FCCOB3: u32 = FTFA_BASE + 0x04;
+const FTFA_FCCOB2: u32 = FTFA_BASE + 0x05;
+const FTFA_FCCOB1: u32 = FTFA_BASE + 0x06;
+const FTFA_FCCOB0: u32 = FTFA_BASE + 0x07;
+const FTFA_FCCOB7: u32 = FTFA_BASE + 0x08;
+const FTFA_FCCOB6: u32 = FTFA_BASE + 0x09;
+const FTFA_FCCOB5: u32 = FTFA_BASE + 0x0a;
+const FTFA_FCCOB4: u32 = FTFA_BASE + 0x0b;
+
+const FTFA_FSTAT_MGSTAT0 : u8 = 1 << 0;
+const FTFA_FSTAT_FPVIOL  : u8 = 1 << 4;
+const FTFA_FSTAT_ACCERR  : u8 = 1 << 5;
+const FTFA_FSTAT_RDCOLERR: u8 = 1 << 6;
+const FTFA_FSTAT_CCIF    : u8 = 1 << 7;
+
+const FCMD_RD1SEC : u8 = 0x01;
+const FCMD_PGM4   : u8 = 0x06;
+const FCMD_ERSSCR : u8 = 0x09;
+
+pub const SECTOR_SIZE : usize = 1024;
+
+// The hardware refuses to run another command (or even take the new command's
+// parameters) until the errors reported by a previous command have been
+// acknowledged (see KL26P121M48SF4RM.pdf p. 445).
+fn prepare_command() {
+    let mut fstat = Reg8::new(FTFA_FSTAT);
+
+    fstat.write(FTFA_FSTAT_FPVIOL | FTFA_FSTAT_ACCERR | FTFA_FSTAT_RDCOLERR);
+}
+
+fn run_command() -> u8 {
+    let mut fstat = Reg8::new(FTFA_FSTAT);
+
+    // Start command.
+    fstat.write(FTFA_FSTAT_CCIF);
+
+    // Wait for it to finish.
+    loop {
+        let v = fstat.read();
+
+        if (v & FTFA_FSTAT_CCIF) != 0 {
+            return v;
+        }
+    }
+}
+
+fn erase_check(sector: u32) -> bool {
+    let address = sector as usize * SECTOR_SIZE;
+    let num_words = SECTOR_SIZE / 4;
+
+    prepare_command();
+
+    Reg8::new(FTFA_FCCOB0).write(FCMD_RD1SEC);
+    Reg8::new(FTFA_FCCOB1).write((address >> 16) as u8);
+    Reg8::new(FTFA_FCCOB2).write((address >>  8) as u8);
+    Reg8::new(FTFA_FCCOB3).write((address >>  0) as u8);
+    Reg8::new(FTFA_FCCOB4).write((num_words >> 8) as u8);
+    Reg8::new(FTFA_FCCOB5).write((num_words >> 0) as u8);
+    Reg8::new(FTFA_FCCOB6).write(0);
+
+    (run_command() & FTFA_FSTAT_MGSTAT0) == 0
+}
+
+pub fn erase(sector: u32) {
+    // Already erased?
+    if erase_check(sector) {
+        return;
+    }
+
+    let address = sector as usize * SECTOR_SIZE;
+
+    prepare_command();
+
+    Reg8::new(FTFA_FCCOB0).write(FCMD_ERSSCR);
+    Reg8::new(FTFA_FCCOB1).write((address >> 16) as u8);
+    Reg8::new(FTFA_FCCOB2).write((address >>  8) as u8);
+    Reg8::new(FTFA_FCCOB3).write((address >>  0) as u8);
+
+    run_command();
+}
+
+pub fn program(sector: u32, data: &[u8]) {
+    prepare_command();
+
+    for (i, bytes) in data.chunks(4).enumerate() {
+        if bytes[0] == 0xff &&
+           bytes[1] == 0xff &&
+           bytes[2] == 0xff &&
+           bytes[3] == 0xff {
+            continue;
+        }
+
+        let address = (sector as usize * SECTOR_SIZE) + (i * 4);
+
+        Reg8::new(FTFA_FCCOB0).write(FCMD_PGM4);
+        Reg8::new(FTFA_FCCOB1).write((address >> 16) as u8);
+        Reg8::new(FTFA_FCCOB2).write((address >>  8) as u8);
+        Reg8::new(FTFA_FCCOB3).write((address >>  0) as u8);
+        Reg8::new(FTFA_FCCOB4).write(bytes[3]);
+        Reg8::new(FTFA_FCCOB5).write(bytes[2]);
+        Reg8::new(FTFA_FCCOB6).write(bytes[1]);
+        Reg8::new(FTFA_FCCOB7).write(bytes[0]);
+
+        run_command();
+    }
+}
+
+pub fn verify(sector: u32, data: &[u8]) -> (u32, u32) {
+    let mut num_mismatches = 0;
+    let mut first_mismatch_offset = 0;
+
+    for i in (0..data.len()).rev() {
+        let address = (sector as usize * SECTOR_SIZE) + i;
+        let ptr = address as *const u8;
+        let actual_byte = unsafe { core::ptr::read(ptr) };
+        let expected_byte = data[i];
+
+        if actual_byte != expected_byte {
+            num_mismatches += 1;
+            first_mismatch_offset = i;
+        }
+    }
+
+    (num_mismatches, first_mismatch_offset as u32)
+}
index fbd3aa0c01c03d6bbe34352e97b5349920f1a6ad..16255f601b4a0c2ab8874fe19d434174a3312a59 100644 (file)
@@ -27,6 +27,8 @@
 
 extern crate common;
 
+mod flash;
+
 use common::clock;
 use common::usb_serial;