avr-boot 0.3.2

Self Programming functions for AVR MCUs
Documentation
struct McuInfo {
    spmcsr: u8,
    page_erase: u8,
    page_write: u8,
    page_fill: u8,
    rww_enable: Option<u8>,
    boot_lock_bits_set: u8,
    rampz: Option<u8>,
    page_size: usize,
}

fn main() {
    println!("cargo:rustc-link-arg=-lc");
    println!("cargo::rustc-check-cfg=cfg(rww_enable)");
    println!("cargo::rustc-check-cfg=cfg(extended_addressing)");

    let info = get_mcu_info();

    println!("cargo:rustc-env=AVR_BOOT_SPMCSR={}", info.spmcsr);
    println!("cargo:rustc-env=AVR_BOOT_PAGE_ERASE={}", info.page_erase);
    println!("cargo:rustc-env=AVR_BOOT_PAGE_WRITE={}", info.page_write);
    println!("cargo:rustc-env=AVR_BOOT_PAGE_FILL={}", info.page_fill);

    if let Some(rww_enable) = info.rww_enable {
        println!("cargo:rustc-env=AVR_BOOT_RWW_ENABLE={rww_enable}");
        println!("cargo:rustc-cfg=rww_enable");
    }

    println!(
        "cargo:rustc-env=AVR_BOOT_LOCK_BITS_SET={}",
        info.boot_lock_bits_set
    );
    println!("cargo:rustc-env=AVR_BOOT_SPM_PAGESIZE={}", info.page_size);
    if let Some(rampz) = info.rampz {
        println!("cargo:rustc-cfg=extended_addressing");
        println!("cargo:rustc-env=AVR_RAMPZ={rampz}");
    }
}

fn get_mcu_info() -> McuInfo {
    const DEFAULT_MCU_FOR_NON_AVR_DOCS: &str = "atmega1280";

    let current_mcu = if avr_mcu::current::is_compiling_for_avr() {
        avr_mcu::current::mcu().expect("no target cpu specified")
    } else {
        avr_mcu::microcontroller(DEFAULT_MCU_FOR_NON_AVR_DOCS)
    };

    let spm_reg = current_mcu
        .registers()
        .find(|r| r.name == "SPMCSR" || r.name == "SPMCR")
        .expect("could not find SPMCSR or SPMCR register");

    let spm_enable = spm_reg
        .bitfields
        .iter()
        .find(|b| b.name == "SPMEN" || b.name == "SELFPRGEN")
        .expect("could not find spm enable bitfield");

    let page_erase = spm_reg
        .bitfields
        .iter()
        .find(|b| b.name == "PGERS")
        .expect("could not find page erase bitfield");

    let page_write = spm_reg
        .bitfields
        .iter()
        .find(|b| b.name == "PGWRT")
        .expect("could not find page write bitfield");

    let rww_enable = spm_reg
        .bitfields
        .iter()
        .find(|b| b.name == "ASRE" || b.name == "RWWSRE");

    let blb_set = spm_reg
        .bitfields
        .iter()
        .find(|b| b.name == "LBSET" || b.name == "BLBSET" || b.name == "RFLB")
        .expect("could not find blb set bitfield");

    let prog_space = current_mcu
        .device
        .address_spaces
        .iter()
        .find(|space| space.name == "prog")
        .expect("could not find prog address space");

    let page_size = prog_space
        .segments
        .iter()
        .find(|seg| seg.name == "FLASH")
        .and_then(|seg| seg.page_size)
        .expect("failed to find page size");

    let rampz = if prog_space.size > 0xffff {
        current_mcu
            .registers()
            .find(|r| r.name == "RAMPZ" || r.name == "RAMPZ0")
            .map(|r| r.offset as u8)
    } else {
        None
    };

    McuInfo {
        spmcsr: spm_reg.offset as u8,
        page_erase: (spm_enable.mask | page_erase.mask) as u8,
        page_write: (spm_enable.mask | page_write.mask) as u8,
        page_fill: spm_enable.mask as u8,
        rww_enable: rww_enable.map(|e| (e.mask | spm_enable.mask) as u8),
        boot_lock_bits_set: (spm_enable.mask | blb_set.mask) as u8,
        rampz,
        page_size: page_size as usize,
    }
}