From bbc9613f62e04c06b0992d503a32f6fa2c1d6514 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Tue, 28 Oct 2025 09:50:15 -0600 Subject: [PATCH] cleanup --- kernel/Cargo.toml | 4 +-- kernel/src/display.rs | 23 ++++-------- kernel/src/framebuffer.rs | 70 +++++++++++++----------------------- kernel/src/heap.rs | 76 +++++++++++---------------------------- kernel/src/main.rs | 2 ++ kernel/src/ui.rs | 8 ++--- 6 files changed, 58 insertions(+), 125 deletions(-) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index b2f237a..607c8f6 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -11,7 +11,7 @@ doctest = false bench = false [features] -default = ["rp235x", "defmt"] +default = ["rp235x", "defmt", "pimoroni2w"] pimoroni2w = ["rp235x"] rp2040 = ["embassy-rp/rp2040"] rp235x = ["embassy-rp/rp235xb"] @@ -90,7 +90,7 @@ spin = "0.10.0" num_enum = { version = "0.7.4", default-features = false } goblin = { version = "0.10.1", default-features = false, features = ["elf32"] } talc = "4.4.3" -embedded-alloc = "0.6.0" +embedded-alloc = { version = "0.6.0", features = ["allocator_api"] } bumpalo = "3.19.0" abi_sys = { path = "../abi_sys" } diff --git a/kernel/src/display.rs b/kernel/src/display.rs index a30c2ba..dd0d147 100644 --- a/kernel/src/display.rs +++ b/kernel/src/display.rs @@ -9,6 +9,7 @@ use embassy_rp::{ }; use embassy_time::{Delay, Timer}; use embedded_hal_bus::spi::ExclusiveDevice; +use once_cell::unsync::Lazy; use st7365p_lcd::ST7365P; type DISPLAY = ST7365P< @@ -21,7 +22,10 @@ type DISPLAY = ST7365P< pub const SCREEN_WIDTH: usize = 320; pub const SCREEN_HEIGHT: usize = 320; -pub static mut FRAMEBUFFER: AtomicFrameBuffer = AtomicFrameBuffer; +pub static mut FRAMEBUFFER: Lazy = Lazy::new(|| { + static mut BUF: [u16; framebuffer::SIZE] = [0; framebuffer::SIZE]; + AtomicFrameBuffer::new(unsafe { &mut BUF }) +}); pub static FB_PAUSED: AtomicBool = AtomicBool::new(false); pub async fn init_display( @@ -44,10 +48,6 @@ pub async fn init_display( unsafe { FRAMEBUFFER.draw(&mut display).await.unwrap() } display.set_on().await.unwrap(); - // create double buffer if board has psram - #[cfg(feature = "pimoroni2w")] - framebuffer::init_double_buffer(); - display } @@ -55,18 +55,7 @@ pub async fn init_display( pub async fn display_handler(mut display: DISPLAY) { loop { if !FB_PAUSED.load(Ordering::Acquire) { - unsafe { - FRAMEBUFFER - .partial_draw_batched(&mut display) - .await - .unwrap() - } - } - - // Only do swap if feature enabled - #[cfg(feature = "pimoroni2w")] - { - framebuffer::swap_buffers(); + unsafe { FRAMEBUFFER.partial_draw(&mut display).await.unwrap() } } // small yield to allow other tasks to run diff --git a/kernel/src/framebuffer.rs b/kernel/src/framebuffer.rs index 63f9ce8..f450691 100644 --- a/kernel/src/framebuffer.rs +++ b/kernel/src/framebuffer.rs @@ -12,7 +12,6 @@ use embedded_graphics::{ }; use embedded_hal_2::digital::OutputPin; use embedded_hal_async::{delay::DelayNs, spi::SpiDevice}; -use heapless::Vec; use st7365p_lcd::ST7365P; pub const TILE_SIZE: usize = 16; // 16x16 tile @@ -22,30 +21,10 @@ pub const TILE_COUNT: usize = (SCREEN_WIDTH / TILE_SIZE) * (SCREEN_HEIGHT / TILE pub const MAX_META_TILES: usize = SCREEN_WIDTH / TILE_SIZE; // max number of meta tiles in buffer type MetaTileVec = heapless::Vec; -const SIZE: usize = SCREEN_HEIGHT * SCREEN_WIDTH; - -static mut BUFFER: [u16; SIZE] = [0; SIZE]; - -#[cfg(feature = "pimoroni2w")] -static mut DOUBLE_BUFFER: Option> = None; - -#[cfg(feature = "pimoroni2w")] -pub fn init_double_buffer() { - unsafe { DOUBLE_BUFFER = Some(alloc::vec![0_u16; SIZE]) }; -} - -#[cfg(feature = "pimoroni2w")] -pub fn swap_buffers() { - unsafe { - core::mem::swap( - &mut BUFFER[..].as_mut_ptr(), - &mut DOUBLE_BUFFER.as_mut().unwrap().as_mut_slice().as_mut_ptr(), - ); - } -} +pub const SIZE: usize = SCREEN_HEIGHT * SCREEN_WIDTH; static mut DIRTY_TILES: LazyLock> = LazyLock::new(|| { - let mut tiles = Vec::new(); + let mut tiles = heapless::Vec::new(); for _ in 0..TILE_COUNT { tiles.push(AtomicBool::new(true)).unwrap(); } @@ -53,9 +32,14 @@ static mut DIRTY_TILES: LazyLock> = LazyLo }); #[allow(dead_code)] -pub struct AtomicFrameBuffer; +pub struct AtomicFrameBuffer<'a>(&'a mut [u16]); + +impl<'a> AtomicFrameBuffer<'a> { + pub fn new(buffer: &'a mut [u16]) -> Self { + assert!(buffer.len() == SIZE); + Self(buffer) + } -impl AtomicFrameBuffer { fn mark_tiles_dirty(&mut self, rect: Rectangle) { let tiles_x = (SCREEN_WIDTH + TILE_SIZE - 1) / TILE_SIZE; let start_tx = (rect.top_left.x as usize) / TILE_SIZE; @@ -71,7 +55,7 @@ impl AtomicFrameBuffer { } } - fn set_pixels_buffered>( + fn set_pixels>( &mut self, sx: u16, sy: u16, @@ -92,7 +76,7 @@ impl AtomicFrameBuffer { for y in sy..=ey { for x in sx..=ex { if let Some(color) = color_iter.next() { - unsafe { BUFFER[(y as usize * SCREEN_WIDTH) + x as usize] = color }; + self.0[(y as usize * SCREEN_WIDTH) + x as usize] = color; } else { return Err(()); // Not enough data } @@ -179,7 +163,7 @@ impl AtomicFrameBuffer { 0, self.size().width as u16 - 1, self.size().height as u16 - 1, - unsafe { &BUFFER }, + &self.0[..], ) .await?; @@ -193,7 +177,7 @@ impl AtomicFrameBuffer { } /// Sends only dirty tiles (16x16px) in batches to the display - pub async fn partial_draw_batched( + pub async fn partial_draw( &mut self, display: &mut ST7365P, ) -> Result<(), ()> @@ -211,7 +195,7 @@ impl AtomicFrameBuffer { // buffer for copying meta tiles before sending to display let mut pixel_buffer: heapless::Vec = - Vec::new(); + heapless::Vec::new(); for rect in meta_tiles { let rect_width = rect.size.width as usize; @@ -227,9 +211,7 @@ impl AtomicFrameBuffer { let end = start + rect_width; // Safe: we guarantee buffer will not exceed MAX_META_TILE_PIXELS - pixel_buffer - .extend_from_slice(unsafe { &BUFFER[start..end] }) - .unwrap(); + pixel_buffer.extend_from_slice(&self.0[start..end]).unwrap(); } display @@ -261,7 +243,7 @@ impl AtomicFrameBuffer { } } -impl DrawTarget for AtomicFrameBuffer { +impl<'a> DrawTarget for AtomicFrameBuffer<'a> { type Error = (); type Color = Rgb565; @@ -280,11 +262,9 @@ impl DrawTarget for AtomicFrameBuffer { if (x as usize) < SCREEN_WIDTH && (y as usize) < SCREEN_HEIGHT { let idx = (y as usize) * SCREEN_WIDTH + (x as usize); let raw_color = RawU16::from(color).into_inner(); - unsafe { - if BUFFER[idx] != raw_color { - BUFFER[idx] = raw_color; - changed = true; - } + if self.0[idx] != raw_color { + self.0[idx] = raw_color; + changed = true; } if let Some(ref mut rect) = dirty_rect { @@ -332,11 +312,9 @@ impl DrawTarget for AtomicFrameBuffer { if let Some(color) = colors.next() { let idx = (p.y as usize * SCREEN_WIDTH) + (p.x as usize); let raw_color = RawU16::from(color).into_inner(); - unsafe { - if BUFFER[idx] != raw_color { - BUFFER[idx] = raw_color; - changed = true; - } + if self.0[idx] != raw_color { + self.0[idx] = raw_color; + changed = true; } } else { break; @@ -364,7 +342,7 @@ impl DrawTarget for AtomicFrameBuffer { } fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> { - self.set_pixels_buffered( + self.set_pixels( 0, 0, self.size().width as u16 - 1, @@ -381,7 +359,7 @@ impl DrawTarget for AtomicFrameBuffer { } } -impl OriginDimensions for AtomicFrameBuffer { +impl<'a> OriginDimensions for AtomicFrameBuffer<'a> { fn size(&self) -> Size { Size::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32) } diff --git a/kernel/src/heap.rs b/kernel/src/heap.rs index 638e787..2b1de30 100644 --- a/kernel/src/heap.rs +++ b/kernel/src/heap.rs @@ -2,13 +2,10 @@ // https://github.com/wezterm/picocalc-wezterm/blob/main/src/heap.rs use core::alloc::{GlobalAlloc, Layout}; -use core::mem::MaybeUninit; use core::sync::atomic::{AtomicUsize, Ordering}; use embedded_alloc::LlffHeap as Heap; -pub static HEAP: DualHeap = DualHeap::empty(); -const HEAP_SIZE: usize = 64 * 1024; -static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; +pub static mut HEAP: PsramHeap = PsramHeap::empty(); struct Region { start: AtomicUsize, @@ -37,95 +34,62 @@ impl Region { } } -/// This is an allocator that combines two regions of memory. -/// The intent is to use some of the directly connected RAM -/// for this, and if we find some XIP capable PSRAM, add that -/// as a secondary region. -/// Allocation from the primary region is always preferred, -/// as it is expected to be a bit faster than PSRAM. /// FIXME: PSRAM-allocated memory isn't compatible with /// CAS atomics, so we might need a bit of a think about this! -pub struct DualHeap { - primary: Heap, - primary_region: Region, - secondary: Heap, +pub struct PsramHeap { + heap: Heap, + region: Region, } -impl DualHeap { +impl PsramHeap { pub const fn empty() -> Self { Self { - primary: Heap::empty(), - primary_region: Region::default(), - secondary: Heap::empty(), + heap: Heap::empty(), + region: Region::default(), } } - unsafe fn add_primary(&self, region: Region) { + unsafe fn add_psram(&self, region: Region) { let start = region.start.load(Ordering::SeqCst); let size = region.size.load(Ordering::SeqCst); unsafe { - self.primary.init(start, size); - } - self.primary_region.start.store(start, Ordering::SeqCst); - self.primary_region.size.store(size, Ordering::SeqCst); - } - - unsafe fn add_secondary(&self, region: Region) { - let start = region.start.load(Ordering::SeqCst); - let size = region.size.load(Ordering::SeqCst); - unsafe { - self.secondary.init(start, size); + self.heap.init(start, size); } + self.region.start.store(start, Ordering::SeqCst); + self.region.size.store(size, Ordering::SeqCst); } pub fn used(&self) -> usize { - self.primary.used() + self.secondary.used() + self.heap.used() } pub fn free(&self) -> usize { - self.primary.free() + self.secondary.free() + self.heap.free() } } -unsafe impl GlobalAlloc for DualHeap { +unsafe impl GlobalAlloc for PsramHeap { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { unsafe { - let ptr = self.primary.alloc(layout); + let ptr = self.heap.alloc(layout); if !ptr.is_null() { return ptr; + } else { + panic!("HEAP FULL"); } - // start using secondary area when primary heap is full - self.secondary.alloc(layout) } } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { unsafe { let ptr_usize = ptr as usize; - if self.primary_region.contains(ptr_usize) { - self.primary.dealloc(ptr, layout); - } else { - self.secondary.dealloc(ptr, layout); + if self.region.contains(ptr_usize) { + self.heap.dealloc(ptr, layout); } } } } -pub fn init_heap() { - let primary_start = &raw mut HEAP_MEM as usize; - unsafe { HEAP.add_primary(Region::new(primary_start, HEAP_SIZE)) } -} - pub fn init_qmi_psram_heap(size: u32) { - unsafe { HEAP.add_secondary(Region::new(0x11000000, size as usize)) } -} - -pub async fn free_command(_args: &[&str]) { - let ram_used = HEAP.primary.used(); - let ram_free = HEAP.primary.free(); - let ram_total = ram_used + ram_free; - - let qmi_used = HEAP.secondary.used(); - let qmi_free = HEAP.secondary.free(); - let qmi_total = qmi_used + qmi_free; + unsafe { HEAP.add_psram(Region::new(0x11000000, size as usize)) } } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 1a15960..6d33dc3 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -3,6 +3,8 @@ #![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_main)] #![allow(static_mut_refs)] +#![feature(allocator_api)] +#![feature(slice_ptr_get)] extern crate alloc; diff --git a/kernel/src/ui.rs b/kernel/src/ui.rs index 1c91c40..ceeb7f9 100644 --- a/kernel/src/ui.rs +++ b/kernel/src/ui.rs @@ -71,7 +71,7 @@ pub async fn clear_selection() { if let Some(area) = sel.last_bounds { Rectangle::new(area.top_left, area.size) .into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK)) - .draw(unsafe { &mut FRAMEBUFFER }) + .draw(unsafe { &mut *FRAMEBUFFER }) .unwrap(); } } @@ -97,7 +97,7 @@ async fn draw_selection() { ), text_style, ) - .draw(unsafe { &mut FRAMEBUFFER }) + .draw(unsafe { &mut *FRAMEBUFFER }) .unwrap(); } else { let mut views: alloc::vec::Vec>> = Vec::new(); @@ -122,12 +122,12 @@ async fn draw_selection() { .bounding_box(); Rectangle::new(selected_bounds.top_left, selected_bounds.size) .into_styled(PrimitiveStyle::with_stroke(Rgb565::WHITE, 1)) - .draw(unsafe { &mut FRAMEBUFFER }) + .draw(unsafe { &mut *FRAMEBUFFER }) .unwrap(); guard.last_bounds = Some(layout.bounds()); - layout.draw(unsafe { &mut FRAMEBUFFER }).unwrap(); + layout.draw(unsafe { &mut *FRAMEBUFFER }).unwrap(); } guard.changed = false;