From a660b2ca26636148eccab74df8d085bfd67edc09 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 18 Sep 2024 22:39:22 +0200 Subject: [PATCH] bootloader adaption --- Cargo.toml | 3 +- README.md | 6 +- bootloader/Cargo.toml | 5 + bootloader/src/main.rs | 44 ++++-- examples/rtic/src/main.rs | 24 +++- examples/simple/examples/spi.rs | 42 +++--- va416xx-hal/CHANGELOG.md | 10 ++ va416xx-hal/src/spi.rs | 228 +++++++++++++++++--------------- 8 files changed, 212 insertions(+), 150 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1947a61..eda5e47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,4 +41,5 @@ debug-assertions = false # <- lto = true opt-level = 'z' # <- overflow-checks = false # <- -# strip = true # Automatically strip symbols from the binary. +panic = "abort" +strip = true # Automatically strip symbols from the binary. diff --git a/README.md b/README.md index e0b27c9..a3db5da 100644 --- a/README.md +++ b/README.md @@ -99,9 +99,9 @@ example. ### Using VS Code -Assuming a working debug connection to your VA108xx board, you can debug using VS Code with -the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug). Please make sure that -[`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146) +Assuming a working debug connection to your VA416xx board, you can debug using VS Code with +the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug). +Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146) are installed as well. Some sample configuration files for VS code were provided and can be used by running diff --git a/bootloader/Cargo.toml b/bootloader/Cargo.toml index e9406e1..9304846 100644 --- a/bootloader/Cargo.toml +++ b/bootloader/Cargo.toml @@ -8,8 +8,13 @@ cortex-m = "0.7" cortex-m-rt = "0.7" embedded-hal = "1" panic-rtt-target = { version = "0.1.3" } +panic-halt = { version = "0.2" } rtt-target = { version = "0.5" } crc = "3" [dependencies.va416xx-hal] path = "../va416xx-hal" + +[features] +default = ["rtt-panic"] +rtt-panic = [] diff --git a/bootloader/src/main.rs b/bootloader/src/main.rs index 3a35405..f69cd55 100644 --- a/bootloader/src/main.rs +++ b/bootloader/src/main.rs @@ -19,6 +19,9 @@ use cortex_m_rt::entry; use crc::{Crc, CRC_32_ISO_HDLC}; +#[cfg(not(feature = "rtt-panic"))] +use panic_halt as _; +#[cfg(feature = "rtt-panic")] use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; use va416xx_hal::{ @@ -42,6 +45,9 @@ const DEBUG_PRINTOUTS: bool = true; // self-flash itself. It is recommended that you use a tool like probe-rs, Keil IDE, or a flash // loader to boot a bootloader without this feature. const FLASH_SELF: bool = false; +// Useful for debugging and see what the bootloader is doing. Enabled currently, because +// the binary stays small enough. +const RTT_PRINTOUT: bool = true; // Important bootloader addresses and offsets, vector table information. @@ -88,8 +94,10 @@ impl WdtInterface for OptWdt { #[entry] fn main() -> ! { - rtt_init_print!(); - rprintln!("-- VA416xx bootloader --"); + if RTT_PRINTOUT { + rtt_init_print!(); + rprintln!("-- VA416xx bootloader --"); + } let mut dp = pac::Peripherals::take().unwrap(); let cp = cortex_m::Peripherals::take().unwrap(); // Disable ROM protection. @@ -133,18 +141,24 @@ fn main() -> ! { nvm.write_data(0x0, &first_four_bytes); nvm.write_data(0x4, bootloader_data); if let Err(e) = nvm.verify_data(0x0, &first_four_bytes) { - rprintln!("verification of self-flash to NVM failed: {:?}", e); + if RTT_PRINTOUT { + rprintln!("verification of self-flash to NVM failed: {:?}", e); + } } if let Err(e) = nvm.verify_data(0x4, bootloader_data) { - rprintln!("verification of self-flash to NVM failed: {:?}", e); + if RTT_PRINTOUT { + rprintln!("verification of self-flash to NVM failed: {:?}", e); + } } nvm.write_data(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes()); if let Err(e) = nvm.verify_data(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes()) { - rprintln!( - "error: CRC verification for bootloader self-flash failed: {:?}", - e - ); + if RTT_PRINTOUT { + rprintln!( + "error: CRC verification for bootloader self-flash failed: {:?}", + e + ); + } } } @@ -156,7 +170,7 @@ fn main() -> ! { } else if check_app_crc(AppSel::B, &opt_wdt) { boot_app(AppSel::B, &cp) } else { - if DEBUG_PRINTOUTS { + if DEBUG_PRINTOUTS && RTT_PRINTOUT { rprintln!("both images corrupt! booting image A"); } // TODO: Shift a CCSDS packet out to inform host/OBC about image corruption. @@ -184,7 +198,7 @@ fn check_own_crc(wdt: &OptWdt, nvm: &Nvm, cp: &cortex_m::Peripherals) { let crc_calc = digest.finalize(); wdt.feed(); if crc_exp == 0x0000 || crc_exp == 0xffff { - if DEBUG_PRINTOUTS { + if DEBUG_PRINTOUTS && RTT_PRINTOUT { rprintln!("BL CRC blank - prog new CRC"); } // Blank CRC, write it to NVM. @@ -194,7 +208,7 @@ fn check_own_crc(wdt: &OptWdt, nvm: &Nvm, cp: &cortex_m::Peripherals) { // cortex_m::peripheral::SCB::sys_reset(); } else if crc_exp != crc_calc { // Bootloader is corrupted. Try to run App A. - if DEBUG_PRINTOUTS { + if DEBUG_PRINTOUTS && RTT_PRINTOUT { rprintln!( "bootloader CRC corrupt, read {} and expected {}. booting image A immediately", crc_calc, @@ -217,7 +231,7 @@ fn read_four_bytes_at_addr_zero(buf: &mut [u8; 4]) { } } fn check_app_crc(app_sel: AppSel, wdt: &OptWdt) -> bool { - if DEBUG_PRINTOUTS { + if DEBUG_PRINTOUTS && RTT_PRINTOUT { rprintln!("Checking image {:?}", app_sel); } if app_sel == AppSel::A { @@ -237,7 +251,9 @@ fn check_app_given_addr( let image_size = unsafe { (image_size_addr as *const u32).read_unaligned().to_be() }; // Sanity check. if image_size > APP_A_END_ADDR - APP_A_START_ADDR - 8 { - rprintln!("detected invalid app size {}", image_size); + if RTT_PRINTOUT { + rprintln!("detected invalid app size {}", image_size); + } return false; } wdt.feed(); @@ -252,7 +268,7 @@ fn check_app_given_addr( } fn boot_app(app_sel: AppSel, cp: &cortex_m::Peripherals) -> ! { - if DEBUG_PRINTOUTS { + if DEBUG_PRINTOUTS && RTT_PRINTOUT { rprintln!("booting app {:?}", app_sel); } let clkgen = unsafe { pac::Clkgen::steal() }; diff --git a/examples/rtic/src/main.rs b/examples/rtic/src/main.rs index f412cc3..eb72cb9 100644 --- a/examples/rtic/src/main.rs +++ b/examples/rtic/src/main.rs @@ -2,8 +2,13 @@ #![no_main] #![no_std] +use va416xx_hal::time::Hertz; + +const EXTCLK_FREQ: Hertz = Hertz::from_raw(40_000_000); + #[rtic::app(device = pac, dispatchers = [U1, U2, U3])] mod app { + use super::*; use cortex_m::asm; use embedded_hal::digital::StatefulOutputPin; use panic_rtt_target as _; @@ -13,6 +18,7 @@ mod app { use va416xx_hal::{ gpio::{OutputReadablePushPull, Pin, PinsG, PG5}, pac, + prelude::*, }; #[local] @@ -23,14 +29,22 @@ mod app { #[shared] struct Shared {} - rtic_monotonics::systick_monotonic!(Mono, 10_000); + rtic_monotonics::systick_monotonic!(Mono, 1_000); #[init] - fn init(_ctx: init::Context) -> (Shared, Local) { + fn init(mut cx: init::Context) -> (Shared, Local) { rtt_init_default!(); - rprintln!("-- Vorago RTIC template --"); - let mut dp = pac::Peripherals::take().unwrap(); - let portg = PinsG::new(&mut dp.sysconfig, dp.portg); + rprintln!("-- Vorago RTIC example application --"); + // Use the external clock connected to XTAL_N. + let clocks = cx + .device + .clkgen + .constrain() + .xtal_n_clk_with_src_freq(EXTCLK_FREQ) + .freeze(&mut cx.device.sysconfig) + .unwrap(); + Mono::start(cx.core.SYST, clocks.sysclk().raw()); + let portg = PinsG::new(&mut cx.device.sysconfig, cx.device.portg); let led = portg.pg5.into_readable_push_pull_output(); blinky::spawn().ok(); (Shared {}, Local { led }) diff --git a/examples/simple/examples/spi.rs b/examples/simple/examples/spi.rs index e918236..d5b36ad 100644 --- a/examples/simple/examples/spi.rs +++ b/examples/simple/examples/spi.rs @@ -3,13 +3,12 @@ //! If you do not use the loopback mode, MOSI and MISO need to be tied together on the board. #![no_main] #![no_std] - use cortex_m_rt::entry; use embedded_hal::spi::{Mode, SpiBus, MODE_0}; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; use simple_examples::peb1; -use va416xx_hal::spi::{clk_div_for_target_clock, Spi, TransferConfig}; +use va416xx_hal::spi::{Spi, SpiClkConfig, TransferConfigWithHwcs}; use va416xx_hal::{ gpio::{PinsB, PinsC}, pac, @@ -22,9 +21,8 @@ use va416xx_hal::{ pub enum ExampleSelect { // Enter loopback mode. It is not necessary to tie MOSI/MISO together for this Loopback, - // Send a test buffer and print everything received. You need to tie together MOSI/MISO in this - // mode. - TestBuffer, + // You need to tie together MOSI/MISO in this mode. + MosiMisoTiedTogether, } const EXAMPLE_SEL: ExampleSelect = ExampleSelect::Loopback; @@ -50,21 +48,21 @@ fn main() -> ! { let pins_b = PinsB::new(&mut dp.sysconfig, dp.portb); let pins_c = PinsC::new(&mut dp.sysconfig, dp.portc); - // Configure SPI1 pins. + // Configure SPI0 pins. let (sck, miso, mosi) = ( pins_b.pb15.into_funsel_1(), pins_c.pc0.into_funsel_1(), pins_c.pc1.into_funsel_1(), ); - let mut spi_cfg = SpiConfig::default().clk_div( - clk_div_for_target_clock(Hertz::from_raw(SPI_SPEED_KHZ), &clocks) + let mut spi_cfg = SpiConfig::default().clk_cfg( + SpiClkConfig::from_clk(Hertz::from_raw(SPI_SPEED_KHZ), &clocks) .expect("invalid target clock"), ); if EXAMPLE_SEL == ExampleSelect::Loopback { spi_cfg = spi_cfg.loopback(true) } - let transfer_cfg = TransferConfig::new_no_hw_cs(None, Some(SPI_MODE), BLOCKMODE, false); + let transfer_cfg = TransferConfigWithHwcs::new_no_hw_cs(None, Some(SPI_MODE), BLOCKMODE, false); // Create SPI peripheral. let mut spi0 = Spi::new( &mut dp.sysconfig, @@ -77,24 +75,22 @@ fn main() -> ! { .expect("creating SPI peripheral failed"); spi0.set_fill_word(FILL_WORD); loop { - let mut tx_buf: [u8; 3] = [1, 2, 3]; - let mut rx_buf: [u8; 3] = [0; 3]; - // Can't really verify correct reply here. - spi0.write(&[0x42]).expect("write failed"); - // Need small delay.. otherwise we will read back the sent byte (which we don't want here). - // The write function will return as soon as all bytes were shifted out, ignoring the - // reply bytes. - delay_sysclk.delay_us(50); - // Because of the loopback mode, we should get back the fill word here. - spi0.read(&mut rx_buf[0..1]).unwrap(); - assert_eq!(rx_buf[0], FILL_WORD); + let tx_buf: [u8; 4] = [1, 2, 3, 0]; + let mut rx_buf: [u8; 4] = [0; 4]; + // Can't really verify correct behaviour here. Just verify nothing crazy happens or it hangs up. + spi0.write(&[0x42, 0x43]).expect("write failed"); - spi0.transfer_in_place(&mut tx_buf) + // Can't really verify correct behaviour here. Just verify nothing crazy happens or it hangs up. + spi0.read(&mut rx_buf[0..2]).unwrap(); + + let mut inplace_buf = tx_buf; + spi0.transfer_in_place(&mut inplace_buf) .expect("SPI transfer_in_place failed"); - assert_eq!([1, 2, 3], tx_buf); + assert_eq!([1, 2, 3, 0], inplace_buf); + spi0.transfer(&mut rx_buf, &tx_buf) .expect("SPI transfer failed"); - assert_eq!(rx_buf, tx_buf); + assert_eq!(rx_buf, [1, 2, 3, 0]); delay_sysclk.delay_ms(500); } } diff --git a/va416xx-hal/CHANGELOG.md b/va416xx-hal/CHANGELOG.md index d80e080..79f5c84 100644 --- a/va416xx-hal/CHANGELOG.md +++ b/va416xx-hal/CHANGELOG.md @@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/). # [unreleased] +## Changed + +- Simplification of clock configuration API for SPI HAL. + +## Fixed + +- Fixes for SPI peripheral: Flush implementation was incorrect and should now flush properly. +- Fixes for SPI example +- Fixes for RTIC example + # [v0.2.0] 2024-09-18 - Documentation improvements diff --git a/va416xx-hal/src/spi.rs b/va416xx-hal/src/spi.rs index b956bd6..1d75479 100644 --- a/va416xx-hal/src/spi.rs +++ b/va416xx-hal/src/spi.rs @@ -228,30 +228,23 @@ pub trait TransferConfigProvider { fn sod(&mut self, sod: bool); fn blockmode(&mut self, blockmode: bool); fn mode(&mut self, mode: Mode); - fn clk_div(&mut self, clk_div: u16); + fn clk_cfg(&mut self, clk_cfg: SpiClkConfig); fn hw_cs_id(&self) -> u8; } /// This struct contains all configuration parameter which are transfer specific /// and might change for transfers to different SPI slaves -#[derive(Copy, Clone)] -pub struct TransferConfig { - pub clk_div: Option, - pub mode: Option, - /// This only works if the Slave Output Disable (SOD) bit of the [`SpiConfig`] is set to - /// false +#[derive(Copy, Clone, Debug)] +pub struct TransferConfigWithHwcs { pub hw_cs: Option, - pub sod: bool, - /// If this is enabled, all data in the FIFO is transmitted in a single frame unless - /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the - /// duration of multiple data words - pub blockmode: bool, + pub cfg: TransferConfig, } /// Type erased variant of the transfer configuration. This is required to avoid generics in /// the SPI constructor. -pub struct ErasedTransferConfig { - pub clk_div: Option, +#[derive(Copy, Clone, Debug)] +pub struct TransferConfig { + pub clk_cfg: Option, pub mode: Option, pub sod: bool, /// If this is enabled, all data in the FIFO is transmitted in a single frame unless @@ -261,67 +254,67 @@ pub struct ErasedTransferConfig { pub hw_cs: HwChipSelectId, } -impl TransferConfig { +impl TransferConfigWithHwcs { pub fn new_no_hw_cs( - clk_div: Option, + clk_cfg: Option, mode: Option, blockmode: bool, sod: bool, ) -> Self { - TransferConfig { - clk_div, - mode, + TransferConfigWithHwcs { hw_cs: None, - sod, - blockmode, + cfg: TransferConfig { + clk_cfg, + mode, + sod, + blockmode, + hw_cs: HwChipSelectId::Invalid, + }, } } } -impl TransferConfig { +impl TransferConfigWithHwcs { pub fn new( - clk_div: Option, + clk_cfg: Option, mode: Option, hw_cs: Option, blockmode: bool, sod: bool, ) -> Self { - TransferConfig { - clk_div, - mode, + TransferConfigWithHwcs { hw_cs, - sod, - blockmode, + cfg: TransferConfig { + clk_cfg, + mode, + sod, + blockmode, + hw_cs: HwCs::CS_ID, + }, } } - pub fn downgrade(self) -> ErasedTransferConfig { - ErasedTransferConfig { - clk_div: self.clk_div, - mode: self.mode, - sod: self.sod, - blockmode: self.blockmode, - hw_cs: HwCs::CS_ID, - } + pub fn downgrade(self) -> TransferConfig { + self.cfg } } -impl TransferConfigProvider for TransferConfig { +impl TransferConfigProvider for TransferConfigWithHwcs { /// Slave Output Disable fn sod(&mut self, sod: bool) { - self.sod = sod; + self.cfg.sod = sod; } fn blockmode(&mut self, blockmode: bool) { - self.blockmode = blockmode; + self.cfg.blockmode = blockmode; } fn mode(&mut self, mode: Mode) { - self.mode = Some(mode); + self.cfg.mode = Some(mode); } - fn clk_div(&mut self, clk_div: u16) { - self.clk_div = Some(clk_div); + fn clk_cfg(&mut self, clk_cfg: SpiClkConfig) { + self.cfg.clk_cfg = Some(clk_cfg); } fn hw_cs_id(&self) -> u8 { @@ -331,7 +324,7 @@ impl TransferConfigProvider for TransferConfig { /// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details pub struct SpiConfig { - clk_div: u16, + clk: SpiClkConfig, /// By default, configure SPI for master mode (ms == false) ms: bool, /// Slave output disable. Useful if separate GPIO pins or decoders are used for CS control @@ -345,7 +338,8 @@ pub struct SpiConfig { impl Default for SpiConfig { fn default() -> Self { Self { - clk_div: DEFAULT_CLK_DIV, + // Default value is definitely valid. + clk: SpiClkConfig::from_div(DEFAULT_CLK_DIV).unwrap(), ms: Default::default(), slave_output_disable: Default::default(), loopback_mode: Default::default(), @@ -360,8 +354,8 @@ impl SpiConfig { self } - pub fn clk_div(mut self, clk_div: u16) -> Self { - self.clk_div = clk_div; + pub fn clk_cfg(mut self, clk_cfg: SpiClkConfig) -> Self { + self.clk = clk_cfg; self } @@ -479,7 +473,8 @@ pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) { } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SpiClkConfig { prescale_val: u16, scrdv: u8, @@ -494,6 +489,23 @@ impl SpiClkConfig { } } +impl SpiClkConfig { + pub fn new(prescale_val: u16, scrdv: u8) -> Self { + Self { + prescale_val, + scrdv, + } + } + + pub fn from_div(div: u16) -> Result { + spi_clk_config_from_div(div) + } + + pub fn from_clk(spi_clk: Hertz, clocks: &Clocks) -> Option { + clk_div_for_target_clock(spi_clk, clocks).map(|div| spi_clk_config_from_div(div).unwrap()) + } +} + #[derive(Debug)] pub enum SpiClkConfigError { DivIsZero, @@ -566,27 +578,21 @@ where >::Error: core::fmt::Debug, { #[inline] - pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError> { - let val = spi_clk_config_from_div(div)?; - self.spi_instance() + pub fn cfg_clock(&mut self, cfg: SpiClkConfig) { + self.spi .ctrl0() - .modify(|_, w| unsafe { w.scrdv().bits(val.scrdv as u8) }); - self.spi_instance() - .clkprescale() - .write(|w| unsafe { w.bits(val.prescale_val as u32) }); - Ok(()) - } - - /* - #[inline] - pub fn cfg_clock(&mut self, spi_clk: impl Into) { - let clk_prescale = - self.apb1_clk.raw() / (spi_clk.into().raw() * (self.cfg.ser_clock_rate_div as u32 + 1)); + .modify(|_, w| unsafe { w.scrdv().bits(cfg.scrdv) }); self.spi .clkprescale() - .write(|w| unsafe { w.bits(clk_prescale) }); + .write(|w| unsafe { w.bits(cfg.prescale_val as u32) }); + } + + #[inline] + pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError> { + let val = spi_clk_config_from_div(div)?; + self.cfg_clock(val); + Ok(()) } - */ #[inline] pub fn cfg_mode(&mut self, mode: Mode) { @@ -646,17 +652,17 @@ where pub fn cfg_transfer>( &mut self, - transfer_cfg: &TransferConfig, + transfer_cfg: &TransferConfigWithHwcs, ) -> Result<(), SpiClkConfigError> { - if let Some(trans_clk_div) = transfer_cfg.clk_div { - self.cfg_clock_from_div(trans_clk_div)?; + if let Some(trans_clk_div) = transfer_cfg.cfg.clk_cfg { + self.cfg_clock(trans_clk_div); } - if let Some(mode) = transfer_cfg.mode { + if let Some(mode) = transfer_cfg.cfg.mode { self.cfg_mode(mode); } - self.blockmode = transfer_cfg.blockmode; + self.blockmode = transfer_cfg.cfg.blockmode; self.spi.ctrl1().modify(|_, w| { - if transfer_cfg.sod { + if transfer_cfg.cfg.sod { w.sod().set_bit(); } else if transfer_cfg.hw_cs.is_some() { w.sod().clear_bit(); @@ -666,7 +672,7 @@ where } else { w.sod().clear_bit(); } - if transfer_cfg.blockmode { + if transfer_cfg.cfg.blockmode { w.blockmode().set_bit(); } else { w.blockmode().clear_bit(); @@ -676,6 +682,19 @@ where Ok(()) } + fn flush_internal(&self) { + let mut status_reg = self.spi.status().read(); + while status_reg.tfe().bit_is_clear() + || status_reg.rne().bit_is_set() + || status_reg.busy().bit_is_set() + { + if status_reg.rne().bit_is_set() { + self.read_single_word(); + } + status_reg = self.spi.status().read(); + } + } + /// Sends a word to the slave #[inline(always)] fn send_blocking(&self, word: Word) { @@ -708,30 +727,35 @@ where if words.is_empty() { return Ok(()); } - let mut status_reg = self.spi.status().read(); - // Wait until all bytes have been transferred. - while status_reg.tfe().bit_is_clear() { - // Ignore all received read words. - if status_reg.rne().bit_is_set() { - self.clear_rx_fifo(); - } - status_reg = self.spi.status().read(); - } - // Ignore all received read words. - if status_reg.rne().bit_is_set() { - self.clear_rx_fifo(); - } + self.flush_internal(); Ok(()) } - fn initial_send_fifo_pumping(&self, words: Option<&[Word]>) -> usize { + // Returns the actual bytes sent. + fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize { if self.blockmode { self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit()) } // Fill the first half of the write FIFO let mut current_write_idx = 0; - for _ in 0..core::cmp::min(FILL_DEPTH, words.map_or(0, |words| words.len())) { - self.send_blocking(words.map_or(self.fill_word, |words| words[current_write_idx])); + for _ in 0..core::cmp::min(FILL_DEPTH, words.len()) { + self.send_blocking(words[current_write_idx]); + current_write_idx += 1; + } + if self.blockmode { + self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit()) + } + current_write_idx + } + + fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize { + if self.blockmode { + self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit()) + } + // Fill the first half of the write FIFO + let mut current_write_idx = 0; + for _ in 0..core::cmp::min(FILL_DEPTH, send_len) { + self.send_blocking(self.fill_word); current_write_idx += 1; } if self.blockmode { @@ -772,13 +796,13 @@ where spi: SpiI, pins: (Sck, Miso, Mosi), spi_cfg: SpiConfig, - transfer_cfg: Option<&ErasedTransferConfig>, + transfer_cfg: Option<&TransferConfig>, ) -> Result { crate::clock::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL); // This is done in the C HAL. syscfg.assert_periph_reset_for_two_cycles(SpiI::PERIPH_SEL); let SpiConfig { - clk_div, + clk, ms, slave_output_disable, loopback_mode, @@ -792,19 +816,17 @@ where if let Some(mode) = transfer_cfg.mode { init_mode = mode; } - //self.cfg_clock_from_div(transfer_cfg.clk_div); if transfer_cfg.hw_cs != HwChipSelectId::Invalid { ss = transfer_cfg.hw_cs as u8; } init_blockmode = transfer_cfg.blockmode; } - let spi_clk_cfg = spi_clk_config_from_div(clk_div)?; let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(init_mode); spi.ctrl0().write(|w| { unsafe { w.size().bits(Word::word_reg()); - w.scrdv().bits(spi_clk_cfg.scrdv); + w.scrdv().bits(clk.scrdv); // Clear clock phase and polarity. Will be set to correct value for each // transfer w.spo().bit(cpo_bit); @@ -820,7 +842,7 @@ where unsafe { w.ss().bits(ss) } }); spi.clkprescale() - .write(|w| unsafe { w.bits(spi_clk_cfg.prescale_val as u32) }); + .write(|w| unsafe { w.bits(clk.prescale_val as u32) }); spi.fifo_clr().write(|w| { w.rxfifo().set_bit(); @@ -844,6 +866,9 @@ where delegate::delegate! { to self.inner { + #[inline] + pub fn cfg_clock(&mut self, cfg: SpiClkConfig); + #[inline] pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError>; @@ -857,7 +882,7 @@ where pub fn perid(&self) -> u32; pub fn cfg_transfer>( - &mut self, transfer_cfg: &TransferConfig + &mut self, transfer_cfg: &TransferConfigWithHwcs ) -> Result<(), SpiClkConfigError>; } } @@ -941,7 +966,7 @@ where fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { self.transfer_preparation(words)?; let mut current_read_idx = 0; - let mut current_write_idx = self.initial_send_fifo_pumping(None); + let mut current_write_idx = self.initial_send_fifo_pumping_with_fill_words(words.len()); loop { if current_write_idx < words.len() { self.send_blocking(self.fill_word); @@ -960,7 +985,7 @@ where fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { self.transfer_preparation(words)?; - let mut current_write_idx = self.initial_send_fifo_pumping(Some(words)); + let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words); while current_write_idx < words.len() { self.send_blocking(words[current_write_idx]); current_write_idx += 1; @@ -975,7 +1000,7 @@ where fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { self.transfer_preparation(write)?; let mut current_read_idx = 0; - let mut current_write_idx = self.initial_send_fifo_pumping(Some(write)); + let mut current_write_idx = self.initial_send_fifo_pumping_with_words(write); while current_read_idx < read.len() || current_write_idx < write.len() { if current_write_idx < write.len() { self.send_blocking(write[current_write_idx]); @@ -993,7 +1018,7 @@ where fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { self.transfer_preparation(words)?; let mut current_read_idx = 0; - let mut current_write_idx = self.initial_send_fifo_pumping(Some(words)); + let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words); while current_read_idx < words.len() || current_write_idx < words.len() { if current_write_idx < words.len() { @@ -1009,12 +1034,7 @@ where } fn flush(&mut self) -> Result<(), Self::Error> { - let status_reg = self.spi.status().read(); - while status_reg.tfe().bit_is_clear() || status_reg.rne().bit_is_set() { - if status_reg.rne().bit_is_set() { - self.read_single_word(); - } - } + self.flush_internal(); Ok(()) } }