Skip to content
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

Flash changes #104

Closed
wants to merge 10 commits into from
56 changes: 56 additions & 0 deletions examples/flash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#![no_main]
#![no_std]

use cortex_m_rt::entry;
use hal::flash::FlashExt;
use hal::flash::FlashSize;
use hal::stm32;
use stm32g4xx_hal as hal;
extern crate cortex_m_rt as rt;

#[macro_use]
mod utils;

use utils::logger::println;

#[entry]
fn main() -> ! {
utils::logger::init();

let dp = stm32::Peripherals::take().expect("cannot take peripherals");

let mut flash = dp.FLASH.constrain();
let is_dual_bank = flash.is_dual_bank();

println!("Is dual bank: {}", is_dual_bank);

let mut flash_writer = flash.writer::<2048>(FlashSize::Sz256K);

let mut words = [0; 1];
flash_writer.read_exact(0x0000_0000, &mut words[..]);
println!("Bank 1 - first 4 bytes: {:#X}", words[0]);

if is_dual_bank {
let address = 0x0004_0000;
flash_writer.read_exact(address, &mut words[..]);
println!("Bank 2 - First 4 bytes before write: {:#X}", words[0]);

flash_writer.page_erase(address).unwrap();
flash_writer.read_exact(address, &mut words[..]);
println!("Bank 2 - First 4 bytes after erase: {:#X}", words[0]);

let new_value = words[0].wrapping_add(2);
println!("Bank 2 - Writing: {:#X} to first 4 bytes", new_value);
flash_writer
.write(address, &new_value.to_ne_bytes(), true)
.unwrap();
flash_writer.read_exact(address, &mut words[..]);
println!("Bank 2 - First 4 bytes after write: {:#X}", words[0]);
}

println!("Done!");

loop {
cortex_m::asm::nop()
}
}
278 changes: 41 additions & 237 deletions examples/flash_with_rtic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

mod utils;

