Skip to content

FLASH driver #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ rand_core = "0.6.3"
sdio-host = "0.5.0"
embassy-hal-internal = "0.2.0"

embedded-storage = "0.3.1"

[build-dependencies]
ch32-metapac = { features = [
"metadata",
Expand Down
1 change: 1 addition & 0 deletions examples/ch32v208/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ qingke-rt = "*"
qingke = "*"

panic-halt = "1.0"
embedded-storage = "0.3.1"

[profile.release]
strip = false # symbols are not flashed to the microcontroller, so don't strip them.
Expand Down
74 changes: 74 additions & 0 deletions examples/ch32v208/src/bin/flash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
#![feature(impl_trait_in_assoc_type)]

use ch32_hal::flash::{Flash, FLASH_BASE};
use ch32_hal::rcc::*;
use ch32_hal::{interrupt, println};
use embassy_executor::Spawner;
use embassy_time::Timer;
use panic_halt as _;

#[embassy_executor::main(entry = "qingke_rt::entry")]
async fn main(_spawner: Spawner) -> ! {
// NOTE: TRM, 3.2:
// > When carrying out FLASH-related operations, it is strongly recommended that the system main
// > frequency is not greater than 120M.
// Here we configure the clock at 60MHz
let p = ch32_hal::init(ch32_hal::Config {
rcc: ch32_hal::rcc::Config {
hse: None,
sys: Sysclk::PLL,
pll_src: PllSource::HSI,
pll: Some(Pll {
prediv: PllPreDiv::DIV2,
mul: PllMul::MUL15,
}),
pllx: None,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: LsConfig::default(),
hspll_src: HsPllSource::HSI,
hspll: None,
},
dma_interrupt_priority: interrupt::Priority::P0,
});
ch32_hal::debug::SDIPrint::enable();

// Give a chance to catch CPU if it craps itself writing to FLASH
Timer::after_millis(1000).await;

let mut f = Flash::new_blocking(p.FLASH);
const size: u32 = 256;
let start: u32 = 1024 * 32;
let stop = start + 256 * 3;
for offset in (start..stop).step_by(size as usize) {
println!("Testing offset: {:#X}, size: {:#X}", offset, size);

println!("Reading...");
let mut buf = [0u8; 32];
println!("{:?}", f.blocking_read(offset, &mut buf));
println!("Read: {:?}", buf);

println!("Erasing...");
println!("{:?}", f.blocking_erase(offset, offset + size));

println!("Reading...");
let mut buf = [0u8; 32];
println!("{:?}", f.blocking_read(offset, &mut buf));
println!("Read after erase: {:?}", buf);

println!("Writing...");
println!("{:?}", f.blocking_write(offset, &[0xabu8; size as usize]));

println!("Reading...");
let mut buf = [0u8; 32];
println!("{:?}", f.blocking_read(offset, &mut buf));
println!("Read: {:?}", buf);
}

println!("Blocking tests done.");
loop {}
}
74 changes: 74 additions & 0 deletions examples/ch32v307/src/bin/flash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
#![feature(impl_trait_in_assoc_type)]

use ch32_hal::flash::{Flash, FLASH_BASE};
use ch32_hal::rcc::*;
use ch32_hal::{interrupt, println};
use embassy_executor::Spawner;
use embassy_time::Timer;
use panic_halt as _;

#[embassy_executor::main(entry = "qingke_rt::entry")]
async fn main(_spawner: Spawner) -> ! {
// NOTE: TRM, 3.2:
// > When carrying out FLASH-related operations, it is strongly recommended that the system main
// > frequency is not greater than 120M.
// Here we configure the clock at 60MHz
let p = ch32_hal::init(ch32_hal::Config {
rcc: ch32_hal::rcc::Config {
hse: None,
sys: Sysclk::PLL,
pll_src: PllSource::HSI,
pll: Some(Pll {
prediv: PllPreDiv::DIV2,
mul: PllMul::MUL15,
}),
pllx: None,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: LsConfig::default(),
hspll_src: HsPllSource::HSI,
hspll: None,
},
dma_interrupt_priority: interrupt::Priority::P0,
});
ch32_hal::debug::SDIPrint::enable();

// Give a chance to catch CPU if it craps itself writing to FLASH
Timer::after_millis(1000).await;

let mut f = Flash::new_blocking(p.FLASH);
const size: u32 = 256;
let start: u32 = 1024 * 32;
let stop = start + 256 * 3;
for offset in (start..stop).step_by(size as usize) {
println!("Testing offset: {:#X}, size: {:#X}", offset, size);

println!("Reading...");
let mut buf = [0u8; 32];
println!("{:?}", f.blocking_read(offset, &mut buf));
println!("Read: {:?}", buf);

println!("Erasing...");
println!("{:?}", f.blocking_erase(offset, offset + size));

println!("Reading...");
let mut buf = [0u8; 32];
println!("{:?}", f.blocking_read(offset, &mut buf));
println!("Read after erase: {:?}", buf);

println!("Writing...");
println!("{:?}", f.blocking_write(offset, &[0xabu8; size as usize]));

println!("Reading...");
let mut buf = [0u8; 32];
println!("{:?}", f.blocking_read(offset, &mut buf));
println!("Read: {:?}", buf);
}

println!("Blocking tests done.");
loop {}
}
226 changes: 226 additions & 0 deletions src/flash/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
#[allow(dead_code)]
use core::marker::PhantomData;
use core::sync::atomic::{fence, Ordering};

use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embedded_storage::nor_flash::ReadNorFlash;

use super::{family, Async, Blocking, Error, FlashSector, FLASH_SIZE, READ_SIZE, WRITE_SIZE};
use crate::peripherals::FLASH;
use crate::Peripheral;

// FIXME! the pac incorrectly defines it to 0 (wich is the mapped execute address).
pub const FLASH_BASE: usize = 0x0800_0000;
// when the pac is fixed, remove this const and use
// use crate::pac::FLASH_BASE;

/// Internal flash memory driver.
pub struct Flash<'d, MODE = Async> {
pub(crate) inner: PeripheralRef<'d, FLASH>,
pub(crate) _mode: PhantomData<MODE>,
}

impl<'d> Flash<'d, Blocking> {
/// Create a new flash driver, usable in blocking mode.
pub fn new_blocking(p: impl Peripheral<P = FLASH> + 'd) -> Self {
into_ref!(p);

Self {
inner: p,
_mode: PhantomData,
}
}
}

impl<'d, MODE> Flash<'d, MODE> {
/// Blocking read.
///
/// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
/// For example, to read address `0x0800_1234` you have to use offset `0x1234`.
pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes)
}

