From 04c9130d326990dc92577f2ed4b2dc927efe2c13 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Sat, 26 Oct 2024 23:50:16 +0800 Subject: feat(example): move ospi memory mapped example for stm32h7b0 to separate folder Signed-off-by: Haobo Gu --- examples/stm32h7/src/bin/ospi_memory_mapped.rs | 435 ----------------------- examples/stm32h7b0/.cargo/config.toml | 8 + examples/stm32h7b0/Cargo.toml | 74 ++++ examples/stm32h7b0/build.rs | 35 ++ examples/stm32h7b0/memory.x | 5 + examples/stm32h7b0/src/bin/ospi_memory_mapped.rs | 434 ++++++++++++++++++++++ 6 files changed, 556 insertions(+), 435 deletions(-) delete mode 100644 examples/stm32h7/src/bin/ospi_memory_mapped.rs create mode 100644 examples/stm32h7b0/.cargo/config.toml create mode 100644 examples/stm32h7b0/Cargo.toml create mode 100644 examples/stm32h7b0/build.rs create mode 100644 examples/stm32h7b0/memory.x create mode 100644 examples/stm32h7b0/src/bin/ospi_memory_mapped.rs (limited to 'examples') diff --git a/examples/stm32h7/src/bin/ospi_memory_mapped.rs b/examples/stm32h7/src/bin/ospi_memory_mapped.rs deleted file mode 100644 index 40f39f751..000000000 --- a/examples/stm32h7/src/bin/ospi_memory_mapped.rs +++ /dev/null @@ -1,435 +0,0 @@ -#![no_main] -#![no_std] - -// Tested on weact stm32h7b0 board + w25q64 spi flash - -use defmt::info; -use defmt_rtt as _; -use embassy_executor::Spawner; -use embassy_stm32::{ - gpio::{Level, Output, Speed}, - mode::Blocking, - ospi::{AddressSize, DummyCycles, Instance, Ospi, OspiWidth, TransferConfig}, - ospi::{ChipSelectHighTime, FIFOThresholdLevel, MemorySize, MemoryType, WrapSize}, - time::Hertz, - Config, -}; -use embassy_time::Timer; -use panic_probe as _; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // RCC config - let mut config = Config::default(); - info!("START"); - { - use embassy_stm32::rcc::*; - config.rcc.hsi = Some(HSIPrescaler::DIV1); - config.rcc.csi = true; - // Needed for USB - config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); - // External oscillator 25MHZ - config.rcc.hse = Some(Hse { - freq: Hertz(25_000_000), - mode: HseMode::Oscillator, - }); - config.rcc.pll1 = Some(Pll { - source: PllSource::HSE, - prediv: PllPreDiv::DIV5, - mul: PllMul::MUL112, - divp: Some(PllDiv::DIV2), - divq: Some(PllDiv::DIV2), - divr: Some(PllDiv::DIV2), - }); - config.rcc.sys = Sysclk::PLL1_P; - config.rcc.ahb_pre = AHBPrescaler::DIV2; - config.rcc.apb1_pre = APBPrescaler::DIV2; - config.rcc.apb2_pre = APBPrescaler::DIV2; - config.rcc.apb3_pre = APBPrescaler::DIV2; - config.rcc.apb4_pre = APBPrescaler::DIV2; - config.rcc.voltage_scale = VoltageScale::Scale0; - } - - // Initialize peripherals - let p = embassy_stm32::init(config); - - let qspi_config = embassy_stm32::ospi::Config { - fifo_threshold: FIFOThresholdLevel::_16Bytes, - memory_type: MemoryType::Micron, - device_size: MemorySize::_8MiB, - chip_select_high_time: ChipSelectHighTime::_1Cycle, - free_running_clock: false, - clock_mode: false, - wrap_size: WrapSize::None, - clock_prescaler: 2, - sample_shifting: true, - delay_hold_quarter_cycle: false, - chip_select_boundary: 0, - delay_block_bypass: true, - max_transfer: 0, - refresh: 0, - }; - let ospi = embassy_stm32::ospi::Ospi::new_blocking_quadspi( - p.OCTOSPI1, - p.PB2, - p.PD11, - p.PD12, - p.PE2, - p.PD13, - p.PB6, - qspi_config, - ); - - let mut flash = FlashMemory::new(ospi).await; - - let flash_id = flash.read_id(); - info!("FLASH ID: {=[u8]:x}", flash_id); - let mut wr_buf = [0u8; 8]; - for i in 0..8 { - wr_buf[i] = i as u8; - } - let mut rd_buf = [0u8; 8]; - flash.erase_sector(0).await; - flash.write_memory(0, &wr_buf, true).await; - flash.read_memory(0, &mut rd_buf, true); - info!("WRITE BUF: {=[u8]:#X}", wr_buf); - info!("READ BUF: {=[u8]:#X}", rd_buf); - info!("Enabling memory mapped mode"); - flash.enable_mm().await; - - let first_u32 = unsafe { *(0x90000000 as *const u32) }; - assert_eq!(first_u32, 0x03020100); - - let second_u32 = unsafe { *(0x90000004 as *const u32) }; - assert_eq!(second_u32, 0x07060504); - flash.disable_mm().await; - - info!("DONE"); - // Output pin PE3 - let mut led = Output::new(p.PE3, Level::Low, Speed::Low); - - loop { - led.toggle(); - Timer::after_millis(1000).await; - } -} - -const MEMORY_PAGE_SIZE: usize = 8; - -const CMD_QUAD_READ: u8 = 0x6B; - -const CMD_QUAD_WRITE_PG: u8 = 0x32; - -const CMD_READ_ID: u8 = 0x9F; -const CMD_READ_UUID: u8 = 0x4B; - -const CMD_ENABLE_RESET: u8 = 0x66; -const CMD_RESET: u8 = 0x99; - -const CMD_WRITE_ENABLE: u8 = 0x06; - -const CMD_CHIP_ERASE: u8 = 0xC7; -const CMD_SECTOR_ERASE: u8 = 0x20; -const CMD_BLOCK_ERASE_32K: u8 = 0x52; -const CMD_BLOCK_ERASE_64K: u8 = 0xD8; - -const CMD_READ_SR: u8 = 0x05; -const CMD_READ_CR: u8 = 0x35; - -const CMD_WRITE_SR: u8 = 0x01; -const CMD_WRITE_CR: u8 = 0x31; - -/// Implementation of access to flash chip. -/// Chip commands are hardcoded as it depends on used chip. -/// This implementation is using chip GD25Q64C from Giga Device -pub struct FlashMemory { - ospi: Ospi<'static, I, Blocking>, -} - -impl FlashMemory { - pub async fn new(ospi: Ospi<'static, I, Blocking>) -> Self { - let mut memory = Self { ospi }; - - memory.reset_memory().await; - memory.enable_quad(); - memory - } - - async fn qpi_mode(&mut self) { - // Enter qpi mode - self.exec_command(0x38).await; - - // Set read param - let transaction = TransferConfig { - iwidth: OspiWidth::QUAD, - dwidth: OspiWidth::QUAD, - instruction: Some(0xC0), - ..Default::default() - }; - self.enable_write().await; - self.ospi.blocking_write(&[0x30_u8], transaction).unwrap(); - self.wait_write_finish(); - } - - pub async fn disable_mm(&mut self) { - self.ospi.disable_memory_mapped_mode(); - } - - pub async fn enable_mm(&mut self) { - self.qpi_mode().await; - - let read_config = TransferConfig { - iwidth: OspiWidth::QUAD, - isize: AddressSize::_8Bit, - adwidth: OspiWidth::QUAD, - adsize: AddressSize::_24bit, - dwidth: OspiWidth::QUAD, - instruction: Some(0x0B), // Fast read in QPI mode - dummy: DummyCycles::_8, - ..Default::default() - }; - - let write_config = TransferConfig { - iwidth: OspiWidth::SING, - isize: AddressSize::_8Bit, - adwidth: OspiWidth::SING, - adsize: AddressSize::_24bit, - dwidth: OspiWidth::QUAD, - instruction: Some(0x32), // Write config - dummy: DummyCycles::_0, - ..Default::default() - }; - self.ospi.enable_memory_mapped_mode(read_config, write_config).unwrap(); - } - - fn enable_quad(&mut self) { - let cr = self.read_cr(); - // info!("Read cr: {:x}", cr); - self.write_cr(cr | 0x02); - // info!("Read cr after writing: {:x}", cr); - } - - pub fn disable_quad(&mut self) { - let cr = self.read_cr(); - self.write_cr(cr & (!(0x02))); - } - - async fn exec_command_4(&mut self, cmd: u8) { - let transaction = TransferConfig { - iwidth: OspiWidth::QUAD, - adwidth: OspiWidth::NONE, - // adsize: AddressSize::_24bit, - dwidth: OspiWidth::NONE, - instruction: Some(cmd as u32), - address: None, - dummy: DummyCycles::_0, - ..Default::default() - }; - self.ospi.command(&transaction).await.unwrap(); - } - - async fn exec_command(&mut self, cmd: u8) { - let transaction = TransferConfig { - iwidth: OspiWidth::SING, - adwidth: OspiWidth::NONE, - // adsize: AddressSize::_24bit, - dwidth: OspiWidth::NONE, - instruction: Some(cmd as u32), - address: None, - dummy: DummyCycles::_0, - ..Default::default() - }; - // info!("Excuting command: {:x}", transaction.instruction); - self.ospi.command(&transaction).await.unwrap(); - } - - pub async fn reset_memory(&mut self) { - self.exec_command_4(CMD_ENABLE_RESET).await; - self.exec_command_4(CMD_RESET).await; - self.exec_command(CMD_ENABLE_RESET).await; - self.exec_command(CMD_RESET).await; - self.wait_write_finish(); - } - - pub async fn enable_write(&mut self) { - self.exec_command(CMD_WRITE_ENABLE).await; - } - - pub fn read_id(&mut self) -> [u8; 3] { - let mut buffer = [0; 3]; - let transaction: TransferConfig = TransferConfig { - iwidth: OspiWidth::SING, - isize: AddressSize::_8Bit, - adwidth: OspiWidth::NONE, - // adsize: AddressSize::_24bit, - dwidth: OspiWidth::SING, - instruction: Some(CMD_READ_ID as u32), - ..Default::default() - }; - // info!("Reading id: 0x{:X}", transaction.instruction); - self.ospi.blocking_read(&mut buffer, transaction).unwrap(); - buffer - } - - pub fn read_id_4(&mut self) -> [u8; 3] { - let mut buffer = [0; 3]; - let transaction: TransferConfig = TransferConfig { - iwidth: OspiWidth::SING, - isize: AddressSize::_8Bit, - adwidth: OspiWidth::NONE, - dwidth: OspiWidth::QUAD, - instruction: Some(CMD_READ_ID as u32), - ..Default::default() - }; - info!("Reading id: 0x{:X}", transaction.instruction); - self.ospi.blocking_read(&mut buffer, transaction).unwrap(); - buffer - } - - pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { - let transaction = TransferConfig { - iwidth: OspiWidth::SING, - adwidth: OspiWidth::SING, - adsize: AddressSize::_24bit, - dwidth: OspiWidth::QUAD, - instruction: Some(CMD_QUAD_READ as u32), - address: Some(addr), - dummy: DummyCycles::_8, - ..Default::default() - }; - if use_dma { - self.ospi.blocking_read(buffer, transaction).unwrap(); - } else { - self.ospi.blocking_read(buffer, transaction).unwrap(); - } - } - - fn wait_write_finish(&mut self) { - while (self.read_sr() & 0x01) != 0 {} - } - - async fn perform_erase(&mut self, addr: u32, cmd: u8) { - let transaction = TransferConfig { - iwidth: OspiWidth::SING, - adwidth: OspiWidth::SING, - adsize: AddressSize::_24bit, - dwidth: OspiWidth::NONE, - instruction: Some(cmd as u32), - address: Some(addr), - dummy: DummyCycles::_0, - ..Default::default() - }; - self.enable_write().await; - self.ospi.command(&transaction).await.unwrap(); - self.wait_write_finish(); - } - - pub async fn erase_sector(&mut self, addr: u32) { - self.perform_erase(addr, CMD_SECTOR_ERASE).await; - } - - pub async fn erase_block_32k(&mut self, addr: u32) { - self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await; - } - - pub async fn erase_block_64k(&mut self, addr: u32) { - self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await; - } - - pub async fn erase_chip(&mut self) { - self.exec_command(CMD_CHIP_ERASE).await; - } - - async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { - assert!( - (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, - "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", - len, - addr - ); - - let transaction = TransferConfig { - iwidth: OspiWidth::SING, - adsize: AddressSize::_24bit, - adwidth: OspiWidth::SING, - dwidth: OspiWidth::QUAD, - instruction: Some(CMD_QUAD_WRITE_PG as u32), - address: Some(addr), - dummy: DummyCycles::_0, - ..Default::default() - }; - self.enable_write().await; - if use_dma { - self.ospi.blocking_write(buffer, transaction).unwrap(); - } else { - self.ospi.blocking_write(buffer, transaction).unwrap(); - } - self.wait_write_finish(); - } - - pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { - let mut left = buffer.len(); - let mut place = addr; - let mut chunk_start = 0; - - while left > 0 { - let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; - let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; - let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; - self.write_page(place, chunk, chunk_size, use_dma).await; - place += chunk_size as u32; - left -= chunk_size; - chunk_start += chunk_size; - } - } - - fn read_register(&mut self, cmd: u8) -> u8 { - let mut buffer = [0; 1]; - let transaction: TransferConfig = TransferConfig { - iwidth: OspiWidth::SING, - isize: AddressSize::_8Bit, - adwidth: OspiWidth::NONE, - adsize: AddressSize::_24bit, - dwidth: OspiWidth::SING, - instruction: Some(cmd as u32), - address: None, - dummy: DummyCycles::_0, - ..Default::default() - }; - self.ospi.blocking_read(&mut buffer, transaction).unwrap(); - // info!("Read w25q64 register: 0x{:x}", buffer[0]); - buffer[0] - } - - fn write_register(&mut self, cmd: u8, value: u8) { - let buffer = [value; 1]; - let transaction: TransferConfig = TransferConfig { - iwidth: OspiWidth::SING, - isize: AddressSize::_8Bit, - instruction: Some(cmd as u32), - adsize: AddressSize::_24bit, - adwidth: OspiWidth::NONE, - dwidth: OspiWidth::SING, - address: None, - dummy: DummyCycles::_0, - ..Default::default() - }; - self.ospi.blocking_write(&buffer, transaction).unwrap(); - } - - pub fn read_sr(&mut self) -> u8 { - self.read_register(CMD_READ_SR) - } - - pub fn read_cr(&mut self) -> u8 { - self.read_register(CMD_READ_CR) - } - - pub fn write_sr(&mut self, value: u8) { - self.write_register(CMD_WRITE_SR, value); - } - - pub fn write_cr(&mut self, value: u8) { - self.write_register(CMD_WRITE_CR, value); - } -} diff --git a/examples/stm32h7b0/.cargo/config.toml b/examples/stm32h7b0/.cargo/config.toml new file mode 100644 index 000000000..870849a27 --- /dev/null +++ b/examples/stm32h7b0/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.thumbv7em-none-eabihf] +runner = 'probe-rs run --chip STM32H7B0VBTx' + +[build] +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml new file mode 100644 index 000000000..02c620443 --- /dev/null +++ b/examples/stm32h7b0/Cargo.toml @@ -0,0 +1,74 @@ +[package] +edition = "2021" +name = "embassy-stm32h7b0-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } +embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } +embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } +embedded-hal-async = { version = "1.0" } +embedded-nal-async = "0.8.0" +embedded-io-async = { version = "0.6.1" } +panic-probe = { version = "0.3", features = ["print-defmt"] } +heapless = { version = "0.8", default-features = false } +rand_core = "0.6.3" +critical-section = "1.1" +micromath = "2.0.0" +stm32-fmc = "0.3.0" +embedded-storage = "0.3.1" +static_cell = "2" +chrono = { version = "^0.4", default-features = false } +grounded = "0.2.0" + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- diff --git a/examples/stm32h7b0/build.rs b/examples/stm32h7b0/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/stm32h7b0/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32h7b0/memory.x b/examples/stm32h7b0/memory.x new file mode 100644 index 000000000..6eb1bb7c1 --- /dev/null +++ b/examples/stm32h7b0/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 128K /* BANK_1 */ + RAM : ORIGIN = 0x24000000, LENGTH = 512K /* SRAM */ +} \ No newline at end of file diff --git a/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs new file mode 100644 index 000000000..e32a22e41 --- /dev/null +++ b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs @@ -0,0 +1,434 @@ +#![no_main] +#![no_std] + +// Tested on weact stm32h7b0 board + w25q64 spi flash + +use defmt::info; +use defmt_rtt as _; +use embassy_executor::Spawner; +use embassy_stm32::{ + gpio::{Level, Output, Speed}, + mode::Blocking, + ospi::{AddressSize, DummyCycles, Instance, Ospi, OspiWidth, TransferConfig}, + ospi::{ChipSelectHighTime, FIFOThresholdLevel, MemorySize, MemoryType, WrapSize}, + time::Hertz, + Config, +}; +use embassy_time::Timer; +use panic_probe as _; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // RCC config + let mut config = Config::default(); + info!("START"); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(HSIPrescaler::DIV1); + config.rcc.csi = true; + // Needed for USB + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); + // External oscillator 25MHZ + config.rcc.hse = Some(Hse { + freq: Hertz(25_000_000), + mode: HseMode::Oscillator, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV5, + mul: PllMul::MUL112, + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV2), + divr: Some(PllDiv::DIV2), + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV2; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.apb3_pre = APBPrescaler::DIV2; + config.rcc.apb4_pre = APBPrescaler::DIV2; + config.rcc.voltage_scale = VoltageScale::Scale0; + } + + // Initialize peripherals + let p = embassy_stm32::init(config); + + let qspi_config = embassy_stm32::ospi::Config { + fifo_threshold: FIFOThresholdLevel::_16Bytes, + memory_type: MemoryType::Micron, + device_size: MemorySize::_8MiB, + chip_select_high_time: ChipSelectHighTime::_1Cycle, + free_running_clock: false, + clock_mode: false, + wrap_size: WrapSize::None, + clock_prescaler: 4, + sample_shifting: true, + delay_hold_quarter_cycle: false, + chip_select_boundary: 0, + delay_block_bypass: true, + max_transfer: 0, + refresh: 0, + }; + let ospi = embassy_stm32::ospi::Ospi::new_blocking_quadspi( + p.OCTOSPI1, + p.PB2, + p.PD11, + p.PD12, + p.PE2, + p.PD13, + p.PB6, + qspi_config, + ); + + let mut flash = FlashMemory::new(ospi).await; + + let flash_id = flash.read_id(); + info!("FLASH ID: {=[u8]:x}", flash_id); + let mut wr_buf = [0u8; 8]; + for i in 0..8 { + wr_buf[i] = i as u8; + } + let mut rd_buf = [0u8; 8]; + flash.erase_sector(0).await; + flash.write_memory(0, &wr_buf, true).await; + flash.read_memory(0, &mut rd_buf, true); + info!("WRITE BUF: {=[u8]:#X}", wr_buf); + info!("READ BUF: {=[u8]:#X}", rd_buf); + flash.enable_mm().await; + info!("Enabled memory mapped mode"); + + let first_u32 = unsafe { *(0x90000000 as *const u32) }; + assert_eq!(first_u32, 0x03020100); + + let second_u32 = unsafe { *(0x90000004 as *const u32) }; + assert_eq!(second_u32, 0x07060504); + flash.disable_mm().await; + + info!("DONE"); + // Output pin PE3 + let mut led = Output::new(p.PE3, Level::Low, Speed::Low); + + loop { + led.toggle(); + Timer::after_millis(1000).await; + } +} + +const MEMORY_PAGE_SIZE: usize = 8; + +const CMD_QUAD_READ: u8 = 0x6B; + +const CMD_QUAD_WRITE_PG: u8 = 0x32; + +const CMD_READ_ID: u8 = 0x9F; + +const CMD_ENABLE_RESET: u8 = 0x66; +const CMD_RESET: u8 = 0x99; + +const CMD_WRITE_ENABLE: u8 = 0x06; + +const CMD_CHIP_ERASE: u8 = 0xC7; +const CMD_SECTOR_ERASE: u8 = 0x20; +const CMD_BLOCK_ERASE_32K: u8 = 0x52; +const CMD_BLOCK_ERASE_64K: u8 = 0xD8; + +const CMD_READ_SR: u8 = 0x05; +const CMD_READ_CR: u8 = 0x35; + +const CMD_WRITE_SR: u8 = 0x01; +const CMD_WRITE_CR: u8 = 0x31; + +/// Implementation of access to flash chip. +/// Chip commands are hardcoded as it depends on used chip. +/// This implementation is using chip GD25Q64C from Giga Device +pub struct FlashMemory { + ospi: Ospi<'static, I, Blocking>, +} + +impl FlashMemory { + pub async fn new(ospi: Ospi<'static, I, Blocking>) -> Self { + let mut memory = Self { ospi }; + + memory.reset_memory().await; + memory.enable_quad(); + memory + } + + async fn qpi_mode(&mut self) { + // Enter qpi mode + self.exec_command(0x38).await; + + // Set read param + let transaction = TransferConfig { + iwidth: OspiWidth::QUAD, + dwidth: OspiWidth::QUAD, + instruction: Some(0xC0), + ..Default::default() + }; + self.enable_write().await; + self.ospi.blocking_write(&[0x30_u8], transaction).unwrap(); + self.wait_write_finish(); + } + + pub async fn disable_mm(&mut self) { + self.ospi.disable_memory_mapped_mode(); + } + + pub async fn enable_mm(&mut self) { + self.qpi_mode().await; + + let read_config = TransferConfig { + iwidth: OspiWidth::QUAD, + isize: AddressSize::_8Bit, + adwidth: OspiWidth::QUAD, + adsize: AddressSize::_24bit, + dwidth: OspiWidth::QUAD, + instruction: Some(0x0B), // Fast read in QPI mode + dummy: DummyCycles::_8, + ..Default::default() + }; + + let write_config = TransferConfig { + iwidth: OspiWidth::SING, + isize: AddressSize::_8Bit, + adwidth: OspiWidth::SING, + adsize: AddressSize::_24bit, + dwidth: OspiWidth::QUAD, + instruction: Some(0x32), // Write config + dummy: DummyCycles::_0, + ..Default::default() + }; + self.ospi.enable_memory_mapped_mode(read_config, write_config).unwrap(); + } + + fn enable_quad(&mut self) { + let cr = self.read_cr(); + // info!("Read cr: {:x}", cr); + self.write_cr(cr | 0x02); + // info!("Read cr after writing: {:x}", cr); + } + + pub fn disable_quad(&mut self) { + let cr = self.read_cr(); + self.write_cr(cr & (!(0x02))); + } + + async fn exec_command_4(&mut self, cmd: u8) { + let transaction = TransferConfig { + iwidth: OspiWidth::QUAD, + adwidth: OspiWidth::NONE, + // adsize: AddressSize::_24bit, + dwidth: OspiWidth::NONE, + instruction: Some(cmd as u32), + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + self.ospi.command(&transaction).await.unwrap(); + } + + async fn exec_command(&mut self, cmd: u8) { + let transaction = TransferConfig { + iwidth: OspiWidth::SING, + adwidth: OspiWidth::NONE, + // adsize: AddressSize::_24bit, + dwidth: OspiWidth::NONE, + instruction: Some(cmd as u32), + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + // info!("Excuting command: {:x}", transaction.instruction); + self.ospi.command(&transaction).await.unwrap(); + } + + pub async fn reset_memory(&mut self) { + self.exec_command_4(CMD_ENABLE_RESET).await; + self.exec_command_4(CMD_RESET).await; + self.exec_command(CMD_ENABLE_RESET).await; + self.exec_command(CMD_RESET).await; + self.wait_write_finish(); + } + + pub async fn enable_write(&mut self) { + self.exec_command(CMD_WRITE_ENABLE).await; + } + + pub fn read_id(&mut self) -> [u8; 3] { + let mut buffer = [0; 3]; + let transaction: TransferConfig = TransferConfig { + iwidth: OspiWidth::SING, + isize: AddressSize::_8Bit, + adwidth: OspiWidth::NONE, + // adsize: AddressSize::_24bit, + dwidth: OspiWidth::SING, + instruction: Some(CMD_READ_ID as u32), + ..Default::default() + }; + // info!("Reading id: 0x{:X}", transaction.instruction); + self.ospi.blocking_read(&mut buffer, transaction).unwrap(); + buffer + } + + pub fn read_id_4(&mut self) -> [u8; 3] { + let mut buffer = [0; 3]; + let transaction: TransferConfig = TransferConfig { + iwidth: OspiWidth::SING, + isize: AddressSize::_8Bit, + adwidth: OspiWidth::NONE, + dwidth: OspiWidth::QUAD, + instruction: Some(CMD_READ_ID as u32), + ..Default::default() + }; + info!("Reading id: 0x{:X}", transaction.instruction); + self.ospi.blocking_read(&mut buffer, transaction).unwrap(); + buffer + } + + pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { + let transaction = TransferConfig { + iwidth: OspiWidth::SING, + adwidth: OspiWidth::SING, + adsize: AddressSize::_24bit, + dwidth: OspiWidth::QUAD, + instruction: Some(CMD_QUAD_READ as u32), + address: Some(addr), + dummy: DummyCycles::_8, + ..Default::default() + }; + if use_dma { + self.ospi.blocking_read(buffer, transaction).unwrap(); + } else { + self.ospi.blocking_read(buffer, transaction).unwrap(); + } + } + + fn wait_write_finish(&mut self) { + while (self.read_sr() & 0x01) != 0 {} + } + + async fn perform_erase(&mut self, addr: u32, cmd: u8) { + let transaction = TransferConfig { + iwidth: OspiWidth::SING, + adwidth: OspiWidth::SING, + adsize: AddressSize::_24bit, + dwidth: OspiWidth::NONE, + instruction: Some(cmd as u32), + address: Some(addr), + dummy: DummyCycles::_0, + ..Default::default() + }; + self.enable_write().await; + self.ospi.command(&transaction).await.unwrap(); + self.wait_write_finish(); + } + + pub async fn erase_sector(&mut self, addr: u32) { + self.perform_erase(addr, CMD_SECTOR_ERASE).await; + } + + pub async fn erase_block_32k(&mut self, addr: u32) { + self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await; + } + + pub async fn erase_block_64k(&mut self, addr: u32) { + self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await; + } + + pub async fn erase_chip(&mut self) { + self.exec_command(CMD_CHIP_ERASE).await; + } + + async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { + assert!( + (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, + "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", + len, + addr + ); + + let transaction = TransferConfig { + iwidth: OspiWidth::SING, + adsize: AddressSize::_24bit, + adwidth: OspiWidth::SING, + dwidth: OspiWidth::QUAD, + instruction: Some(CMD_QUAD_WRITE_PG as u32), + address: Some(addr), + dummy: DummyCycles::_0, + ..Default::default() + }; + self.enable_write().await; + if use_dma { + self.ospi.blocking_write(buffer, transaction).unwrap(); + } else { + self.ospi.blocking_write(buffer, transaction).unwrap(); + } + self.wait_write_finish(); + } + + pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { + let mut left = buffer.len(); + let mut place = addr; + let mut chunk_start = 0; + + while left > 0 { + let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; + let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; + let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; + self.write_page(place, chunk, chunk_size, use_dma).await; + place += chunk_size as u32; + left -= chunk_size; + chunk_start += chunk_size; + } + } + + fn read_register(&mut self, cmd: u8) -> u8 { + let mut buffer = [0; 1]; + let transaction: TransferConfig = TransferConfig { + iwidth: OspiWidth::SING, + isize: AddressSize::_8Bit, + adwidth: OspiWidth::NONE, + adsize: AddressSize::_24bit, + dwidth: OspiWidth::SING, + instruction: Some(cmd as u32), + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + self.ospi.blocking_read(&mut buffer, transaction).unwrap(); + // info!("Read w25q64 register: 0x{:x}", buffer[0]); + buffer[0] + } + + fn write_register(&mut self, cmd: u8, value: u8) { + let buffer = [value; 1]; + let transaction: TransferConfig = TransferConfig { + iwidth: OspiWidth::SING, + isize: AddressSize::_8Bit, + instruction: Some(cmd as u32), + adsize: AddressSize::_24bit, + adwidth: OspiWidth::NONE, + dwidth: OspiWidth::SING, + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + self.ospi.blocking_write(&buffer, transaction).unwrap(); + } + + pub fn read_sr(&mut self) -> u8 { + self.read_register(CMD_READ_SR) + } + + pub fn read_cr(&mut self) -> u8 { + self.read_register(CMD_READ_CR) + } + + pub fn write_sr(&mut self, value: u8) { + self.write_register(CMD_WRITE_SR, value); + } + + pub fn write_cr(&mut self, value: u8) { + self.write_register(CMD_WRITE_CR, value); + } +} -- cgit