#[rtic::app(device = stm32g4xx_hal::stm32g4::stm32g474, peripherals = true)]
#[rtic::app(device = stm32g4xx_hal::stm32, peripherals = true)]
mod app {
use crate::utils::logger;
use stm32g4xx_hal::flash::{FlashExt, FlashSize, FlashWriter, Parts};
Expand All @@ -25,18 +25,6 @@ mod app {
#[local]
struct Local {}

fn compare_arrays(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
for i in 0..a.len() {
if a[i] != b[i] {
return false;
}
}
true
}

#[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
crate::utils::logger::init();
Expand All @@ -45,258 +33,74 @@ mod app {
let cp = cx.core;

let rcc = dp.RCC.constrain();
let mut pll_config = stm32g4xx_hal::rcc::PllConfig::default();

// Sysclock is based on PLL_R
pll_config.mux = stm32g4xx_hal::rcc::PLLSrc::HSI; // 16MHz
pll_config.n = stm32g4xx_hal::rcc::PllNMul::MUL_32;
pll_config.m = stm32g4xx_hal::rcc::PllMDiv::DIV_2; // f(vco) = 16MHz*32/2 = 256MHz
pll_config.r = Some(stm32g4xx_hal::rcc::PllRDiv::DIV_2); // f(sysclock) = 256MHz/2 = 128MHz
Copy link
Contributor Author

@usbalbin usbalbin Dec 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I am wrong but the clock setup is not really needed for this example, right? Thought I'd remove all but the essentials to give more focus on the flash related things


// Note to future self: The AHB clock runs the timers, among other things.
// Please refer to the Clock Tree manual to determine if it is worth
// changing to a lower speed for battery life savings.
let mut clock_config = stm32g4xx_hal::rcc::Config::default()
.pll_cfg(pll_config)
.clock_src(stm32g4xx_hal::rcc::SysClockSrc::PLL);

// After clock configuration, the following should be true:
// Sysclock is 128MHz
// AHB clock is 128MHz
// APB1 clock is 128MHz
// APB2 clock is 128MHz
// The ADC will ultimately be put into synchronous mode and will derive
// its clock from the AHB bus clock, with a prescalar of 2 or 4.

let pwr = dp.PWR.constrain().freeze();
let mut rcc = rcc.freeze(clock_config, pwr);

unsafe {
let mut flash = &(*stm32g4xx_hal::stm32::FLASH::ptr());
flash.acr.modify(|_, w| {
w.latency().bits(0b1000) // 8 wait states
});
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even with the custom clock setup above, this should not really be needed, right? This should be done automatically here when setting the clock settings. However by my math, the auto-thing would set it to 4 wait states for a f_sys of 128MHz. Does it for some reason need to be 8, or is that incorrect?

let mut rcc = rcc.freeze(stm32g4xx_hal::rcc::Config::hsi(), pwr);
let mut delay = cp.SYST.delay(&rcc.clocks);

// *** FLASH Memory ***
let one_byte = [0x12 as u8];
let two_bytes = [0xAB, 0xCD as u8];
let three_bytes = [0x12, 0x34, 0x56 as u8];
let four_bytes = [0xAB, 0xCD, 0xEF, 0xBA as u8];
let eight_bytes = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 as u8];
let one_byte = [0x12];
let two_bytes = [0xAB, 0xCD];
let three_bytes = [0x12, 0x34, 0x56];
let four_bytes = [0xAB, 0xCD, 0xEF, 0xBA];
let eight_bytes = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
let sixteen_bytes = [
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC,
0xDE, 0xF0 as u8,
0xDE, 0xF0,
];
let mut flash = dp.FLASH.constrain();
let mut flash_writer = flash.writer::<2048>(FlashSize::Sz256K);

const FLASH_SPACING: u32 = 16; // Separate flash writes by 16 bytes
const FLASH_EXAMPLE_START_ADDRESS: u32 = 0x1FC00;

let address = |i| FLASH_EXAMPLE_START_ADDRESS + i as u32 * FLASH_SPACING;

logger::info!(
"Erasing 128 bytes at address {}",
"Erasing 128 bytes at address {:#X}",
FLASH_EXAMPLE_START_ADDRESS
);
flash_writer
.erase(FLASH_EXAMPLE_START_ADDRESS, 128)
.unwrap(); // Erase entire page

for i in 0..6 {
match i {
0 => {
// This test should fail, as the data needs to be divisible by 8 and force padding is false
let result = flash_writer.write(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
&one_byte,
false,
);
assert!(result.is_err());
assert_eq!(
result.err().unwrap(),
stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8
);

// This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF
let result = flash_writer.write(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
&one_byte,
true,
);
assert!(result.is_ok());
logger::info!(
"Wrote 1 byte to address {}",
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING
);
}
1 => {
// This test should fail, as the data needs to be divisible by 8 and force padding is false
let result = flash_writer.write(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
&two_bytes,
false,
);
assert!(result.is_err());
assert_eq!(
result.err().unwrap(),
stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8
);

// This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF
let result = flash_writer.write(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
&two_bytes,
true,
);
assert!(result.is_ok());
logger::info!(
"Wrote 2 bytes to address {}",
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING
);
}
2 => {
// This test should fail, as the data needs to be divisible by 8 and force padding is false
let result = flash_writer.write(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
&three_bytes,
false,
);
assert!(result.is_err());
assert_eq!(
result.err().unwrap(),
stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8
);
let datasets = [
&one_byte[..],
&two_bytes[..],
&three_bytes[..],
&four_bytes[..],
&eight_bytes[..],
&sixteen_bytes[..],
];

// This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF
let result = flash_writer.write(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
&three_bytes,
true,
);
assert!(result.is_ok());
logger::info!(
"Wrote 3 bytes to address {}",
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING
);
}
3 => {
// This test should fail, as the data needs to be divisible by 8 and force padding is false
let result = flash_writer.write(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
&four_bytes,
false,
);
assert!(result.is_err());
assert_eq!(
result.err().unwrap(),
stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8
);
for (i, data) in datasets.into_iter().enumerate() {
let address = address(i);
let mut do_write = |force_padding| flash_writer.write(address, &data, force_padding);

// This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF
let result = flash_writer.write(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
&four_bytes,
true,
);
assert!(result.is_ok());
logger::info!(
"Wrote 4 bytes to address {}",
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING
);
}
4 => {
flash_writer
.write(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
&eight_bytes,
false,
)
.unwrap();
logger::info!(
"Wrote 8 bytes to address {}",
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING
);
}
5 => {
flash_writer
.write(FLASH_EXAMPLE_START_ADDRESS + i * 16, &sixteen_bytes, false)
.unwrap();
logger::info!(
"Wrote 16 bytes to address {}",
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING
);
}
_ => (),
if data.len() % 8 != 0 {
// This test should fail, as the data needs to be divisible by 8 and force padding is false
assert_eq!(
do_write(false),
Err(stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8)
);
}
}

// This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF
do_write(true).unwrap();
logger::info!("Wrote {} byte(s) to address {:#X}", data.len(), address);
}
logger::info!("Validating data written data by performing read and compare");

for i in 0..6 {
match i {
0 => {
let bytes = flash_writer
.read(FLASH_EXAMPLE_START_ADDRESS as u32, one_byte.len())
.unwrap();
assert!(compare_arrays(&bytes, &one_byte));
logger::info!("Validated 1 byte data");
}
1 => {
let bytes = flash_writer
.read(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
two_bytes.len(),
)
.unwrap();
assert!(compare_arrays(&bytes, &two_bytes));
logger::info!("Validated 2 byte data");
}
2 => {
let bytes = flash_writer
.read(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
three_bytes.len(),
)
.unwrap();
assert!(compare_arrays(&bytes, &three_bytes));
logger::info!("Validated 3 byte data");
}
3 => {
let bytes = flash_writer
.read(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
four_bytes.len(),
)
.unwrap();
assert!(compare_arrays(&bytes, &four_bytes));
logger::info!("Validated 4 byte data");
}
4 => {
let bytes = flash_writer
.read(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
eight_bytes.len(),
)
.unwrap();
assert!(compare_arrays(&bytes, &eight_bytes));
logger::info!("Validated 8 byte data");
}
5 => {
let bytes = flash_writer
.read(
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING,
sixteen_bytes.len(),
)
.unwrap();
assert!(compare_arrays(&bytes, &sixteen_bytes));
logger::info!("Validated 5 byte data");
}
_ => (),
}
for (i, data) in datasets.into_iter().enumerate() {
logger::info!("Stuff: {}", i);
let mut bytes = [0; 16];
flash_writer.read_exact(address(i), &mut bytes[..data.len()]);

assert_eq!(&bytes[..data.len()], *data);
logger::info!("Validated {} byte data", data.len());
}

logger::info!(
"Finished flash example at address {}",
"Finished flash example at address {:#X}",
FLASH_EXAMPLE_START_ADDRESS
);

Expand Down
Loading
Loading