From: Tilman Sauerbeck Date: Tue, 25 Jun 2019 04:06:30 +0000 (+0200) Subject: bootloader: Add the flash module. X-Git-Url: http://git.code-monkey.de/?a=commitdiff_plain;h=2cd9f6b175d16cb0a5c8bb7f3e387fecd27a7199;p=gps-watch.git bootloader: Add the flash module. 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. --- diff --git a/SConscript.target b/SConscript.target index 6dd2f41..39f0fad 100644 --- a/SConscript.target +++ b/SConscript.target @@ -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 index 0000000..f6c00d9 --- /dev/null +++ b/src/bootloader/flash.rs @@ -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; + +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) +} diff --git a/src/bootloader/main.rs b/src/bootloader/main.rs index fbd3aa0..16255f6 100644 --- a/src/bootloader/main.rs +++ b/src/bootloader/main.rs @@ -27,6 +27,8 @@ extern crate common; +mod flash; + use common::clock; use common::usb_serial;