bootloader and flashloader update
This commit is contained in:
@ -11,6 +11,7 @@ panic-rtt-target = { version = "0.1.3" }
|
||||
panic-halt = { version = "0.2" }
|
||||
rtt-target = { version = "0.5" }
|
||||
crc = "3"
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
static_assertions = "1"
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
|
@ -9,14 +9,15 @@ The bootloader uses the following memory map:
|
||||
|
||||
| Address | Notes | Size |
|
||||
| ------ | ---- | ---- |
|
||||
| 0x0 | Bootloader start | code up to 0x3FFC bytes |
|
||||
| 0x2FFE | Bootloader CRC | word |
|
||||
| 0x3000 | App image A start | code up to 0xE7F8 (~58K) bytes |
|
||||
| 0x0 | Bootloader start | code up to 0x2FFE bytes |
|
||||
| 0x2FFE | Bootloader CRC | half-word |
|
||||
| 0x3000 | App image A start | code up to 0xE7F4 (~59K) bytes |
|
||||
| 0x117F8 | App image A CRC check length | word |
|
||||
| 0x117FC | App image A CRC check value | word |
|
||||
| 0x11800 | App image B start | code up to 0xE7F8 (~58K) bytes |
|
||||
| 0x1FFF8 | App image B CRC check length | word |
|
||||
| 0x1FFFC | App image B CRC check value | word |
|
||||
| 0x117FC | App image B start | code up to 0xE7F4 (~59K) bytes |
|
||||
| 0x1FFF0 | App image B CRC check length | word |
|
||||
| 0x1FFF4 | App image B CRC check value | word |
|
||||
| 0x1FFF8 | Reserved section, contains boot select parameter | 8 bytes |
|
||||
| 0x20000 | End of NVM | end |
|
||||
|
||||
## Additional Information
|
||||
@ -35,13 +36,16 @@ The bootloader performs the following steps:
|
||||
1. The application will calculate the checksum of itself if the bootloader CRC is blank (all zeroes
|
||||
or all ones). If the CRC is not blank and the checksum check fails, it will immediately boot
|
||||
application image A. Otherwise, it proceeds to the next step.
|
||||
2. Check the checksum of App A. If that checksum is valid, it will boot App A. If not, it will
|
||||
proceed to the next step.
|
||||
3. Check the checksum of App B. If that checksum is valid, it will boot App B. If not, it will
|
||||
boot App A as the fallback image.
|
||||
2. Read the boot slot from a reserved section at the end of the EEPROM. It is assumed that the full
|
||||
128 kB are copied from the ST EEPROM to the code RAM at startup. The boot slot is read from
|
||||
the code RAM directly.
|
||||
3. Check the checksum of the boot slot. If that checksum is valid, it will boot that slot. If not,
|
||||
it will proceed to the next step.
|
||||
4. Check the checksum of the other slot . If that checksum is valid, it will boot that slot. If
|
||||
not, it will boot App A as the fallback image.
|
||||
|
||||
You could adapt and combine this bootloader with a non-volatile memory to select a prefered app
|
||||
image, which would be a first step towards an updatable flight software.
|
||||
In your actual production application, a command to update the preferred boot slot could be exposed
|
||||
to allow performing software updates in a safe way.
|
||||
|
||||
Please note that you *MUST* compile the application at slot A and slot B with an appropriate
|
||||
`memory.x` file where the base address of the `FLASH` was adapted according to the base address
|
||||
|
@ -5,6 +5,7 @@ use bootloader::NvmInterface;
|
||||
use cortex_m_rt::entry;
|
||||
use crc::{Crc, CRC_16_IBM_3740};
|
||||
use embedded_hal::delay::DelayNs;
|
||||
use num_enum::TryFromPrimitive;
|
||||
#[cfg(not(feature = "rtt-panic"))]
|
||||
use panic_halt as _;
|
||||
#[cfg(feature = "rtt-panic")]
|
||||
@ -59,8 +60,9 @@ const APP_B_SIZE_ADDR: u32 = APP_B_END_ADDR - 8;
|
||||
// Four bytes reserved, even when only 2 byte CRC is used. Leaves flexibility to switch to CRC32.
|
||||
// 0x1FFFC
|
||||
const APP_B_CRC_ADDR: u32 = APP_B_END_ADDR - 4;
|
||||
// 0x20000
|
||||
pub const APP_B_END_ADDR: u32 = NVM_SIZE;
|
||||
// 0x20000. 8 bytes at end of EEPROM reserved for preferred image parameter. This reserved
|
||||
// size should be a multiple of 8 due to alignment requirements.
|
||||
pub const APP_B_END_ADDR: u32 = NVM_SIZE - 8;
|
||||
pub const APP_IMG_SZ: u32 = (APP_B_END_ADDR - APP_A_START_ADDR) / 2;
|
||||
|
||||
static_assertions::const_assert!((APP_B_END_ADDR - BOOTLOADER_END_ADDR) % 2 == 0);
|
||||
@ -68,13 +70,15 @@ static_assertions::const_assert!((APP_B_END_ADDR - BOOTLOADER_END_ADDR) % 2 == 0
|
||||
pub const VECTOR_TABLE_OFFSET: u32 = 0x0;
|
||||
pub const VECTOR_TABLE_LEN: u32 = 0xC0;
|
||||
pub const RESET_VECTOR_OFFSET: u32 = 0x4;
|
||||
pub const PREFERRED_SLOT_OFFSET: u32 = 0x20000 - 1;
|
||||
|
||||
const CRC_ALGO: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
enum AppSel {
|
||||
A,
|
||||
B,
|
||||
A = 0,
|
||||
B = 1,
|
||||
}
|
||||
|
||||
pub struct NvmWrapper(pub M95M01);
|
||||
@ -154,10 +158,24 @@ fn main() -> ! {
|
||||
// Check bootloader's CRC (and write it if blank)
|
||||
check_own_crc(&dp.sysconfig, &cp, &mut nvm, &mut timer);
|
||||
|
||||
if check_app_crc(AppSel::A) {
|
||||
boot_app(&dp.sysconfig, &cp, AppSel::A, &mut timer)
|
||||
} else if check_app_crc(AppSel::B) {
|
||||
boot_app(&dp.sysconfig, &cp, AppSel::B, &mut timer)
|
||||
// This is technically read from the EEPROM. We assume that the full 128 kB were copied
|
||||
// from the EEPROM to the code RAM and read the boot slot from the code ram directly.
|
||||
let preferred_app = AppSel::try_from(unsafe {
|
||||
(PREFERRED_SLOT_OFFSET as *const u8)
|
||||
.read_unaligned()
|
||||
.to_be()
|
||||
})
|
||||
.unwrap_or(AppSel::A);
|
||||
let other_app = if preferred_app == AppSel::A {
|
||||
AppSel::B
|
||||
} else {
|
||||
AppSel::A
|
||||
};
|
||||
|
||||
if check_app_crc(preferred_app) {
|
||||
boot_app(&dp.sysconfig, &cp, preferred_app, &mut timer)
|
||||
} else if check_app_crc(other_app) {
|
||||
boot_app(&dp.sysconfig, &cp, other_app, &mut timer)
|
||||
} else {
|
||||
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
|
||||
rprintln!("both images corrupt! booting image A");
|
||||
|
Reference in New Issue
Block a user