itcm: add some comments, make it safe

This commit is contained in:
Robert Jördens 2021-05-28 16:03:40 +02:00
parent 441c81d135
commit c5d3837745
1 changed files with 37 additions and 16 deletions

View File

@ -105,8 +105,17 @@ pub struct PounderDevices {
/// Static storage for the ethernet DMA descriptor ring. /// Static storage for the ethernet DMA descriptor ring.
static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new();
/// Setup ITCM and load its code from flash /// Setup ITCM and load its code from flash.
unsafe fn setup_itcm() { ///
/// For portability and maintainability this is implemented in Rust.
/// Since this is implemented in Rust the compiler may assume that bss and data are set
/// up already. There is no easy way to ensure this implementation will never need bss
/// or data. Hence we can't safely run this as the cortex-m-rt `pre_init` hook before
/// bss/data is setup.
///
/// Calling (through IRQ or directly) any code in ITCM before having called
/// this method is undefined.
fn load_itcm() {
extern "C" { extern "C" {
static mut __sitcm: u32; static mut __sitcm: u32;
static mut __eitcm: u32; static mut __eitcm: u32;
@ -114,18 +123,29 @@ unsafe fn setup_itcm() {
} }
use core::{ptr, slice, sync::atomic}; use core::{ptr, slice, sync::atomic};
// ITCM is enabled on reset on our CPU but might not be on others. // NOTE(unsafe): Assuming the address symbols from the linker as well as
// Keep for completeness. // the source instruction data are all valid, this is safe as it only
const ITCMCR: *mut u32 = 0xE000_EF90usize as _; // copies linker-prepared data to where the code expects it to be.
ptr::write_volatile(ITCMCR, ptr::read_volatile(ITCMCR) | 1); // Calling it multiple times is safe as well.
atomic::fence(atomic::Ordering::SeqCst);
let len = unsafe {
(&__eitcm as *const u32).offset_from(&__sitcm as *const _) as usize; // ITCM is enabled on reset on our CPU but might not be on others.
let dst = slice::from_raw_parts_mut(&mut __sitcm as *mut _, len); // Keep for completeness.
let src = slice::from_raw_parts(&__siitcm as *const _, len); const ITCMCR: *mut u32 = 0xE000_EF90usize as _;
dst.copy_from_slice(src); ptr::write_volatile(ITCMCR, ptr::read_volatile(ITCMCR) | 1);
// Ensure ITCM is enabled before loading.
atomic::fence(atomic::Ordering::SeqCst);
let len =
(&__eitcm as *const u32).offset_from(&__sitcm as *const _) as usize;
let dst = slice::from_raw_parts_mut(&mut __sitcm as *mut _, len);
let src = slice::from_raw_parts(&__siitcm as *const _, len);
// Load code into ITCM.
dst.copy_from_slice(src);
}
// Ensure ITCM is loaded before potentially executing any instructions from it.
atomic::fence(atomic::Ordering::SeqCst); atomic::fence(atomic::Ordering::SeqCst);
cortex_m::asm::dsb(); cortex_m::asm::dsb();
cortex_m::asm::isb(); cortex_m::asm::isb();
@ -183,12 +203,12 @@ pub fn setup(
log::set_logger(&LOGGER) log::set_logger(&LOGGER)
.map(|()| log::set_max_level(log::LevelFilter::Trace)) .map(|()| log::set_max_level(log::LevelFilter::Trace))
.unwrap(); .unwrap();
log::info!("starting..."); log::info!("Starting");
} }
unsafe { // Before being able to call any code in ITCM, load that code from flash.
setup_itcm(); log::info!("Loading ITCM");
} load_itcm();
// Set up the system timer for RTIC scheduling. // Set up the system timer for RTIC scheduling.
{ {
@ -902,6 +922,7 @@ pub fn setup(
#[cfg(feature = "pounder_v1_1")] #[cfg(feature = "pounder_v1_1")]
let pounder_stamper = { let pounder_stamper = {
logger::info!("Pounder v1.1 or later");
let etr_pin = gpioa.pa0.into_alternate_af3(); let etr_pin = gpioa.pa0.into_alternate_af3();
// The frequency in the constructor is dont-care, as we will modify the period + clock // The frequency in the constructor is dont-care, as we will modify the period + clock