alloc user to access kernel backed heap (psram, if equiped)

This commit is contained in:
2025-10-27 12:25:57 -06:00
parent b872e6be7c
commit f6d5fb98ab
11 changed files with 150 additions and 82 deletions

3
Cargo.lock generated
View File

@@ -20,8 +20,6 @@ dependencies = [
"embedded-graphics", "embedded-graphics",
"embedded-sdmmc", "embedded-sdmmc",
"rand_core 0.9.3", "rand_core 0.9.3",
"spin 0.10.0",
"talc",
] ]
[[package]] [[package]]
@@ -1039,6 +1037,7 @@ dependencies = [
[[package]] [[package]]
name = "embedded-snake" name = "embedded-snake"
version = "0.0.3" version = "0.0.3"
source = "git+https://github.com/LegitCamper/embedded-snake-rs#3986819c55819283dc5c1e89717d2a2f52e9b161"
dependencies = [ dependencies = [
"embedded-graphics", "embedded-graphics",
"rand_core 0.9.3", "rand_core 0.9.3",

View File

@@ -7,6 +7,4 @@ edition = "2024"
embedded-sdmmc = { version = "0.9.0", default-features = false } embedded-sdmmc = { version = "0.9.0", default-features = false }
embedded-graphics = "0.8.1" embedded-graphics = "0.8.1"
abi_sys = { path = "../abi_sys" } abi_sys = { path = "../abi_sys" }
talc = "4.4.3"
spin = "0.10.0"
rand_core = "0.9.3" rand_core = "0.9.3"

View File

@@ -1,18 +1,26 @@
#![no_std] #![no_std]
pub use abi_sys::keyboard;
use abi_sys::{RngRequest, keyboard::KeyEvent};
use rand_core::RngCore;
use talc::*;
extern crate alloc; extern crate alloc;
static mut ARENA: [u8; 10000] = [0; 10000]; pub use abi_sys::keyboard;
use abi_sys::{RngRequest, alloc, dealloc, keyboard::KeyEvent};
use core::alloc::{GlobalAlloc, Layout};
use rand_core::RngCore;
#[global_allocator] #[global_allocator]
static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> = static ALLOC: Alloc = Alloc;
Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) })
.lock(); struct Alloc;
unsafe impl GlobalAlloc for Alloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
alloc(layout.into())
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
dealloc(ptr, layout.into());
}
}
pub fn print(msg: &str) { pub fn print(msg: &str) {
abi_sys::print(msg.as_ptr(), msg.len()); abi_sys::print(msg.as_ptr(), msg.len());

View File

@@ -4,7 +4,8 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[features] [features]
default = [] default = ["alloc"]
alloc = []
defmt = ["dep:defmt"] defmt = ["dep:defmt"]
[dependencies] [dependencies]

View File

@@ -1,42 +1,89 @@
#![no_std] #![no_std]
#[cfg(feature = "alloc")]
use core::alloc::Layout;
use embedded_graphics::{ use embedded_graphics::{
Pixel, Pixel,
pixelcolor::{Rgb565, raw::RawU16}, pixelcolor::{Rgb565, raw::RawU16},
prelude::{IntoStorage, Point}, prelude::{IntoStorage, Point},
}; };
use embedded_sdmmc::DirEntry; use embedded_sdmmc::DirEntry;
use strum::EnumIter; use strum::{EnumCount, EnumIter};
pub const ABI_CALL_TABLE_COUNT: usize = 10;
#[derive(Clone, Copy, EnumIter)]
#[repr(u8)]
pub enum CallAbiTable {
PrintString = 0,
SleepMs = 1,
GetMs = 2,
LockDisplay = 3,
DrawIter = 4,
GetKey = 5,
GenRand = 6,
ListDir = 7,
ReadFile = 8,
FileLen = 9,
}
pub type EntryFn = fn(); pub type EntryFn = fn();
pub const ABI_CALL_TABLE_COUNT: usize = 12;
const _: () = assert!(ABI_CALL_TABLE_COUNT == CallTable::COUNT);
#[derive(Clone, Copy, EnumIter, EnumCount)]
#[repr(u8)]
pub enum CallTable {
Alloc = 0,
Dealloc = 1,
PrintString = 2,
SleepMs = 3,
GetMs = 4,
LockDisplay = 5,
DrawIter = 6,
GetKey = 7,
GenRand = 8,
ListDir = 9,
ReadFile = 10,
FileLen = 11,
}
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
#[unsafe(link_section = ".syscall_table")] #[unsafe(link_section = ".syscall_table")]
pub static mut CALL_ABI_TABLE: [usize; ABI_CALL_TABLE_COUNT] = [0; ABI_CALL_TABLE_COUNT]; pub static mut CALL_ABI_TABLE: [usize; ABI_CALL_TABLE_COUNT] = [0; ABI_CALL_TABLE_COUNT];
#[cfg(feature = "alloc")]
#[repr(C)]
pub struct CLayout {
size: usize,
alignment: usize,
}
#[cfg(feature = "alloc")]
impl Into<Layout> for CLayout {
fn into(self) -> Layout {
unsafe { Layout::from_size_align_unchecked(self.size, self.alignment) }
}
}
#[cfg(feature = "alloc")]
impl From<Layout> for CLayout {
fn from(value: Layout) -> Self {
Self {
size: value.size(),
alignment: value.align(),
}
}
}
pub type AllocAbi = extern "C" fn(layout: CLayout) -> *mut u8;
#[unsafe(no_mangle)]
pub extern "C" fn alloc(layout: CLayout) -> *mut u8 {
let f: AllocAbi = unsafe { core::mem::transmute(CALL_ABI_TABLE[CallTable::Alloc as usize]) };
f(layout)
}
pub type DeallocAbi = extern "C" fn(ptr: *mut u8, layout: CLayout);
#[unsafe(no_mangle)]
pub extern "C" fn dealloc(ptr: *mut u8, layout: CLayout) {
let f: DeallocAbi =
unsafe { core::mem::transmute(CALL_ABI_TABLE[CallTable::Dealloc as usize]) };
f(ptr, layout)
}
pub type PrintAbi = extern "C" fn(ptr: *const u8, len: usize); pub type PrintAbi = extern "C" fn(ptr: *const u8, len: usize);
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn print(ptr: *const u8, len: usize) { pub extern "C" fn print(ptr: *const u8, len: usize) {
let f: PrintAbi = let f: PrintAbi =
unsafe { core::mem::transmute(CALL_ABI_TABLE[CallAbiTable::PrintString as usize]) }; unsafe { core::mem::transmute(CALL_ABI_TABLE[CallTable::PrintString as usize]) };
f(ptr, len); f(ptr, len);
} }
@@ -45,7 +92,7 @@ pub type SleepMsAbi = extern "C" fn(ms: u64);
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn sleep(ms: u64) { pub extern "C" fn sleep(ms: u64) {
let f: SleepMsAbi = let f: SleepMsAbi =
unsafe { core::mem::transmute(CALL_ABI_TABLE[CallAbiTable::SleepMs as usize]) }; unsafe { core::mem::transmute(CALL_ABI_TABLE[CallTable::SleepMs as usize]) };
f(ms); f(ms);
} }
@@ -53,7 +100,7 @@ pub type GetMsAbi = extern "C" fn() -> u64;
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn get_ms() -> u64 { pub extern "C" fn get_ms() -> u64 {
let f: GetMsAbi = unsafe { core::mem::transmute(CALL_ABI_TABLE[CallAbiTable::GetMs as usize]) }; let f: GetMsAbi = unsafe { core::mem::transmute(CALL_ABI_TABLE[CallTable::GetMs as usize]) };
f() f()
} }
@@ -62,7 +109,7 @@ pub type LockDisplay = extern "C" fn(lock: bool);
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn lock_display(lock: bool) { pub extern "C" fn lock_display(lock: bool) {
let f: LockDisplay = let f: LockDisplay =
unsafe { core::mem::transmute(CALL_ABI_TABLE[CallAbiTable::LockDisplay as usize]) }; unsafe { core::mem::transmute(CALL_ABI_TABLE[CallTable::LockDisplay as usize]) };
f(lock); f(lock);
} }
@@ -105,12 +152,12 @@ pub type DrawIterAbi = extern "C" fn(ptr: *const CPixel, len: usize);
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn draw_iter(ptr: *const CPixel, len: usize) { pub extern "C" fn draw_iter(ptr: *const CPixel, len: usize) {
let f: DrawIterAbi = let f: DrawIterAbi =
unsafe { core::mem::transmute(CALL_ABI_TABLE[CallAbiTable::DrawIter as usize]) }; unsafe { core::mem::transmute(CALL_ABI_TABLE[CallTable::DrawIter as usize]) };
f(ptr, len); f(ptr, len);
} }
pub mod keyboard { pub mod keyboard {
use crate::{CALL_ABI_TABLE, CallAbiTable}; use crate::{CALL_ABI_TABLE, CallTable};
bitflags::bitflags! { bitflags::bitflags! {
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] #[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
@@ -333,7 +380,7 @@ pub mod keyboard {
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn get_key() -> KeyEventC { pub extern "C" fn get_key() -> KeyEventC {
let f: GetKeyAbi = let f: GetKeyAbi =
unsafe { core::mem::transmute(CALL_ABI_TABLE[CallAbiTable::GetKey as usize]) }; unsafe { core::mem::transmute(CALL_ABI_TABLE[CallTable::GetKey as usize]) };
f() f()
} }
} }
@@ -350,7 +397,7 @@ pub type GenRand = extern "C" fn(req: &mut RngRequest);
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn gen_rand(req: &mut RngRequest) { pub extern "C" fn gen_rand(req: &mut RngRequest) {
unsafe { unsafe {
let ptr = CALL_ABI_TABLE[CallAbiTable::GenRand as usize]; let ptr = CALL_ABI_TABLE[CallTable::GenRand as usize];
let f: GenRand = core::mem::transmute(ptr); let f: GenRand = core::mem::transmute(ptr);
f(req) f(req)
} }
@@ -371,7 +418,7 @@ pub extern "C" fn list_dir(
file_len: usize, file_len: usize,
) -> usize { ) -> usize {
unsafe { unsafe {
let ptr = CALL_ABI_TABLE[CallAbiTable::ListDir as usize]; let ptr = CALL_ABI_TABLE[CallTable::ListDir as usize];
let f: ListDir = core::mem::transmute(ptr); let f: ListDir = core::mem::transmute(ptr);
f(str, len, files, file_len) f(str, len, files, file_len)
} }
@@ -394,7 +441,7 @@ pub extern "C" fn read_file(
buf_len: usize, buf_len: usize,
) -> usize { ) -> usize {
unsafe { unsafe {
let ptr = CALL_ABI_TABLE[CallAbiTable::ReadFile as usize]; let ptr = CALL_ABI_TABLE[CallTable::ReadFile as usize];
let f: ReadFile = core::mem::transmute(ptr); let f: ReadFile = core::mem::transmute(ptr);
f(str, len, read_from, buf, buf_len) f(str, len, read_from, buf, buf_len)
} }
@@ -405,7 +452,7 @@ pub type FileLen = extern "C" fn(str: *const u8, len: usize) -> usize;
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn file_len(str: *const u8, len: usize) -> usize { pub extern "C" fn file_len(str: *const u8, len: usize) -> usize {
unsafe { unsafe {
let ptr = CALL_ABI_TABLE[CallAbiTable::FileLen as usize]; let ptr = CALL_ABI_TABLE[CallTable::FileLen as usize];
let f: FileLen = core::mem::transmute(ptr); let f: FileLen = core::mem::transmute(ptr);
f(str, len) f(str, len)
} }

View File

@@ -1,6 +1,6 @@
use abi_sys::{ use abi_sys::{
CPixel, DrawIterAbi, FileLen, GenRand, GetMsAbi, ListDir, LockDisplay, PrintAbi, ReadFile, AllocAbi, CLayout, CPixel, DeallocAbi, DrawIterAbi, FileLen, GenRand, GetMsAbi, ListDir,
RngRequest, SleepMsAbi, keyboard::*, LockDisplay, PrintAbi, ReadFile, RngRequest, SleepMsAbi, keyboard::*,
}; };
use alloc::{string::ToString, vec::Vec}; use alloc::{string::ToString, vec::Vec};
use core::sync::atomic::Ordering; use core::sync::atomic::Ordering;
@@ -15,6 +15,18 @@ use crate::{
storage::{Dir, File, SDCARD}, storage::{Dir, File, SDCARD},
}; };
const _: AllocAbi = alloc;
pub extern "C" fn alloc(layout: CLayout) -> *mut u8 {
// SAFETY: caller guarantees layout is valid
unsafe { alloc::alloc::alloc(layout.into()) }
}
const _: DeallocAbi = dealloc;
pub extern "C" fn dealloc(ptr: *mut u8, layout: CLayout) {
// SAFETY: caller guarantees ptr and layout are valid
unsafe { alloc::alloc::dealloc(ptr, layout.into()) };
}
const _: PrintAbi = print; const _: PrintAbi = print;
pub extern "C" fn print(ptr: *const u8, len: usize) { pub extern "C" fn print(ptr: *const u8, len: usize) {
// SAFETY: caller guarantees `ptr` is valid for `len` bytes // SAFETY: caller guarantees `ptr` is valid for `len` bytes

View File

@@ -21,7 +21,7 @@ type DISPLAY = ST7365P<
pub const SCREEN_WIDTH: usize = 320; pub const SCREEN_WIDTH: usize = 320;
pub const SCREEN_HEIGHT: usize = 320; pub const SCREEN_HEIGHT: usize = 320;
pub static mut FRAMEBUFFER: AtomicFrameBuffer = AtomicFrameBuffer::new(); pub static mut FRAMEBUFFER: AtomicFrameBuffer = AtomicFrameBuffer;
pub static FB_PAUSED: AtomicBool = AtomicBool::new(false); pub static FB_PAUSED: AtomicBool = AtomicBool::new(false);
pub async fn init_display( pub async fn init_display(

View File

@@ -2,7 +2,7 @@ use crate::{
abi, abi,
storage::{File, SDCARD}, storage::{File, SDCARD},
}; };
use abi_sys::CallAbiTable; use abi_sys::CallTable;
use abi_sys::EntryFn; use abi_sys::EntryFn;
use alloc::{vec, vec::Vec}; use alloc::{vec, vec::Vec};
use bumpalo::Bump; use bumpalo::Bump;
@@ -194,18 +194,20 @@ fn patch_abi(
unsafe { base.add((sym.st_value as usize) - min_vaddr as usize) } unsafe { base.add((sym.st_value as usize) - min_vaddr as usize) }
as *mut usize; as *mut usize;
for (idx, call) in CallAbiTable::iter().enumerate() { for (idx, call) in CallTable::iter().enumerate() {
let ptr = match call { let ptr = match call {
CallAbiTable::PrintString => abi::print as usize, CallTable::Alloc => abi::alloc as usize,
CallAbiTable::SleepMs => abi::sleep as usize, CallTable::Dealloc => abi::dealloc as usize,
CallAbiTable::GetMs => abi::get_ms as usize, CallTable::PrintString => abi::print as usize,
CallAbiTable::LockDisplay => abi::lock_display as usize, CallTable::SleepMs => abi::sleep as usize,
CallAbiTable::DrawIter => abi::draw_iter as usize, CallTable::GetMs => abi::get_ms as usize,
CallAbiTable::GetKey => abi::get_key as usize, CallTable::LockDisplay => abi::lock_display as usize,
CallAbiTable::GenRand => abi::gen_rand as usize, CallTable::DrawIter => abi::draw_iter as usize,
CallAbiTable::ListDir => abi::list_dir as usize, CallTable::GetKey => abi::get_key as usize,
CallAbiTable::ReadFile => abi::read_file as usize, CallTable::GenRand => abi::gen_rand as usize,
CallAbiTable::FileLen => abi::file_len as usize, CallTable::ListDir => abi::list_dir as usize,
CallTable::ReadFile => abi::read_file as usize,
CallTable::FileLen => abi::file_len as usize,
}; };
unsafe { unsafe {
table_base.add(idx as usize).write(ptr); table_base.add(idx as usize).write(ptr);

View File

@@ -241,19 +241,21 @@ async fn setup_display(display: Display, spawner: Spawner) {
spawner.spawn(display_handler(display)).unwrap(); spawner.spawn(display_handler(display)).unwrap();
} }
// psram is kind of useless on the pico calc
// ive opted to use the pimoroni with on onboard xip psram instead
async fn setup_psram(psram: Psram) { async fn setup_psram(psram: Psram) {
let psram = init_psram( // let psram = init_psram(
psram.pio, psram.sclk, psram.mosi, psram.miso, psram.cs, psram.dma1, psram.dma2, // psram.pio, psram.sclk, psram.mosi, psram.miso, psram.cs, psram.dma1, psram.dma2,
) // )
.await; // .await;
#[cfg(feature = "defmt")] // #[cfg(feature = "defmt")]
defmt::info!("psram size: {}", psram.size); // defmt::info!("psram size: {}", psram.size);
if psram.size == 0 { // if psram.size == 0 {
#[cfg(feature = "defmt")] // #[cfg(feature = "defmt")]
defmt::info!("\u{1b}[1mExternal PSRAM was NOT found!\u{1b}[0m"); // defmt::info!("\u{1b}[1mExternal PSRAM was NOT found!\u{1b}[0m");
} // }
#[cfg(feature = "pimoroni2w")] #[cfg(feature = "pimoroni2w")]
{ {
@@ -291,6 +293,7 @@ async fn kernel_task(
setup_mcu(mcu).await; setup_mcu(mcu).await;
Timer::after_millis(250).await; Timer::after_millis(250).await;
setup_display(display, spawner).await; setup_display(display, spawner).await;
#[cfg(feature = "pimoroni2w")]
setup_psram(psram).await; setup_psram(psram).await;
setup_sd(sd).await; setup_sd(sd).await;

View File

@@ -4,15 +4,15 @@
extern crate alloc; extern crate alloc;
use abi::{ use abi::{
display::{Display, lock_display}, display::{Display, lock_display},
fs::read_file, fs::{file_len, read_file},
get_key, get_ms, get_key, get_ms,
keyboard::{KeyCode, KeyState}, keyboard::{KeyCode, KeyState},
print, sleep, print, sleep,
}; };
use alloc::format; use alloc::{format, vec::Vec};
use core::panic::PanicInfo; use core::panic::PanicInfo;
use embedded_graphics::{image::ImageDrawable, pixelcolor::Rgb565}; use embedded_graphics::{image::ImageDrawable, pixelcolor::Rgb565};
use tinygif::{Gif, Header}; use tinygif::Gif;
#[panic_handler] #[panic_handler]
fn panic(info: &PanicInfo) -> ! { fn panic(info: &PanicInfo) -> ! {
@@ -33,19 +33,20 @@ pub fn main() {
print("Starting Gif app"); print("Starting Gif app");
let mut display = Display; let mut display = Display;
static mut BUF: [u8; 256] = [0_u8; 256]; let size = file_len("/gifs/bad_apple.gif");
let mut buf = Vec::with_capacity(size);
let read = read_file("/gifs/bad_apple.gif", 0, &mut buf);
assert!(read == size);
read_file("/gif/bad_apple.gif", 0, unsafe { &mut BUF[0..6] }); let gif = Gif::<Rgb565>::from_slice(&buf).unwrap();
let gif_header = Header::parse(unsafe { &BUF[0..6] });
let image = Gif::<Rgb565>::from_slice().unwrap();
loop { loop {
for frame in image.frames() { for frame in gif.frames() {
let start = get_ms(); let start = get_ms();
lock_display(true);
frame.draw(&mut display).unwrap(); frame.draw(&mut display).unwrap();
lock_display(false);
sleep(((frame.delay_centis as u64) * 10).saturating_sub(start)); sleep(((frame.delay_centis as u64) * 10).saturating_sub(start));
@@ -56,9 +57,6 @@ pub fn main() {
_ => (), _ => (),
}; };
}; };
lock_display(true);
lock_display(false);
} }
} }
} }

View File

@@ -6,5 +6,5 @@ edition = "2024"
[dependencies] [dependencies]
abi = { path = "../../abi" } abi = { path = "../../abi" }
embedded-graphics = "0.8.1" embedded-graphics = "0.8.1"
embedded-snake = { path = "../../../embedded-snake-rs" } embedded-snake = { git = "https://github.com/LegitCamper/embedded-snake-rs" }
rand = { version = "0.9.0", default-features = false } rand = { version = "0.9.0", default-features = false }