fixed broken merge commit
This commit is contained in:
@@ -1,67 +0,0 @@
|
||||
pub enum AsmOpcode {
|
||||
Nop,
|
||||
|
||||
// move
|
||||
Mov,
|
||||
CMov,
|
||||
|
||||
// load
|
||||
Ldb,
|
||||
Ldbs,
|
||||
Ldh,
|
||||
Ldhs,
|
||||
Ldw,
|
||||
|
||||
// store
|
||||
Stb,
|
||||
Sth,
|
||||
Stw,
|
||||
|
||||
// load immediate
|
||||
Lli,
|
||||
Lui,
|
||||
Lwi,
|
||||
|
||||
// comparison
|
||||
Ieq,
|
||||
Ine,
|
||||
Ilt,
|
||||
Ile,
|
||||
Igt,
|
||||
Ige,
|
||||
|
||||
// jump
|
||||
Jmp,
|
||||
Jez,
|
||||
Jnz,
|
||||
Jic,
|
||||
Jnc,
|
||||
|
||||
// bitwise
|
||||
And,
|
||||
Nand,
|
||||
Or,
|
||||
Nor,
|
||||
Xor,
|
||||
Xnor,
|
||||
Not,
|
||||
|
||||
// arithmetic
|
||||
Add,
|
||||
Sub,
|
||||
Shl,
|
||||
Shr,
|
||||
Addi,
|
||||
Subi,
|
||||
|
||||
// utility
|
||||
Push,
|
||||
Pop,
|
||||
Call,
|
||||
Ret,
|
||||
|
||||
// system
|
||||
Int,
|
||||
IRet,
|
||||
Hlt,
|
||||
}
|
||||
@@ -2,29 +2,21 @@ use std::{
|
||||
hint::unlikely,
|
||||
ops::AddAssign,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::Ordering,
|
||||
mpsc::{self, TryRecvError},
|
||||
Arc,
|
||||
},
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use common::isa::instructions::{Instruction, Opcode};
|
||||
use common::isa::register::Register;
|
||||
use crate::{
|
||||
<<<<<<< HEAD:dsa/emulator/src/processor/processor.rs
|
||||
config::{IoMapping, MemoryMap, RegionType},
|
||||
io::{IoDevice, MappedDevice},
|
||||
memory::{mmu::MMU, ram::RandomAccessMemory},
|
||||
processor::{interrupts::Interrupt, state::SharedState},
|
||||
Page,
|
||||
=======
|
||||
MemoryBank, Page, SharedState,
|
||||
backend::{memory::mmu::MMU, processor::interrupts::Interrupt},
|
||||
config::{IoMapping, MemoryMap, RegionType},
|
||||
>>>>>>> refs/remotes/origin/main:dsa/emulator/src/backend/processor/processor.rs
|
||||
config::{MemoryMap, RegionType},
|
||||
};
|
||||
use common::isa::instructions::{Instruction, Opcode};
|
||||
use common::isa::register::Register;
|
||||
|
||||
pub struct Emulator {
|
||||
internal_state: ProcessorSnapshot,
|
||||
|
||||
+298
-296
@@ -1,330 +1,332 @@
|
||||
use common::isa::instructions::Instruction;
|
||||
use dsa::{BulkAllocStore, Emulator, Page, RandomAccessMemory};
|
||||
/// DSA Emulator Benchmark
|
||||
///
|
||||
/// Place this in `dsa/src/bin/bench.rs` and run with:
|
||||
/// cargo run --bin bench --release
|
||||
///
|
||||
/// Three workloads are tested:
|
||||
/// 1. ALU-heavy — tight arithmetic loop, no memory, no branches
|
||||
/// 2. Memory-heavy — repeated load/store to a working set of addresses
|
||||
/// 3. Branch-heavy — the bf.dsa dispatch pattern (ieq + jnz chains)
|
||||
///
|
||||
/// Each workload is a hand-assembled flat binary loaded directly into
|
||||
/// the emulator, bypassing all the args/display/IO plumbing.
|
||||
use std::{
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
u16,
|
||||
};
|
||||
// use common::isa::instructions::Instruction;
|
||||
// use dsa::{Emulator, MemoryBank, Page};
|
||||
// /// DSA Emulator Benchmark
|
||||
// ///
|
||||
// /// Place this in `dsa/src/bin/bench.rs` and run with:
|
||||
// /// cargo run --bin bench --release
|
||||
// ///
|
||||
// /// Three workloads are tested:
|
||||
// /// 1. ALU-heavy — tight arithmetic loop, no memory, no branches
|
||||
// /// 2. Memory-heavy — repeated load/store to a working set of addresses
|
||||
// /// 3. Branch-heavy — the bf.dsa dispatch pattern (ieq + jnz chains)
|
||||
// ///
|
||||
// /// Each workload is a hand-assembled flat binary loaded directly into
|
||||
// /// the emulator, bypassing all the args/display/IO plumbing.
|
||||
// use std::{
|
||||
// thread,
|
||||
// time::{Duration, Instant},
|
||||
// u16,
|
||||
// };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Minimal no-op memory map + IO so the emulator boots without a config file.
|
||||
// ---------------------------------------------------------------------------
|
||||
// // ---------------------------------------------------------------------------
|
||||
// // Minimal no-op memory map + IO so the emulator boots without a config file.
|
||||
// // ---------------------------------------------------------------------------
|
||||
|
||||
fn make_emulator() -> Emulator<BulkAllocStore> {
|
||||
use dsa::{
|
||||
config::{MemoryMap, Region, RegionType},
|
||||
io::display::DisplayDevice,
|
||||
};
|
||||
// fn make_emulator() -> Emulator {
|
||||
// use dsa::{
|
||||
// config::{MemoryMap, Region, RegionType},
|
||||
// io::display::Display,
|
||||
// };
|
||||
|
||||
let mmap = MemoryMap {
|
||||
table_addr: 0x40000,
|
||||
regions: vec![
|
||||
Region {
|
||||
base: 0x0000_0000,
|
||||
size: 0x0010_0000, // 1 MiB RAM
|
||||
region_type: RegionType::Usable,
|
||||
identity_map_on_boot: true,
|
||||
},
|
||||
Region {
|
||||
base: 0x0020_0000,
|
||||
size: 0x0002_0000, // 128 KiB MMIO (display)
|
||||
region_type: RegionType::MMIO,
|
||||
identity_map_on_boot: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
// let mmap = MemoryMap {
|
||||
// table_addr: 0x40000,
|
||||
// regions: vec![
|
||||
// Region {
|
||||
// base: 0x0000_0000,
|
||||
// size: 0x0010_0000, // 1 MiB RAM
|
||||
// region_type: RegionType::Usable,
|
||||
// identity_map_on_boot: true,
|
||||
// },
|
||||
// Region {
|
||||
// base: 0x0020_0000,
|
||||
// size: 0x0002_0000, // 128 KiB MMIO (display)
|
||||
// region_type: RegionType::MMIO,
|
||||
// identity_map_on_boot: false,
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
let iomap = vec![dsa::config::IoMapping {
|
||||
device: dsa::io::DeviceId::Display,
|
||||
base: 0x0020_0000,
|
||||
size: 0x0000_07D0,
|
||||
}];
|
||||
// // let iomap = vec![dsa::config::IoMapping {
|
||||
// // device: dsa::io::DeviceId::Display,
|
||||
// // base: 0x0020_0000,
|
||||
// // size: 0x0000_07D0,
|
||||
// // }];
|
||||
|
||||
let (display, _handle) = DisplayDevice::new(80, 25);
|
||||
Emulator::new(BulkAllocStore::new())
|
||||
.with_device(display)
|
||||
.apply_memory_map(mmap)
|
||||
.apply_io_map(iomap)
|
||||
.unwrap()
|
||||
}
|
||||
// // let (display, _handle) = Display::new(80, 25);
|
||||
// // Emulator::new(MemoryBank::new())
|
||||
// // .with_device(display)
|
||||
// // .apply_memory_map(mmap)
|
||||
// // .apply_io_map(iomap)
|
||||
// // .unwrap()
|
||||
// }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Instruction encoding helpers (little-endian words)
|
||||
// ---------------------------------------------------------------------------
|
||||
// // ---------------------------------------------------------------------------
|
||||
// // Instruction encoding helpers (little-endian words)
|
||||
// // ---------------------------------------------------------------------------
|
||||
|
||||
/// Encode an I-type instruction: [op:6][src:5][dst:5][imm:16]
|
||||
fn enc_i(op: u8, src: u8, dst: u8, imm: u16) -> u32 {
|
||||
((op as u32 & 0x3F) << 26)
|
||||
| ((src as u32 & 0x1F) << 21)
|
||||
| ((dst as u32 & 0x1F) << 16)
|
||||
| imm as u32
|
||||
}
|
||||
// /// Encode an I-type instruction: [op:6][src:5][dst:5][imm:16]
|
||||
// fn enc_i(op: u8, src: u8, dst: u8, imm: u16) -> u32 {
|
||||
// ((op as u32 & 0x3F) << 26)
|
||||
// | ((src as u32 & 0x1F) << 21)
|
||||
// | ((dst as u32 & 0x1F) << 16)
|
||||
// | imm as u32
|
||||
// }
|
||||
|
||||
/// Encode an R-type instruction: [op:6][r1:5][r2:5][r3:5][r4:5][u6:6]
|
||||
fn enc_r(op: u8, r1: u8, r2: u8, r3: u8, r4: u8, u6: u8) -> u32 {
|
||||
((op as u32 & 0x3F) << 26)
|
||||
| ((r1 as u32 & 0x1F) << 21)
|
||||
| ((r2 as u32 & 0x1F) << 16)
|
||||
| ((r3 as u32 & 0x1F) << 11)
|
||||
| ((r4 as u32 & 0x1F) << 6)
|
||||
| (u6 as u32 & 0x3F)
|
||||
}
|
||||
// /// Encode an R-type instruction: [op:6][r1:5][r2:5][r3:5][r4:5][u6:6]
|
||||
// fn enc_r(op: u8, r1: u8, r2: u8, r3: u8, r4: u8, u6: u8) -> u32 {
|
||||
// ((op as u32 & 0x3F) << 26)
|
||||
// | ((r1 as u32 & 0x1F) << 21)
|
||||
// | ((r2 as u32 & 0x1F) << 16)
|
||||
// | ((r3 as u32 & 0x1F) << 11)
|
||||
// | ((r4 as u32 & 0x1F) << 6)
|
||||
// | (u6 as u32 & 0x3F)
|
||||
// }
|
||||
|
||||
// Register indices — must match your Register enum discriminants
|
||||
const RG0: u8 = 0;
|
||||
const RG1: u8 = 1;
|
||||
const RG2: u8 = 2;
|
||||
const RG3: u8 = 3;
|
||||
const RG4: u8 = 4;
|
||||
const RG5: u8 = 5;
|
||||
const ZERO: u8 = 16;
|
||||
// // Register indices — must match your Register enum discriminants
|
||||
// const RG0: u8 = 0;
|
||||
// const RG1: u8 = 1;
|
||||
// const RG2: u8 = 2;
|
||||
// const RG3: u8 = 3;
|
||||
// const RG4: u8 = 4;
|
||||
// const RG5: u8 = 5;
|
||||
// const ZERO: u8 = 16;
|
||||
|
||||
// Opcodes — must match your Opcode enum discriminants.
|
||||
// Fill these in from your ISA.md / encode.rs.
|
||||
const OP_NOP: u8 = 0x0D; // adjust to match your actual opcode values
|
||||
const OP_HLT: u8 = 0x2B;
|
||||
const OP_ADD: u8 = 0x22;
|
||||
const OP_ADDI: u8 = 0x23;
|
||||
const OP_SUBI: u8 = 0x24;
|
||||
const OP_LDW: u8 = 0x07;
|
||||
const OP_STW: u8 = 0x0A;
|
||||
const OP_LLI: u8 = 0x0B;
|
||||
const OP_LUI: u8 = 0x0C;
|
||||
const OP_IEQ: u8 = 0x0E;
|
||||
const OP_JNZ: u8 = 0x15;
|
||||
const OP_JMP: u8 = 0x12;
|
||||
// // Opcodes — must match your Opcode enum discriminants.
|
||||
// // Fill these in from your ISA.md / encode.rs.
|
||||
// const OP_NOP: u8 = 0x0D; // adjust to match your actual opcode values
|
||||
// const OP_HLT: u8 = 0x2B;
|
||||
// const OP_ADD: u8 = 0x22;
|
||||
// const OP_ADDI: u8 = 0x23;
|
||||
// const OP_SUBI: u8 = 0x24;
|
||||
// const OP_LDW: u8 = 0x07;
|
||||
// const OP_STW: u8 = 0x0A;
|
||||
// const OP_LLI: u8 = 0x0B;
|
||||
// const OP_LUI: u8 = 0x0C;
|
||||
// const OP_IEQ: u8 = 0x0E;
|
||||
// const OP_JNZ: u8 = 0x15;
|
||||
// const OP_JMP: u8 = 0x12;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Workload 1: ALU-heavy
|
||||
//
|
||||
// Counts down from 10_000_000 using subi, jumps back to top.
|
||||
// Pure register arithmetic, no memory accesses after init.
|
||||
// Instruction mix: lli, subi, ieq, jnz, hlt
|
||||
//
|
||||
// Layout (each word at address = index * 4):
|
||||
// 0: lli rg0, 0 (upper half of count) [lui follows]
|
||||
// 4: lui rg0, <high>
|
||||
// 8: loop: subi rg0, 1, rg0
|
||||
// 12: ieq rg0, zero, rg1
|
||||
// 16: jnz rg1, zero, 8 [jump to loop]
|
||||
// 20: hlt
|
||||
// ---------------------------------------------------------------------------
|
||||
fn alu_workload() -> Vec<u8> {
|
||||
let count: u32 = 10_000_000;
|
||||
let lo = (count & 0xFFFF) as u16;
|
||||
let hi = ((count >> 16) & 0xFFFF) as u16;
|
||||
// // ---------------------------------------------------------------------------
|
||||
// // Workload 1: ALU-heavy
|
||||
// //
|
||||
// // Counts down from 10_000_000 using subi, jumps back to top.
|
||||
// // Pure register arithmetic, no memory accesses after init.
|
||||
// // Instruction mix: lli, subi, ieq, jnz, hlt
|
||||
// //
|
||||
// // Layout (each word at address = index * 4):
|
||||
// // 0: lli rg0, 0 (upper half of count) [lui follows]
|
||||
// // 4: lui rg0, <high>
|
||||
// // 8: loop: subi rg0, 1, rg0
|
||||
// // 12: ieq rg0, zero, rg1
|
||||
// // 16: jnz rg1, zero, 8 [jump to loop]
|
||||
// // 20: hlt
|
||||
// // ---------------------------------------------------------------------------
|
||||
// fn alu_workload() -> Vec<u8> {
|
||||
// let count: u32 = 10_000_000;
|
||||
// let lo = (count & 0xFFFF) as u16;
|
||||
// let hi = ((count >> 16) & 0xFFFF) as u16;
|
||||
|
||||
let words: &[u32] = &[
|
||||
enc_i(OP_LLI, 0, RG0, lo), // 0x00: lli rg0, lo(count)
|
||||
enc_i(OP_LUI, 0, RG0, hi), // 0x04: lui rg0, hi(count)
|
||||
// loop @ 0x08:
|
||||
enc_i(OP_SUBI, RG0, RG0, 1), // 0x08: subi rg0, rg0, 1
|
||||
enc_r(OP_IEQ, RG0, ZERO, RG1, 0, 0), // 0x0C: ieq rg0, zero, rg1
|
||||
enc_i(OP_JNZ, RG1, ZERO, 0x0008), // 0x10: jnz rg1, zero, 0x08 <- BUG: jnz jumps when rg1!=0
|
||||
// rg1=1 when rg0==0, so we want jez here to keep looping
|
||||
// fix: use jez (jump when rg1==0, i.e. rg0!=0)
|
||||
enc_i(OP_HLT, 0, 0, 0), // 0x14: hlt
|
||||
];
|
||||
// Note: replace OP_JNZ above with your JEZ opcode so the loop continues
|
||||
// while rg0 != 0. JNZ exits when rg1 != 0, i.e. when rg0 == 0 (done).
|
||||
// The ieq sets rg1=1 only when rg0==0, so jnz rg1 exits the loop correctly.
|
||||
// Actually this IS correct: loop while ieq returns 0 (rg0 != 0),
|
||||
// exit when ieq returns 1 (rg0 == 0). jnz rg1 = jump when rg1 != 0 = jump when done.
|
||||
// We want to jump BACK while not done, so we need jez:
|
||||
// jez rg1, zero, 0x08 = jump back when rg1==0 (rg0 != 0, not done yet)
|
||||
// Swap OP_JNZ for your JEZ opcode.
|
||||
words_to_bytes(words)
|
||||
}
|
||||
// let words: &[u32] = &[
|
||||
// enc_i(OP_LLI, 0, RG0, lo), // 0x00: lli rg0, lo(count)
|
||||
// enc_i(OP_LUI, 0, RG0, hi), // 0x04: lui rg0, hi(count)
|
||||
// // loop @ 0x08:
|
||||
// enc_i(OP_SUBI, RG0, RG0, 1), // 0x08: subi rg0, rg0, 1
|
||||
// enc_r(OP_IEQ, RG0, ZERO, RG1, 0, 0), // 0x0C: ieq rg0, zero, rg1
|
||||
// enc_i(OP_JNZ, RG1, ZERO, 0x0008), // 0x10: jnz rg1, zero, 0x08 <- BUG: jnz jumps when rg1!=0
|
||||
// // rg1=1 when rg0==0, so we want jez here to keep looping
|
||||
// // fix: use jez (jump when rg1==0, i.e. rg0!=0)
|
||||
// enc_i(OP_HLT, 0, 0, 0), // 0x14: hlt
|
||||
// ];
|
||||
// // Note: replace OP_JNZ above with your JEZ opcode so the loop continues
|
||||
// // while rg0 != 0. JNZ exits when rg1 != 0, i.e. when rg0 == 0 (done).
|
||||
// // The ieq sets rg1=1 only when rg0==0, so jnz rg1 exits the loop correctly.
|
||||
// // Actually this IS correct: loop while ieq returns 0 (rg0 != 0),
|
||||
// // exit when ieq returns 1 (rg0 == 0). jnz rg1 = jump when rg1 != 0 = jump when done.
|
||||
// // We want to jump BACK while not done, so we need jez:
|
||||
// // jez rg1, zero, 0x08 = jump back when rg1==0 (rg0 != 0, not done yet)
|
||||
// // Swap OP_JNZ for your JEZ opcode.
|
||||
// words_to_bytes(words)
|
||||
// }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Workload 2: Memory-heavy
|
||||
//
|
||||
// Repeatedly writes and reads a 64-word working set.
|
||||
// Tests page table lookup performance under repeated memory access.
|
||||
//
|
||||
// rg0 = base address (0x1000, well within RAM, page-aligned)
|
||||
// rg1 = loop counter (1000 outer iterations)
|
||||
// rg2 = inner counter (64 words per pass)
|
||||
// rg3 = scratch for store value
|
||||
// ---------------------------------------------------------------------------
|
||||
fn memory_workload() -> Vec<u8> {
|
||||
let base: u32 = 0x1000;
|
||||
let outer: u16 = 1000;
|
||||
let inner: u16 = 64;
|
||||
// // ---------------------------------------------------------------------------
|
||||
// // Workload 2: Memory-heavy
|
||||
// //
|
||||
// // Repeatedly writes and reads a 64-word working set.
|
||||
// // Tests page table lookup performance under repeated memory access.
|
||||
// //
|
||||
// // rg0 = base address (0x1000, well within RAM, page-aligned)
|
||||
// // rg1 = loop counter (1000 outer iterations)
|
||||
// // rg2 = inner counter (64 words per pass)
|
||||
// // rg3 = scratch for store value
|
||||
// // ---------------------------------------------------------------------------
|
||||
// fn memory_workload() -> Vec<u8> {
|
||||
// let base: u32 = 0x1000;
|
||||
// let outer: u16 = 1000;
|
||||
// let inner: u16 = 64;
|
||||
|
||||
let words: &[u32] = &[
|
||||
// init
|
||||
enc_i(OP_LLI, 0, RG0, (base & 0xFFFF) as u16), // rg0 = base
|
||||
enc_i(OP_LLI, 0, RG1, outer), // rg1 = outer count
|
||||
// outer_loop @ 0x08:
|
||||
enc_i(OP_LLI, 0, RG2, inner), // rg2 = inner count
|
||||
enc_i(OP_LLI, 0, RG3, 0xABCD), // rg3 = value to write
|
||||
// inner_loop @ 0x10:
|
||||
enc_i(OP_STW, RG3, RG0, 0), // stw rg3, rg0, 0
|
||||
enc_i(OP_LDW, RG0, RG3, 0), // ldw rg0, rg3, 0 (read back)
|
||||
enc_i(OP_ADDI, RG0, RG0, 4), // rg0 += 4
|
||||
enc_i(OP_SUBI, RG2, RG2, 1), // rg2 -= 1
|
||||
enc_r(OP_IEQ, RG2, ZERO, RG4, 0, 0), // ieq rg2, zero, rg4
|
||||
enc_i(OP_JNZ, RG4, ZERO, 0x0010), // jez rg4 -> inner_loop (swap opcode)
|
||||
// restore base, dec outer
|
||||
enc_i(OP_LLI, 0, RG0, (base & 0xFFFF) as u16),
|
||||
enc_i(OP_SUBI, RG1, RG1, 1),
|
||||
enc_r(OP_IEQ, RG1, ZERO, RG4, 0, 0),
|
||||
enc_i(OP_JNZ, RG4, ZERO, 0x0008), // jez rg4 -> outer_loop (swap opcode)
|
||||
enc_i(OP_HLT, 0, 0, 0),
|
||||
];
|
||||
words_to_bytes(words)
|
||||
}
|
||||
// let words: &[u32] = &[
|
||||
// // init
|
||||
// enc_i(OP_LLI, 0, RG0, (base & 0xFFFF) as u16), // rg0 = base
|
||||
// enc_i(OP_LLI, 0, RG1, outer), // rg1 = outer count
|
||||
// // outer_loop @ 0x08:
|
||||
// enc_i(OP_LLI, 0, RG2, inner), // rg2 = inner count
|
||||
// enc_i(OP_LLI, 0, RG3, 0xABCD), // rg3 = value to write
|
||||
// // inner_loop @ 0x10:
|
||||
// enc_i(OP_STW, RG3, RG0, 0), // stw rg3, rg0, 0
|
||||
// enc_i(OP_LDW, RG0, RG3, 0), // ldw rg0, rg3, 0 (read back)
|
||||
// enc_i(OP_ADDI, RG0, RG0, 4), // rg0 += 4
|
||||
// enc_i(OP_SUBI, RG2, RG2, 1), // rg2 -= 1
|
||||
// enc_r(OP_IEQ, RG2, ZERO, RG4, 0, 0), // ieq rg2, zero, rg4
|
||||
// enc_i(OP_JNZ, RG4, ZERO, 0x0010), // jez rg4 -> inner_loop (swap opcode)
|
||||
// // restore base, dec outer
|
||||
// enc_i(OP_LLI, 0, RG0, (base & 0xFFFF) as u16),
|
||||
// enc_i(OP_SUBI, RG1, RG1, 1),
|
||||
// enc_r(OP_IEQ, RG1, ZERO, RG4, 0, 0),
|
||||
// enc_i(OP_JNZ, RG4, ZERO, 0x0008), // jez rg4 -> outer_loop (swap opcode)
|
||||
// enc_i(OP_HLT, 0, 0, 0),
|
||||
// ];
|
||||
// words_to_bytes(words)
|
||||
// }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Workload 3: Branch-heavy
|
||||
//
|
||||
// Mimics the bf.dsa dispatch pattern: compare a value against 8 constants,
|
||||
// branch on each. Worst case for branch predictor.
|
||||
// rg0 = current "instruction" (cycles through 8 values)
|
||||
// rg8-rgf = the 8 bf opcode constants
|
||||
// ---------------------------------------------------------------------------
|
||||
fn branch_workload() -> Vec<u8> {
|
||||
// 8 bf opcodes: + - > < . , [ ]
|
||||
let opcodes: [u16; 8] = [43, 45, 62, 60, 46, 44, 91, 93];
|
||||
// We'll cycle rg0 through these values and do the full dispatch chain.
|
||||
// To keep it self-contained: load opcodes, set outer loop counter,
|
||||
// inner loop cycles rg0 through all 8 values doing ieq+jnz for each.
|
||||
let outer: u16 = u16::MAX;
|
||||
// // ---------------------------------------------------------------------------
|
||||
// // Workload 3: Branch-heavy
|
||||
// //
|
||||
// // Mimics the bf.dsa dispatch pattern: compare a value against 8 constants,
|
||||
// // branch on each. Worst case for branch predictor.
|
||||
// // rg0 = current "instruction" (cycles through 8 values)
|
||||
// // rg8-rgf = the 8 bf opcode constants
|
||||
// // ---------------------------------------------------------------------------
|
||||
// fn branch_workload() -> Vec<u8> {
|
||||
// // 8 bf opcodes: + - > < . , [ ]
|
||||
// let opcodes: [u16; 8] = [43, 45, 62, 60, 46, 44, 91, 93];
|
||||
// // We'll cycle rg0 through these values and do the full dispatch chain.
|
||||
// // To keep it self-contained: load opcodes, set outer loop counter,
|
||||
// // inner loop cycles rg0 through all 8 values doing ieq+jnz for each.
|
||||
// let outer: u16 = u16::MAX;
|
||||
|
||||
let mut w: Vec<u32> = Vec::new();
|
||||
// let mut w: Vec<u32> = Vec::new();
|
||||
|
||||
// Load 8 opcode constants into rg8-rgf (regs 8-15)
|
||||
for (i, &op) in opcodes.iter().enumerate() {
|
||||
w.push(enc_i(OP_LLI, 0, 8 + i as u8, op));
|
||||
}
|
||||
// rg1 = outer counter
|
||||
w.push(enc_i(OP_LLI, 0, RG1, outer));
|
||||
// // Load 8 opcode constants into rg8-rgf (regs 8-15)
|
||||
// for (i, &op) in opcodes.iter().enumerate() {
|
||||
// w.push(enc_i(OP_LLI, 0, 8 + i as u8, op));
|
||||
// }
|
||||
// // rg1 = outer counter
|
||||
// w.push(enc_i(OP_LLI, 0, RG1, outer));
|
||||
|
||||
// outer_loop:
|
||||
let outer_loop_addr = (w.len() * 4) as u16;
|
||||
// // outer_loop:
|
||||
// let outer_loop_addr = (w.len() * 4) as u16;
|
||||
|
||||
// rg5 = inner counter (8 opcodes)
|
||||
w.push(enc_i(OP_LLI, 0, RG5, 8));
|
||||
// rg2 = pointer into opcode table (we'll use addi to step rg0 through values)
|
||||
// For simplicity: just set rg0 to each opcode in sequence via lli each iteration
|
||||
// This is branch-heavy, not arithmetic-heavy, so the lli overhead is fine.
|
||||
for (i, &op) in opcodes.iter().enumerate() {
|
||||
let next_offset = ((w.len() + 4) * 4) as u16; // addr after the jnz
|
||||
w.push(enc_i(OP_LLI, 0, RG0, op)); // rg0 = opcode
|
||||
w.push(enc_r(OP_IEQ, RG0, 8 + i as u8, RG4, 0, 0)); // ieq rg0, rgX, rg4
|
||||
w.push(enc_i(OP_JNZ, RG4, ZERO, next_offset)); // jnz rg4 (jez to skip)
|
||||
w.push(enc_i(OP_NOP, 0, 0, 0)); // "handler" nop
|
||||
}
|
||||
// dec outer, loop
|
||||
let after_dispatch = (w.len() * 4) as u16;
|
||||
w.push(enc_i(OP_SUBI, RG1, RG1, 1));
|
||||
w.push(enc_r(OP_IEQ, RG1, ZERO, RG4, 0, 0));
|
||||
w.push(enc_i(OP_JNZ, RG4, ZERO, outer_loop_addr)); // jez -> outer_loop
|
||||
w.push(enc_i(OP_HLT, 0, 0, 0));
|
||||
// // rg5 = inner counter (8 opcodes)
|
||||
// w.push(enc_i(OP_LLI, 0, RG5, 8));
|
||||
// // rg2 = pointer into opcode table (we'll use addi to step rg0 through values)
|
||||
// // For simplicity: just set rg0 to each opcode in sequence via lli each iteration
|
||||
// // This is branch-heavy, not arithmetic-heavy, so the lli overhead is fine.
|
||||
// for (i, &op) in opcodes.iter().enumerate() {
|
||||
// let next_offset = ((w.len() + 4) * 4) as u16; // addr after the jnz
|
||||
// w.push(enc_i(OP_LLI, 0, RG0, op)); // rg0 = opcode
|
||||
// w.push(enc_r(OP_IEQ, RG0, 8 + i as u8, RG4, 0, 0)); // ieq rg0, rgX, rg4
|
||||
// w.push(enc_i(OP_JNZ, RG4, ZERO, next_offset)); // jnz rg4 (jez to skip)
|
||||
// w.push(enc_i(OP_NOP, 0, 0, 0)); // "handler" nop
|
||||
// }
|
||||
// // dec outer, loop
|
||||
// let after_dispatch = (w.len() * 4) as u16;
|
||||
// w.push(enc_i(OP_SUBI, RG1, RG1, 1));
|
||||
// w.push(enc_r(OP_IEQ, RG1, ZERO, RG4, 0, 0));
|
||||
// w.push(enc_i(OP_JNZ, RG4, ZERO, outer_loop_addr)); // jez -> outer_loop
|
||||
// w.push(enc_i(OP_HLT, 0, 0, 0));
|
||||
|
||||
words_to_bytes(&w)
|
||||
}
|
||||
// words_to_bytes(&w)
|
||||
// }
|
||||
|
||||
fn words_to_bytes(words: &[u32]) -> Vec<u8> {
|
||||
words.iter().flat_map(|w| w.to_le_bytes()).collect()
|
||||
}
|
||||
// fn words_to_bytes(words: &[u32]) -> Vec<u8> {
|
||||
// words.iter().flat_map(|w| w.to_le_bytes()).collect()
|
||||
// }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Runner
|
||||
// ---------------------------------------------------------------------------
|
||||
// // ---------------------------------------------------------------------------
|
||||
// // Runner
|
||||
// // ---------------------------------------------------------------------------
|
||||
|
||||
struct BenchResult {
|
||||
name: &'static str,
|
||||
instructions: u64,
|
||||
elapsed: Duration,
|
||||
mips: f64,
|
||||
}
|
||||
// struct BenchResult {
|
||||
// name: &'static str,
|
||||
// instructions: u64,
|
||||
// elapsed: Duration,
|
||||
// mips: f64,
|
||||
// }
|
||||
|
||||
fn run_bench(name: &'static str, binary: Vec<u8>) -> BenchResult {
|
||||
let mut emu = make_emulator();
|
||||
let handle = emu.state_handle();
|
||||
// fn run_bench(name: &'static str, binary: Vec<u8>) -> BenchResult {
|
||||
// let mut emu = make_emulator();
|
||||
// let handle = emu.state_handle();
|
||||
|
||||
let pages: Vec<Page> = Page::paginate(&binary).collect();
|
||||
for (i, page) in pages.iter().enumerate() {
|
||||
emu.memory_mut().write_page(i as u32 * 4096, page);
|
||||
}
|
||||
// let pages: Vec<Page> = Page::paginate(&binary).collect();
|
||||
// for (i, page) in pages.iter().enumerate() {
|
||||
// emu.memory_mut().write_page(i as u32 * 4096, page);
|
||||
// }
|
||||
|
||||
let start = Instant::now();
|
||||
// run() blocks until HLT
|
||||
emu.run().expect("emulator run failed");
|
||||
let elapsed = start.elapsed();
|
||||
// let start = Instant::now();
|
||||
// // run() blocks until HLT
|
||||
// emu.run().expect("emulator run failed");
|
||||
// let elapsed = start.elapsed();
|
||||
|
||||
// read clock from emulator — we need to expose it.
|
||||
// For now, estimate from elapsed + target MIPS.
|
||||
// TODO: expose emulator.clock() -> u64 and use that directly.
|
||||
let instructions = handle.proc.load().clock as u64;
|
||||
let mips = instructions as f64 / elapsed.as_micros() as f64;
|
||||
// // read clock from emulator — we need to expose it.
|
||||
// // For now, estimate from elapsed + target MIPS.
|
||||
// // TODO: expose emulator.clock() -> u64 and use that directly.
|
||||
// let instructions = handle.proc.load().clock as u64;
|
||||
// let mips = instructions as f64 / elapsed.as_micros() as f64;
|
||||
|
||||
BenchResult {
|
||||
name,
|
||||
instructions,
|
||||
elapsed,
|
||||
mips,
|
||||
}
|
||||
}
|
||||
// BenchResult {
|
||||
// name,
|
||||
// instructions,
|
||||
// elapsed,
|
||||
// mips,
|
||||
// }
|
||||
// }
|
||||
|
||||
fn main() {
|
||||
println!("DSA Emulator Benchmark");
|
||||
println!("======================");
|
||||
println!();
|
||||
// fn main() {
|
||||
// println!("DSA Emulator Benchmark");
|
||||
// println!("======================");
|
||||
// println!();
|
||||
|
||||
// NOTE: you need to expose `pub fn clock(&self) -> usize` on Emulator
|
||||
// and ensure `run()` returns normally on HLT without blocking in idle_wait.
|
||||
// The idle_wait loop currently spins forever after HLT — for benchmarking,
|
||||
// you want run() to return when the program halts. Add a flag or check:
|
||||
// if !self.internal_state.running { break 'emu; }
|
||||
// after the HLT handler sets running=false.
|
||||
// // NOTE: you need to expose `pub fn clock(&self) -> usize` on Emulator
|
||||
// // and ensure `run()` returns normally on HLT without blocking in idle_wait.
|
||||
// // The idle_wait loop currently spins forever after HLT — for benchmarking,
|
||||
// // you want run() to return when the program halts. Add a flag or check:
|
||||
// // if !self.internal_state.running { break 'emu; }
|
||||
// // after the HLT handler sets running=false.
|
||||
|
||||
let workloads: &[(&'static str, fn() -> Vec<u8>)] = &[
|
||||
("ALU-heavy (10M countdown)", alu_workload),
|
||||
("Memory-heavy (64K rw ops)", memory_workload),
|
||||
("Branch-heavy (bf dispatch)", branch_workload),
|
||||
];
|
||||
// let workloads: &[(&'static str, fn() -> Vec<u8>)] = &[
|
||||
// ("ALU-heavy (10M countdown)", alu_workload),
|
||||
// ("Memory-heavy (64K rw ops)", memory_workload),
|
||||
// ("Branch-heavy (bf dispatch)", branch_workload),
|
||||
// ];
|
||||
|
||||
let mut results = Vec::new();
|
||||
for &(name, build) in workloads {
|
||||
print!("Running {}... ", name);
|
||||
let binary = build();
|
||||
let result = run_bench(name, binary);
|
||||
println!("done ({:.1}ms)", result.elapsed.as_secs_f64() * 1000.0);
|
||||
results.push(result);
|
||||
}
|
||||
// let mut results = Vec::new();
|
||||
// for &(name, build) in workloads {
|
||||
// print!("Running {}... ", name);
|
||||
// let binary = build();
|
||||
// let result = run_bench(name, binary);
|
||||
// println!("done ({:.1}ms)", result.elapsed.as_secs_f64() * 1000.0);
|
||||
// results.push(result);
|
||||
// }
|
||||
|
||||
println!();
|
||||
println!(
|
||||
"{:<35} {:>12} {:>12} {:>10}",
|
||||
"Workload", "Instructions", "Time", "MIPS"
|
||||
);
|
||||
println!("{}", "-".repeat(72));
|
||||
for r in &results {
|
||||
println!(
|
||||
"{:<35} {:>12} {:>10.1}ms {:>10.1}",
|
||||
r.name,
|
||||
r.instructions,
|
||||
r.elapsed.as_secs_f64() * 1000.0,
|
||||
r.mips,
|
||||
);
|
||||
}
|
||||
// println!();
|
||||
// println!(
|
||||
// "{:<35} {:>12} {:>12} {:>10}",
|
||||
// "Workload", "Instructions", "Time", "MIPS"
|
||||
// );
|
||||
// println!("{}", "-".repeat(72));
|
||||
// for r in &results {
|
||||
// println!(
|
||||
// "{:<35} {:>12} {:>10.1}ms {:>10.1}",
|
||||
// r.name,
|
||||
// r.instructions,
|
||||
// r.elapsed.as_secs_f64() * 1000.0,
|
||||
// r.mips,
|
||||
// );
|
||||
// }
|
||||
|
||||
let avg_mips: f64 = results.iter().map(|r| r.mips).sum::<f64>() / results.len() as f64;
|
||||
println!("{}", "-".repeat(72));
|
||||
println!("{:<35} {:>35.1} MIPS avg", "Overall", avg_mips);
|
||||
}
|
||||
// let avg_mips: f64 = results.iter().map(|r| r.mips).sum::<f64>() / results.len() as f64;
|
||||
// println!("{}", "-".repeat(72));
|
||||
// println!("{:<35} {:>35.1} MIPS avg", "Overall", avg_mips);
|
||||
// }
|
||||
|
||||
fn main() {}
|
||||
|
||||
@@ -1,17 +1,4 @@
|
||||
use clap::Parser;
|
||||
<<<<<<< HEAD
|
||||
use common::isa::instructions::Instruction;
|
||||
use dsa::{
|
||||
args::DsaArgs, io::display::{DisplayDevice, DisplayHandle}, BulkAllocStore, Emulator, Page,
|
||||
RandomAccessMemory,
|
||||
SharedState,
|
||||
};
|
||||
use std::{
|
||||
sync::{atomic::Ordering, Arc},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
=======
|
||||
|
||||
use common::prelude::Instruction;
|
||||
use dsa::{Emulator, Page, args::DsaArgs, frontend::run_app};
|
||||
@@ -19,7 +6,6 @@ use std::thread;
|
||||
|
||||
const STACK_SIZE: usize = 1024 * 1024 * 16;
|
||||
|
||||
>>>>>>> refs/remotes/origin/main
|
||||
fn main() {
|
||||
let args = DsaArgs::parse();
|
||||
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user