bootloader and flashloader
This commit is contained in:
66
va416xx-hal/src/edac.rs
Normal file
66
va416xx-hal/src/edac.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use crate::{enable_interrupt, pac};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn enable_rom_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) {
|
||||
syscfg
|
||||
.rom_scrub()
|
||||
.write(|w| unsafe { w.bits(counter_reset as u32) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn enable_ram0_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) {
|
||||
syscfg
|
||||
.ram0_scrub()
|
||||
.write(|w| unsafe { w.bits(counter_reset as u32) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn enable_ram1_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) {
|
||||
syscfg
|
||||
.ram1_scrub()
|
||||
.write(|w| unsafe { w.bits(counter_reset as u32) })
|
||||
}
|
||||
|
||||
/// This function enables the SBE related interrupts. The user should also provide a
|
||||
/// [pac::EDAC_SBE] ISR and use [clear_sbe_irq] inside that ISR at the very least.
|
||||
#[inline(always)]
|
||||
pub fn enable_sbe_irq() {
|
||||
unsafe {
|
||||
enable_interrupt(pac::Interrupt::EDAC_SBE);
|
||||
}
|
||||
}
|
||||
|
||||
/// This function enables the SBE related interrupts. The user should also provide a
|
||||
/// [pac::EDAC_MBE] ISR and use [clear_mbe_irq] inside that ISR at the very least.
|
||||
#[inline(always)]
|
||||
pub fn enable_mbe_irq() {
|
||||
unsafe {
|
||||
enable_interrupt(pac::Interrupt::EDAC_MBE);
|
||||
}
|
||||
}
|
||||
|
||||
/// This function should be called in the user provided [pac::EDAC_SBE] interrupt-service routine
|
||||
/// to clear the SBE related interrupts.
|
||||
#[inline(always)]
|
||||
pub fn clear_sbe_irq() {
|
||||
// Safety: This function only clears SBE related IRQs
|
||||
let syscfg = unsafe { pac::Sysconfig::steal() };
|
||||
syscfg.irq_clr().write(|w| {
|
||||
w.romsbe().set_bit();
|
||||
w.ram0sbe().set_bit();
|
||||
w.ram1sbe().set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
/// This function should be called in the user provided [pac::EDAC_MBE] interrupt-service routine
|
||||
/// to clear the MBE related interrupts.
|
||||
#[inline(always)]
|
||||
pub fn clear_mbe_irq() {
|
||||
// Safety: This function only clears SBE related IRQs
|
||||
let syscfg = unsafe { pac::Sysconfig::steal() };
|
||||
syscfg.irq_clr().write(|w| {
|
||||
w.rommbe().set_bit();
|
||||
w.ram0mbe().set_bit();
|
||||
w.ram1mbe().set_bit()
|
||||
});
|
||||
}
|
@ -12,8 +12,10 @@ pub mod adc;
|
||||
pub mod clock;
|
||||
pub mod dac;
|
||||
pub mod dma;
|
||||
pub mod edac;
|
||||
pub mod gpio;
|
||||
pub mod i2c;
|
||||
pub mod nvm;
|
||||
pub mod pwm;
|
||||
pub mod spi;
|
||||
pub mod time;
|
||||
|
220
va416xx-hal/src/nvm.rs
Normal file
220
va416xx-hal/src/nvm.rs
Normal file
@ -0,0 +1,220 @@
|
||||
use embedded_hal::spi::MODE_0;
|
||||
|
||||
use crate::clock::{Clocks, SyscfgExt};
|
||||
use crate::pac;
|
||||
use crate::spi::{mode_to_cpo_cph_bit, Instance, BMSTART_BMSTOP_MASK};
|
||||
|
||||
const NVM_SER_CLOCK_RATE_DIV: u8 = 4;
|
||||
|
||||
// Commands. The internal FRAM is based on the Cypress FM25V20A device.
|
||||
|
||||
/// Write enable register.
|
||||
pub const FRAM_WREN: u8 = 0x06;
|
||||
pub const FRAM_WRDI: u8 = 0x04;
|
||||
pub const FRAM_RDSR: u8 = 0x05;
|
||||
/// Write single status register
|
||||
pub const FRAM_WRSR: u8 = 0x01;
|
||||
pub const FRAM_READ: u8 = 0x03;
|
||||
pub const FRAM_WRITE: u8 = 0x02;
|
||||
pub const FRAM_RDID: u8 = 0x9F;
|
||||
pub const FRAM_SLEEP: u8 = 0xB9;
|
||||
|
||||
/* Address Masks */
|
||||
const ADDR_MSB_MASK: u32 = 0xFF0000;
|
||||
const ADDR_MID_MASK: u32 = 0x00FF00;
|
||||
const ADDR_LSB_MASK: u32 = 0x0000FF;
|
||||
|
||||
#[inline(always)]
|
||||
const fn msb_addr_byte(addr: u32) -> u8 {
|
||||
((addr & ADDR_MSB_MASK) >> 16) as u8
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn mid_addr_byte(addr: u32) -> u8 {
|
||||
((addr & ADDR_MID_MASK) >> 8) as u8
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn lsb_addr_byte(addr: u32) -> u8 {
|
||||
(addr & ADDR_LSB_MASK) as u8
|
||||
}
|
||||
|
||||
pub const WPEN_ENABLE_MASK: u8 = 1 << 7;
|
||||
pub const BP_0_ENABLE_MASK: u8 = 1 << 2;
|
||||
pub const BP_1_ENABLE_MASK: u8 = 1 << 3;
|
||||
|
||||
pub struct Nvm {
|
||||
spi: pac::Spi3,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", defmt::Format)]
|
||||
pub struct VerifyError {
|
||||
addr: u32,
|
||||
found: u8,
|
||||
expected: u8,
|
||||
}
|
||||
|
||||
impl Nvm {
|
||||
pub fn new(syscfg: &mut pac::Sysconfig, spi: pac::Spi3, _clocks: &Clocks) -> Self {
|
||||
crate::clock::enable_peripheral_clock(syscfg, pac::Spi3::PERIPH_SEL);
|
||||
// This is done in the C HAL.
|
||||
syscfg.assert_periph_reset_for_two_cycles(pac::Spi3::PERIPH_SEL);
|
||||
|
||||
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(MODE_0);
|
||||
spi.ctrl0().write(|w| {
|
||||
unsafe {
|
||||
w.size().bits(8);
|
||||
w.scrdv().bits(NVM_SER_CLOCK_RATE_DIV);
|
||||
// Clear clock phase and polarity. Will be set to correct value for each
|
||||
// transfer
|
||||
w.spo().bit(cpo_bit);
|
||||
w.sph().bit(cph_bit)
|
||||
}
|
||||
});
|
||||
spi.ctrl1().write(|w| {
|
||||
w.lbm().clear_bit();
|
||||
w.sod().clear_bit();
|
||||
w.ms().set_bit();
|
||||
w.mdlycap().clear_bit();
|
||||
w.blockmode().set_bit();
|
||||
unsafe { w.ss().bits(0) };
|
||||
w.bmstall().set_bit()
|
||||
});
|
||||
|
||||
spi.fifo_clr().write(|w| {
|
||||
w.rxfifo().set_bit();
|
||||
w.txfifo().set_bit()
|
||||
});
|
||||
// Enable the peripheral as the last step as recommended in the
|
||||
// programmers guide
|
||||
spi.ctrl1().modify(|_, w| w.enable().set_bit());
|
||||
|
||||
let mut nvm = Self { spi };
|
||||
nvm.disable_write_prot();
|
||||
nvm
|
||||
}
|
||||
|
||||
pub fn disable_write_prot(&mut self) {
|
||||
self.wait_for_tx_idle();
|
||||
self.write_with_bmstop(FRAM_WREN);
|
||||
self.wait_for_tx_idle();
|
||||
self.write_single(FRAM_WRSR);
|
||||
self.write_with_bmstop(0x00);
|
||||
}
|
||||
|
||||
pub fn enable_write_prot(&mut self) {
|
||||
self.wait_for_tx_idle();
|
||||
self.write_with_bmstop(FRAM_WREN);
|
||||
self.wait_for_tx_idle();
|
||||
self.write_single(FRAM_WRSR);
|
||||
self.write_with_bmstop(0x00);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_single(&self, word: u8) {
|
||||
self.spi.data().write(|w| unsafe { w.bits(word as u32) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_with_bmstop(&self, word: u8) {
|
||||
self.spi
|
||||
.data()
|
||||
.write(|w| unsafe { w.bits(BMSTART_BMSTOP_MASK | word as u32) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn wait_for_tx_idle(&self) {
|
||||
while self.spi.status().read().tfe().bit_is_clear() {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn wait_for_rx_available(&self) {
|
||||
while !self.spi.status().read().rne().bit_is_set() {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_single_word(&self) -> u32 {
|
||||
self.spi.data().read().bits()
|
||||
}
|
||||
|
||||
pub fn write_data(&self, addr: u32, data: &[u8]) {
|
||||
self.wait_for_tx_idle();
|
||||
self.write_with_bmstop(FRAM_WREN);
|
||||
self.wait_for_tx_idle();
|
||||
self.write_single(FRAM_WRITE);
|
||||
self.write_single(msb_addr_byte(addr));
|
||||
self.write_single(mid_addr_byte(addr));
|
||||
self.write_single(lsb_addr_byte(addr));
|
||||
for byte in data.iter().take(data.len() - 1) {
|
||||
while self.spi.status().read().tnf().bit_is_clear() {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
self.write_single(*byte);
|
||||
self.read_single_word();
|
||||
}
|
||||
while self.spi.status().read().tnf().bit_is_clear() {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
self.write_with_bmstop(*data.last().unwrap());
|
||||
self.wait_for_tx_idle();
|
||||
}
|
||||
|
||||
pub fn read_data(&self, addr: u32, buf: &mut [u8]) {
|
||||
self.common_read_start(addr);
|
||||
for byte in buf {
|
||||
// Pump the SPI.
|
||||
self.write_single(0);
|
||||
self.wait_for_rx_available();
|
||||
*byte = self.read_single_word() as u8;
|
||||
}
|
||||
self.write_with_bmstop(0);
|
||||
self.wait_for_tx_idle();
|
||||
}
|
||||
|
||||
pub fn verify_data(&self, addr: u32, comp_buf: &[u8]) -> Result<(), VerifyError> {
|
||||
self.common_read_start(addr);
|
||||
for (idx, byte) in comp_buf.iter().enumerate() {
|
||||
// Pump the SPI.
|
||||
self.write_single(0);
|
||||
self.wait_for_rx_available();
|
||||
let next_word = self.read_single_word() as u8;
|
||||
if next_word != *byte {
|
||||
return Err(VerifyError {
|
||||
addr: addr + idx as u32,
|
||||
found: next_word,
|
||||
expected: *byte,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This function releases the ROM SPI and enables chip write protection again.
|
||||
pub fn release(self) -> pac::Spi3 {
|
||||
self.wait_for_tx_idle();
|
||||
self.write_with_bmstop(FRAM_WREN);
|
||||
self.wait_for_tx_idle();
|
||||
self.write_single(WPEN_ENABLE_MASK | BP_0_ENABLE_MASK | BP_1_ENABLE_MASK);
|
||||
self.spi
|
||||
}
|
||||
|
||||
fn common_read_start(&self, addr: u32) {
|
||||
self.wait_for_tx_idle();
|
||||
self.write_single(FRAM_READ);
|
||||
self.write_single(msb_addr_byte(addr));
|
||||
self.write_single(mid_addr_byte(addr));
|
||||
self.write_single(lsb_addr_byte(addr));
|
||||
for _ in 0..4 {
|
||||
// Pump the SPI.
|
||||
self.write_single(0);
|
||||
self.wait_for_rx_available();
|
||||
// The first 4 data bytes received need to be ignored.
|
||||
self.read_single_word();
|
||||
}
|
||||
}
|
||||
}
|
@ -27,6 +27,9 @@ use crate::{
|
||||
// FIFO has a depth of 16.
|
||||
const FILL_DEPTH: usize = 12;
|
||||
|
||||
pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
|
||||
pub const BMSKIPDATA_MASK: u32 = 1 << 30;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum HwChipSelectId {
|
||||
Id0 = 0,
|
||||
@ -99,6 +102,14 @@ impl OptionalHwCs<pac::Spi1> for NoneT {}
|
||||
impl OptionalHwCs<pac::Spi2> for NoneT {}
|
||||
impl OptionalHwCs<pac::Spi3> for NoneT {}
|
||||
|
||||
pub struct RomSpiSck;
|
||||
pub struct RomSpiMiso;
|
||||
pub struct RomSpiMosi;
|
||||
|
||||
impl Sealed for RomSpiSck {}
|
||||
impl Sealed for RomSpiMosi {}
|
||||
impl Sealed for RomSpiMiso {}
|
||||
|
||||
// SPI 0
|
||||
|
||||
impl PinSck<pac::Spi0> for Pin<PB15, AltFunc1> {}
|
||||
@ -137,6 +148,10 @@ impl PinMosi<pac::Spi2> for Pin<PF7, AltFunc2> {}
|
||||
impl PinMiso<pac::Spi2> for Pin<PF6, AltFunc2> {}
|
||||
|
||||
// SPI3 is shared with the ROM SPI pins and has its own dedicated pins.
|
||||
//
|
||||
impl PinSck<pac::Spi3> for RomSpiSck {}
|
||||
impl PinMosi<pac::Spi3> for RomSpiMosi {}
|
||||
impl PinMiso<pac::Spi3> for RomSpiMiso {}
|
||||
|
||||
// SPI 0 HW CS pins
|
||||
|
||||
@ -315,6 +330,11 @@ impl SpiConfig {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ser_clock_rate_div(mut self, div: u8) -> Self {
|
||||
self.ser_clock_rate_div = div;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn master_mode(mut self, master: bool) -> Self {
|
||||
self.ms = !master;
|
||||
self
|
||||
@ -391,6 +411,16 @@ impl Instance for pac::Spi2 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for pac::Spi3 {
|
||||
const IDX: u8 = 3;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi3;
|
||||
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const SpiRegBlock {
|
||||
Self::ptr()
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Spi
|
||||
//==================================================================================================
|
||||
@ -410,7 +440,7 @@ pub struct Spi<SpiInstance, Pins, Word = u8> {
|
||||
pins: Pins,
|
||||
}
|
||||
|
||||
fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
|
||||
pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
|
||||
match mode {
|
||||
embedded_hal::spi::MODE_0 => (false, false),
|
||||
embedded_hal::spi::MODE_1 => (false, true),
|
||||
@ -441,6 +471,11 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn spi_instance(&self) -> &SpiInstance {
|
||||
&self.spi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_tx_fifo(&self) {
|
||||
self.spi.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
@ -601,11 +636,11 @@ where
|
||||
/// to be done once.
|
||||
/// * `syscfg` - Can be passed optionally to enable the peripheral clock
|
||||
pub fn new(
|
||||
spi: SpiI,
|
||||
pins: (Sck, Miso, Mosi),
|
||||
clocks: &crate::clock::Clocks,
|
||||
spi_cfg: SpiConfig,
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
spi: SpiI,
|
||||
clocks: &crate::clock::Clocks,
|
||||
pins: (Sck, Miso, Mosi),
|
||||
spi_cfg: SpiConfig,
|
||||
transfer_cfg: Option<&ErasedTransferConfig>,
|
||||
) -> Self {
|
||||
crate::clock::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
|
||||
@ -674,33 +709,35 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
|
||||
self.inner.cfg_clock(spi_clk);
|
||||
delegate::delegate! {
|
||||
to self.inner {
|
||||
#[inline]
|
||||
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>);
|
||||
|
||||
#[inline]
|
||||
pub fn spi_instance(&self) -> &SpiI;
|
||||
|
||||
#[inline]
|
||||
pub fn cfg_mode(&mut self, mode: Mode);
|
||||
|
||||
#[inline]
|
||||
pub fn perid(&self) -> u32;
|
||||
|
||||
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiI>>(&mut self, transfer_cfg: &TransferConfig<HwCs>);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cfg_mode(&mut self, mode: Mode) {
|
||||
self.inner.cfg_mode(mode);
|
||||
}
|
||||
|
||||
pub fn set_fill_word(&mut self, fill_word: Word) {
|
||||
self.inner.fill_word = fill_word;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fill_word(&self) -> Word {
|
||||
self.inner.fill_word
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn perid(&self) -> u32 {
|
||||
self.inner.perid()
|
||||
}
|
||||
|
||||
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiI>>(&mut self, transfer_cfg: &TransferConfig<HwCs>) {
|
||||
self.inner.cfg_transfer(transfer_cfg);
|
||||
}
|
||||
|
||||
/// Releases the SPI peripheral and associated pins
|
||||
pub fn release(self) -> (SpiI, (Sck, Miso, Mosi), SpiConfig) {
|
||||
(self.inner.spi, self.pins, self.inner.cfg)
|
||||
|
@ -40,7 +40,6 @@ pub fn disable_wdt_interrupts() {
|
||||
|
||||
impl Wdt {
|
||||
pub fn new(
|
||||
&self,
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
wdt: pac::WatchDog,
|
||||
clocks: &Clocks,
|
||||
|
Reference in New Issue
Block a user