1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
//! A heap allocator for microcontollers with MIPS core such as PIC32 controllers
//!
//! The heap is placed at a location determined by the linker and automatically extended
//! to fullfil allocation requests. Automatic heap extension fails if the heap would collide
//! with the stack.
//!
//! # Example
//!
//! ```
//! #![feature(global_allocator)]
//! #![feature(alloc_error_handler)]
//!
//! // Plug in the allocator crate
//! extern crate alloc;
//!
//! use alloc::Vec;
//! use mips_mcu_alloc::MipsMcuHeap;
//!
//! #[global_allocator]
//! static ALLOCATOR: MipsMcuHeap = MipsMcuHeap::empty();
//!
//! entry!(main);
//!
//! #[entry]
//! fn main() -> ! {
//! ALLOCATOR.init();
//!
//! let mut xs = Vec::new();
//! xs.push(1);
//!
//! loop { /* .. */ }
//! }
//!
//! #[alloc_error_handler]
//! fn alloc_error(layout: core::alloc::Layout) -> ! {
//! panic!("Cannot allocate heap memory: {:?}", layout);
//! }
//!
//! ```
#![no_std]
#![feature(asm_experimental_arch)]
use core::alloc::{GlobalAlloc, Layout};
use core::arch::asm;
use core::cell::RefCell;
use core::ptr::{self, NonNull};
use critical_section::{self, Mutex};
use linked_list_allocator::Heap;
use mips_rt::heap_start;
/// Heap extension is performed stepwise. This constant defines the size of one extension step.
const EXTEND_INCREMENT: usize = 1024;
pub struct MipsMcuHeap {
heap: Mutex<RefCell<Heap>>,
}
impl MipsMcuHeap {
/// Crate a new UNINITIALIZED heap allocator
///
/// You must initialize this heap using the
/// [`init`](struct.CortexMHeap.html#method.init) method before using the allocator.
pub const fn empty() -> MipsMcuHeap {
MipsMcuHeap {
heap: Mutex::new(RefCell::new(Heap::empty())),
}
}
/// Initialize heap with heap start location from linker and a defined initial size
pub fn init(&self) {
let bottom = heap_start() as *mut u8;
critical_section::with(|cs| {
unsafe {
self.heap
.borrow(cs)
.borrow_mut()
.init(bottom, EXTEND_INCREMENT)
};
});
}
/// Returns an estimate of the amount of bytes in use.
pub fn used(&self) -> usize {
critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().used())
}
/// Returns an estimate of the amount of bytes available.
pub fn free(&self) -> usize {
critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free())
}
/// Returns the start (bottom) of the heap
pub fn bottom(&self) -> *mut u8 {
critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().bottom())
}
/// Returns the end (top) of the heap
pub fn top(&self) -> *mut u8 {
critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().top())
}
}
unsafe impl GlobalAlloc for MipsMcuHeap {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// try to allocate and successively extend by EXTEND_INCREMENT until memory is exhausted
loop {
if let Ok(p) = critical_section::with(|cs| {
self.heap.borrow(cs).borrow_mut().allocate_first_fit(layout)
}) {
break p.as_ptr();
} else {
// this must be a u8 pointer
let new_top: *mut u8 =
critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().top())
.add(EXTEND_INCREMENT);
// avoid collision with stack
if new_top < stack_pointer() {
critical_section::with(|cs| {
self.heap.borrow(cs).borrow_mut().extend(EXTEND_INCREMENT)
});
} else {
break ptr::null_mut();
}
}
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
critical_section::with(|cs| {
self.heap
.borrow(cs)
.borrow_mut()
.deallocate(NonNull::new_unchecked(ptr), layout)
});
}
}
fn stack_pointer() -> *mut u8 {
let sp: *mut u8;
unsafe {
asm!(".set noat",
"move {0}, $29", out(reg) sp);
}
sp
}