From 6132a2ea61c3573372e7b0f27741cfed150af7cd Mon Sep 17 00:00:00 2001 From: cscherr Date: Tue, 15 Jul 2025 12:57:30 +0200 Subject: [PATCH] feat: something resembling a benchmark runs --- .cargo/config.toml | 14 +++++ crates/algorithms/algorithms-c/project.yml | 5 +- crates/algorithms/build.rs | 3 +- crates/algorithms/src/ffi/mod.rs | 1 + src/bench.rs | 59 ++++++++++++++++++++ src/{hardfaults.rs => error_handlers.rs} | 8 +++ src/main.rs | 62 ++++++++++------------ 7 files changed, 116 insertions(+), 36 deletions(-) create mode 100644 src/bench.rs rename src/{hardfaults.rs => error_handlers.rs} (81%) diff --git a/.cargo/config.toml b/.cargo/config.toml index 74d2107..75ed16a 100755 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,20 @@ [target.thumbv6m-none-eabi] runner = 'probe-rs run --chip STM32L053R8' +[build] +target = "thumbv6m-none-eabi" + +[run] +target = "thumbv6m-none-eabi" + +[check] +target = "thumbv6m-none-eabi" + +[clippy] +target = "thumbv6m-none-eabi" + +[test] + [alias] arun = "run --target thumbv6m-none-eabi" abuild = "build --target thumbv6m-none-eabi" diff --git a/crates/algorithms/algorithms-c/project.yml b/crates/algorithms/algorithms-c/project.yml index b0dfb5f..48452d9 100755 --- a/crates/algorithms/algorithms-c/project.yml +++ b/crates/algorithms/algorithms-c/project.yml @@ -79,7 +79,9 @@ - -Wall # all warnings on - -O3 # optimizations - -g # debug info - - -march=armv6-m # NOTE: This is essential! It tells the compiler which target architecture to compile for!!! + # NOTE: This is essential! It tells the compiler which target architecture to compile for!!! + # Currently, this is set by build.rs with the CC_FLAGS env var + # - -march=armv6-m :unity: :defines: - UNITY_EXCLUDE_FLOAT @@ -98,6 +100,7 @@ - "${1}" - "-o" - "${2}" + - "#{ENV['CC_FLAGS']}" :optional: FALSE :release_linker: :executable: "#{ENV['AR']}" diff --git a/crates/algorithms/build.rs b/crates/algorithms/build.rs index b693c3c..53f2a08 100755 --- a/crates/algorithms/build.rs +++ b/crates/algorithms/build.rs @@ -38,7 +38,8 @@ fn main() { if arch == "arm" && pw == "32" && os == "none" { cmd.env("CC", "arm-none-eabi-gcc") - .env("AR", "arm-none-eabi-ar"); + .env("AR", "arm-none-eabi-ar") + .env("CC_FLAGS", "-march=armv6-m"); } else if os == "linux" || os == "windows" { cmd.env("CC", "gcc").env("AR", "ar"); } else { diff --git a/crates/algorithms/src/ffi/mod.rs b/crates/algorithms/src/ffi/mod.rs index 1adb782..091b773 100755 --- a/crates/algorithms/src/ffi/mod.rs +++ b/crates/algorithms/src/ffi/mod.rs @@ -4,6 +4,7 @@ unsafe extern "C" { fn algorithms_c_is_loaded() -> i32; } +#[inline] pub fn ffi_is_loaded() -> bool { unsafe { algorithms_c_is_loaded() == 1 } } diff --git a/src/bench.rs b/src/bench.rs new file mode 100644 index 0000000..8563486 --- /dev/null +++ b/src/bench.rs @@ -0,0 +1,59 @@ +use algorithms::crc::{Crc, Crc32 as Crc32Rust, ffi::Crc32 as Crc32C}; +use defmt::info; +use hal::{ + delay::Delay, + gpio::{Output, Pin, PushPull}, + prelude::*, +}; + +macro_rules! display_results { + ($name:expr, $rust_cycles:expr, $c_cycles:expr) => { + info!( + "Benchmark {}:\nRust\t: {}\nC\t: {}", + $name, $rust_cycles, $c_cycles + ) + }; +} + +#[inline] +pub fn bench(pin: &mut Pin>, delay: &mut Delay) { + bench_delay(pin, delay); + bench_crc(pin); +} + +fn bench_delay(pin: &mut Pin>, delay: &mut Delay) { + let short_sleep = time_exec(pin, || delay.delay_ms(20u32)); + let long_sleep = time_exec(pin, || delay.delay_ms(50u32)); + info!("Bench for short sleep: {}", short_sleep); + info!("Bench for long sleep: {}", long_sleep); +} + +fn time_exec(pin: &mut Pin>, mut f: F) -> u32 +where + F: FnMut(), +{ + info!("Running the requested function 50 times and returning the average"); + const RUNS: u32 = 50; + let start = cortex_m::peripheral::SYST::get_current(); + pin.set_high().expect("could not set pun to high"); + for _ in 0..RUNS { + f(); + } + pin.set_low().expect("could not set pun to low"); + let end = cortex_m::peripheral::SYST::get_current(); + // the timer counts down + info!("Start: {}\nEnd: {}", start, end); + let total = start.wrapping_sub(end); + total / RUNS +} + +fn bench_crc(pin: &mut Pin>) { + const DATA: &[u8] = b"hello world AAAAAAAAAAAAAAAAAAAAAAA"; + let native = time_exec(pin, || { + Crc32Rust::checksum(DATA); + }); + let c_code = time_exec(pin, || { + Crc32C::checksum(DATA); + }); + display_results!("CRC", native, c_code); +} diff --git a/src/hardfaults.rs b/src/error_handlers.rs similarity index 81% rename from src/hardfaults.rs rename to src/error_handlers.rs index a0a25b0..49f310e 100644 --- a/src/hardfaults.rs +++ b/src/error_handlers.rs @@ -1,4 +1,12 @@ use cortex_m_rt::ExceptionFrame; +use defmt::error; + +// same panicking *behavior* as `panic-probe` but doesn't print a panic message +// this prevents the panic message being printed *twice* when `defmt::panic` is invoked +#[defmt::panic_handler] +fn panic() -> ! { + cortex_m::asm::udf() +} fn display_ef(ef: &ExceptionFrame) { struct Wrapper(ExceptionFrame); diff --git a/src/main.rs b/src/main.rs index b67c543..a5d31a4 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,31 @@ -#![no_main] +// If rust-analyzer says it can't find `test`, you need to disable `allTargets` in it's +// configuration. This project has a `.cargo/config.toml` file that explicitly sets targets. +// See https://github.com/rust-lang/rust-analyzer/issues/3297 #![no_std] +#![no_main] -use algorithms::crc::{Crc, Crc32 as Crc32Rust, ffi::Crc32 as Crc32C}; +#[cfg(not(all(target_os = "none", target_pointer_width = "32", target_arch = "arm")))] +compile_error!("This program can only be compiled for arm32 bare metal"); -use defmt::{debug, error, info}; +use defmt::{debug, info}; use panic_probe as _; use defmt_rtt as _; // global logger -use cortex_m_rt::{ExceptionFrame, entry}; -use hal::{pac, prelude::*, rcc::Config}; +use cortex_m_rt::entry; +use hal::{ + pac::{self}, + prelude::*, + rcc::Config, +}; -mod hardfaults; // defines a handler for evil hardfaults - -// same panicking *behavior* as `panic-probe` but doesn't print a panic message -// this prevents the panic message being printed *twice* when `defmt::panic` is invoked -#[defmt::panic_handler] -fn panic() -> ! { - cortex_m::asm::udf() -} - -const DATA: &[u8] = b"hello world AAAAAAAAAAAAAAAAAAAAAAA"; +mod bench; +mod error_handlers; // defines a handler for evil hardfaults #[entry] fn main() -> ! { let dp = pac::Peripherals::take().unwrap(); - let cp = cortex_m::Peripherals::take().unwrap(); + let mut cp = cortex_m::Peripherals::take().unwrap(); // Configure the clock. let mut rcc = dp.RCC.freeze(Config::hsi16()); @@ -35,31 +35,25 @@ fn main() -> ! { let gpioa = dp.GPIOA.split(&mut rcc); // Configure PA5 as output. - let mut led = gpioa.pa5.into_push_pull_output(); + let mut led = gpioa.pa5.into_push_pull_output().downgrade(); + + // setup timer for measuring time of benchmarks + cp.SYST.set_reload(0x00ffffff); // max + cp.SYST.clear_current(); + cp.SYST.enable_counter(); - // Get the delay provider. let mut delay = cp.SYST.delay(rcc.clocks); debug!("Asserting that libalgorithms is correctly loaded"); assert!(algorithms::ffi_is_loaded(), "ffi is not loaded?"); - let mut crc; + info!("Starting the Rust vs C benchmark"); + bench::bench(&mut led, &mut delay); + info!("Benchmark done, nothing else to do"); - #[allow(clippy::never_loop)] + led.set_high().unwrap(); + #[allow(clippy::empty_loop)] loop { - led.set_high().unwrap(); - delay.delay_ms(500_u16); - - info!("Calculating CRCs for: {:?}", DATA); - - debug!("Now calculating Crc32Rust..."); - crc = Crc32Rust::checksum(DATA); - info!("CRC (Rust): {:08x}", crc); - debug!("Now calculating Crc32C..."); - crc = Crc32C::checksum(DATA); - info!("CRC (C) : {:08x}", crc); - - led.set_low().unwrap(); - delay.delay_ms(500_u16); + delay.delay_ms(200u32); } }