diff --git a/Cargo.toml b/Cargo.toml index 6f6b72f..6785a4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,11 +22,31 @@ name = "alloc-cortex-m" version = "0.4.0" [dependencies] +tlsf = { git = "https://github.com/FluenTech/tlsf" } cortex-m = "0.6.2" -[dependencies.linked_list_allocator] -default-features = false -version = "0.8.1" +[features] +FLI6 = ["tlsf/FLI6"] +FLI7 = ["tlsf/FLI7"] +FLI8 = ["tlsf/FLI8"] +FLI9 = ["tlsf/FLI9"] +FLI10 = ["tlsf/FLI10"] +FLI11 = ["tlsf/FLI11"] +FLI12 = ["tlsf/FLI12"] +FLI13 = ["tlsf/FLI13"] +FLI14 = ["tlsf/FLI14"] +FLI15 = ["tlsf/FLI15"] + +#[dependencies.linked_list_allocator] +#default-features = false +#version = "0.8.1" + [dev-dependencies] +nrf52832-hal = { version = "0.10.0", default-features = false, features = ["rt", "xxAA-package"] } +cortex-m = "0.6.2" +panic-halt = "0.2.0" +spin = "0.5.2" +jlink_rtt = "0.2.0" +panic_rtt = "0.3.0" cortex-m-rt = "0.6.12" diff --git a/examples/.cargo/config b/examples/.cargo/config new file mode 100644 index 0000000..11dd1b6 --- /dev/null +++ b/examples/.cargo/config @@ -0,0 +1,14 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +rustflags = [ + # LLD (shipped with the Rust toolchain) is used as the default linker + "-C", "link-arg=-Tlink.x", +] + +[build] +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + +[profile.release] +codegen-units = 1 # better optimizations +debug = true # symbols are nice and they don't increase the size on Flash +lto = true # better optimizations +opt-level = "z" diff --git a/examples/global_alloc.rs b/examples/global_alloc.rs index 0623f65..b5eb78c 100644 --- a/examples/global_alloc.rs +++ b/examples/global_alloc.rs @@ -4,21 +4,25 @@ extern crate alloc; -use core::panic::PanicInfo; -use core::alloc::Layout; use alloc::vec::Vec; use alloc_cortex_m::CortexMHeap; +use core::alloc::Layout; +use core::fmt::Write; +use core::panic::PanicInfo; use cortex_m_rt::entry; #[global_allocator] static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); +#[repr(align(4))] +struct Aligned(T); + #[entry] fn main() -> ! { - // Initialize the allocator BEFORE you use it - let start = cortex_m_rt::heap_start() as usize; - let size = 1024; // in bytes - unsafe { ALLOCATOR.init(start, size) } + static mut M: Aligned<[u8; tlsf::MAX_BLOCK_SIZE as usize]> = + Aligned([0; tlsf::MAX_BLOCK_SIZE as usize]); + + ALLOCATOR.extend(&mut M.0); let mut xs = Vec::new(); xs.push(1); diff --git a/examples/nrf52_dk.rs b/examples/nrf52_dk.rs new file mode 100644 index 0000000..56c2a6d --- /dev/null +++ b/examples/nrf52_dk.rs @@ -0,0 +1,48 @@ +#![no_main] +#![no_std] +#![feature(alloc_prelude)] +#![feature(alloc_error_handler)] + +extern crate alloc; +extern crate nrf52832_hal; +extern crate panic_rtt; + +use alloc::vec::Vec; +use alloc_cortex_m::CortexMHeap; +use core::alloc::Layout; +use core::fmt::Write; +use cortex_m_rt::entry; +use jlink_rtt::NonBlockingOutput; + +#[global_allocator] +static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); + +#[repr(align(4))] +struct Aligned(T); + +#[entry] +fn main() -> ! { + static mut M: Aligned<[u8; tlsf::MAX_BLOCK_SIZE as usize]> = + Aligned([0; tlsf::MAX_BLOCK_SIZE as usize]); + + let mut log = NonBlockingOutput::new(); + writeln!(log, "Output stream opened").ok().unwrap(); + + ALLOCATOR.extend(&mut M.0); + + writeln!(log, "Heap extended").ok().unwrap(); + + let mut xs = Vec::new(); + xs.push(1); + + writeln!(log, "Vector instantiated").ok().unwrap(); + + loop { + xs.push(1); + } +} + +#[alloc_error_handler] +fn oom(_: Layout) -> ! { + panic!("alloc error"); +} diff --git a/src/lib.rs b/src/lib.rs index 2eb99ba..2c32368 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,78 +5,94 @@ //! # Example //! //! For a usage example, see `examples/global_alloc.rs`. +//! +//! # Features +//! +//! The First Level Index (FLI) must be specified. The FLI determines the tlsf overhead required, +//! the largest object that can be stored on the heap, and the largest block that can be used to +//! _extend_ the memory pool. +//! +//! The FLI can be specified by setting one of the FLIx features in the range of FLI6..=FLI15. +//! +//! |FLI|`size_of(Tlsf)`|`MAX_REQUEST_SIZE`|`MAX_BLOCK_SIZE`| +//! |---|---------------|------------------|----------------| +//! |6 |36 |60 |60 | +//! |7 |70 |120 |124 | +//! |8 |104 |240 |252 | +//! |9 |138 |480 |508 | +//! |10 |172 |960 |1,020 | +//! |11 |206 |1,920 |2,044 | +//! |12 |240 |3,840 |4,092 | +//! |13 |274 |7,680 |8,188 | +//! |14 |308 |15,360 |16,380 | +//! |15 |342 |30,720 |32,764 | +//! +//! *All sizes are in bytes* #![no_std] -use core::cell::RefCell; use core::alloc::{GlobalAlloc, Layout}; +use core::cell::RefCell; use core::ptr::NonNull; use cortex_m::interrupt::Mutex; -use linked_list_allocator::Heap; +use tlsf::Tlsf as Heap; pub struct CortexMHeap { heap: Mutex>, } impl CortexMHeap { - /// Crate a new UNINITIALIZED heap allocator + /// Create a new heap allocator, with an empty memory pool /// - /// You must initialize this heap using the - /// [`init`](struct.CortexMHeap.html#method.init) method before using the allocator. + /// The allocator's memory pool must be extended using the + /// [`extend`](struct.CortexMHeap.html#method.extend) method before using the allocator. pub const fn empty() -> CortexMHeap { CortexMHeap { - heap: Mutex::new(RefCell::new(Heap::empty())), + heap: Mutex::new(RefCell::new(Heap::new())), } } - /// Initializes the heap + /// Adds a memory _chunk_ to the allocator's memory pool /// - /// This function must be called BEFORE you run any code that makes use of the + /// This function must be called at least once BEFORE any code makes use of the /// allocator. /// - /// `start_addr` is the address where the heap will be located. + /// # Examples /// - /// `size` is the size of the heap in bytes. + /// ```rust + /// use alloc_cortex_m::CortexMHeap; /// - /// Note that: + /// static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); /// - /// - The heap grows "upwards", towards larger addresses. Thus `end_addr` must - /// be larger than `start_addr` + /// static mut CHUNK: [u8; tlsf::MAX_BLOCK_SIZE as usize] = + /// [0; tlsf::MAX_BLOCK_SIZE as usize]; /// - /// - The size of the heap is `(end_addr as usize) - (start_addr as usize)`. The - /// allocator won't use the byte at `end_addr`. - /// - /// # Unsafety - /// - /// Obey these or Bad Stuff will happen. - /// - /// - This function must be called exactly ONCE. - /// - `size > 0` - pub unsafe fn init(&self, start_addr: usize, size: usize) { - cortex_m::interrupt::free(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .init(start_addr, size); + /// unsafe { ALLOCATOR.extend(&mut CHUNK) } + /// ``` + pub fn extend(&self, block: &'static mut [u8]) { + cortex_m::interrupt::free(move |cs| { + self.heap.borrow(cs).borrow_mut().extend(block); }); } } unsafe impl GlobalAlloc for CortexMHeap { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - cortex_m::interrupt::free(|cs| self.heap - .borrow(cs) - .borrow_mut() - .allocate_first_fit(layout) - .ok() - .map_or(0 as *mut u8, |allocation| allocation.as_ptr())) + cortex_m::interrupt::free(|cs| { + let returned_mem = self.heap.borrow(cs).borrow_mut().alloc(layout); + match returned_mem { + Ok(mem_ptr) => mem_ptr.as_ptr(), + Err(_) => 0_usize as *mut u8, + } + }) } - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - cortex_m::interrupt::free(|cs| self.heap - .borrow(cs) - .borrow_mut() - .deallocate(NonNull::new_unchecked(ptr), layout)); + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + if let Some(mem_ptr) = NonNull::new(ptr) { + cortex_m::interrupt::free(|cs| { + self.heap.borrow(cs).borrow_mut().dealloc(mem_ptr); + }); + } } }