idk why these weren't alr committed
This commit is contained in:
+146
@@ -0,0 +1,146 @@
|
|||||||
|
## Architecture
|
||||||
|
32 bit instruction width/word size
|
||||||
|
|
||||||
|
## Instruction Types:
|
||||||
|
|
||||||
|
Type Sizes:
|
||||||
|
- u16: [16 bits]
|
||||||
|
- u6: [6 bits]
|
||||||
|
- Reg: [5 bits]
|
||||||
|
- Opcode: [6 bits]
|
||||||
|
|
||||||
|
Instruction Types:
|
||||||
|
- R: [Opcode] [Reg] [Reg] [Reg] [Reg] [u6]
|
||||||
|
- I: [Opcode] [Reg] [Reg] [u16 ]
|
||||||
|
|
||||||
|
## Instruction Set
|
||||||
|
|
||||||
|
// move
|
||||||
|
mov <src: Reg> <dst: Reg> [ unused ]
|
||||||
|
cmov <src: Reg> <dst: Reg> <cmp: Reg>
|
||||||
|
|
||||||
|
// load
|
||||||
|
ldb <src: Reg> <dst: Reg> <offset: u16>
|
||||||
|
ldbs <src: Reg> <dst: Reg> <offset: u16>
|
||||||
|
ldh <src: Reg> <dst: Reg> <offset: u16>
|
||||||
|
ldhs <src: Reg> <dst: Reg> <offset: u16>
|
||||||
|
ldw <src: Reg> <dst: Reg> <offset: u16>
|
||||||
|
|
||||||
|
// store
|
||||||
|
stb <src: Reg> <dst: Reg> <offset: u16>
|
||||||
|
sth <src: Reg> <dst: Reg> <offset: u16>
|
||||||
|
stw <src: Reg> <dst: Reg> <offset: u16>
|
||||||
|
|
||||||
|
// immediate
|
||||||
|
lli <dst: Reg> [unused] <imm: u16>
|
||||||
|
lui <dst: Reg> [unused] <imm: u16>
|
||||||
|
|
||||||
|
// comparison
|
||||||
|
ieq <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
|
||||||
|
ine <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
|
||||||
|
igt <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
|
||||||
|
ige <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
|
||||||
|
ilt <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
|
||||||
|
ile <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
|
||||||
|
|
||||||
|
// Jump
|
||||||
|
jmp [ unused ] <addr: Reg> <offset: u16> // unconditional
|
||||||
|
|
||||||
|
jez <cmp: Reg> <addr: Reg> <offset: u16> // conditional on cmp == 0
|
||||||
|
jnz <cmp: Reg> <addr: Reg> <offset: u16>
|
||||||
|
|
||||||
|
jic [ unused ] <addr: Reg> <offset: u16> // conditional on carry
|
||||||
|
jnc [ unused ] <addr: Reg> <offset: u16>
|
||||||
|
|
||||||
|
// Bitwise
|
||||||
|
and <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // and
|
||||||
|
nnd <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // nand
|
||||||
|
or <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // or
|
||||||
|
nor <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // nor
|
||||||
|
xor <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // xor
|
||||||
|
xnr <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // xnor
|
||||||
|
not <src: Reg> [ unused ] <dst: Reg> [unused] // not
|
||||||
|
|
||||||
|
// Arithmetic
|
||||||
|
add <sr1: Reg> <sr2: Reg> <dst: Reg>
|
||||||
|
sub <sr1: Reg> <sr2: Reg> <dst: Reg>
|
||||||
|
shl <src: Reg> <rshamt: Reg> <dst: Reg> <ishamt: u6> // left shift of rshamt+ishamt
|
||||||
|
shr <src: Reg> <rshamt: Reg> <dst: Reg> <ishamt: u6> // right shift
|
||||||
|
|
||||||
|
// Immediate Arithmetic
|
||||||
|
addi <sr1: Reg> <dst: Reg> <imm: u16>
|
||||||
|
subi <sr1: Reg> <dst: Reg> <imm: u16>
|
||||||
|
|
||||||
|
// Misc
|
||||||
|
nop (zero/do nothing)
|
||||||
|
int <imm: u6>
|
||||||
|
hlt
|
||||||
|
|
||||||
|
// Atomic
|
||||||
|
|
||||||
|
// Atomic Compare and Swap - compares src and dst, if src
|
||||||
|
acs <src: Reg> <cmp: Reg> <dst: Reg> <res: Reg>
|
||||||
|
|
||||||
|
## Assembler Pseudoinstructions
|
||||||
|
Definitions:
|
||||||
|
Label - named address, a function or global
|
||||||
|
|
||||||
|
```
|
||||||
|
lwi <src: Label|Imm> <dest: Reg> // loads the literal value of Label|Imm into dest
|
||||||
|
// if src is a Label, take literal value of Label
|
||||||
|
=> lli $src, $dest
|
||||||
|
=> lui $src, $dest
|
||||||
|
lli <src: Label> <dest: Reg> // loads the lower 16 bits of the literal value of Label into dest
|
||||||
|
... -> similarly defined for lui
|
||||||
|
|
||||||
|
ldw <src: Label> <dest: Reg> // loads the word at the address of Label into dest
|
||||||
|
... => similarly defined for ldb, ldbs, ldh, ldhs
|
||||||
|
// load the label value of src into dest with lui/lli
|
||||||
|
=> lli $src, $dest
|
||||||
|
=> lui $src, $dest
|
||||||
|
=> ldw $dest, $dest
|
||||||
|
|
||||||
|
stw <src: Reg> <dest: Label> // stores the word in src using the address/label that is dest.
|
||||||
|
// load the literal value of dest into the accumulator with lui/lli
|
||||||
|
=> lli $dest, acc
|
||||||
|
=> lui $dest, acc
|
||||||
|
=> stw $src, acc
|
||||||
|
|
||||||
|
jmp <dest: Label>
|
||||||
|
CASE dest > u16::MAX:
|
||||||
|
=> lli $dest, acc
|
||||||
|
=> lui $dest, acc
|
||||||
|
=> jmp $dest, 0
|
||||||
|
CASE dest < u16::MAX:
|
||||||
|
// assembler inserts the literal value of dest
|
||||||
|
=> jmp $dest, 0
|
||||||
|
|
||||||
|
jmp <lbl: Label> <dest: Reg>
|
||||||
|
CASE lbl > u16::MAX:
|
||||||
|
AssemblerError!
|
||||||
|
CASE lbl < u16::MAX:
|
||||||
|
=> jmp $dest, lbl
|
||||||
|
|
||||||
|
call <addr: Label>
|
||||||
|
=> subi spr, 4, spr
|
||||||
|
=> stw pcx, spr, 0
|
||||||
|
=> jmp $addr
|
||||||
|
|
||||||
|
func
|
||||||
|
// push bpr
|
||||||
|
=> subi spr, 4, spr
|
||||||
|
=> stw bpr, spr, 0
|
||||||
|
// replace bpr with spr
|
||||||
|
=> mov spr, bpr
|
||||||
|
|
||||||
|
return
|
||||||
|
=> mov bpr, spr
|
||||||
|
// pop bpr
|
||||||
|
=> addi spr, 4, spr
|
||||||
|
=> ldw spr, bpr, 0
|
||||||
|
// load return addr
|
||||||
|
=> ldw spr, ret, 0
|
||||||
|
=> addi, spr, 4, spr
|
||||||
|
=> jmp ret, 4
|
||||||
|
|
||||||
|
```
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
|
use clap::{Parser, ValueEnum};
|
||||||
|
|
||||||
|
use crate::{MemoryMap, RandomAccessMemory};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
pub struct DsaArgs {
|
||||||
|
/// Memory map to use on boot.
|
||||||
|
/// Overrides default value.
|
||||||
|
#[arg(long = "mmap")]
|
||||||
|
memory_map: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[arg(value_enum, long = "mem", default_value = "bulk-alloc")]
|
||||||
|
pub memory_bank: MemoryBank,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DsaArgs {
|
||||||
|
pub fn get_memory_map(&self) -> Option<MemoryMap> {
|
||||||
|
self.memory_map.as_ref().and_then(|m| {
|
||||||
|
fs::read_to_string(m)
|
||||||
|
.ok()
|
||||||
|
.and_then(|map| ron::from_str(&map).ok())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, ValueEnum, Default)]
|
||||||
|
pub enum MemoryBank {
|
||||||
|
#[cfg(feature = "mainstore-bulkalloc")]
|
||||||
|
#[default]
|
||||||
|
BulkAlloc,
|
||||||
|
#[cfg(feature = "mainstore-arraymap")]
|
||||||
|
ArrayMap,
|
||||||
|
#[cfg(feature = "mainstore-hashmap")]
|
||||||
|
HashMap,
|
||||||
|
#[cfg(feature = "mainstore-prealloc")]
|
||||||
|
PreAlloc,
|
||||||
|
#[cfg(feature = "mainstore-stackarray")]
|
||||||
|
StackArray,
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct InstructionWord(pub u32);
|
||||||
|
|
||||||
|
impl InstructionWord {
|
||||||
|
#[inline]
|
||||||
|
pub fn opcode(self) -> u8 {
|
||||||
|
((self.0 >> 26) & 0x3F) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Src shared between R type and I type
|
||||||
|
#[inline]
|
||||||
|
pub fn src1(self) -> Reg {
|
||||||
|
Reg::from_u8_unchecked(((self.0 >> 21) & 0x1F) as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dest shared between R type and I type
|
||||||
|
#[inline]
|
||||||
|
pub fn dest(self) -> Reg {
|
||||||
|
Reg::from_u8_unchecked(((self.0 >> 16) & 0x1F) as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secondary Src for R type only
|
||||||
|
#[inline]
|
||||||
|
pub fn src2(self) -> Reg {
|
||||||
|
Reg::from_u8_unchecked(((self.0 >> 11) & 0x1F) as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Misc/Conditional reg for R type only
|
||||||
|
#[inline]
|
||||||
|
pub fn misc(self) -> Reg {
|
||||||
|
Reg::from_u8_unchecked(((self.0 >> 6) & 0x1F) as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift amount / misc data for R type only
|
||||||
|
#[inline]
|
||||||
|
pub fn shamt(self) -> u8 {
|
||||||
|
(self.0 & 0x3F) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
// 16 Bit immediate for I type only
|
||||||
|
#[inline]
|
||||||
|
pub fn imm16(self) -> u16 {
|
||||||
|
self.0 as u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Opcode {
|
||||||
|
Nop = 0,
|
||||||
|
|
||||||
|
// move
|
||||||
|
Mov = 1,
|
||||||
|
CMov = 2,
|
||||||
|
|
||||||
|
// load
|
||||||
|
Ldb = 3,
|
||||||
|
Ldbs = 4,
|
||||||
|
Ldh = 5,
|
||||||
|
Ldhs = 6,
|
||||||
|
Ldw = 7,
|
||||||
|
|
||||||
|
// store
|
||||||
|
Stb = 8,
|
||||||
|
Sth = 9,
|
||||||
|
Stw = 10,
|
||||||
|
|
||||||
|
// load immediate
|
||||||
|
Lli = 11,
|
||||||
|
Lui = 12,
|
||||||
|
|
||||||
|
// comparison
|
||||||
|
Ieq = 13,
|
||||||
|
Ine = 14,
|
||||||
|
Ilt = 15,
|
||||||
|
Ile = 16,
|
||||||
|
Igt = 17,
|
||||||
|
Ige = 18,
|
||||||
|
|
||||||
|
// jump
|
||||||
|
Jmp = 19,
|
||||||
|
Jez = 20,
|
||||||
|
Jnz = 21,
|
||||||
|
Jic = 22,
|
||||||
|
Jnc = 23,
|
||||||
|
|
||||||
|
// bitwise
|
||||||
|
And = 24,
|
||||||
|
Nand = 25,
|
||||||
|
Or = 26,
|
||||||
|
Nor = 27,
|
||||||
|
Xor = 28,
|
||||||
|
Xnor = 29,
|
||||||
|
Not = 30,
|
||||||
|
|
||||||
|
// arithmetic
|
||||||
|
Add = 31,
|
||||||
|
Sub = 32,
|
||||||
|
Shl = 33,
|
||||||
|
Shr = 34,
|
||||||
|
Addi = 35,
|
||||||
|
Subi = 36,
|
||||||
|
|
||||||
|
// system
|
||||||
|
Int = 37,
|
||||||
|
Hlt = 38,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Opcode {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_u8(val: u8) -> Option<Self> {
|
||||||
|
if val <= Self::Hlt as u8 {
|
||||||
|
Some(unsafe { std::mem::transmute(val) })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Reg {
|
||||||
|
// general purpose
|
||||||
|
Rg0 = 0,
|
||||||
|
Rg1 = 1,
|
||||||
|
Rg2 = 2,
|
||||||
|
Rg3 = 3,
|
||||||
|
Rg4 = 4,
|
||||||
|
Rg5 = 5,
|
||||||
|
Rg6 = 6,
|
||||||
|
Rg7 = 7,
|
||||||
|
Rg8 = 8,
|
||||||
|
Rg9 = 9,
|
||||||
|
Rga = 10,
|
||||||
|
Rgb = 11,
|
||||||
|
Rgc = 12,
|
||||||
|
Rgd = 13,
|
||||||
|
Rge = 14,
|
||||||
|
Rgf = 15,
|
||||||
|
|
||||||
|
// special purpose
|
||||||
|
Zero = 16,
|
||||||
|
Acc = 17,
|
||||||
|
Spr = 18,
|
||||||
|
Bpr = 19,
|
||||||
|
Ret = 20,
|
||||||
|
Idr = 21,
|
||||||
|
Mmr = 22,
|
||||||
|
|
||||||
|
// system - read only
|
||||||
|
Mar = 23,
|
||||||
|
Mdr = 24,
|
||||||
|
Sts = 25,
|
||||||
|
Cir = 26,
|
||||||
|
Pcx = 27,
|
||||||
|
|
||||||
|
#[default]
|
||||||
|
Null = 28,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Reg {
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
pub fn from_u8_unchecked(idx: u8) -> Self {
|
||||||
|
debug_assert!(idx <= 28);
|
||||||
|
unsafe { std::mem::transmute(idx) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_u8(idx: u8) -> Result<Self, ()> {
|
||||||
|
if idx > 28 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(Self::from_u8_unchecked(idx))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod instructions;
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
use std::{
|
||||||
|
alloc::{Layout, alloc_zeroed},
|
||||||
|
hint::{likely, unlikely},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::memory::{
|
||||||
|
PhysAddr, RandomAccessMemory,
|
||||||
|
ram::{NUM_PAGES, Page, idx},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ArrayStore {
|
||||||
|
pages: Box<[*mut Page; NUM_PAGES]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for ArrayStore {}
|
||||||
|
impl ArrayStore {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let pages = vec![0 as *mut Page; 2 << 20]
|
||||||
|
.into_boxed_slice()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
Self { pages }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc(&mut self) -> *mut Page {
|
||||||
|
let layout = Layout::from_size_align(4096, 4096).unwrap();
|
||||||
|
unsafe { alloc_zeroed(layout) as *mut Page }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RandomAccessMemory for ArrayStore {
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_word(&self, addr: PhysAddr) -> u32 {
|
||||||
|
debug_assert_eq!(addr % 4, 0);
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if likely(!self.pages[idx].is_null()) {
|
||||||
|
return unsafe { (self.pages[idx] as *const u32).byte_add(offset).read() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow path: MMIO, fault, etc — never inlined
|
||||||
|
// Since we're only reading, we can safely return 0
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_byte(&self, addr: PhysAddr) -> u8 {
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if likely(!self.pages[idx].is_null()) {
|
||||||
|
return unsafe { (self.pages[idx] as *const u8).byte_add(offset).read() };
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
|
||||||
|
debug_assert_eq!(addr % 4096, 0);
|
||||||
|
let (idx, _) = idx(addr);
|
||||||
|
|
||||||
|
if likely(!self.pages[idx].is_null()) {
|
||||||
|
return unsafe { &*(self.pages[idx] as *const [u8; 4096]) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return &[0; 4096];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_byte(&mut self, addr: PhysAddr, value: u8) {
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if unlikely(self.pages[idx].is_null()) {
|
||||||
|
self.pages[idx] = self.alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
(self.pages[idx] as *mut u8).byte_add(offset).write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_word(&mut self, addr: PhysAddr, value: u32) {
|
||||||
|
debug_assert_eq!(addr % 4, 0);
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if unlikely(self.pages[idx].is_null()) {
|
||||||
|
self.pages[idx] = self.alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { (self.pages[idx] as *mut u32).byte_add(offset).write(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
|
||||||
|
debug_assert_eq!(addr % 4096, 0);
|
||||||
|
let (idx, _) = idx(addr);
|
||||||
|
|
||||||
|
if unlikely(self.pages[idx].is_null()) {
|
||||||
|
self.pages[idx] = self.alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { (self.pages[idx] as *mut [u8; 4096]).write(*value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
use std::{
|
||||||
|
alloc::{Layout, alloc_zeroed},
|
||||||
|
hint::{likely, unlikely},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::memory::{
|
||||||
|
PhysAddr, RandomAccessMemory,
|
||||||
|
ram::{NUM_PAGES, Page, idx},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct BulkAllocStore {
|
||||||
|
pages: Box<[*mut Page; NUM_PAGES]>,
|
||||||
|
pre_allocated: [*mut Page; Self::PREALLOC],
|
||||||
|
idx: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for BulkAllocStore {}
|
||||||
|
impl BulkAllocStore {
|
||||||
|
const PREALLOC: usize = u8::MAX as usize;
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let pages = vec![0 as *mut Page; 2 << 20]
|
||||||
|
.into_boxed_slice()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
pages,
|
||||||
|
pre_allocated: Self::alloc_set(),
|
||||||
|
idx: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_set() -> [*mut Page; Self::PREALLOC] {
|
||||||
|
let layout = Layout::from_size_align(4096 * Self::PREALLOC, 4096).unwrap();
|
||||||
|
let mut region = unsafe { alloc_zeroed(layout) as *mut Page };
|
||||||
|
let mut set = [0 as *mut Page; Self::PREALLOC];
|
||||||
|
for i in 0..Self::PREALLOC {
|
||||||
|
set[i] = region;
|
||||||
|
region = unsafe { region.byte_add(4096) };
|
||||||
|
}
|
||||||
|
set
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc(&mut self) -> *mut Page {
|
||||||
|
if self.idx < Self::PREALLOC as u8 {
|
||||||
|
let page = self.pre_allocated[self.idx as usize];
|
||||||
|
self.idx += 1;
|
||||||
|
page
|
||||||
|
} else {
|
||||||
|
self.idx = 0;
|
||||||
|
self.pre_allocated = Self::alloc_set();
|
||||||
|
self.pre_allocated[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RandomAccessMemory for BulkAllocStore {
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_word(&self, addr: PhysAddr) -> u32 {
|
||||||
|
debug_assert_eq!(addr % 4, 0);
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if likely(!self.pages[idx].is_null()) {
|
||||||
|
return unsafe { (self.pages[idx] as *const u32).byte_add(offset).read() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow path: MMIO, fault, etc — never inlined
|
||||||
|
// Since we're only reading, we can safely return 0
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_byte(&self, addr: PhysAddr) -> u8 {
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if likely(!self.pages[idx].is_null()) {
|
||||||
|
return unsafe { (self.pages[idx] as *const u8).byte_add(offset).read() };
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
|
||||||
|
debug_assert_eq!(addr % 4096, 0);
|
||||||
|
let (idx, _) = idx(addr);
|
||||||
|
|
||||||
|
if likely(!self.pages[idx].is_null()) {
|
||||||
|
return unsafe { &*(self.pages[idx] as *const [u8; 4096]) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return &[0; 4096];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_byte(&mut self, addr: PhysAddr, value: u8) {
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if unlikely(self.pages[idx].is_null()) {
|
||||||
|
self.pages[idx] = self.alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
(self.pages[idx] as *mut u8).byte_add(offset).write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_word(&mut self, addr: PhysAddr, value: u32) {
|
||||||
|
debug_assert_eq!(addr % 4, 0);
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if unlikely(self.pages[idx].is_null()) {
|
||||||
|
self.pages[idx] = self.alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { (self.pages[idx] as *mut u32).byte_add(offset).write(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
|
||||||
|
debug_assert_eq!(addr % 4096, 0);
|
||||||
|
let (idx, _) = idx(addr);
|
||||||
|
|
||||||
|
if unlikely(self.pages[idx].is_null()) {
|
||||||
|
self.pages[idx] = self.alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { (self.pages[idx] as *mut [u8; 4096]).write(*value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
use fxhash::FxHashMap;
|
||||||
|
|
||||||
|
use crate::memory::{PhysAddr, RandomAccessMemory, ram::Page};
|
||||||
|
|
||||||
|
pub struct HashStore {
|
||||||
|
pages: FxHashMap<PhysAddr, Page>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HashStore {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
pages: FxHashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn segment_addr(addr: PhysAddr) -> (PhysAddr, usize) {
|
||||||
|
(addr & !(0xFFF), (addr & 0xFFF) as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RandomAccessMemory for HashStore {
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_byte(&self, addr: PhysAddr) -> u8 {
|
||||||
|
let (page, offset) = Self::segment_addr(addr);
|
||||||
|
self.pages.get(&page).map(|p| p[offset]).unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_word(&self, addr: PhysAddr) -> u32 {
|
||||||
|
let (page, offset) = Self::segment_addr(addr);
|
||||||
|
debug_assert_eq!(offset % 4, 0);
|
||||||
|
let page = self.pages.get(&page).unwrap_or(&[0; 4096]);
|
||||||
|
u32::from_be_bytes(page[offset..=offset + 3].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
|
||||||
|
debug_assert_eq!(addr % 0x1000, 0);
|
||||||
|
self.pages.get(&addr).unwrap_or(&[0; 4096])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_byte(&mut self, addr: PhysAddr, value: u8) {
|
||||||
|
let (page, offset) = Self::segment_addr(addr);
|
||||||
|
self.pages.entry(page).or_insert_with(|| [0; 4096])[offset] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_word(&mut self, addr: PhysAddr, value: u32) {
|
||||||
|
let (page, offset) = Self::segment_addr(addr);
|
||||||
|
debug_assert_eq!(offset % 4, 0);
|
||||||
|
let page = self.pages.entry(page).or_insert_with(|| [0; 4096]);
|
||||||
|
page[offset..=offset + 3].copy_from_slice(&value.to_be_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
|
||||||
|
debug_assert_eq!(addr % 0x1000, 0);
|
||||||
|
let page = self.pages.entry(addr).or_insert_with(|| [0; 4096]);
|
||||||
|
page.copy_from_slice(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
use crate::memory::PhysAddr;
|
||||||
|
|
||||||
|
#[cfg(feature = "mainstore-arraymap")]
|
||||||
|
pub mod arraymap;
|
||||||
|
#[cfg(feature = "mainstore-arraymap")]
|
||||||
|
pub use arraymap::ArrayStore;
|
||||||
|
|
||||||
|
#[cfg(feature = "mainstore-hashmap")]
|
||||||
|
pub mod hashmap;
|
||||||
|
#[cfg(feature = "mainstore-hashmap")]
|
||||||
|
pub use hashmap::HashStore;
|
||||||
|
|
||||||
|
#[cfg(feature = "mainstore-stackarray")]
|
||||||
|
pub mod stackarray;
|
||||||
|
#[cfg(feature = "mainstore-stackarray")]
|
||||||
|
pub use stackarray::StackArrayStore;
|
||||||
|
|
||||||
|
#[cfg(feature = "mainstore-bulkalloc")]
|
||||||
|
pub mod bulkalloc;
|
||||||
|
#[cfg(feature = "mainstore-bulkalloc")]
|
||||||
|
pub use bulkalloc::BulkAllocStore;
|
||||||
|
|
||||||
|
#[cfg(feature = "mainstore-prealloc")]
|
||||||
|
pub mod prealloc;
|
||||||
|
#[cfg(feature = "mainstore-prealloc")]
|
||||||
|
pub use prealloc::PreAllocStore;
|
||||||
|
|
||||||
|
const NUM_PAGES: usize = 2 << 20;
|
||||||
|
|
||||||
|
type Page = [u8; 4096];
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn idx(addr: PhysAddr) -> (usize, usize) {
|
||||||
|
((addr >> 12) as usize, (addr & 0xFFF) as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RandomAccessMemory {
|
||||||
|
fn read_byte(&self, addr: PhysAddr) -> u8;
|
||||||
|
|
||||||
|
fn write_byte(&mut self, addr: PhysAddr, value: u8);
|
||||||
|
|
||||||
|
fn read_word(&self, addr: PhysAddr) -> u32;
|
||||||
|
|
||||||
|
fn write_word(&mut self, addr: PhysAddr, value: u32);
|
||||||
|
|
||||||
|
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096];
|
||||||
|
|
||||||
|
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
use crate::memory::{PhysAddr, RandomAccessMemory, ram::NUM_PAGES};
|
||||||
|
|
||||||
|
pub struct PreAllocStore {
|
||||||
|
heap: Box<[u8; NUM_PAGES * 4096]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for PreAllocStore {}
|
||||||
|
impl PreAllocStore {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
heap: unsafe { Box::new_zeroed().assume_init() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RandomAccessMemory for PreAllocStore {
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_word(&self, addr: PhysAddr) -> u32 {
|
||||||
|
unsafe { (self.heap.as_ptr().add(addr as usize) as *const u32).read() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_byte(&self, addr: PhysAddr) -> u8 {
|
||||||
|
self.heap[addr as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
|
||||||
|
debug_assert_eq!(addr % 4096, 0);
|
||||||
|
unsafe { (self.heap.as_ptr().add(addr as usize) as *const [u8; 4096]).as_ref_unchecked() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_byte(&mut self, addr: PhysAddr, value: u8) {
|
||||||
|
self.heap[addr as usize] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_word(&mut self, addr: PhysAddr, value: u32) {
|
||||||
|
unsafe { (self.heap.as_ptr().add(addr as usize) as *mut u32).write(value) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
|
||||||
|
self.heap[addr as usize..addr as usize + 4096].copy_from_slice(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
use std::{
|
||||||
|
alloc::{Layout, alloc_zeroed},
|
||||||
|
hint::{likely, unlikely},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::memory::{
|
||||||
|
PhysAddr, RandomAccessMemory,
|
||||||
|
ram::{NUM_PAGES, Page, idx},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct StackArrayStore {
|
||||||
|
pages: [*mut Page; NUM_PAGES],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for StackArrayStore {}
|
||||||
|
impl StackArrayStore {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
pages: [0 as *mut Page; 2 << 20],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc(&mut self) -> *mut Page {
|
||||||
|
let layout = Layout::from_size_align(4096, 4096).unwrap();
|
||||||
|
unsafe { alloc_zeroed(layout) as *mut Page }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RandomAccessMemory for StackArrayStore {
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_word(&self, addr: PhysAddr) -> u32 {
|
||||||
|
debug_assert_eq!(addr % 4, 0);
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if likely(!self.pages[idx].is_null()) {
|
||||||
|
return unsafe { (self.pages[idx] as *const u32).byte_add(offset).read() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow path: MMIO, fault, etc — never inlined
|
||||||
|
// Since we're only reading, we can safely return 0
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_byte(&self, addr: PhysAddr) -> u8 {
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if likely(!self.pages[idx].is_null()) {
|
||||||
|
return unsafe { (self.pages[idx] as *const u8).byte_add(offset).read() };
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
|
||||||
|
debug_assert_eq!(addr % 4096, 0);
|
||||||
|
let (idx, _) = idx(addr);
|
||||||
|
|
||||||
|
if likely(!self.pages[idx].is_null()) {
|
||||||
|
return unsafe { &*(self.pages[idx] as *const [u8; 4096]) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return &[0; 4096];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_byte(&mut self, addr: PhysAddr, value: u8) {
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if unlikely(self.pages[idx].is_null()) {
|
||||||
|
self.pages[idx] = self.alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
(self.pages[idx] as *mut u8).byte_add(offset).write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_word(&mut self, addr: PhysAddr, value: u32) {
|
||||||
|
debug_assert_eq!(addr % 4, 0);
|
||||||
|
let (idx, offset) = idx(addr);
|
||||||
|
|
||||||
|
if unlikely(self.pages[idx].is_null()) {
|
||||||
|
self.pages[idx] = self.alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { (self.pages[idx] as *mut u32).byte_add(offset).write(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
|
||||||
|
debug_assert_eq!(addr % 4096, 0);
|
||||||
|
let (idx, _) = idx(addr);
|
||||||
|
|
||||||
|
if unlikely(self.pages[idx].is_null()) {
|
||||||
|
self.pages[idx] = self.alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { (self.pages[idx] as *mut [u8; 4096]).write(*value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user