/// Blocking write.
///
/// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
/// For example, to write address `0x0800_1234` you have to use offset `0x1234`.
pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
unsafe {
blocking_write(
FLASH_BASE as u32,
FLASH_SIZE as u32,
offset,
bytes,
write_chunk_unlocked,
)
}
}

/// Blocking erase.
///
/// NOTE: `from` and `to` are offsets from the flash start, NOT an absolute address.
/// For example, to erase address `0x0801_0000` you have to use offset `0x1_0000`.
pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
unsafe { blocking_erase(FLASH_BASE as u32, from, to, erase_sector_unlocked) }
}
}

pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
if offset + bytes.len() as u32 > size {
return Err(Error::Size);
}

let start_address = base + offset;

#[cfg(flash_f4)]
family::assert_not_corrupted_read(start_address + bytes.len() as u32);

let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) };
bytes.copy_from_slice(flash_data);
Ok(())
}

pub(super) unsafe fn blocking_write(
base: u32,
size: u32,
offset: u32,
bytes: &[u8],
write_chunk: unsafe fn(u32, &[u8]) -> Result<(), Error>,
) -> Result<(), Error> {
if offset + bytes.len() as u32 > size {
return Err(Error::Size);
}
if offset % WRITE_SIZE as u32 != 0 || bytes.len() % WRITE_SIZE != 0 {
return Err(Error::Unaligned);
}

let mut address = base + offset;

for chunk in bytes.chunks(WRITE_SIZE) {
write_chunk(address, chunk)?;
address += WRITE_SIZE as u32;
}
Ok(())
}

