implement vmv in pim-simulator
Validate Operations / validate-operations (push) Has been cancelled

fix offset selection implementation to match the pim isa
automatic code format for pim-simulator
This commit is contained in:
NiccoloN
2026-07-31 17:24:19 +02:00
parent a0131c6f7a
commit 9ca1a0ed9f
23 changed files with 584 additions and 497 deletions
@@ -13,7 +13,7 @@ use pimcore::memory_manager::CoreMemory;
use pimcore::tracing::TRACER;
use serde_json::Value;
use std::collections::HashMap;
use std::fs::{self, File, read_link};
use std::fs::{self, File};
use std::io::{BufReader, Write};
use std::path::PathBuf;
@@ -110,7 +110,7 @@ fn map_crossbars_to_cores<'c>(
sym_link_files.sort_by_key(|&(num, _)| num);
for (_, symlink) in sym_link_files {
let real_path = read_link(symlink).unwrap();
let real_path = symlink.canonicalize().unwrap();
let path_as_str = real_path.to_str().unwrap();
assert!(
global_crossbars.contains_key(path_as_str),
@@ -162,6 +162,8 @@ fn get_crossbars(config: &Value, args: &Args) -> anyhow::Result<HashMap<String,
res.insert(
weight_file
.path()
.canonicalize()
.context("Failed to resolve crossbar path")?
.to_str()
.context("file name not utf-8")?
.to_string(),
@@ -1,7 +1,6 @@
use crate::memory_manager::{CoreMemory, MemoryStorable};
use anyhow::{Result, bail, ensure};
#[derive(Debug, Clone)]
pub struct Crossbar {
max_width: usize,
@@ -12,7 +11,12 @@ pub struct Crossbar {
impl Crossbar {
pub fn new(width: usize, height: usize, memory: CoreMemory) -> Self {
Self { max_width: width, max_height: height, memory, stored_bytes:0 }
Self {
max_width: width,
max_height: height,
memory,
stored_bytes: 0,
}
}
pub fn width(&self) -> usize {
@@ -27,27 +31,36 @@ impl Crossbar {
self.stored_bytes
}
pub fn execute_store<T>(&mut self, element: &[T]) -> Result<()> where
T: MemoryStorable, {
pub fn execute_store<T>(&mut self, element: &[T]) -> Result<()>
where
T: MemoryStorable,
{
self.memory.clear();
let total_size = self.max_width * self.max_height;
self.memory.set_capacity(total_size);
let stored_size = std::mem::size_of_val(element);
ensure!(stored_size <= total_size, "Storing more than crossbar can handle");
self.stored_bytes=stored_size;
ensure!(
stored_size <= total_size,
"Storing more than crossbar can handle"
);
self.stored_bytes = stored_size;
self.memory.execute_store(0, element)
}
pub fn load<T>(&self, size: usize) -> Result<Vec<&[T]>> where
T: MemoryStorable, {
if self.memory.get_len() < size
//|| self.stored_bytes < size
pub fn load<T>(&self, size: usize) -> Result<Vec<&[T]>>
where
T: MemoryStorable,
{
if self.memory.get_len() < size
//|| self.stored_bytes < size
{
bail!("Loading outside crossbar boundary [{} {}] < {}", self.stored_bytes, self.memory.get_len() , size);
bail!(
"Loading outside crossbar boundary [{} {}] < {}",
self.stored_bytes,
self.memory.get_len(),
size
);
}
self.memory.load_const(0, size)
}
}
@@ -1,6 +1,6 @@
use crate::utility::AddressArg;
use std::{collections::HashMap, fmt::Debug};
use anyhow::{Context, Result, ensure};
use std::{collections::HashMap, fmt::Debug};
use crate::{
cpu::crossbar::Crossbar,
@@ -10,14 +10,13 @@ use crate::{
pub mod crossbar;
#[derive(Debug, Clone)]
pub struct CPU<'a> {
cores: Box<[Core<'a>]>,
}
impl<'a> CPU<'a> {
pub fn new(num_cores: impl TryToUsize, crossbars: Vec<Vec<&'a Crossbar>> ) -> Self {
pub fn new(num_cores: impl TryToUsize, crossbars: Vec<Vec<&'a Crossbar>>) -> Self {
let num_cores = num_cores.try_into().expect("num_cores can not be negative");
assert!(crossbars.len() == num_cores + 1);
let mut cores = Vec::new();
@@ -30,25 +29,31 @@ impl<'a> CPU<'a> {
}
pub fn host<'b>(&'b mut self) -> &'b mut Core<'a>
where 'a : 'b
where
'a: 'b,
{
& mut self.cores[0]
&mut self.cores[0]
}
pub fn core<'b >(&'b mut self, index: impl TryToUsize) -> &'b mut Core<'a>
where 'a : 'b
pub fn core<'b>(&'b mut self, index: impl TryToUsize) -> &'b mut Core<'a>
where
'a: 'b,
{
let index = index.try_into().expect("can not be negative");
& mut self.cores[index]
&mut self.cores[index]
}
pub fn num_core(&self) -> usize {
self.cores.len()
}
pub(crate) fn host_and_cores<'b, 'c >(&'b mut self, core: impl TryToUsize) -> (&'c mut Core<'a>, &'c mut Core<'a>)
where 'a: 'b,
'b: 'c
pub(crate) fn host_and_cores<'b, 'c>(
&'b mut self,
core: impl TryToUsize,
) -> (&'c mut Core<'a>, &'c mut Core<'a>)
where
'a: 'b,
'b: 'c,
{
let core = core.try_into().expect("core can not be negative");
assert_ne!(
@@ -63,8 +68,12 @@ impl<'a> CPU<'a> {
(host, core)
}
pub fn get_multiple_cores<'b, const N: usize>(&'b mut self, indices: [usize; N]) -> [&'b mut Core<'a>; N]
where 'a : 'b
pub fn get_multiple_cores<'b, const N: usize>(
&'b mut self,
indices: [usize; N],
) -> [&'b mut Core<'a>; N]
where
'a: 'b,
{
self.cores.get_disjoint_mut(indices).unwrap()
}
@@ -78,7 +87,7 @@ pub struct Core<'a> {
}
impl<'a> Core<'a> {
fn new(crossbars : Vec<&'a Crossbar>) -> Self {
fn new(crossbars: Vec<&'a Crossbar>) -> Self {
Self {
crossbars,
memory: CoreMemory::new(),
@@ -139,7 +148,12 @@ impl<'a> Core<'a> {
(memory, crossbars)
}
pub fn memset(&mut self, address: impl AddressArg, size: impl TryToUsize, val: u8) -> Result<()> {
pub fn memset(
&mut self,
address: impl AddressArg,
size: impl TryToUsize,
val: u8,
) -> Result<()> {
let address = address.to_address_usize()?;
let size = size.try_into().context("size can not be negative")?;
self.memory.memset(address, size, val)
@@ -278,9 +278,18 @@ impl InstructionDataBuilder {
}
fn check_sanity(&self) {
assert!(!(self.get_r2() != 0 && self.get_imm() != 0 && self.get_mbiw() != 0 && self.get_imm_core() != 0));
assert!(!(self.get_ibiw() != 0 && self.get_offset_select() != 0 && self.get_imm_relu() != 0));
assert!(!(self.get_obiw() != 0 && self.get_offset_value() != 0 && self.get_imm_group() != 0));
assert!(
!(self.get_r2() != 0
&& self.get_imm() != 0
&& self.get_mbiw() != 0
&& self.get_imm_core() != 0)
);
assert!(
!(self.get_ibiw() != 0 && self.get_offset_select() != 0 && self.get_imm_relu() != 0)
);
assert!(
!(self.get_obiw() != 0 && self.get_offset_value() != 0 && self.get_imm_group() != 0)
);
}
pub fn build(&mut self) -> InstructionData {
@@ -9,7 +9,7 @@ use crate::{
type_traits::{FromFloat, UpcastDestTraits, UpcastSlice},
},
tracing::TRACER,
utility::{add_offset_r1, add_offset_r2, add_offset_rd},
utility::{AddressArg, add_offset_r1, add_offset_r2, add_offset_rd},
};
use aligned_vec::{AVec, ConstAlign};
use anyhow::{Context, Result, ensure};
@@ -58,7 +58,7 @@ pub static NAMES: LazyLock<HashMap<usize, &'static str>> = LazyLock::new(|| {
add_name_simd!(hash, vtanh);
add_name_simd!(hash, vsigm);
add_name_simd!(hash, vsoftmax);
add_name!(hash, vmv);
add_name_simd!(hash, vmv);
add_name!(hash, vrsu);
add_name!(hash, vrsl);
add_name!(hash, ld);
@@ -189,6 +189,7 @@ static SIMD: LazyLock<HashMap<usize, HashMap<(usize, usize), InstructionType>>>
add_simd_to_map!(storage, vtanh);
add_simd_to_map!(storage, vsigm);
add_simd_to_map!(storage, vsoftmax);
add_simd_to_map!(storage, vmv);
add_simd_to_map!(storage, mvmul);
storage
});
@@ -273,7 +274,10 @@ where
"Stored crossbar bytes do not describe an integral number of columns"
);
let crossbar_elem_width = crossbar_stored_bytes / bytes_per_column;
ensure!(crossbar_elem_width != 0, "Crossbar contains no stored columns");
ensure!(
crossbar_elem_width != 0,
"Crossbar contains no stored columns"
);
let loads = memory
.reserve_load(r1_val, crossbar_height * size_of::<F>())?
@@ -742,7 +746,41 @@ where
#[inline(never)]
pub fn vmv(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
todo!()
panic!("You are calling a placeholder, the real call is the generic version");
}
#[inline(never)]
pub(super) fn vmv_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
where
F: Copy + MemoryStorable,
{
let (core_indx, rd, r1, r2, imm_len, _, _) = data.get_core_rd_r1_r2_immlen_offset();
let core = cores.core(core_indx);
let source = core.register(r1).to_address_usize()?;
let destination = core.register(rd);
let stride: usize = core
.register(r2)
.try_into()
.context("vmv stride can not be negative")?;
let (element_count, _) = vector_lengths::<F>(imm_len)?;
let stride_bytes = stride
.checked_mul(size_of::<F>())
.context("vmv byte stride overflow")?;
let mut result = Vec::with_capacity(element_count);
for index in 0..element_count {
let offset = index
.checked_mul(stride_bytes)
.context("vmv source offset overflow")?;
let address = source
.checked_add(offset)
.context("vmv source address overflow")?;
result.push(
core.reserve_load(address, size_of::<F>())?
.execute_load::<F>()?[0][0],
);
}
core.execute_store(destination, &result)?;
Ok(InstructionStatus::Completed)
}
#[inline(never)]
@@ -827,7 +865,7 @@ pub fn lmv(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
}
#[inline(never)]
pub fn isa_send(functor : usize) -> bool{
pub fn isa_send(functor: usize) -> bool {
(send as *const () as usize) == functor
}
@@ -837,7 +875,7 @@ pub fn send(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
}
#[inline(never)]
pub fn isa_recv(functor : usize) -> bool{
pub fn isa_recv(functor: usize) -> bool {
(recv as *const () as usize) == functor
}
@@ -7,9 +7,9 @@ use crate::{
};
use anyhow::{Context, Result};
use std::mem::swap;
pub mod helper;
pub mod instruction_data;
pub mod isa;
pub mod helper;
#[derive(Clone, Copy, Debug)]
pub struct Instruction {
@@ -41,7 +41,8 @@ impl Instruction {
}
pub fn execute<'a, 'b>(&'b self, cpu: &mut CPU<'a>) -> InstructionStatus
where 'a : 'b
where
'a: 'b,
{
(self.functor)(cpu, self.data)
.with_context(|| format!("Instruction: {}", functor_to_name(self.functor as usize)))
@@ -78,7 +78,11 @@ impl CoreMemory {
}
}
pub fn reserve_load(&mut self, address: impl TryToUsize, size: impl TryToUsize) -> Result<&mut Self>
pub fn reserve_load(
&mut self,
address: impl TryToUsize,
size: impl TryToUsize,
) -> Result<&mut Self>
where {
let address = address.try_into().context("address can not be negative")?;
let size = size.try_into().context("size can not be negative")?;
@@ -87,7 +91,8 @@ where {
size,
};
if self.memory.len() < address + size {
self.memory.resize(min((address + size) * 2, u32::MAX as usize), 0);
self.memory
.resize(min((address + size) * 2, u32::MAX as usize), 0);
}
self.load_requests.push(load_request);
Ok(self)
@@ -105,14 +110,24 @@ where {
for (load_index, load_request) in load_requests.drain(..).enumerate() {
let LoadRequest { index, size } = load_request;
let memory_slice = &memory[index..index + size];
let memory_slice = unsafe { slice_from_u8(memory_slice) }
.with_context(|| format!("Load number: {} Accessing from {} to {}", load_index, index, index + size))?;
let memory_slice = unsafe { slice_from_u8(memory_slice) }.with_context(|| {
format!(
"Load number: {} Accessing from {} to {}",
load_index,
index,
index + size
)
})?;
res.push(memory_slice);
}
Ok(res)
}
pub fn load_const<T>(&self, address: impl TryToUsize, size: impl TryToUsize) -> Result<Vec<&[T]>>
pub fn load_const<T>(
&self,
address: impl TryToUsize,
size: impl TryToUsize,
) -> Result<Vec<&[T]>>
where
T: MemoryStorable,
{
@@ -125,7 +140,7 @@ where {
let mut res = Vec::new();
let memory_slice = &memory[address..address + size];
let memory_slice = unsafe { slice_from_u8(memory_slice) }
.with_context(|| format!("Accessing from {} to {}", address, address + size))?;
.with_context(|| format!("Accessing from {} to {}", address, address + size))?;
res.push(memory_slice);
Ok(res)
}
@@ -155,7 +170,12 @@ where {
Ok(())
}
pub fn memset(&mut self, address: impl TryToUsize, size: impl TryToUsize, val: u8) -> Result<()> {
pub fn memset(
&mut self,
address: impl TryToUsize,
size: impl TryToUsize,
val: u8,
) -> Result<()> {
let address = address.try_into().expect("address can not be negative");
let size = size.try_into().expect("size can not be negative");
let Self { memory, .. } = self;
@@ -174,11 +194,11 @@ where {
}
}
pub fn get_len(&self) ->usize {
pub fn get_len(&self) -> usize {
self.memory.len()
}
pub(crate) fn clear(&mut self) {
pub(crate) fn clear(&mut self) {
self.memory.clear();
}
}
@@ -257,7 +277,11 @@ mod test {
let mut data = [0_f32; 2];
data[0] = loads[0][0] + loads[1][0];
core_memory.execute_store(4, &data[0..1]).unwrap();
let loads: &[f32] = core_memory.reserve_load(0, 16).unwrap().execute_load().unwrap()[0];
let loads: &[f32] = core_memory
.reserve_load(0, 16)
.unwrap()
.execute_load()
.unwrap()[0];
println!("{:?}", loads);
assert!(loads[0] == 5_f32 && loads[1] == 12_f32 && loads[2] == 7_f32 && loads[3] == 15_f32)
}
@@ -1,5 +1,3 @@
use std::{
borrow::Cow,
fmt::Debug,
@@ -9,32 +7,32 @@ use std::{
use anyhow::Context;
pub trait FromFloat {
fn from_f32(val :f32) -> Self;
fn from_f64(val :f64) -> Self;
fn from_f32(val: f32) -> Self;
fn from_f64(val: f64) -> Self;
}
impl FromFloat for f32 {
fn from_f32(val :f32) -> Self {
fn from_f32(val: f32) -> Self {
val
}
fn from_f64(val :f64) -> Self {
fn from_f64(val: f64) -> Self {
val as f32
}
}
impl FromFloat for f64 {
fn from_f32(val :f32) -> Self {
fn from_f32(val: f32) -> Self {
val as f64
}
fn from_f64(val :f64) -> Self {
val
fn from_f64(val: f64) -> Self {
val
}
}
pub trait HasTanh {
fn tanh(self) -> Self ;
fn tanh(self) -> Self;
}
impl HasTanh for f32 {
@@ -50,7 +48,7 @@ impl HasTanh for f64 {
}
pub trait HasSigm {
fn sigm(self) -> Self ;
fn sigm(self) -> Self;
}
impl HasSigm for f32 {
@@ -91,34 +89,36 @@ impl HasExp for f64 {
}
}
pub trait TryToUsize: TryInto<usize, Error = Self::TryError>
where std::result::Result<usize, Self::TryError> : Context<usize, Self::TryError>
pub trait TryToUsize: TryInto<usize, Error = Self::TryError>
where
std::result::Result<usize, Self::TryError>: Context<usize, Self::TryError>,
{
type TryError: Debug + Send + Sync + 'static + std::error::Error;
}
impl<T, E> TryToUsize for T
where
impl<T, E> TryToUsize for T
where
T: TryInto<usize, Error = E>,
E: Debug + Send + Sync + 'static + std::error::Error,
std::result::Result<usize, E> : Context<usize, E>
std::result::Result<usize, E>: Context<usize, E>,
{
type TryError = E;
}
pub trait FromUsize {
fn from_usize(v: usize) -> Self;
}
impl FromUsize for f32 {
fn from_usize(v: usize) -> Self { v as f32 }
fn from_usize(v: usize) -> Self {
v as f32
}
}
impl FromUsize for f64 {
fn from_usize(v: usize) -> Self { v as f64 }
fn from_usize(v: usize) -> Self {
v as f64
}
}
pub trait UpcastDestTraits<T>:
@@ -250,7 +250,10 @@ fn detect_deadlock(cores_instructions: &[CoreInstructions]) -> Option<DeadlockIn
let (this_core, target_core) = data.get_core_immcore();
if isa_recv(functor_address) {
states.insert(this_core, CoreState::ReceivingFrom(target_core, data.imm_len()));
states.insert(
this_core,
CoreState::ReceivingFrom(target_core, data.imm_len()),
);
} else if isa_send(functor_address) {
states.insert(this_core, CoreState::SendingTo(target_core, data.imm_len()));
} else {
@@ -274,8 +277,7 @@ fn detect_deadlock(cores_instructions: &[CoreInstructions]) -> Option<DeadlockIn
wait_for.insert(core_id, *target_core);
}
}
CoreState::Working | CoreState::Halted => {
}
CoreState::Working | CoreState::Halted => {}
}
}
@@ -41,16 +41,17 @@ impl SendRecv {
}
}
pub fn handle_send_recv<'a, 'b >(
pub fn handle_send_recv<'a, 'b>(
cpu: &'b mut CPU<'a>,
core_instructions: & mut [CoreInstructions],
send_recv: & mut SendRecv,
core_instructions: &mut [CoreInstructions],
send_recv: &mut SendRecv,
core_result: InstructionStatus,
) -> (bool, usize)
where 'a : 'b
where
'a: 'b,
{
let transfer_memory = |cpu: &'b mut CPU<'a>,
core_instructions: & mut [CoreInstructions],
core_instructions: &mut [CoreInstructions],
sender: Option<SendRecvInfo>,
receiver: Option<SendRecvInfo>| {
if let Some(sender) = sender
@@ -1,4 +1,3 @@
use std::fs::File;
use crate::{
@@ -19,58 +18,41 @@ impl Trace {
/////////////////Scalar/register Instructions//////////////////
///////////////////////////////////////////////////////////////
pub fn pre_sldi(&mut self, cores: &mut CPU, data: InstructionData) {
pub fn pre_sldi(&mut self, cores: &mut CPU, data: InstructionData) {}
}
pub fn post_sldi(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_sldi(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_sld(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_sld(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_sld(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_sld(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_sadd(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_sadd(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_sadd(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_sadd(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_ssub(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_ssub(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_ssub(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_ssub(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_smul(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_smul(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_smul(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_smul(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_saddi(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_saddi(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_saddi(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_saddi(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_smuli(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_smuli(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_smuli(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_smuli(&mut self, cores: &mut CPU, data: InstructionData) {}
/////////////////////////////////////////////////////////////////
///////////////////Matrix/vector Instructions////////////////////
/////////////////////////////////////////////////////////////////
pub fn pre_setbw(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_setbw(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_setbw(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_setbw(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_mvm<F, M, T>(&mut self, cores: &mut CPU, data: InstructionData)
where
@@ -80,7 +62,7 @@ impl Trace {
M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
F: UpcastDestTraits<F> + MemoryStorable,
{
self.mvm_impl::<F,M,T>(cores, data, "Pre");
self.mvm_impl::<F, M, T>(cores, data, "Pre");
}
pub fn post_mvm<F, M, T>(&mut self, cores: &mut CPU, data: InstructionData)
@@ -91,11 +73,15 @@ impl Trace {
M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
F: UpcastDestTraits<F> + MemoryStorable,
{
self.mvm_impl::<F,M,T>(cores, data, "Post");
self.mvm_impl::<F, M, T>(cores, data, "Post");
}
pub fn mvm_impl<F, M, T>(&mut self, cores: &mut CPU, data: InstructionData, prefix : &'static str)
where
pub fn mvm_impl<F, M, T>(
&mut self,
cores: &mut CPU,
data: InstructionData,
prefix: &'static str,
) where
[F]: UpcastSlice<T> + UpcastSlice<M>,
[M]: UpcastSlice<T>,
T: UpcastDestTraits<T> + MemoryStorable,
@@ -267,39 +253,27 @@ impl Trace {
/////////////////////////////////////////////////////////////////
/////Communication/synchronization Instructions/////////////////
/////////////////////////////////////////////////////////////////
pub fn pre_ld(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_ld(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_ld(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_ld(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_st(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_st(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_st(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_st(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_lldi(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_lldi(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_lldi(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_lldi(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_lmv(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_lmv(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_lmv(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_lmv(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_send(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_send(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_send(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_send(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn pre_recv(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn pre_recv(&mut self, cores: &mut CPU, data: InstructionData) {}
pub fn post_recv(&mut self, cores: &mut CPU, data: InstructionData) {
}
pub fn post_recv(&mut self, cores: &mut CPU, data: InstructionData) {}
}
@@ -15,7 +15,6 @@ use crate::Executable;
use std::path::PathBuf;
use std::sync::{LazyLock, Mutex};
#[cfg(not(any(feature = "tracing", feature = "profile_time")))]
pub struct Trace {}
@@ -28,5 +27,4 @@ impl Trace {
pub fn init(&mut self, num_core: usize, path: PathBuf) {}
}
pub static TRACER: LazyLock<Mutex<Trace>> = LazyLock::new(|| Trace::new().into());
@@ -8,7 +8,7 @@ pub mod profile_analysis;
pub mod profile_isa;
pub struct Trace {
instruction_times: HashMap<String, Vec<(u128,u128)>>,
instruction_times: HashMap<String, Vec<(u128, u128)>>,
core_start_time: HashMap<usize, Option<Instant>>,
start_time: Instant,
}
@@ -51,7 +51,7 @@ impl Trace {
Self {
instruction_times,
core_start_time: HashMap::new(),
start_time: Instant::now()
start_time: Instant::now(),
}
}
@@ -145,17 +145,15 @@ pub fn print_textual_report(stats: &[InstructionStats]) {
println!("{table}");
}
pub fn generate_interactive_report(
timings: &HashMap<String, Vec<(u128, u128)>>,
instructions_to_plot: &[&str], // <-- NEW: Only plot these
file_path: &str,
) {
use plotly::common::{Mode, Marker, Line};
use plotly::layout::{Axis, Layout};
use plotly::{Plot, Scatter};
use std::collections::HashMap;
use plotly::common::{Line, Marker, Mode};
use plotly::layout::{Axis, Layout};
use plotly::{Plot, Scatter};
use std::collections::HashMap;
let mut plot = Plot::new();
for &instruction_name in instructions_to_plot {
@@ -163,8 +161,9 @@ use std::collections::HashMap;
if let Some(times) = timings.get(instruction_name) {
let x_axis: Vec<f64> = times.iter().map(|&(ts, _)| ts as f64).collect();
let y_axis: Vec<f64> = times.iter().map(|&(_, dur)| dur as f64).collect();
let text_array: Vec<String> = times.iter()
let text_array: Vec<String> = times
.iter()
.map(|&(_, dur)| format_time(dur as f64))
.collect();
@@ -181,7 +180,9 @@ use std::collections::HashMap;
}
let layout = Layout::new()
.title(plotly::common::Title::new("Simulator Timeline: Top Offenders"))
.title(plotly::common::Title::new(
"Simulator Timeline: Top Offenders",
))
.x_axis(Axis::new().title(plotly::common::Title::new("Absolute Time (ns)")))
.y_axis(Axis::new().title(plotly::common::Title::new("Execution Duration")));
@@ -189,4 +190,3 @@ use std::collections::HashMap;
plot.write_html(file_path);
println!("🌐 Interactive timeline saved to {}", file_path);
}
@@ -34,10 +34,11 @@ impl Trace {
start_time,
} = self;
let now = Instant::now();
instruction_times
.get_mut(name)
.unwrap()
.push((now.duration_since(*start_time).as_nanos(), now.duration_since(core_start_time[&core_indx].unwrap()).as_nanos()));
instruction_times.get_mut(name).unwrap().push((
now.duration_since(*start_time).as_nanos(),
now.duration_since(core_start_time[&core_indx].unwrap())
.as_nanos(),
));
self.core_start_time.insert(core_indx, None);
}
@@ -7,7 +7,6 @@ pub struct Trace {
out_files: Vec<File>,
}
impl Trace {
pub fn new() -> Self {
Self {
@@ -25,4 +24,3 @@ impl Trace {
}
}
}
@@ -283,7 +283,6 @@ impl Trace {
M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
F: UpcastDestTraits<F> + MemoryStorable,
{
let (core_indx, rd, r1, mbiw, relu, group) = data.get_core_rd_r1_mbiw_immrelu_immgroup();
let file: &mut File = self
.out_files
@@ -337,7 +336,7 @@ impl Trace {
);
pretty_print::print_slice::<_, F>(file, loads[0], 30);
writeln!(file, "\tCrossbar[{}:{}](B): ", 0, crossbar_byte_size);
pretty_print::print_slice::<_,F>(file, matrix, 30);
pretty_print::print_slice::<_, F>(file, matrix, 30);
writeln!(
file,
"\tLocal[{}:{}](out): ",
@@ -409,11 +408,11 @@ impl Trace {
.unwrap();
writeln!(file, "{} Memory:", prefix);
write!(file, "\tLocal[{}:{}](A): ", r1_final, r1_final + byte_len);
pretty_print::print_slice::<_,f32>(file, loads[0], 30);
write!(file, "\tLocal[{}:{}](B): ", r2_final , r2_final + byte_len);
pretty_print::print_slice::<_,f32>(file, loads[1], 30);
write!(file, "\tLocal[{}:{}](out): ", rd_final, rd_final+ byte_len);
pretty_print::print_slice::<_,f32>(file, loads[2], 30);
pretty_print::print_slice::<_, f32>(file, loads[0], 30);
write!(file, "\tLocal[{}:{}](B): ", r2_final, r2_final + byte_len);
pretty_print::print_slice::<_, f32>(file, loads[1], 30);
write!(file, "\tLocal[{}:{}](out): ", rd_final, rd_final + byte_len);
pretty_print::print_slice::<_, f32>(file, loads[2], 30);
if prefix == "Post" {
writeln!(file, "\n###############################################\n");
}
@@ -1027,9 +1026,9 @@ impl Trace {
let core_memory = core.load::<u8>(rd_val, imm_len).unwrap();
writeln!(file, "{} Memory:", prefix);
writeln!(file, "\tHost[{}:{}]: ", r1_val, r1_val + imm_len as usize,);
pretty_print::print_slice::<_,f32>(file, global_memory[0], 30);
pretty_print::print_slice::<_, f32>(file, global_memory[0], 30);
writeln!(file, "\tLocal[{}:{}]: ", rd_val, rd_val + imm_len as usize,);
pretty_print::print_slice::<_,f32>(file, core_memory[0], 30);
pretty_print::print_slice::<_, f32>(file, core_memory[0], 30);
if prefix == "Post" {
writeln!(file, "\n###############################################\n");
@@ -1079,9 +1078,9 @@ impl Trace {
let global_memory = host.load::<u8>(rd_val, imm_len).unwrap();
writeln!(file, "{} Memory:", prefix);
writeln!(file, "\tLocal[{}:{}]: ", r1_val, r1_val + imm_len as usize,);
pretty_print::print_slice::<_,f32>(file, core_memory[0], 30);
pretty_print::print_slice::<_, f32>(file, core_memory[0], 30);
writeln!(file, "\tHost[{}:{}]: ", rd_val, rd_val + imm_len as usize,);
pretty_print::print_slice::<_,f32>(file, global_memory[0], 30);
pretty_print::print_slice::<_, f32>(file, global_memory[0], 30);
if prefix == "Post" {
writeln!(file, "\n###############################################\n");
@@ -1096,7 +1095,6 @@ impl Trace {
self.st_impl(cores, data, "Pre");
}
pub fn pre_lldi(&mut self, cores: &mut CPU, data: InstructionData) {
let (core, rd, imm) = data.get_core_rd_imm();
let file: &mut File = self
@@ -1136,8 +1134,7 @@ impl Trace {
// Ok(InstructionStatus::Completed)
}
fn lmv_impl (&mut self, cores: &mut CPU, data: InstructionData, prefix: &'static str) {
fn lmv_impl(&mut self, cores: &mut CPU, data: InstructionData, prefix: &'static str) {
let (core, rd, r1, _, imm_len, offset_select, offset_value) =
data.get_core_rd_r1_r2_immlen_offset();
let file: &mut File = self
@@ -1169,14 +1166,17 @@ impl Trace {
let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
let core_memory = core
.reserve_load(r1_val, imm_len).unwrap()
.reserve_load(rd_val, imm_len).unwrap()
.execute_load::<u8>().unwrap();
.reserve_load(r1_val, imm_len)
.unwrap()
.reserve_load(rd_val, imm_len)
.unwrap()
.execute_load::<u8>()
.unwrap();
writeln!(file, "{} Memory:", prefix);
writeln!(file, "\tLocal[{}:{}]: ", r1_val, r1_val + imm_len as usize,);
pretty_print::print_slice::<_,f32>(file, core_memory[0], 30);
pretty_print::print_slice::<_, f32>(file, core_memory[0], 30);
writeln!(file, "\tLocal[{}:{}]: ", rd_val, rd_val + imm_len as usize,);
pretty_print::print_slice::<_,f32>(file, core_memory[1], 30);
pretty_print::print_slice::<_, f32>(file, core_memory[1], 30);
if prefix == "Post" {
writeln!(file, "\n###############################################\n");
@@ -1,7 +1,6 @@
use anyhow::{Result,Context};
use anyhow::{Context, Result};
use std::{fmt::Debug, mem::transmute};
pub trait AddressArg {
fn to_address_usize(self) -> Result<usize>;
}
@@ -36,42 +35,56 @@ impl AddressArg for i64 {
}
}
fn address_to_usize(address: i32) -> usize {
address as u32 as usize
}
fn add_offset_impl(address: usize, offset_select : i32, offset_value : i32, id:i32) -> usize{
assert!(offset_select == 1 || offset_select == 2 || offset_select == 4 || offset_value == 0, "offset_select not a bit field");
let offset_value = (offset_select & id) * offset_value;
if offset_value > 0 {
address + offset_value as usize
} else {
address - offset_value as usize
}
fn add_offset_impl(address: usize, offset_select: i32, offset_value: i32, id: i32) -> usize {
assert!(
(0..=7).contains(&offset_select),
"offset_select is not a 3-bit field"
);
if offset_select & id == 0 {
address
} else {
address
.checked_add_signed(offset_value as isize)
.expect("address offset overflow")
}
}
pub fn add_offset_rd(address: i32, offset_select : i32, offset_value : i32) -> usize
{
let address = address_to_usize(address);
add_offset_impl(address, offset_select, offset_value, 4)
}
pub fn add_offset_r1(address: i32, offset_select : i32, offset_value : i32) -> usize
{
pub fn add_offset_rd(address: i32, offset_select: i32, offset_value: i32) -> usize {
let address = address_to_usize(address);
add_offset_impl(address, offset_select, offset_value, 1)
}
pub fn add_offset_r2(address: i32, offset_select : i32, offset_value : i32) -> usize
{
pub fn add_offset_r1(address: i32, offset_select: i32, offset_value: i32) -> usize {
let address = address_to_usize(address);
add_offset_impl(address, offset_select, offset_value, 2)
}
pub fn pack_float_in_i32(val : impl TryInto<f32>) -> i32 {
let val = val.try_into().unwrap_or_else( |x| panic!("Cannot parse into f32"));
f32::to_bits(val).cast_signed()
pub fn add_offset_r2(address: i32, offset_select: i32, offset_value: i32) -> usize {
let address = address_to_usize(address);
add_offset_impl(address, offset_select, offset_value, 4)
}
#[cfg(test)]
mod tests {
use super::{add_offset_r1, add_offset_r2, add_offset_rd};
#[test]
fn offset_select_uses_rd_rs1_rs2_bit_order() {
assert_eq!(add_offset_rd(100, 1, 7), 107);
assert_eq!(add_offset_r1(100, 2, 7), 107);
assert_eq!(add_offset_r2(100, 4, 7), 107);
assert_eq!(add_offset_rd(100, 6, 7), 100);
assert_eq!(add_offset_r1(100, 7, -7), 93);
}
}
pub fn pack_float_in_i32(val: impl TryInto<f32>) -> i32 {
let val = val
.try_into()
.unwrap_or_else(|x| panic!("Cannot parse into f32"));
f32::to_bits(val).cast_signed()
}