stupid nvm
This commit is contained in:
parent
78b4bbcd49
commit
bc0b18ab7c
@ -26,7 +26,6 @@ incremental = false
|
|||||||
lto = 'fat'
|
lto = 'fat'
|
||||||
opt-level = 3 # <-
|
opt-level = 3 # <-
|
||||||
overflow-checks = false # <-
|
overflow-checks = false # <-
|
||||||
strip = true
|
|
||||||
|
|
||||||
[profile.small]
|
[profile.small]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
@ -35,5 +34,3 @@ debug-assertions = false # <-
|
|||||||
lto = true
|
lto = true
|
||||||
opt-level = 'z' # <-
|
opt-level = 'z' # <-
|
||||||
overflow-checks = false # <-
|
overflow-checks = false # <-
|
||||||
strip = true
|
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@ fn main() -> ! {
|
|||||||
if DEBUG_PRINTOUTS {
|
if DEBUG_PRINTOUTS {
|
||||||
rprintln!("both images corrupt! booting image A");
|
rprintln!("both images corrupt! booting image A");
|
||||||
}
|
}
|
||||||
|
loop {}
|
||||||
// TODO: Shift a CCSDS packet out to inform host/OBC about image corruption.
|
// TODO: Shift a CCSDS packet out to inform host/OBC about image corruption.
|
||||||
// Both images seem to be corrupt. Boot default image A.
|
// Both images seem to be corrupt. Boot default image A.
|
||||||
boot_app(AppSel::A, &cp)
|
boot_app(AppSel::A, &cp)
|
||||||
|
@ -7,9 +7,27 @@ edition = "2021"
|
|||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
panic-rtt-target = { version = "0.1.3" }
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
rtt-target = { version = "0.5" }
|
rtt-target = { version = "0.5" }
|
||||||
|
rtt-log = "0.3"
|
||||||
|
log = "0.4"
|
||||||
crc = "3"
|
crc = "3"
|
||||||
|
rtic-sync = "1"
|
||||||
|
|
||||||
|
[dependencies.once_cell]
|
||||||
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
features = ["critical-section"]
|
||||||
|
|
||||||
|
[dependencies.spacepackets]
|
||||||
|
version = "0.11"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.cobs]
|
||||||
|
git = "https://github.com/robamu/cobs.rs.git"
|
||||||
|
branch = "all_features"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dependencies.va416xx-hal]
|
[dependencies.va416xx-hal]
|
||||||
path = "../va416xx-hal"
|
path = "../va416xx-hal"
|
||||||
|
9
flashloader/src/lib.rs
Normal file
9
flashloader/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn simple() {
|
||||||
|
assert_eq!(1 + 1, 2);
|
||||||
|
}
|
||||||
|
}
|
@ -18,48 +18,18 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use crc::{Crc, CRC_16_IBM_3740};
|
use embedded_hal_nb::serial::Read;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{clock::Clocks, edac, pac, time::Hertz, wdt::Wdt};
|
||||||
edac,
|
|
||||||
pac::{self},
|
|
||||||
time::Hertz,
|
|
||||||
wdt::Wdt,
|
|
||||||
};
|
|
||||||
|
|
||||||
const EXTCLK_FREQ: u32 = 40_000_000;
|
const EXTCLK_FREQ: u32 = 40_000_000;
|
||||||
const WITH_WDT: bool = true;
|
const COBS_FRAME_SEPARATOR: u8 = 0x0;
|
||||||
const WDT_FREQ_MS: u32 = 50;
|
const MAX_PACKET_SIZE: usize = 1024;
|
||||||
const DEBUG_PRINTOUTS: bool = true;
|
const MAX_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_PACKET_SIZE);
|
||||||
|
const UART_BAUDRATE: u32 = 115200;
|
||||||
|
|
||||||
// Important bootloader addresses and offsets, vector table information.
|
const BOOT_NVM_MEMORY_ID: u8 = 1;
|
||||||
|
|
||||||
const BOOTLOADER_START_ADDR: u32 = 0x0;
|
|
||||||
const BOOTLOADER_END_ADDR: u32 = 0x4000;
|
|
||||||
const BOOTLOADER_CRC_ADDR: u32 = 0x3FFE;
|
|
||||||
const APP_A_START_ADDR: u32 = 0x4000;
|
|
||||||
pub const APP_A_END_ADDR: u32 = 0x22000;
|
|
||||||
// The actual size of the image which is relevant for CRC calculation.
|
|
||||||
const APP_A_SIZE_ADDR: u32 = 0x21FF8;
|
|
||||||
const APP_A_CRC_ADDR: u32 = 0x21FFC;
|
|
||||||
const APP_B_START_ADDR: u32 = 0x22000;
|
|
||||||
pub const APP_B_END_ADDR: u32 = 0x40000;
|
|
||||||
// The actual size of the image which is relevant for CRC calculation.
|
|
||||||
const APP_B_SIZE_ADDR: u32 = 0x3FFF8;
|
|
||||||
const APP_B_CRC_ADDR: u32 = 0x3FFFC;
|
|
||||||
pub const APP_IMG_SZ: u32 = 0x1E000;
|
|
||||||
|
|
||||||
pub const VECTOR_TABLE_OFFSET: u32 = 0x0;
|
|
||||||
pub const VECTOR_TABLE_LEN: u32 = 0x350;
|
|
||||||
pub const RESET_VECTOR_OFFSET: u32 = 0x4;
|
|
||||||
|
|
||||||
const CRC_ALGO: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
enum AppSel {
|
|
||||||
A,
|
|
||||||
B,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait WdtInterface {
|
pub trait WdtInterface {
|
||||||
fn feed(&self);
|
fn feed(&self);
|
||||||
@ -75,25 +45,64 @@ impl WdtInterface for OptWdt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rtic::app(device = pac)]
|
static CLOCKS: OnceCell<Clocks> = OnceCell::new();
|
||||||
|
|
||||||
|
#[rtic::app(device = pac, dispatchers = [U1, U2, U3])]
|
||||||
mod app {
|
mod app {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use embedded_hal_nb::nb;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
|
use rtic_monotonics::systick::ExtU32;
|
||||||
use rtic_monotonics::systick::Systick;
|
use rtic_monotonics::systick::Systick;
|
||||||
use rtt_target::{rprintln, rtt_init_default};
|
use rtic_sync::{
|
||||||
use va416xx_hal::{clock::ClkgenExt, edac, pac};
|
channel::{Receiver, Sender},
|
||||||
|
make_channel,
|
||||||
|
};
|
||||||
|
use rtt_target::rprintln;
|
||||||
|
use spacepackets::ecss::PusServiceId;
|
||||||
|
use spacepackets::ecss::{tc::PusTcReader, PusPacket};
|
||||||
|
use va416xx_hal::{
|
||||||
|
clock::ClkgenExt,
|
||||||
|
edac,
|
||||||
|
gpio::PinsG,
|
||||||
|
nvm::Nvm,
|
||||||
|
pac,
|
||||||
|
uart::{self, Uart},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{setup_edac, EXTCLK_FREQ};
|
use crate::{setup_edac, EXTCLK_FREQ};
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum CobsReaderStates {
|
||||||
|
#[default]
|
||||||
|
WaitingForStart,
|
||||||
|
WatingForEnd,
|
||||||
|
FrameOverflow,
|
||||||
|
}
|
||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {}
|
struct Local {
|
||||||
|
uart_rx: uart::Rx<pac::Uart0>,
|
||||||
|
uart_tx: uart::Tx<pac::Uart0>,
|
||||||
|
cobs_reader_state: CobsReaderStates,
|
||||||
|
tc_tx: TcTx,
|
||||||
|
tc_rx: TcRx,
|
||||||
|
rom_spi: Option<pac::Spi3>,
|
||||||
|
}
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
struct Shared {}
|
struct Shared {
|
||||||
|
decode_buffer_busy: bool,
|
||||||
|
decode_buf: [u8; MAX_PACKET_SIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type TcTx = Sender<'static, usize, 2>;
|
||||||
|
pub type TcRx = Receiver<'static, usize, 2>;
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(mut cx: init::Context) -> (Shared, Local) {
|
fn init(mut cx: init::Context) -> (Shared, Local) {
|
||||||
rtt_init_default!();
|
//rtt_init_default!();
|
||||||
|
rtt_log::init();
|
||||||
rprintln!("-- Vorago flashloader --");
|
rprintln!("-- Vorago flashloader --");
|
||||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||||
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
||||||
@ -107,8 +116,36 @@ mod app {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
setup_edac(&mut cx.device.sysconfig);
|
setup_edac(&mut cx.device.sysconfig);
|
||||||
|
|
||||||
|
let gpiob = PinsG::new(&mut cx.device.sysconfig, cx.device.portg);
|
||||||
|
let tx = gpiob.pg0.into_funsel_1();
|
||||||
|
let rx = gpiob.pg1.into_funsel_1();
|
||||||
|
|
||||||
|
let uart0 = Uart::new(
|
||||||
|
cx.device.uart0,
|
||||||
|
(tx, rx),
|
||||||
|
Hertz::from_raw(UART_BAUDRATE),
|
||||||
|
&mut cx.device.sysconfig,
|
||||||
|
&clocks,
|
||||||
|
);
|
||||||
|
let (tx, rx) = uart0.split();
|
||||||
|
let (tc_tx, tc_rx) = make_channel!(usize, 2);
|
||||||
|
|
||||||
Systick::start(cx.core.SYST, clocks.sysclk().raw(), systick_mono_token);
|
Systick::start(cx.core.SYST, clocks.sysclk().raw(), systick_mono_token);
|
||||||
(Shared {}, Local {})
|
CLOCKS.set(clocks).unwrap();
|
||||||
|
(
|
||||||
|
Shared {
|
||||||
|
decode_buffer_busy: false,
|
||||||
|
decode_buf: [0; MAX_PACKET_SIZE],
|
||||||
|
},
|
||||||
|
Local {
|
||||||
|
uart_rx: rx,
|
||||||
|
uart_tx: tx,
|
||||||
|
cobs_reader_state: CobsReaderStates::default(),
|
||||||
|
tc_tx,
|
||||||
|
tc_rx,
|
||||||
|
rom_spi: Some(cx.device.spi3),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// `shared` cannot be accessed from this context
|
// `shared` cannot be accessed from this context
|
||||||
@ -118,6 +155,169 @@ mod app {
|
|||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
priority = 3,
|
||||||
|
local=[
|
||||||
|
read_buf: [u8;MAX_FRAME_SIZE] = [0; MAX_FRAME_SIZE],
|
||||||
|
uart_rx,
|
||||||
|
cobs_reader_state,
|
||||||
|
tc_tx
|
||||||
|
],
|
||||||
|
shared=[decode_buffer_busy, decode_buf]
|
||||||
|
)]
|
||||||
|
async fn uart_reader_task(mut cx: uart_reader_task::Context) {
|
||||||
|
let mut current_idx = 0;
|
||||||
|
loop {
|
||||||
|
match nb::block!(cx.local.uart_rx.read()) {
|
||||||
|
Ok(byte) => match cx.local.cobs_reader_state {
|
||||||
|
CobsReaderStates::WaitingForStart => {
|
||||||
|
if byte == COBS_FRAME_SEPARATOR {
|
||||||
|
*cx.local.cobs_reader_state = CobsReaderStates::WatingForEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CobsReaderStates::WatingForEnd => {
|
||||||
|
if byte == COBS_FRAME_SEPARATOR {
|
||||||
|
let mut sending_failed = false;
|
||||||
|
let mut decoding_error = false;
|
||||||
|
let mut decode_buffer_busy = false;
|
||||||
|
cx.shared.decode_buffer_busy.lock(|busy| {
|
||||||
|
if *busy {
|
||||||
|
decode_buffer_busy = true;
|
||||||
|
} else {
|
||||||
|
cx.shared.decode_buf.lock(|buf| {
|
||||||
|
match cobs::decode(&cx.local.read_buf[..current_idx], buf) {
|
||||||
|
Ok(packet_len) => {
|
||||||
|
if cx.local.tc_tx.try_send(packet_len).is_err() {
|
||||||
|
sending_failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
decoding_error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*busy = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if sending_failed {
|
||||||
|
log::warn!("sending TC packet failed, queue full");
|
||||||
|
}
|
||||||
|
if decoding_error {
|
||||||
|
log::warn!("decoding error");
|
||||||
|
}
|
||||||
|
if decode_buffer_busy {
|
||||||
|
log::warn!("decode buffer busy. data arriving too fast");
|
||||||
|
}
|
||||||
|
} else if current_idx >= cx.local.read_buf.len() {
|
||||||
|
*cx.local.cobs_reader_state = CobsReaderStates::FrameOverflow;
|
||||||
|
} else {
|
||||||
|
cx.local.read_buf[current_idx] = byte;
|
||||||
|
current_idx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CobsReaderStates::FrameOverflow => {
|
||||||
|
if byte == COBS_FRAME_SEPARATOR {
|
||||||
|
*cx.local.cobs_reader_state = CobsReaderStates::WaitingForStart;
|
||||||
|
current_idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => match e {
|
||||||
|
uart::Error::Overrun => todo!(),
|
||||||
|
uart::Error::FramingError => todo!(),
|
||||||
|
uart::Error::ParityError => todo!(),
|
||||||
|
uart::Error::BreakCondition => todo!(),
|
||||||
|
uart::Error::TransferPending => todo!(),
|
||||||
|
uart::Error::BufferTooShort => todo!(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
priority = 2,
|
||||||
|
local=[
|
||||||
|
read_buf: [u8;MAX_FRAME_SIZE] = [0; MAX_FRAME_SIZE],
|
||||||
|
tc_rx,
|
||||||
|
rom_spi
|
||||||
|
],
|
||||||
|
shared=[decode_buffer_busy, decode_buf]
|
||||||
|
)]
|
||||||
|
async fn pus_tc_handler(mut cx: pus_tc_handler::Context) {
|
||||||
|
loop {
|
||||||
|
let _ = cx.local.tc_rx.recv().await;
|
||||||
|
// We still copy the data to a local buffer, so the exchange buffer can already be used
|
||||||
|
// for the next packet / decode process.
|
||||||
|
cx.shared
|
||||||
|
.decode_buf
|
||||||
|
.lock(|buf| cx.local.read_buf[0..buf.len()].copy_from_slice(buf));
|
||||||
|
cx.shared.decode_buffer_busy.lock(|busy| *busy = false);
|
||||||
|
match PusTcReader::new(cx.local.read_buf) {
|
||||||
|
Ok((pus_tc, _)) => {
|
||||||
|
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||||
|
log::info!("Received ping TC");
|
||||||
|
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
||||||
|
// Raw memory write TC
|
||||||
|
if pus_tc.subservice() == 2 {
|
||||||
|
let app_data = pus_tc.app_data();
|
||||||
|
if app_data.len() < 10 {
|
||||||
|
log::warn!(
|
||||||
|
"app data for raw memory write is too short: {}",
|
||||||
|
app_data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let memory_id = app_data[0];
|
||||||
|
if memory_id != BOOT_NVM_MEMORY_ID {
|
||||||
|
log::warn!("memory ID {} not supported", memory_id);
|
||||||
|
// TODO: Error reporting
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap());
|
||||||
|
let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap());
|
||||||
|
if 10 + data_len as usize > app_data.len() {
|
||||||
|
log::warn!(
|
||||||
|
"invalid data length {} for raw mem write detected",
|
||||||
|
data_len
|
||||||
|
);
|
||||||
|
// TODO: Error reporting
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let data = &app_data[10..10 + data_len as usize];
|
||||||
|
log::info!("writing {} bytes at offset {} to NVM", data_len, offset);
|
||||||
|
// Safety: We only use this for NVM handling and we only do NVM
|
||||||
|
// handling here.
|
||||||
|
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
||||||
|
let nvm = Nvm::new(
|
||||||
|
&mut sys_cfg,
|
||||||
|
cx.local.rom_spi.take().unwrap(),
|
||||||
|
CLOCKS.get().as_ref().unwrap(),
|
||||||
|
);
|
||||||
|
nvm.write_data(offset, data);
|
||||||
|
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
||||||
|
log::info!("NVM operation done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("PUS TC error: {}", e);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
priority = 1,
|
||||||
|
local=[
|
||||||
|
uart_tx,
|
||||||
|
],
|
||||||
|
shared=[]
|
||||||
|
)]
|
||||||
|
async fn pus_tm_tx_handler(_cx: pus_tm_tx_handler::Context) {
|
||||||
|
loop {
|
||||||
|
Systick::delay(500.millis()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[task(binds = EDAC_SBE, priority = 1)]
|
#[task(binds = EDAC_SBE, priority = 1)]
|
||||||
fn edac_sbe_isr(_cx: edac_sbe_isr::Context) {
|
fn edac_sbe_isr(_cx: edac_sbe_isr::Context) {
|
||||||
// TODO: Send some command via UART for notification purposes. Also identify the problematic
|
// TODO: Send some command via UART for notification purposes. Also identify the problematic
|
||||||
|
@ -44,7 +44,7 @@ pub const BP_0_ENABLE_MASK: u8 = 1 << 2;
|
|||||||
pub const BP_1_ENABLE_MASK: u8 = 1 << 3;
|
pub const BP_1_ENABLE_MASK: u8 = 1 << 3;
|
||||||
|
|
||||||
pub struct Nvm {
|
pub struct Nvm {
|
||||||
spi: pac::Spi3,
|
spi: Option<pac::Spi3>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
@ -90,7 +90,7 @@ impl Nvm {
|
|||||||
// programmers guide
|
// programmers guide
|
||||||
spi.ctrl1().modify(|_, w| w.enable().set_bit());
|
spi.ctrl1().modify(|_, w| w.enable().set_bit());
|
||||||
|
|
||||||
let mut nvm = Self { spi };
|
let mut nvm = Self { spi: Some(spi) };
|
||||||
nvm.disable_write_prot();
|
nvm.disable_write_prot();
|
||||||
nvm
|
nvm
|
||||||
}
|
}
|
||||||
@ -110,36 +110,40 @@ impl Nvm {
|
|||||||
self.write_single(FRAM_WRSR);
|
self.write_single(FRAM_WRSR);
|
||||||
self.write_with_bmstop(0x00);
|
self.write_with_bmstop(0x00);
|
||||||
}
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn spi(&self) -> &pac::Spi3 {
|
||||||
|
self.spi.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn write_single(&self, word: u8) {
|
pub fn write_single(&self, word: u8) {
|
||||||
self.spi.data().write(|w| unsafe { w.bits(word as u32) })
|
self.spi().data().write(|w| unsafe { w.bits(word as u32) })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn write_with_bmstop(&self, word: u8) {
|
pub fn write_with_bmstop(&self, word: u8) {
|
||||||
self.spi
|
self.spi()
|
||||||
.data()
|
.data()
|
||||||
.write(|w| unsafe { w.bits(BMSTART_BMSTOP_MASK | word as u32) })
|
.write(|w| unsafe { w.bits(BMSTART_BMSTOP_MASK | word as u32) })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn wait_for_tx_idle(&self) {
|
pub fn wait_for_tx_idle(&self) {
|
||||||
while self.spi.status().read().tfe().bit_is_clear() {
|
while self.spi().status().read().tfe().bit_is_clear() {
|
||||||
cortex_m::asm::nop();
|
cortex_m::asm::nop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn wait_for_rx_available(&self) {
|
pub fn wait_for_rx_available(&self) {
|
||||||
while !self.spi.status().read().rne().bit_is_set() {
|
while !self.spi().status().read().rne().bit_is_set() {
|
||||||
cortex_m::asm::nop();
|
cortex_m::asm::nop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn read_single_word(&self) -> u32 {
|
pub fn read_single_word(&self) -> u32 {
|
||||||
self.spi.data().read().bits()
|
self.spi().data().read().bits()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_data(&self, addr: u32, data: &[u8]) {
|
pub fn write_data(&self, addr: u32, data: &[u8]) {
|
||||||
@ -151,13 +155,13 @@ impl Nvm {
|
|||||||
self.write_single(mid_addr_byte(addr));
|
self.write_single(mid_addr_byte(addr));
|
||||||
self.write_single(lsb_addr_byte(addr));
|
self.write_single(lsb_addr_byte(addr));
|
||||||
for byte in data.iter().take(data.len() - 1) {
|
for byte in data.iter().take(data.len() - 1) {
|
||||||
while self.spi.status().read().tnf().bit_is_clear() {
|
while self.spi().status().read().tnf().bit_is_clear() {
|
||||||
cortex_m::asm::nop();
|
cortex_m::asm::nop();
|
||||||
}
|
}
|
||||||
self.write_single(*byte);
|
self.write_single(*byte);
|
||||||
self.read_single_word();
|
self.read_single_word();
|
||||||
}
|
}
|
||||||
while self.spi.status().read().tnf().bit_is_clear() {
|
while self.spi().status().read().tnf().bit_is_clear() {
|
||||||
cortex_m::asm::nop();
|
cortex_m::asm::nop();
|
||||||
}
|
}
|
||||||
self.write_with_bmstop(*data.last().unwrap());
|
self.write_with_bmstop(*data.last().unwrap());
|
||||||
@ -194,13 +198,19 @@ impl Nvm {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function releases the ROM SPI and enables chip write protection again.
|
/// Enable write-protection and disables the peripheral clock.
|
||||||
pub fn release(self) -> pac::Spi3 {
|
pub fn shutdown(&mut self, sys_cfg: &mut pac::Sysconfig) {
|
||||||
self.wait_for_tx_idle();
|
self.wait_for_tx_idle();
|
||||||
self.write_with_bmstop(FRAM_WREN);
|
self.write_with_bmstop(FRAM_WREN);
|
||||||
self.wait_for_tx_idle();
|
self.wait_for_tx_idle();
|
||||||
self.write_single(WPEN_ENABLE_MASK | BP_0_ENABLE_MASK | BP_1_ENABLE_MASK);
|
self.write_single(WPEN_ENABLE_MASK | BP_0_ENABLE_MASK | BP_1_ENABLE_MASK);
|
||||||
self.spi
|
crate::clock::disable_peripheral_clock(sys_cfg, pac::Spi3::PERIPH_SEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function calls [Self::shutdown] and gives back the peripheral structure.
|
||||||
|
pub fn release(mut self, sys_cfg: &mut pac::Sysconfig) -> pac::Spi3 {
|
||||||
|
self.shutdown(sys_cfg);
|
||||||
|
self.spi.take().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common_read_start(&self, addr: u32) {
|
fn common_read_start(&self, addr: u32) {
|
||||||
@ -218,3 +228,10 @@ impl Nvm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Call [Self::shutdown] on drop.
|
||||||
|
impl Drop for Nvm {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.shutdown(unsafe { &mut pac::Sysconfig::steal() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user