diff --git a/firmware/acid-firmware/src/ffi/alloc.rs b/firmware/acid-firmware/src/ffi/alloc.rs index 17e4adb..fcb6531 100644 --- a/firmware/acid-firmware/src/ffi/alloc.rs +++ b/firmware/acid-firmware/src/ffi/alloc.rs @@ -8,7 +8,7 @@ use enumset::EnumSet; use crate::ffi::string::__xkbc_memcpy; // Here we select the allocator to use for libxkbcommon. -pub use crate::PSRAM_ALLOCATOR as XKBC_ALLOCATOR; +pub use crate::ram::PSRAM_ALLOCATOR as XKBC_ALLOCATOR; // Implementation based on esp-alloc's `compat` feature. diff --git a/firmware/acid-firmware/src/main.rs b/firmware/acid-firmware/src/main.rs index 595251e..408f9f7 100644 --- a/firmware/acid-firmware/src/main.rs +++ b/firmware/acid-firmware/src/main.rs @@ -30,12 +30,10 @@ use embassy_sync::blocking_mutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_time::{Duration, Timer}; -use esp_alloc::{HeapRegion, MemoryCapability}; +use esp_alloc::MemoryCapability; use esp_hal::clock::CpuClock; use esp_hal::dma::{BurstConfig, ExternalBurstConfig, InternalBurstConfig}; use esp_hal::efuse::Efuse; -#[cfg(not(feature = "alt-log"))] -use esp_hal::gpio::NoPin; use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull}; use esp_hal::i2c::master::{I2c, I2cAddress}; use esp_hal::interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl}; @@ -44,7 +42,6 @@ use esp_hal::lcd_cam::lcd::dpi::Dpi; use esp_hal::ledc::{self, LSGlobalClkSource, Ledc, LowSpeed}; use esp_hal::peripherals::{DMA_CH0, SPI2}; use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock}; -use esp_hal::ram; use esp_hal::rng::TrngSource; use esp_hal::sha::ShaBackend; use esp_hal::spi::master::AnySpi; @@ -78,6 +75,7 @@ use {esp_alloc as _, esp_backtrace as _}; use crate::matrix::IoeMatrix; use crate::peripherals::st7701s::{St7701s, St7701sController}; use crate::proxy::create_hid_report_interceptor; +use crate::ram::{PSRAM_ALLOCATOR, STACK_SIZE_CORE_APP}; use crate::ui::backend::SlintBackend; use crate::ui::dpi::Framebuffer; use crate::vial::{ @@ -96,6 +94,7 @@ mod logging; mod matrix; mod peripherals; mod proxy; +mod ram; mod ui; mod util; mod vial; @@ -107,20 +106,9 @@ mod console; // For more information see: esp_bootloader_esp_idf::esp_app_desc!(); -// Memory allocation regions. -// These can be debugged using `xtensa-esp32s3-elf-size -A `. -// A panic such as `memory allocation of 3740121773 bytes failed` is caused by a heap overflow. The size is `DEEDBAAD` in hex. - -/// Total heap size -const HEAP_SIZE: usize = 112 * 1024; -/// Size of the app core's stack -const STACK_SIZE_CORE_APP: usize = 80 * 1024; - // const FRAME_DURATION_MIN: Duration = Duration::from_millis(40); // 25 FPS const FRAME_DURATION_MIN: Duration = Duration::from_millis(100); // 10 FPS -pub static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); - static KEYBOARD_REPORT_PROXY: Channel = Channel::new(); static LCD_ENABLED: AtomicBool = AtomicBool::new(false); @@ -130,114 +118,6 @@ static LCD_ENABLED: AtomicBool = AtomicBool::new(false); // /// Used to signal that the MCU is ready to render the GUI. // static SIGNAL_UI_RENDER: Signal = Signal::new(); -#[embassy_executor::task] -async fn test_bounce_buffers_task( - channel: DMA_CH0<'static>, - peripheral: SPI2<'static>, - st7701s: St7701s<'static, Blocking>, -) { - test_bounce_buffers(channel, peripheral, st7701s).await; -} - -#[allow(unused)] -async fn test_bounce_buffers( - channel: DMA_CH0<'static>, - peripheral: SPI2<'static>, - mut st7701s: St7701s<'static, Blocking>, -) { - error!("TEST BOUNCE BUFFERS SECTION ENTERED"); - - const BYTES_PER_PIXEL: usize = core::mem::size_of::(); - // Assume highest burst config setting. - const EXTERNAL_BURST_CONFIG: ExternalBurstConfig = ExternalBurstConfig::Size32; - const ALIGNMENT_PIXELS: usize = EXTERNAL_BURST_CONFIG as usize / BYTES_PER_PIXEL; - // The total number of pixels demanded by the DPI, per row. - const WIDTH_TOTAL_PIXELS: usize = 368; - // The total number of rows demanded by the DPI, per frame. - const HEIGHT_PIXELS: usize = 960; - // The number of unused pixels at the start of the row. - const FRONT_PORCH_ACTUAL_PIXELS: usize = 120; - // The number of actually visible pixels, per row. - const WIDTH_VISIBLE_PIXELS: usize = 240; - // The number of pixels not stored in a bounce buffer, per row. - // This many arbitrary pixels are sent to the DPI. - const FRONT_PORCH_SKIPPED_PIXELS: usize = - (FRONT_PORCH_ACTUAL_PIXELS / ALIGNMENT_PIXELS) * ALIGNMENT_PIXELS; - const WIDTH_STORED_PIXELS: usize = WIDTH_TOTAL_PIXELS - FRONT_PORCH_SKIPPED_PIXELS; - const VISIBLE_OFFSET_IN_BUFFER_PIXELS: usize = - FRONT_PORCH_ACTUAL_PIXELS - FRONT_PORCH_SKIPPED_PIXELS; - const ROWS_PER_WINDOW: usize = 16; - let burst_config = BurstConfig { - internal_memory: InternalBurstConfig::Enabled, - external_memory: EXTERNAL_BURST_CONFIG, - }; - let (swapchain_reader, mut swapchain_writer) = Swapchain { - framebuffers: [ - Box::leak(allocate_dma_buffer_in( - HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL, - burst_config, - &PSRAM_ALLOCATOR, - )), - Box::leak(allocate_dma_buffer_in( - HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL, - burst_config, - &PSRAM_ALLOCATOR, - )), - ], - } - .into_reader_writer(); - - { - let write_guard = &mut swapchain_writer.write(); - let buffer_src = bytemuck::cast_slice_mut::(write_guard); - let colors = (0..WIDTH_VISIBLE_PIXELS as u8 / 2) - .rev() - .map(|val| Rgb565Pixel::from_rgb(0xFF, val * 2, 0)) - .collect::>(); - for (index, pixel) in buffer_src.iter_mut().enumerate() { - let mut x = - (index % WIDTH_STORED_PIXELS) as i16 - VISIBLE_OFFSET_IN_BUFFER_PIXELS as i16; - let mut y = (index / WIDTH_STORED_PIXELS) as i16; - - if x < WIDTH_VISIBLE_PIXELS as i16 { - x = core::cmp::min(x, WIDTH_VISIBLE_PIXELS as i16 - 1 - x); - y = core::cmp::min(y, HEIGHT_PIXELS as i16 - 1 - y); - let min = core::cmp::min(x, y); - - *pixel = colors[min as usize % colors.len()]; - continue; - } - - *pixel = Rgb565Pixel::default(); - } - } - - let mut dma_bounce = DmaBounce::new( - Global, - channel, - AnySpi::from(peripheral), - st7701s.dpi, - swapchain_reader, - FRONT_PORCH_SKIPPED_PIXELS * BYTES_PER_PIXEL, - WIDTH_STORED_PIXELS * BYTES_PER_PIXEL, - ROWS_PER_WINDOW, - burst_config, - true, - ); - let mut bb_controller = DmaBounceController::new(dma_bounce); - - error!("TEST BOUNCE BUFFERS TASK LAUNCHED"); - - loop { - bb_controller.start().await.unwrap(); - st7701s.controller.sleep_off().await; - Timer::after_secs(1).await; - st7701s.controller.sleep_on().await; - bb_controller.stop().await.unwrap(); - Timer::after_secs(1).await; - } -} - #[esp_rtos::main] async fn main(_spawner: Spawner) { let config = esp_hal::Config::default() @@ -255,7 +135,7 @@ async fn main(_spawner: Spawner) { #[cfg(feature = "alt-log")] let (tx, rx) = (peripherals.GPIO12, peripherals.GPIO5); #[cfg(not(feature = "alt-log"))] - let (tx, rx) = (NoPin, NoPin); + let (tx, rx) = (esp_hal::gpio::NoPin, esp_hal::gpio::NoPin); Uart::new(peripherals.UART2, Default::default()) .unwrap() @@ -271,32 +151,8 @@ async fn main(_spawner: Spawner) { #[cfg(feature = "rtt-log")] logging::rtt::setup_logging(); - // Use the internal DRAM as the heap. - // Memory reclaimed from the esp-idf bootloader. - const HEAP_SIZE_RECLAIMED: usize = const { - let range = esp_metadata_generated::memory_range!("DRAM2_UNINIT"); - range.end - range.start - }; - - esp_alloc::heap_allocator!(#[ram(reclaimed)] size: HEAP_SIZE_RECLAIMED); - esp_alloc::heap_allocator!(size: HEAP_SIZE - HEAP_SIZE_RECLAIMED); - info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats()); - - // Initialize the PSRAM allocator. - { - let (psram_offset, psram_size) = esp_hal::psram::psram_raw_parts(&peripherals.PSRAM); - unsafe { - PSRAM_ALLOCATOR.add_region(HeapRegion::new( - psram_offset, - psram_size, - MemoryCapability::External.into(), - )); - } - info!( - "PSRAM allocator initialized with capacity of {} MiB!", - psram_size / 1024 / 1024 - ); - } + // Set up allocators. + ram::initialize(peripherals.PSRAM); // let mut io = Io::new(peripherals.IO_MUX); // io.set_interrupt_handler(interrupt_handler); @@ -657,7 +513,7 @@ async fn main_task(peripherals: MainPeripherals) { let window_size = [framebuffer.height, framebuffer.width]; let swapchain_writer = framebuffer.swapchain.take().unwrap(); - static SECOND_CORE_STACK: StaticCell> = StaticCell::new(); + static SECOND_CORE_STACK: StaticCell> = StaticCell::new(); let second_core_stack = SECOND_CORE_STACK.init(Stack::new()); esp_rtos::start_second_core( peripherals.CPU_CTRL, diff --git a/firmware/acid-firmware/src/ram.rs b/firmware/acid-firmware/src/ram.rs new file mode 100644 index 0000000..6e41724 --- /dev/null +++ b/firmware/acid-firmware/src/ram.rs @@ -0,0 +1,44 @@ +use esp_alloc::{HeapRegion, MemoryCapability}; +use esp_hal::ram; +use log::info; + +// Memory allocation regions. +// These can be debugged using `xtensa-esp32s3-elf-size -A `. +// A panic such as `memory allocation of 3740121773 bytes failed` is caused by a heap overflow. The size is `DEEDBAAD` in hex. + +/// Total heap size +pub const HEAP_SIZE: usize = 112 * 1024; +/// Size of the app core's stack +pub const STACK_SIZE_CORE_APP: usize = 80 * 1024; + +pub static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); + +pub fn initialize(psram_peripheral: esp_hal::peripherals::PSRAM) { + // Use the internal DRAM as the heap. + // Memory reclaimed from the esp-idf bootloader. + const HEAP_SIZE_RECLAIMED: usize = const { + let range = esp_metadata_generated::memory_range!("DRAM2_UNINIT"); + range.end - range.start + }; + + esp_alloc::heap_allocator!(#[ram(reclaimed)] size: HEAP_SIZE_RECLAIMED); + esp_alloc::heap_allocator!(size: HEAP_SIZE - HEAP_SIZE_RECLAIMED); + info!("IRAM heap initialized!\n{}", esp_alloc::HEAP.stats()); + + // Initialize the PSRAM allocator. + { + let (psram_offset, psram_size) = esp_hal::psram::psram_raw_parts(&psram_peripheral); + unsafe { + PSRAM_ALLOCATOR.add_region(HeapRegion::new( + psram_offset, + psram_size, + MemoryCapability::External.into(), + )); + } + info!( + "PSRAM heap initialized with capacity of {} MiB!\n{}", + psram_size / 1024 / 1024, + PSRAM_ALLOCATOR.stats(), + ); + } +} diff --git a/firmware/acid-firmware/src/ui/dpi.rs b/firmware/acid-firmware/src/ui/dpi.rs index cef0d55..e385f86 100644 --- a/firmware/acid-firmware/src/ui/dpi.rs +++ b/firmware/acid-firmware/src/ui/dpi.rs @@ -1,10 +1,16 @@ -use alloc::{alloc::Global, boxed::Box}; +use alloc::{alloc::Global, boxed::Box, vec::Vec}; +use embassy_time::Timer; use esp_hal::{ - Blocking, dma::BurstConfig, lcd_cam::lcd::dpi::Dpi, peripherals::DMA_CH0, spi::master::AnySpi, + Blocking, + dma::{BurstConfig, ExternalBurstConfig, InternalBurstConfig}, + lcd_cam::lcd::dpi::Dpi, + spi::master::AnySpi, }; use esp_hal_bounce_buffers::{DmaBounce, Swapchain, SwapchainWriter, allocate_dma_buffer_in}; +use i_slint_core::software_renderer::{Rgb565Pixel, TargetPixel}; +use log::error; -use crate::PSRAM_ALLOCATOR; +use crate::{DmaBounceController, PSRAM_ALLOCATOR, peripherals::st7701s::St7701s}; // TODO: Rename or get rid of. pub struct Framebuffer { @@ -16,7 +22,7 @@ pub struct Framebuffer { impl Framebuffer { pub fn new( - channel: DMA_CH0<'static>, + channel: esp_hal::peripherals::DMA_CH0<'static>, peripheral_src: AnySpi<'static>, peripheral_dst: Dpi<'static, Blocking>, burst_config: BurstConfig, @@ -62,3 +68,102 @@ impl Framebuffer { } } } + +#[allow(unused)] +async fn test_bounce_buffers( + channel: esp_hal::peripherals::DMA_CH0<'static>, + peripheral: esp_hal::peripherals::SPI2<'static>, + mut st7701s: St7701s<'static, Blocking>, +) { + error!("TEST BOUNCE BUFFERS SECTION ENTERED"); + + const BYTES_PER_PIXEL: usize = core::mem::size_of::(); + // Assume highest burst config setting. + const EXTERNAL_BURST_CONFIG: ExternalBurstConfig = ExternalBurstConfig::Size32; + const ALIGNMENT_PIXELS: usize = EXTERNAL_BURST_CONFIG as usize / BYTES_PER_PIXEL; + // The total number of pixels demanded by the DPI, per row. + const WIDTH_TOTAL_PIXELS: usize = 368; + // The total number of rows demanded by the DPI, per frame. + const HEIGHT_PIXELS: usize = 960; + // The number of unused pixels at the start of the row. + const FRONT_PORCH_ACTUAL_PIXELS: usize = 120; + // The number of actually visible pixels, per row. + const WIDTH_VISIBLE_PIXELS: usize = 240; + // The number of pixels not stored in a bounce buffer, per row. + // This many arbitrary pixels are sent to the DPI. + const FRONT_PORCH_SKIPPED_PIXELS: usize = + (FRONT_PORCH_ACTUAL_PIXELS / ALIGNMENT_PIXELS) * ALIGNMENT_PIXELS; + const WIDTH_STORED_PIXELS: usize = WIDTH_TOTAL_PIXELS - FRONT_PORCH_SKIPPED_PIXELS; + const VISIBLE_OFFSET_IN_BUFFER_PIXELS: usize = + FRONT_PORCH_ACTUAL_PIXELS - FRONT_PORCH_SKIPPED_PIXELS; + const ROWS_PER_WINDOW: usize = 16; + let burst_config = BurstConfig { + internal_memory: InternalBurstConfig::Enabled, + external_memory: EXTERNAL_BURST_CONFIG, + }; + let (swapchain_reader, mut swapchain_writer) = Swapchain { + framebuffers: [ + Box::leak(allocate_dma_buffer_in( + HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL, + burst_config, + &PSRAM_ALLOCATOR, + )), + Box::leak(allocate_dma_buffer_in( + HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL, + burst_config, + &PSRAM_ALLOCATOR, + )), + ], + } + .into_reader_writer(); + + { + let write_guard = &mut swapchain_writer.write(); + let buffer_src = bytemuck::cast_slice_mut::(write_guard); + let colors = (0..WIDTH_VISIBLE_PIXELS as u8 / 2) + .rev() + .map(|val| Rgb565Pixel::from_rgb(0xFF, val * 2, 0)) + .collect::>(); + for (index, pixel) in buffer_src.iter_mut().enumerate() { + let mut x = + (index % WIDTH_STORED_PIXELS) as i16 - VISIBLE_OFFSET_IN_BUFFER_PIXELS as i16; + let mut y = (index / WIDTH_STORED_PIXELS) as i16; + + if x < WIDTH_VISIBLE_PIXELS as i16 { + x = core::cmp::min(x, WIDTH_VISIBLE_PIXELS as i16 - 1 - x); + y = core::cmp::min(y, HEIGHT_PIXELS as i16 - 1 - y); + let min = core::cmp::min(x, y); + + *pixel = colors[min as usize % colors.len()]; + continue; + } + + *pixel = Rgb565Pixel::default(); + } + } + + let mut dma_bounce = DmaBounce::new( + Global, + channel, + AnySpi::from(peripheral), + st7701s.dpi, + swapchain_reader, + FRONT_PORCH_SKIPPED_PIXELS * BYTES_PER_PIXEL, + WIDTH_STORED_PIXELS * BYTES_PER_PIXEL, + ROWS_PER_WINDOW, + burst_config, + true, + ); + let mut bb_controller = DmaBounceController::new(dma_bounce); + + error!("TEST BOUNCE BUFFERS TASK LAUNCHED"); + + loop { + bb_controller.start().await.unwrap(); + st7701s.controller.sleep_off().await; + Timer::after_secs(1).await; + st7701s.controller.sleep_on().await; + bb_controller.stop().await.unwrap(); + Timer::after_secs(1).await; + } +}