fixed broken merge commit

This commit is contained in:
2026-03-13 16:48:14 +00:00
parent f8a99ac9b3
commit 57ed7eb029
5 changed files with 302 additions and 389 deletions
-67
View File
@@ -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
View File
@@ -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() {}
-14
View File
@@ -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.