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