diff --git a/backend-simulators/pim/pim-simulator/src/bin/pim-simulator/main.rs b/backend-simulators/pim/pim-simulator/src/bin/pim-simulator/main.rs index cf5a5ea..6e4b2c5 100644 --- a/backend-simulators/pim/pim-simulator/src/bin/pim-simulator/main.rs +++ b/backend-simulators/pim/pim-simulator/src/bin/pim-simulator/main.rs @@ -4,7 +4,7 @@ use mimalloc::MiMalloc; static GLOBAL: MiMalloc = MiMalloc; use anyhow::{Context, Result, bail}; -use clap::Parser; +use clap::{Parser, ValueEnum}; use glob::glob; use pimcore::binary_to_instruction::binary_to_executor; use pimcore::cpu::crossbar::Crossbar; @@ -14,7 +14,7 @@ use pimcore::tracing::TRACER; use serde_json::Value; use std::collections::HashMap; use std::fs::{self, File}; -use std::io::{BufReader, Write}; +use std::io::BufReader; use std::path::PathBuf; /// Program to simulate core execution configuration @@ -44,14 +44,40 @@ struct Args { /// Comma separated list of (address,size) for memory output dump #[arg(short, long, value_delimiter = ',', num_args = 1.., value_name = "ADDR,SIZE")] dump: Vec, + + /// Simulator execution mode + #[arg(long, value_enum, default_value_t = ExecutionMode::Latency)] + mode: ExecutionMode, + + /// Number of inputs to execute (required in throughput mode) + #[arg(long)] + batch_size: Option, + + /// Input binary for one iteration; repeat once per batch entry + #[arg(long = "input")] + inputs: Vec, + + /// Optional directory for per-iteration output dumps + #[arg(long)] + batch_output_dir: Option, +} + +#[derive(Clone, Debug, ValueEnum)] +enum ExecutionMode { + Latency, + Throughput, } fn main() -> Result<()> { let args = Args::parse(); - let config_json = retrive_config(&args)?; - let mut core_inputs = retrive_cores(&args)?; - let memory = retrive_memory(&args)?; + let config_json = retrieve_config(&args)?; + let batch_size = batch_size(&args)?; + let input_regions = input_regions(&config_json)?; + let input_data = retrieve_inputs(&args, batch_size)?; + let inputs: Vec<&[u8]> = input_data.iter().map(Vec::as_slice).collect(); + let mut core_inputs = retrieve_cores(&args)?; + let memory = retrieve_memory(&args)?; let global_crossbars = get_crossbars(&config_json, &args).unwrap(); let crossbars = map_crossbars_to_cores(&config_json, &args, &global_crossbars); let mut executor = match &mut core_inputs { @@ -67,11 +93,68 @@ fn main() -> Result<()> { .lock() .unwrap() .init(executor.cpu().num_core(), args.output.clone()); - executor.execute()?; - dump_memory(executor, &args)?; + let dumps = dump_ranges(&args.dump)?; + let batch_outputs = executor.execute_batch(&inputs, &input_regions, &dumps)?; + fs::write( + &args.output, + batch_outputs + .last() + .context("simulation produced no output")?, + )?; + if let Some(batch_output_dir) = args.batch_output_dir { + write_batch_outputs(batch_output_dir, batch_outputs)?; + } Ok(()) } +fn batch_size(args: &Args) -> Result { + match (&args.mode, args.batch_size) { + (ExecutionMode::Latency, None | Some(1)) => Ok(1), + (ExecutionMode::Latency, Some(_)) => bail!("latency mode requires batch size 1"), + (ExecutionMode::Throughput, Some(0)) => bail!("batch size must be positive"), + (ExecutionMode::Throughput, Some(batch_size)) => Ok(batch_size), + (ExecutionMode::Throughput, None) => bail!("throughput mode requires --batch-size"), + } +} + +fn input_regions(config: &Value) -> Result> { + let addresses = config + .get("inputs_addresses") + .and_then(Value::as_array) + .context("config.json has no inputs_addresses array")?; + let sizes = config + .get("inputs_sizes") + .and_then(Value::as_array) + .context("config.json has no inputs_sizes array")?; + if addresses.len() != sizes.len() { + bail!("config.json input address/size count mismatch"); + } + addresses + .iter() + .zip(sizes) + .map(|(address, size)| { + Ok(( + usize::try_from(address.as_u64().context("invalid input address")?)?, + usize::try_from(size.as_u64().context("invalid input size")?)?, + )) + }) + .collect() +} + +fn retrieve_inputs(args: &Args, batch_size: u32) -> Result>> { + if args.inputs.len() != batch_size as usize { + bail!( + "batch size {batch_size} requires {} inputs, got {}", + batch_size, + args.inputs.len() + ); + } + args.inputs + .iter() + .map(|path| fs::read(path).with_context(|| format!("Failed to read input file: {path:?}"))) + .collect() +} + fn map_crossbars_to_cores<'c>( config: &Value, args: &Args, @@ -114,7 +197,7 @@ fn map_crossbars_to_cores<'c>( let path_as_str = real_path.to_str().unwrap(); assert!( global_crossbars.contains_key(path_as_str), - "symlink point to {:?}\n a not stored crossbar", + "symlink points to {:?}\n a crossbar that was not stored", real_path ); @@ -131,7 +214,7 @@ fn map_crossbars_to_cores<'c>( fn get_crossbars(config: &Value, args: &Args) -> anyhow::Result> { let xbar_size = config.get("xbar_size").unwrap().as_array().unwrap(); let rows_crossbar = xbar_size[0].as_i64().unwrap() as usize; - let column_corssbar = xbar_size[1].as_i64().unwrap() as usize; + let column_crossbar = xbar_size[1].as_i64().unwrap() as usize; let mut res = HashMap::new(); if let Some(folder) = args.folder.as_ref() { @@ -154,7 +237,7 @@ fn get_crossbars(config: &Value, args: &Args) -> anyhow::Result anyhow::Result Result<()> { - let dumps: Vec<(usize, usize)> = args - .dump +fn dump_ranges(values: &[usize]) -> Result> { + if !values.len().is_multiple_of(2) { + bail!("memory dump requires address,size pairs"); + } + Ok(values .chunks_exact(2) .map(|chunk| (chunk[0], chunk[1])) - .collect(); - let mut out_file = fs::OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(&args.output) - .with_context(|| format!("cannot open file {:?} for writing", args.output))?; + .collect()) +} - for (address, size) in dumps { - out_file.write_all(executor.cpu_mut().host().load::(address, size).unwrap()[0])?; +fn write_batch_outputs(output_dir: PathBuf, outputs: Vec>) -> Result<()> { + fs::create_dir_all(&output_dir) + .with_context(|| format!("cannot create batch output directory {output_dir:?}"))?; + for (iteration, output) in outputs.into_iter().enumerate() { + let path = output_dir.join(format!("output_{iteration:06}.bin")); + fs::write(&path, output).with_context(|| format!("cannot write batch output {path:?}"))?; } Ok(()) } @@ -197,7 +281,7 @@ fn set_memory(executor: &mut pimcore::Executable, memory: Vec) { executor.cpu_mut().host().execute_store(0, &memory).unwrap(); } -fn retrive_memory(args: &Args) -> Result> { +fn retrieve_memory(args: &Args) -> Result> { let memory_path = if let Some(mem_override) = &args.memory { mem_override.clone() } else if let Some(folder) = &args.folder.as_ref() { @@ -237,7 +321,7 @@ enum CoreInputs { Binary(Vec>), } -fn retrive_cores(args: &Args) -> Result { +fn retrieve_cores(args: &Args) -> Result { if let Some(cores_override) = &args.cores { let first_extension = cores_override .first() @@ -310,7 +394,7 @@ fn core_sort_key(path: &PathBuf) -> i32 { stem.parse::().unwrap() } -fn retrive_config(args: &Args) -> Result { +fn retrieve_config(args: &Args) -> Result { let config_path: PathBuf = { let override_path = args.config.as_ref(); let folder = args.folder.as_ref(); diff --git a/backend-simulators/pim/pim-simulator/src/lib/cpu/mod.rs b/backend-simulators/pim/pim-simulator/src/lib/cpu/mod.rs index d3f2e87..0ad723f 100644 --- a/backend-simulators/pim/pim-simulator/src/lib/cpu/mod.rs +++ b/backend-simulators/pim/pim-simulator/src/lib/cpu/mod.rs @@ -13,6 +13,33 @@ pub mod crossbar; #[derive(Debug, Clone)] pub struct CPU<'a> { cores: Box<[Core<'a>]>, + batch_outputs: Option, +} + +#[derive(Debug, Clone)] +struct BatchOutputs { + iteration: usize, + ranges: Vec<(usize, usize)>, + outputs: Vec>, +} + +impl BatchOutputs { + fn record(&mut self, address: usize, bytes: &[u8]) { + let output = &mut self.outputs[self.iteration]; + let store_end = address + bytes.len(); + let mut output_offset = 0; + for &(range_address, range_size) in &self.ranges { + let start = address.max(range_address); + let end = store_end.min(range_address + range_size); + if start < end { + let size = end - start; + output[output_offset + start - range_address + ..output_offset + start - range_address + size] + .copy_from_slice(&bytes[start - address..start - address + size]); + } + output_offset += range_size; + } + } } impl<'a> CPU<'a> { @@ -25,9 +52,63 @@ impl<'a> CPU<'a> { } Self { cores: cores.into(), + batch_outputs: None, } } + pub(crate) fn set_current_iteration(&mut self, iteration: u32) { + if let Some(batch_outputs) = &mut self.batch_outputs { + batch_outputs.iteration = iteration as usize; + } + } + + pub(crate) fn begin_host_store_recording( + &mut self, + batch_size: usize, + dump_ranges: &[(usize, usize)], + ) -> Result<()> { + let mut initial = Vec::new(); + for &(address, size) in dump_ranges { + initial.extend_from_slice(self.host().load::(address, size)?[0]); + } + self.batch_outputs = Some(BatchOutputs { + iteration: 0, + ranges: dump_ranges.to_vec(), + outputs: vec![initial; batch_size], + }); + Ok(()) + } + + pub(crate) fn store_to_host( + &mut self, + core: impl TryToUsize, + host_address: impl AddressArg, + core_address: impl AddressArg, + size: impl TryToUsize, + ) -> Result<()> { + let core = core.try_into().expect("core can not be negative"); + let host_address = host_address.to_address_usize()?; + let core_address = core_address.to_address_usize()?; + let size = size.try_into().context("size can not be negative")?; + let Self { + cores, + batch_outputs, + } = self; + let (host, cores) = cores.split_at_mut(1); + let bytes = cores[core - 1].load::(core_address, size)?[0]; + host[0].execute_store(host_address, bytes)?; + if let Some(batch_outputs) = batch_outputs { + batch_outputs.record(host_address, bytes); + } + Ok(()) + } + + pub(crate) fn finish_host_store_recording(&mut self) -> Vec> { + self.batch_outputs + .take() + .map_or_else(Vec::new, |batch_outputs| batch_outputs.outputs) + } + pub fn host<'b>(&'b mut self) -> &'b mut Core<'a> where 'a: 'b, diff --git a/backend-simulators/pim/pim-simulator/src/lib/instruction_set/isa.rs b/backend-simulators/pim/pim-simulator/src/lib/instruction_set/isa.rs index a1bc556..5bcae69 100644 --- a/backend-simulators/pim/pim-simulator/src/lib/instruction_set/isa.rs +++ b/backend-simulators/pim/pim-simulator/src/lib/instruction_set/isa.rs @@ -1,7 +1,7 @@ use crate::{ cpu::{CPU, crossbar}, instruction_set::{ - Instruction, InstructionData, InstructionStatus, InstructionType, VectorBitWith, + Instruction, InstructionData, InstructionStatus, InstructionType, VectorBitWidth, helper::add_all, }, memory_manager::{ @@ -200,20 +200,20 @@ pub fn isa_simd(functor: InstructionType) -> bool { pub fn dispatch_simd( functor: InstructionType, - vector_bit_with: VectorBitWith, + vector_bit_width: VectorBitWidth, ) -> Result { - let VectorBitWith { - vector_input_bitwith, - vector_output_bitwith, - } = vector_bit_with; + let VectorBitWidth { + vector_input_bitwidth, + vector_output_bitwidth, + } = vector_bit_width; let res = SIMD .get(&(functor as usize)) .context("Request a non present simd")? - .get(&(vector_input_bitwith, vector_output_bitwith)) + .get(&(vector_input_bitwidth, vector_output_bitwidth)) .with_context(|| { format!( "Function not found for the requested size input:{} output:{}", - vector_input_bitwith, vector_output_bitwith + vector_input_bitwidth, vector_output_bitwidth ) })?; Ok(*res) @@ -819,13 +819,15 @@ pub fn st(cores: &mut CPU, data: InstructionData) -> Result { let (core, rd, r1, _, imm_len, offset_select, offset_value) = data.get_core_rd_r1_r2_immlen_offset(); ensure!(core != 0, "ST cannot be used to move from host to host"); - let (host, core) = cores.host_and_cores(core); - let r1_val = core.register(r1); - let rd_val = core.register(rd); - 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 local_memory = core.load::(r1_val, imm_len)?; - host.execute_store(rd_val, local_memory[0]); + let (rd_val, r1_val) = { + let core = cores.core(core); + let r1_val = core.register(r1); + let rd_val = core.register(rd); + let r1_val = add_offset_r1(r1_val, offset_select, offset_value); + let rd_val = add_offset_rd(rd_val, offset_select, offset_value); + (rd_val, r1_val) + }; + cores.store_to_host(core, rd_val, r1_val, imm_len)?; TRACER.lock().unwrap().post_st(cores, data); Ok(InstructionStatus::Completed) } @@ -881,7 +883,7 @@ pub fn isa_recv(functor: usize) -> bool { #[inline(never)] pub fn recv(cores: &mut CPU, data: InstructionData) -> Result { - Ok(InstructionStatus::Reciving(data)) + Ok(InstructionStatus::Receiving(data)) } #[inline(never)] diff --git a/backend-simulators/pim/pim-simulator/src/lib/instruction_set/mod.rs b/backend-simulators/pim/pim-simulator/src/lib/instruction_set/mod.rs index 8b27bfc..8a02b61 100644 --- a/backend-simulators/pim/pim-simulator/src/lib/instruction_set/mod.rs +++ b/backend-simulators/pim/pim-simulator/src/lib/instruction_set/mod.rs @@ -22,7 +22,7 @@ pub enum InstructionStatus { Completed, Waiting(InstructionData), Sending(InstructionData), - Reciving(InstructionData), + Receiving(InstructionData), Sync(InstructionData), #[default] NotExecuted, @@ -59,21 +59,21 @@ pub type Instructions = Vec; pub type InstructionType = fn(&mut CPU, InstructionData) -> Result; #[derive(Debug, Clone, Copy, Default)] -pub struct VectorBitWith { - pub vector_input_bitwith: usize, - pub vector_output_bitwith: usize, +pub struct VectorBitWidth { + pub vector_input_bitwidth: usize, + pub vector_output_bitwidth: usize, } /// Support for the /// setbw ibiw, obiw /// Set the bit-widths of each element for input vectors and output vectors. Related vector instructions -/// use the configured bit-widths. Once setbw is caled, all subsequent related vector instructions will +/// use the configured bit-widths. Once setbw is called, all subsequent related vector instructions will /// use the configured bit-widths, until a new setbw is called. Once ibiw and obiw are set, ibyw and /// obyw are also set accordingly by the hardware. /// If the hardware does not support variable bit-width, this instruction is invalid and the matrix/vector /// instructions use the fixed bit-width of the hardware. pub struct InstructionsBuilder { - vector_bit_with: VectorBitWith, + vector_bit_width: VectorBitWidth, instructions: Instructions, } @@ -86,9 +86,9 @@ impl Default for InstructionsBuilder { impl InstructionsBuilder { pub fn new() -> Self { Self { - vector_bit_with: VectorBitWith { - vector_input_bitwith: 32, - vector_output_bitwith: 32, + vector_bit_width: VectorBitWidth { + vector_input_bitwidth: 32, + vector_output_bitwidth: 32, }, instructions: Instructions::new(), } @@ -97,9 +97,9 @@ impl InstructionsBuilder { pub fn make_inst(&mut self, functor: InstructionType, data: InstructionData) { if is_setbw(functor) { let (ibiw, obiw) = data.get_ibiw_obiw(); - self.vector_bit_with.vector_input_bitwith = + self.vector_bit_width.vector_input_bitwidth = ibiw.try_into().expect("ibiw can not be negative"); - self.vector_bit_with.vector_output_bitwith = + self.vector_bit_width.vector_output_bitwidth = obiw.try_into().expect("obiw can not be negative"); return; } @@ -107,7 +107,7 @@ impl InstructionsBuilder { if (isa_simd(functor)) { self.instructions.push(Instruction::new( data, - dispatch_simd(functor, self.vector_bit_with).unwrap(), + dispatch_simd(functor, self.vector_bit_width).unwrap(), )) } else { self.instructions.push(Instruction::new(data, functor)) diff --git a/backend-simulators/pim/pim-simulator/src/lib/pimcore.rs b/backend-simulators/pim/pim-simulator/src/lib/pimcore.rs index 744c6f2..aa62c20 100644 --- a/backend-simulators/pim/pim-simulator/src/lib/pimcore.rs +++ b/backend-simulators/pim/pim-simulator/src/lib/pimcore.rs @@ -1,8 +1,12 @@ #![allow(unused)] -use anyhow::{Result, bail}; +use anyhow::{Context, Result, bail}; use std::{ collections::{HashMap, HashSet}, + sync::{ + Mutex, + atomic::{AtomicU32, Ordering}, + }, time::{Duration, SystemTime}, }; @@ -25,6 +29,9 @@ pub mod send_recv; pub mod tracing; pub mod utility; +static GLOBAL_ITERATION: AtomicU32 = AtomicU32::new(0); +static EXECUTION_LOCK: Mutex<()> = Mutex::new(()); + #[derive(Debug, Clone)] pub struct CoreInstructionsBuilder { core_instructions: Vec, @@ -54,6 +61,7 @@ impl CoreInstructionsBuilder { pub struct CoreInstructions { instructions: Instructions, program_counter: usize, + current_iteration: u32, } impl CoreInstructions { @@ -61,6 +69,7 @@ impl CoreInstructions { Self { instructions, program_counter, + current_iteration: 0, } } @@ -68,6 +77,7 @@ impl CoreInstructions { Self { instructions: Vec::new(), program_counter: 0, + current_iteration: 0, } } } @@ -77,6 +87,7 @@ impl From for CoreInstructions { CoreInstructions { instructions: value, program_counter: 0, + current_iteration: 0, } } } @@ -130,6 +141,40 @@ impl<'a> Executable<'a> { where 'a: 'b, { + self.execute_batch(&[&[]], &[], &[]).map(|_| ()) + } + + pub fn execute_batch<'b>( + &'b mut self, + inputs: &[&[u8]], + input_regions: &[(usize, usize)], + dump_ranges: &[(usize, usize)], + ) -> Result>> + where + 'a: 'b, + { + validate_inputs(inputs, input_regions)?; + self.execute_iterations(inputs, input_regions, dump_ranges) + } + + fn execute_iterations<'b>( + &'b mut self, + inputs: &[&[u8]], + input_regions: &[(usize, usize)], + dump_ranges: &[(usize, usize)], + ) -> Result>> + where + 'a: 'b, + { + let _execution_lock = EXECUTION_LOCK.lock().unwrap(); + let batch_size = u32::try_from(inputs.len().max(1)).context("batch size exceeds u32")?; + GLOBAL_ITERATION.store(0, Ordering::SeqCst); + if let Some(input) = inputs.first() { + store_input(&mut self.cpu, input, input_regions)?; + } + self.cpu + .begin_host_store_recording(batch_size as usize, dump_ranges)?; + let Self { cpu, core_instructions: cores_instructions, @@ -147,9 +192,24 @@ impl<'a> Executable<'a> { && let Some(core_instruction) = cores_instructions.get_mut(cpu_index) { core_result = InstructionStatus::NotExecuted; + if core_instruction.program_counter == core_instruction.instructions.len() { + if core_instruction.instructions.is_empty() + || core_instruction.current_iteration + 1 >= batch_size + { + break; + } + core_instruction.current_iteration += 1; + core_instruction.program_counter = 0; + let iteration = core_instruction.current_iteration; + if iteration > GLOBAL_ITERATION.fetch_max(iteration, Ordering::SeqCst) { + store_input(cpu, inputs[iteration as usize], input_regions)?; + } + } + cpu.set_current_iteration(core_instruction.current_iteration); let CoreInstructions { instructions, program_counter, + .. } = core_instruction; core_result = instructions .get(*program_counter) @@ -211,7 +271,7 @@ impl<'a> Executable<'a> { #[cfg(feature = "profile_time")] TRACER.lock().unwrap().report(); - Ok(()) + Ok(cpu.finish_host_store_recording()) } pub fn cpu(&self) -> &CPU<'a> { @@ -233,6 +293,29 @@ impl<'a> Executable<'a> { } } +fn validate_inputs(inputs: &[&[u8]], input_regions: &[(usize, usize)]) -> Result<()> { + let input_size = input_regions.iter().try_fold(0usize, |total, (_, size)| { + total.checked_add(*size).context("input size overflow") + })?; + if inputs.is_empty() { + bail!("at least one input is required"); + } + if inputs.iter().any(|input| input.len() != input_size) { + bail!("each input must contain exactly {input_size} bytes"); + } + Ok(()) +} + +fn store_input(cpu: &mut CPU, input: &[u8], input_regions: &[(usize, usize)]) -> Result<()> { + let mut offset = 0; + for &(address, size) in input_regions { + cpu.host() + .execute_store(address, &input[offset..offset + size])?; + offset += size; + } + Ok(()) +} + fn detect_deadlock(cores_instructions: &[CoreInstructions]) -> Option { #[derive(Debug, PartialEq, Eq)] enum CoreState { diff --git a/backend-simulators/pim/pim-simulator/src/lib/send_recv.rs b/backend-simulators/pim/pim-simulator/src/lib/send_recv.rs index 2fdad1c..ba3a4d4 100644 --- a/backend-simulators/pim/pim-simulator/src/lib/send_recv.rs +++ b/backend-simulators/pim/pim-simulator/src/lib/send_recv.rs @@ -33,10 +33,10 @@ pub struct SendRecv { impl SendRecv { pub fn new(num_core: usize) -> Self { let sending = [Option::None].repeat(num_core); - let reciving = [Option::None].repeat(num_core); + let receiving = [Option::None].repeat(num_core); Self { sending: sending.into(), - receiving: reciving.into(), + receiving: receiving.into(), } } } @@ -73,18 +73,18 @@ where let data = inst.data; TRACER.lock().unwrap().pre_recv(cpu, data); } - let [sender_core, reciver_core] = + let [sender_core, receiver_core] = cpu.get_multiple_cores([sender.internal_core, receiver.internal_core]); let memory = sender_core .load::(sender.address, sender.size) .with_context(|| { format!( - "Sender crash tranfering memroy from {} with size {}", + "Sender crashed while transferring memory from {} with size {}", sender.address, sender.size ) }) .unwrap(); - reciver_core.execute_store(receiver.address, memory[0]); + receiver_core.execute_store(receiver.address, memory[0]); { let sender = &mut core_instructions[sender.internal_core]; let pc = sender.program_counter; @@ -124,19 +124,19 @@ where let receiver: usize = imm_core.try_into().expect("imm_core can not be negative"); assert_ne!(receiver, 0, "Host can not use receive"); send_recv.sending[sender] = Some(SendRecvInfo::new(sender, receiver, address, imm_len)); - let transfered = transfer_memory( + let transferred = transfer_memory( cpu, core_instructions, send_recv.sending[sender], send_recv.receiving[receiver], ); - if transfered { + if transferred { send_recv.sending[sender] = None; send_recv.receiving[receiver] = None; } - (transfered, if transfered { receiver } else { 0 }) + (transferred, if transferred { receiver } else { 0 }) } - InstructionStatus::Reciving(instruction_data) => { + InstructionStatus::Receiving(instruction_data) => { let (core_idx, imm_core) = instruction_data.get_core_immcore(); let rd = instruction_data.rd(); let imm_len = instruction_data @@ -153,17 +153,17 @@ where assert_ne!(sender, 0, "Host can not use send"); send_recv.receiving[receiver] = Some(SendRecvInfo::new(receiver, sender, address, imm_len)); - let transfered = transfer_memory( + let transferred = transfer_memory( cpu, core_instructions, send_recv.sending[sender], send_recv.receiving[receiver], ); - if transfered { + if transferred { send_recv.sending[sender] = None; send_recv.receiving[receiver] = None; } - (transfered, if transfered { sender } else { 0 }) + (transferred, if transferred { sender } else { 0 }) } _ => (false, 0), } diff --git a/backend-simulators/pim/pim-simulator/tests/batch.rs b/backend-simulators/pim/pim-simulator/tests/batch.rs new file mode 100644 index 0000000..e6a4404 --- /dev/null +++ b/backend-simulators/pim/pim-simulator/tests/batch.rs @@ -0,0 +1,70 @@ +mod common; + +use pimcore::{ + CoreInstructionsBuilder, Executable, + instruction_set::{InstructionsBuilder, instruction_data::InstructionDataBuilder, isa::*}, +}; + +#[test] +fn restarts_cores_and_loads_each_input() { + let cpu = common::empty_cpu(1); + let mut cores = CoreInstructionsBuilder::new(1); + let mut instructions = InstructionsBuilder::new(); + let mut data = InstructionDataBuilder::new(); + data.set_core_indx(1).fix_core_indx(); + instructions.make_inst(sldi, data.set_rdimm(1, 0).build()); + instructions.make_inst(sldi, data.set_rdimm(2, 0).build()); + instructions.make_inst(ld, data.set_rdr1(2, 1).set_imm_len(4).build()); + instructions.make_inst(sldi, data.set_rdimm(3, 4).build()); + instructions.make_inst(st, data.set_rdr1(3, 2).set_imm_len(4).build()); + cores.set_core(1, instructions.build()); + + let mut executable = Executable::new(cpu, cores.build()); + let first = 1.0f32.to_ne_bytes(); + let second = 2.0f32.to_ne_bytes(); + assert!( + executable + .execute_batch(&[&first[..3]], &[(0, 4)], &[]) + .is_err() + ); + executable + .execute_batch(&[&first, &second], &[(0, 4)], &[]) + .unwrap(); + + assert_eq!( + executable.cpu_mut().host().load::(4, 4).unwrap()[0], + [2.0] + ); +} + +#[test] +fn records_each_iteration_output() { + let cpu = common::empty_cpu(1); + let mut cores = CoreInstructionsBuilder::new(1); + let mut instructions = InstructionsBuilder::new(); + let mut data = InstructionDataBuilder::new(); + data.set_core_indx(1).fix_core_indx(); + instructions.make_inst(sldi, data.set_rdimm(1, 0).build()); + instructions.make_inst(sldi, data.set_rdimm(2, 0).build()); + instructions.make_inst(ld, data.set_rdr1(2, 1).set_imm_len(4).build()); + instructions.make_inst(sldi, data.set_rdimm(3, 4).build()); + instructions.make_inst(st, data.set_rdr1(3, 2).set_imm_len(4).build()); + cores.set_core(1, instructions.build()); + + let mut executable = Executable::new(cpu, cores.build()); + let first = 1.0f32.to_ne_bytes(); + let second = 2.0f32.to_ne_bytes(); + let outputs = executable + .execute_batch(&[&first, &second], &[(0, 4)], &[(4, 2), (6, 2)]) + .unwrap(); + + assert_eq!(outputs.len(), 2); + assert_eq!( + f32::from_ne_bytes(outputs[0].as_slice().try_into().unwrap()), + 1.0 + ); + assert_eq!( + f32::from_ne_bytes(outputs[1].as_slice().try_into().unwrap()), + 2.0 + ); +}