use core::cell::UnsafeCell;
use super::regs::{JPU_DRAM_PHYSICAL_SIZE, VMEM_PAGE_SIZE};
#[derive(Clone, Copy, Debug)]
pub struct PhysBuffer {
pub addr: usize,
pub size: usize,
}
impl PhysBuffer {
pub fn is_empty(&self) -> bool {
self.addr == 0 || self.size == 0
}
}
struct JpuMemoryPool {
base_addr: usize,
size: usize,
num_pages: usize,
bitmap: [u64; 32],
}
impl JpuMemoryPool {
const fn new() -> Self {
Self {
base_addr: 0,
size: 0,
num_pages: 0,
bitmap: [0; 32],
}
}
fn init(&mut self, base: usize, size: usize) {
self.base_addr = (base + VMEM_PAGE_SIZE - 1) & !(VMEM_PAGE_SIZE - 1);
self.size = size & !(VMEM_PAGE_SIZE - 1);
self.num_pages = self.size / VMEM_PAGE_SIZE;
for word in &mut self.bitmap {
*word = u64::MAX;
}
}
fn alloc(&mut self, size: usize) -> Option<PhysBuffer> {
let npages = size.div_ceil(VMEM_PAGE_SIZE);
let mut consecutive = 0usize;
let mut start_page = 0usize;
for page_idx in 0..self.num_pages {
let word_idx = page_idx / 64;
let bit_idx = page_idx % 64;
if word_idx >= self.bitmap.len() {
break;
}
if self.bitmap[word_idx] & (1 << bit_idx) != 0 {
if consecutive == 0 {
start_page = page_idx;
}
consecutive += 1;
if consecutive >= npages {
for i in 0..npages {
let p = start_page + i;
self.bitmap[p / 64] &= !(1 << (p % 64));
}
let addr = self.base_addr + start_page * VMEM_PAGE_SIZE;
return Some(PhysBuffer {
addr,
size: npages * VMEM_PAGE_SIZE,
});
}
} else {
consecutive = 0;
}
}
None
}
fn free(&mut self, buf: PhysBuffer) {
if buf.is_empty() || buf.addr < self.base_addr || buf.addr >= self.base_addr + self.size {
return;
}
let start_page = (buf.addr - self.base_addr) / VMEM_PAGE_SIZE;
let npages = buf.size.div_ceil(VMEM_PAGE_SIZE);
for i in 0..npages {
let p = start_page + i;
if p >= self.num_pages {
break;
}
self.bitmap[p / 64] |= 1 << (p % 64);
}
}
}
struct SyncUnsafeCell<T>(UnsafeCell<T>);
unsafe impl<T> Sync for SyncUnsafeCell<T> {}
impl<T> SyncUnsafeCell<T> {
const fn new(value: T) -> Self {
Self(UnsafeCell::new(value))
}
fn with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
unsafe { f(&mut *self.0.get()) }
}
}
#[repr(C, align(4096))]
struct AlignedMem<const N: usize>([u8; N]);
struct JpuMemState {
pool: JpuMemoryPool,
initialized: bool,
}
impl JpuMemState {
const fn new() -> Self {
Self {
pool: JpuMemoryPool::new(),
initialized: false,
}
}
}
static MEM_STATE: SyncUnsafeCell<JpuMemState> = SyncUnsafeCell::new(JpuMemState::new());
static DMA_BUFFER: SyncUnsafeCell<AlignedMem<{ JPU_DRAM_PHYSICAL_SIZE }>> =
SyncUnsafeCell::new(AlignedMem([0u8; JPU_DRAM_PHYSICAL_SIZE]));
pub fn init_jpu_memory() {
MEM_STATE.with_mut(|state| {
if state.initialized {
return;
}
let buf_addr = DMA_BUFFER.with_mut(|buf| buf.0.as_ptr() as usize);
state.pool.init(buf_addr, JPU_DRAM_PHYSICAL_SIZE);
state.initialized = true;
});
}
pub fn jpu_alloc(size: usize) -> Option<PhysBuffer> {
MEM_STATE.with_mut(|state| {
if !state.initialized {
None
} else {
state.pool.alloc(size)
}
})
}
pub fn jpu_free(buf: PhysBuffer) {
if buf.is_empty() {
return;
}
MEM_STATE.with_mut(|state| {
if state.initialized {
state.pool.free(buf);
}
});
}
pub fn copy_to_phys(buf: PhysBuffer, src: &[u8]) {
let len = src.len().min(buf.size);
if len == 0 {
return;
}
let dst = phys_slice_mut(buf.addr, len);
dst.copy_from_slice(&src[..len]);
}
pub fn phys_slice(addr: usize, len: usize) -> &'static [u8] {
unsafe { core::slice::from_raw_parts(addr as *const u8, len) }
}
fn phys_slice_mut(addr: usize, len: usize) -> &'static mut [u8] {
unsafe { core::slice::from_raw_parts_mut(addr as *mut u8, len) }
}