pub(super) unsafe fn write_chunk_unlocked(address: u32, chunk: &[u8]) -> Result<(), Error> {
family::clear_all_err();
fence(Ordering::SeqCst);
family::unlock();
fence(Ordering::SeqCst);
family::enable_blocking_write();
fence(Ordering::SeqCst);

let _on_drop = OnDrop::new(|| {
family::disable_blocking_write();
fence(Ordering::SeqCst);
family::lock();
});

family::blocking_write(address, unwrap!(chunk.try_into()))
}

pub(super) unsafe fn write_chunk_with_critical_section(address: u32, chunk: &[u8]) -> Result<(), Error> {
critical_section::with(|_| write_chunk_unlocked(address, chunk))
}

pub(super) unsafe fn blocking_erase(
base: u32,
from: u32,
to: u32,
erase_sector: unsafe fn(&FlashSector) -> Result<(), Error>,
) -> Result<(), Error> {
let start_address = base + from;
let end_address = base + to;

ensure_sector_aligned(start_address, end_address)?;
family::unlock();
let _on_drop = OnDrop::new(|| {
family::lock();
});

trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address);

let mut address = start_address;
while address < end_address {
let sector = get_sector(address);
trace!("Erasing sector: {:?}", sector);
erase_sector(&sector)?;
address += sector.size;
}
Ok(())
}

pub(super) unsafe fn erase_sector_unlocked(sector: &FlashSector) -> Result<(), Error> {
family::clear_all_err();
fence(Ordering::SeqCst);
family::unlock();
fence(Ordering::SeqCst);

let _on_drop = OnDrop::new(|| family::lock());

family::blocking_erase_sector(sector)
}

pub(super) unsafe fn erase_sector_with_critical_section(sector: &FlashSector) -> Result<(), Error> {
critical_section::with(|_| erase_sector_unlocked(sector))
}

pub(super) fn get_sector(address: u32) -> FlashSector {
let mut bank_offset = 0;
let index_in_region = (address - FLASH_BASE as u32) / WRITE_SIZE as u32;
if (address as usize) < (FLASH_BASE + FLASH_SIZE) {
return FlashSector {
start: FLASH_BASE as u32 + index_in_region * WRITE_SIZE as u32,
size: WRITE_SIZE as u32,
};
}

panic!("Flash sector not found");
}

pub(super) fn ensure_sector_aligned(start_address: u32, end_address: u32) -> Result<(), Error> {
let mut address = start_address;
while address < end_address {
let sector = get_sector(address);
if sector.start != address {
return Err(Error::Unaligned);
}
address += sector.size;
}
if address != end_address {
return Err(Error::Unaligned);
}
Ok(())
}

impl<MODE> embedded_storage::nor_flash::ErrorType for Flash<'_, MODE> {
type Error = Error;
}

impl<MODE> embedded_storage::nor_flash::ReadNorFlash for Flash<'_, MODE> {
const READ_SIZE: usize = READ_SIZE;

fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
self.blocking_read(offset, bytes)
}

fn capacity(&self) -> usize {
FLASH_SIZE
}
}

impl<MODE> embedded_storage::nor_flash::NorFlash for Flash<'_, MODE> {
const WRITE_SIZE: usize = WRITE_SIZE;
const ERASE_SIZE: usize = WRITE_SIZE;

fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
self.blocking_write(offset, bytes)
}

fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
self.blocking_erase(from, to)
}
}
Loading