add onnx-mlir submodule
add pim simulator
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.idea
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "onnx-mlir"]
|
||||
path = onnx-mlir
|
||||
url = https://github.com/onnx/onnx-mlir.git
|
||||
1
backend-simulators/pim-simulator/.gitignore
vendored
Normal file
1
backend-simulators/pim-simulator/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
target
|
||||
307
backend-simulators/pim-simulator/Cargo.lock
generated
Normal file
307
backend-simulators/pim-simulator/Cargo.lock
generated
Normal file
@@ -0,0 +1,307 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aligned-vec"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
|
||||
dependencies = [
|
||||
"equator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "equator"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
|
||||
dependencies = [
|
||||
"equator-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equator-macro"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pim-simulator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aligned-vec",
|
||||
"anyhow",
|
||||
"clap",
|
||||
"glob",
|
||||
"hex",
|
||||
"paste",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
29
backend-simulators/pim-simulator/Cargo.toml
Normal file
29
backend-simulators/pim-simulator/Cargo.toml
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
[package]
|
||||
name = "pim-simulator"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
|
||||
[[bin]]
|
||||
name = "pim-simulator"
|
||||
|
||||
[lib]
|
||||
name = "pimcore"
|
||||
path = "src/lib/pimcore.rs"
|
||||
|
||||
[features]
|
||||
default = ["tracing"]
|
||||
tracing = []
|
||||
|
||||
|
||||
|
||||
[dependencies]
|
||||
aligned-vec = "0.6.4"
|
||||
anyhow = "1"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
glob = "0"
|
||||
hex = "0"
|
||||
paste = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
211
backend-simulators/pim-simulator/src/bin/pim-simulator/main.rs
Normal file
211
backend-simulators/pim-simulator/src/bin/pim-simulator/main.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use anyhow::{Context, Result, bail};
|
||||
use clap::Parser;
|
||||
use glob::glob;
|
||||
use pimcore::cpu::crossbar;
|
||||
use pimcore::json_to_instruction::json_to_executor;
|
||||
use serde_json::Value;
|
||||
use std::{fs, usize};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Program to simulate core execution configuration
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// The folder containing the configuration files
|
||||
#[arg(short, long)]
|
||||
folder: Option<PathBuf>,
|
||||
|
||||
/// Override path for the config.json file
|
||||
#[arg(long)]
|
||||
config: Option<PathBuf>,
|
||||
|
||||
/// Override path for the core*.json files
|
||||
#[arg(long, value_delimiter = ',', num_args = 1..)]
|
||||
cores: Option<Vec<PathBuf>>,
|
||||
|
||||
/// Override path for the memory file (.txt or .bin)
|
||||
#[arg(long)]
|
||||
memory: Option<PathBuf>,
|
||||
|
||||
/// Path to output file
|
||||
#[arg(short, long)]
|
||||
output: PathBuf,
|
||||
|
||||
/// Comma separated list of (address,size) for memory output dump
|
||||
#[arg(short, long, value_delimiter = ',', num_args = 1.., value_name = "ADDR,SIZE")]
|
||||
dump: Vec<i32>,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
let config_json = retrive_config(&args)?;
|
||||
let core_jsons = retrive_cores(&args)?;
|
||||
let memory = retrive_memory(&args)?;
|
||||
let mut executor = json_to_executor::json_to_executor(config_json, core_jsons.iter());
|
||||
populate_crossbar(&args, &mut executor);
|
||||
set_memory(&mut executor, memory);
|
||||
executor.execute();
|
||||
dump_memory(executor, &args)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn populate_crossbar(args: &Args, executor: &mut pimcore::Executable) {
|
||||
let num_cores = executor.cpu_mut().num_core();
|
||||
|
||||
if let Some(folder) = args.folder.as_ref() {
|
||||
for core_idx in 0..num_cores {
|
||||
let core_folder = folder.join(format!("core_{}", core_idx));
|
||||
|
||||
if !core_folder.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut bin_files: Vec<(u32, std::path::PathBuf)> = std::fs::read_dir(&core_folder)
|
||||
.expect("Failed to read core directory")
|
||||
.filter_map(|entry| {
|
||||
let path = entry.ok()?.path();
|
||||
let file_name = path.file_name()?.to_str()?;
|
||||
|
||||
if file_name.starts_with("crossbar_") && file_name.ends_with(".bin") {
|
||||
let num_str = &file_name[9..file_name.len() - 4];
|
||||
let num = num_str.parse::<u32>().ok()?;
|
||||
Some((num, path))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
bin_files.sort_by_key(|&(num, _)| num);
|
||||
let core = executor.cpu_mut().core(core_idx+1);
|
||||
let (_memory, crossbars) = core.get_memory_crossbar();
|
||||
|
||||
for (i, path) in bin_files {
|
||||
let bytes = std::fs::read(path).expect("Failed to read binary file");
|
||||
crossbars.get_mut(i as usize)
|
||||
.unwrap().execute_store(&bytes).unwrap();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn dump_memory(mut executor: pimcore::Executable, args: &Args) -> Result<()> {
|
||||
let dumps: Vec<(i32, i32)> = args
|
||||
.dump
|
||||
.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))?;
|
||||
|
||||
for (address, size) in dumps {
|
||||
out_file.write_all(executor.cpu_mut().host().load::<u8>(address, size).unwrap()[0])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_memory(executor: &mut pimcore::Executable, memory: Vec<u8>) {
|
||||
executor.cpu_mut().host().execute_store(0, &memory).unwrap();
|
||||
}
|
||||
|
||||
fn retrive_memory(args: &Args) -> Result<Vec<u8>> {
|
||||
let memory_path = if let Some(mem_override) = &args.memory {
|
||||
mem_override.clone()
|
||||
} else if let Some(folder) = &args.folder.as_ref() {
|
||||
let bin_path = folder.join("memory.bin");
|
||||
let txt_path = folder.join("memory.txt");
|
||||
|
||||
if bin_path.exists() {
|
||||
bin_path
|
||||
} else if txt_path.exists() {
|
||||
txt_path
|
||||
} else {
|
||||
bail!(
|
||||
"No memory file (memory.bin or memory.txt) found in {:?}",
|
||||
folder
|
||||
);
|
||||
}
|
||||
} else {
|
||||
bail!("Either --memory or --folder must be provided.");
|
||||
};
|
||||
let memory_vector: Vec<u8> = if let Some(ext) = memory_path.extension() {
|
||||
if ext == "bin" {
|
||||
fs::read(&memory_path).context("Failed to read binary memory file")?
|
||||
} else {
|
||||
let content =
|
||||
fs::read_to_string(&memory_path).context("Failed to read ascii memory file")?;
|
||||
let clean_hex: String = content.chars().filter(|c| !c.is_whitespace()).collect();
|
||||
hex::decode(&clean_hex).context("Failed to decode ASCII hex string")?
|
||||
}
|
||||
} else {
|
||||
bail!("Memory file has no extension, cannot determine type");
|
||||
};
|
||||
Ok(memory_vector)
|
||||
}
|
||||
|
||||
fn retrive_cores(args: &Args) -> Result<Vec<Value>, anyhow::Error> {
|
||||
let mut core_jsons: Vec<Value> = Vec::new();
|
||||
if let Some(cores_override) = &args.cores {
|
||||
for core in cores_override {
|
||||
let content = fs::read_to_string(core)
|
||||
.with_context(|| format!("Failed to read core file: {:?}", cores_override))?;
|
||||
let json: Value =
|
||||
serde_json::from_str(&content).context("Failed to parse core json override")?;
|
||||
core_jsons.push(json);
|
||||
}
|
||||
} else if let Some(folder) = args.folder.as_ref() {
|
||||
let pattern = folder.join("core*.json");
|
||||
let pattern_str = pattern.to_str().context("Invalid path encoding")?;
|
||||
let paths: Vec<_> = glob(pattern_str)?.collect();
|
||||
if paths.is_empty() {
|
||||
bail!("No core*.json files found in {:?}", folder);
|
||||
}
|
||||
for entry in paths {
|
||||
let path = entry?;
|
||||
let content = fs::read_to_string(&path)
|
||||
.with_context(|| format!("Failed to read core file: {:?}", path))?;
|
||||
let json: Value = serde_json::from_str(&content)
|
||||
.with_context(|| format!("Failed to parse JSON in {:?}", path))?;
|
||||
core_jsons.push(json);
|
||||
}
|
||||
} else {
|
||||
bail!("Either --core or --folder must be provided to find core definitions.");
|
||||
}
|
||||
Ok(core_jsons)
|
||||
}
|
||||
|
||||
fn retrive_config(args: &Args) -> Result<Value, anyhow::Error> {
|
||||
let config_path: PathBuf = {
|
||||
let override_path = args.config.as_ref();
|
||||
let folder = args.folder.as_ref();
|
||||
let filename = "config.json";
|
||||
if let Some(path) = override_path {
|
||||
path.clone()
|
||||
} else if let Some(folder) = folder {
|
||||
let path = folder.join(filename);
|
||||
if path.exists() {
|
||||
path
|
||||
} else {
|
||||
bail!("File {} not found in folder {:?}", filename, folder);
|
||||
}
|
||||
} else {
|
||||
bail!(
|
||||
"Cannot resolve {}: no override path and no input folder provided.",
|
||||
filename
|
||||
);
|
||||
}
|
||||
};
|
||||
let config_content = fs::read_to_string(&config_path)
|
||||
.with_context(|| format!("Failed to read config file: {:?}", config_path))?;
|
||||
let config_json: Value =
|
||||
serde_json::from_str(&config_content).context("Failed to parse config.json")?;
|
||||
Ok(config_json)
|
||||
}
|
||||
53
backend-simulators/pim-simulator/src/lib/cpu/crossbar.rs
Normal file
53
backend-simulators/pim-simulator/src/lib/cpu/crossbar.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use crate::memory_manager::{CoreMemory, MemoryStorable};
|
||||
use anyhow::{Result, bail, ensure};
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Crossbar {
|
||||
max_width: usize,
|
||||
max_height: usize,
|
||||
stored_bytes: usize,
|
||||
memory: CoreMemory,
|
||||
}
|
||||
|
||||
impl Crossbar {
|
||||
pub fn new(width: usize, height: usize, memory: CoreMemory) -> Self {
|
||||
Self { max_width: width, max_height: height, memory, stored_bytes:0 }
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.max_width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> usize {
|
||||
self.max_height
|
||||
}
|
||||
|
||||
pub fn stored_bytes(&self) -> usize {
|
||||
self.stored_bytes
|
||||
}
|
||||
|
||||
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;
|
||||
self.memory.execute_store(0, element)
|
||||
}
|
||||
|
||||
pub fn load<T>(&mut 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);
|
||||
}
|
||||
self.memory.load(0, size)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
174
backend-simulators/pim-simulator/src/lib/cpu/mod.rs
Normal file
174
backend-simulators/pim-simulator/src/lib/cpu/mod.rs
Normal file
@@ -0,0 +1,174 @@
|
||||
use std::{collections::HashMap, fmt::Debug};
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::{
|
||||
cpu::crossbar::Crossbar,
|
||||
instruction_set::Instructions,
|
||||
memory_manager::{CoreMemory, MemoryStorable, type_traits::TryToUsize},
|
||||
};
|
||||
|
||||
pub mod crossbar;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CPU {
|
||||
cores: Box<[Core]>,
|
||||
}
|
||||
|
||||
impl CPU {
|
||||
pub fn new(num_cores: impl TryToUsize) -> Self {
|
||||
let num_cores = num_cores.try_into().expect("num_cores can not be negative");
|
||||
let mut cores: Vec<Core> = std::iter::repeat_with(Core::new)
|
||||
.take(num_cores + 1)
|
||||
.collect();
|
||||
Self {
|
||||
cores: cores.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve_crossbar(
|
||||
&mut self,
|
||||
num_crossbar: impl TryToUsize,
|
||||
byte_width: impl TryToUsize,
|
||||
height: impl TryToUsize,
|
||||
) {
|
||||
let num_crossbar = num_crossbar
|
||||
.try_into()
|
||||
.expect("num_crossbar can not be negative");
|
||||
let byte_width = byte_width
|
||||
.try_into()
|
||||
.expect("byte_width can not be negative");
|
||||
let height = height.try_into().expect("height can not be negative");
|
||||
for core in &mut self.cores {
|
||||
core.reserve_crossbar(num_crossbar, byte_width, height);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host(&mut self) -> &mut Core {
|
||||
&mut self.cores[0]
|
||||
}
|
||||
|
||||
pub fn core(&mut self, index: impl TryToUsize) -> &mut Core {
|
||||
let index = index.try_into().expect("can not be negative");
|
||||
&mut self.cores[index]
|
||||
}
|
||||
|
||||
pub fn num_core(&self) -> usize {
|
||||
self.cores.len()
|
||||
}
|
||||
|
||||
pub(crate) fn host_and_cores(&mut self, core: impl TryToUsize) -> (&mut Core, &mut Core) {
|
||||
let core = core.try_into().expect("core can not be negative");
|
||||
assert_ne!(
|
||||
core, 0,
|
||||
"Retriving a core with the host that will always be at position 0"
|
||||
);
|
||||
let (host, cores) = self.cores.split_at_mut(1);
|
||||
let host = host.get_mut(0).expect("Missing the host??");
|
||||
let core = cores
|
||||
.get_mut(core - 1)
|
||||
.expect("Requested a core not present ");
|
||||
(host, core)
|
||||
}
|
||||
|
||||
pub fn get_multiple_cores<const N: usize>(&mut self, indices: [usize; N]) -> [&mut Core; N] {
|
||||
self.cores.get_disjoint_mut(indices).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Core {
|
||||
crossbars: Vec<Crossbar>,
|
||||
memory: CoreMemory,
|
||||
registers: [i32; 32],
|
||||
}
|
||||
|
||||
impl Core {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
crossbars: Vec::new(),
|
||||
memory: CoreMemory::new(),
|
||||
registers: [0; 32],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve_crossbar(
|
||||
&mut self,
|
||||
num_crossbar: impl TryToUsize,
|
||||
width: impl TryToUsize,
|
||||
height: impl TryToUsize,
|
||||
) {
|
||||
let num_crossbar = num_crossbar
|
||||
.try_into()
|
||||
.expect("num_crossbar can not be negative");
|
||||
let width = width.try_into().expect("width can not be negative");
|
||||
let height = height.try_into().expect("height can not be negative");
|
||||
for _ in 0..num_crossbar {
|
||||
let mut crossbar = CoreMemory::new();
|
||||
crossbar.set_capacity(width * height);
|
||||
self.crossbars.push(Crossbar::new(width, height, crossbar));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_load<T>(&mut self) -> Result<Vec<&[T]>>
|
||||
where
|
||||
T: MemoryStorable,
|
||||
{
|
||||
self.memory.execute_load()
|
||||
}
|
||||
|
||||
pub fn execute_store<T>(&mut self, address: impl TryToUsize, element: &[T]) -> Result<()>
|
||||
where
|
||||
T: MemoryStorable,
|
||||
{
|
||||
let address = address.try_into().context("address can not be negative")?;
|
||||
self.memory.execute_store(address, element)
|
||||
}
|
||||
|
||||
pub fn reserve_load(
|
||||
&mut self,
|
||||
address: impl TryToUsize,
|
||||
size: impl TryToUsize,
|
||||
) -> Result<&mut CoreMemory> {
|
||||
let address = address.try_into().context("address can not be negative")?;
|
||||
let size = size.try_into().context("size can not be negative")?;
|
||||
self.memory.reserve_load(address, size)
|
||||
}
|
||||
|
||||
pub fn set_register(&mut self, index: impl TryToUsize, value: i32) {
|
||||
let index = index.try_into().expect("index can not be negative");
|
||||
assert!(
|
||||
value >= 0,
|
||||
"Register cannot be negative if happens remove this and go check where it's used as usize"
|
||||
);
|
||||
self.registers[index] = value;
|
||||
}
|
||||
|
||||
pub fn register(&mut self, index: impl TryToUsize) -> i32 {
|
||||
let index = index.try_into().expect("index can not be negative");
|
||||
self.registers[index]
|
||||
}
|
||||
|
||||
pub fn load<T>(&mut self, address: impl TryToUsize, size: impl TryToUsize) -> Result<Vec<&[T]>>
|
||||
where
|
||||
T: MemoryStorable,
|
||||
{
|
||||
let address = address.try_into().context("address can not be negative")?;
|
||||
let size = size.try_into().context("size can not be negative")?;
|
||||
self.memory.load(address, size)
|
||||
}
|
||||
|
||||
pub fn get_memory_crossbar(&mut self) -> (&mut CoreMemory, &mut Vec<Crossbar>) {
|
||||
let Self {
|
||||
crossbars,
|
||||
memory,
|
||||
registers,
|
||||
} = self;
|
||||
(memory, crossbars)
|
||||
}
|
||||
|
||||
pub fn memset(&mut self, address: impl TryToUsize, size: impl TryToUsize, val: u8) -> Result<()> {
|
||||
let address = address.try_into().context("address can not be negative")?;
|
||||
let size = size.try_into().context("size can not be negative")?;
|
||||
self.memory.memset(address, size, val)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
use std::mem::transmute;
|
||||
|
||||
use crate::memory_manager::{
|
||||
MemoryStorable,
|
||||
type_traits::{FromFloat, UpcastDestTraits},
|
||||
};
|
||||
|
||||
#[inline]
|
||||
pub fn add_all<M>(sum: &[M]) -> M
|
||||
where
|
||||
M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
|
||||
{
|
||||
if size_of::<M>() == 4 {
|
||||
let (prefix, slice, suffix) = unsafe { sum.align_to::<f32>() };
|
||||
return M::from_f32(add_all_f32_impl(slice));
|
||||
} else if size_of::<M>() == 8 {
|
||||
let (prefix, slice, suffix) = unsafe { sum.align_to::<f64>() };
|
||||
return M::from_f64(add_all_f64_impl(slice));
|
||||
}
|
||||
panic!("Size not found");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_all_f64_impl(sum: &[f64]) -> f64 {
|
||||
let len = sum.len();
|
||||
if len > 64 {
|
||||
let mut acc = 0f64;
|
||||
let mut y = 0;
|
||||
#[cfg(all(
|
||||
any(target_arch = "x86", target_arch = "x86_64"),
|
||||
target_feature = "avx512f"
|
||||
))]
|
||||
{
|
||||
while y + 64 < len {
|
||||
unsafe {
|
||||
use std::arch::x86_64::_mm512_load_pd;
|
||||
use std::arch::x86_64::_mm512_reduce_add_pd;
|
||||
|
||||
let tmp = _mm512_load_pd(&sum[0 + y]);
|
||||
let a = _mm512_reduce_add_pd(tmp);
|
||||
let tmp = _mm512_load_pd(&sum[8 + y]);
|
||||
let b = _mm512_reduce_add_pd(tmp);
|
||||
let tmp = _mm512_load_pd(&sum[16 + y]);
|
||||
let c = _mm512_reduce_add_pd(tmp);
|
||||
let tmp = _mm512_load_pd(&sum[24 + y]);
|
||||
let d = _mm512_reduce_add_pd(tmp);
|
||||
let tmp = _mm512_load_pd(&sum[32 + y]);
|
||||
let e = _mm512_reduce_add_pd(tmp);
|
||||
let tmp = _mm512_load_pd(&sum[40 + y]);
|
||||
let f = _mm512_reduce_add_pd(tmp);
|
||||
let tmp = _mm512_load_pd(&sum[48 + y]);
|
||||
let g = _mm512_reduce_add_pd(tmp);
|
||||
let tmp = _mm512_load_pd(&sum[54 + y]);
|
||||
let h = _mm512_reduce_add_pd(tmp);
|
||||
|
||||
acc = acc + a + b + c + d + e + f + g + h;
|
||||
}
|
||||
y += 64;
|
||||
}
|
||||
}
|
||||
#[cfg(all(
|
||||
any(target_arch = "x86", target_arch = "x86_64"),
|
||||
not(target_feature = "avx512f")
|
||||
))]
|
||||
{
|
||||
while y + 64 < len {
|
||||
let a = sum[y]
|
||||
+ sum[1 + y]
|
||||
+ sum[2 + y]
|
||||
+ sum[3 + y]
|
||||
+ sum[4 + y]
|
||||
+ sum[5 + y]
|
||||
+ sum[6 + y]
|
||||
+ sum[7 + y]
|
||||
+ sum[8 + y]
|
||||
+ sum[9 + y]
|
||||
+ sum[10 + y]
|
||||
+ sum[11 + y]
|
||||
+ sum[12 + y]
|
||||
+ sum[13 + y]
|
||||
+ sum[14 + y]
|
||||
+ sum[15 + y];
|
||||
|
||||
let b = sum[16 + y]
|
||||
+ sum[17 + y]
|
||||
+ sum[18 + y]
|
||||
+ sum[19 + y]
|
||||
+ sum[20 + y]
|
||||
+ sum[21 + y]
|
||||
+ sum[22 + y]
|
||||
+ sum[23 + y]
|
||||
+ sum[24 + y]
|
||||
+ sum[25 + y]
|
||||
+ sum[26 + y]
|
||||
+ sum[27 + y]
|
||||
+ sum[28 + y]
|
||||
+ sum[29 + y]
|
||||
+ sum[30 + y]
|
||||
+ sum[31 + y];
|
||||
|
||||
let c = sum[32 + y]
|
||||
+ sum[33 + y]
|
||||
+ sum[34 + y]
|
||||
+ sum[35 + y]
|
||||
+ sum[36 + y]
|
||||
+ sum[37 + y]
|
||||
+ sum[38 + y]
|
||||
+ sum[39 + y]
|
||||
+ sum[40 + y]
|
||||
+ sum[41 + y]
|
||||
+ sum[42 + y]
|
||||
+ sum[43 + y]
|
||||
+ sum[44 + y]
|
||||
+ sum[45 + y]
|
||||
+ sum[46 + y]
|
||||
+ sum[47 + y];
|
||||
|
||||
let d = sum[48 + y]
|
||||
+ sum[49 + y]
|
||||
+ sum[50 + y]
|
||||
+ sum[51 + y]
|
||||
+ sum[52 + y]
|
||||
+ sum[53 + y]
|
||||
+ sum[54 + y]
|
||||
+ sum[55 + y]
|
||||
+ sum[56 + y]
|
||||
+ sum[57 + y]
|
||||
+ sum[58 + y]
|
||||
+ sum[59 + y]
|
||||
+ sum[60 + y]
|
||||
+ sum[61 + y]
|
||||
+ sum[62 + y]
|
||||
+ sum[63 + y];
|
||||
acc = acc + a + b + c + d;
|
||||
y += 64;
|
||||
}
|
||||
}
|
||||
|
||||
for x in y..len {
|
||||
acc += sum[x];
|
||||
}
|
||||
|
||||
acc
|
||||
} else {
|
||||
let mut acc = 0f64;
|
||||
for y in sum {
|
||||
acc += *y;
|
||||
}
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_all_f32_impl(sum: &[f32]) -> f32 {
|
||||
let len = sum.len();
|
||||
let rem_4 = len % 4;
|
||||
let rem_4 = len % 16;
|
||||
if len > 64 {
|
||||
let mut acc = 0f32;
|
||||
let mut y = 0;
|
||||
#[cfg(all(
|
||||
any(target_arch = "x86", target_arch = "x86_64"),
|
||||
target_feature = "avx512f"
|
||||
))]
|
||||
{
|
||||
while y + 64 < len {
|
||||
unsafe {
|
||||
use std::arch::x86_64::{_mm512_load_ps, _mm512_reduce_add_ps};
|
||||
let a = _mm512_load_ps(&sum[0 + y]);
|
||||
let a = _mm512_reduce_add_ps(a);
|
||||
let b = _mm512_load_ps(&sum[16 + y]);
|
||||
let b = _mm512_reduce_add_ps(b);
|
||||
let c = _mm512_load_ps(&sum[32 + y]);
|
||||
let c = _mm512_reduce_add_ps(c);
|
||||
let d = _mm512_load_ps(&sum[48 + y]);
|
||||
let d = _mm512_reduce_add_ps(d);
|
||||
acc = acc + a + b + c + d;
|
||||
}
|
||||
y += 64;
|
||||
}
|
||||
}
|
||||
#[cfg(all(
|
||||
any(target_arch = "x86", target_arch = "x86_64"),
|
||||
not(target_feature = "avx512f")
|
||||
))]
|
||||
{
|
||||
while y + 64 < len {
|
||||
let a = sum[y]
|
||||
+ sum[1 + y]
|
||||
+ sum[2 + y]
|
||||
+ sum[3 + y]
|
||||
+ sum[4 + y]
|
||||
+ sum[5 + y]
|
||||
+ sum[6 + y]
|
||||
+ sum[7 + y]
|
||||
+ sum[8 + y]
|
||||
+ sum[9 + y]
|
||||
+ sum[10 + y]
|
||||
+ sum[11 + y]
|
||||
+ sum[12 + y]
|
||||
+ sum[13 + y]
|
||||
+ sum[14 + y]
|
||||
+ sum[15 + y];
|
||||
|
||||
let b = sum[16 + y]
|
||||
+ sum[17 + y]
|
||||
+ sum[18 + y]
|
||||
+ sum[19 + y]
|
||||
+ sum[20 + y]
|
||||
+ sum[21 + y]
|
||||
+ sum[22 + y]
|
||||
+ sum[23 + y]
|
||||
+ sum[24 + y]
|
||||
+ sum[25 + y]
|
||||
+ sum[26 + y]
|
||||
+ sum[27 + y]
|
||||
+ sum[28 + y]
|
||||
+ sum[29 + y]
|
||||
+ sum[30 + y]
|
||||
+ sum[31 + y];
|
||||
|
||||
let c = sum[32 + y]
|
||||
+ sum[33 + y]
|
||||
+ sum[34 + y]
|
||||
+ sum[35 + y]
|
||||
+ sum[36 + y]
|
||||
+ sum[37 + y]
|
||||
+ sum[38 + y]
|
||||
+ sum[39 + y]
|
||||
+ sum[40 + y]
|
||||
+ sum[41 + y]
|
||||
+ sum[42 + y]
|
||||
+ sum[43 + y]
|
||||
+ sum[44 + y]
|
||||
+ sum[45 + y]
|
||||
+ sum[46 + y]
|
||||
+ sum[47 + y];
|
||||
|
||||
let d = sum[48 + y]
|
||||
+ sum[49 + y]
|
||||
+ sum[50 + y]
|
||||
+ sum[51 + y]
|
||||
+ sum[52 + y]
|
||||
+ sum[53 + y]
|
||||
+ sum[54 + y]
|
||||
+ sum[55 + y]
|
||||
+ sum[56 + y]
|
||||
+ sum[57 + y]
|
||||
+ sum[58 + y]
|
||||
+ sum[59 + y]
|
||||
+ sum[60 + y]
|
||||
+ sum[61 + y]
|
||||
+ sum[62 + y]
|
||||
+ sum[63 + y];
|
||||
acc = acc + a + b + c + d;
|
||||
y += 64;
|
||||
}
|
||||
}
|
||||
|
||||
for x in y..len {
|
||||
acc += sum[x];
|
||||
}
|
||||
|
||||
acc
|
||||
} else {
|
||||
let mut acc = 0f32;
|
||||
for y in sum {
|
||||
acc += *y;
|
||||
}
|
||||
acc
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
use paste::paste;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct InstructionData {
|
||||
core_indx: i32,
|
||||
rd: i32,
|
||||
r1: i32,
|
||||
//r2 imm mbiw imm_core
|
||||
r2_or_imm: i32,
|
||||
//offset_select imm_relu ibiw
|
||||
generic1: i32,
|
||||
//offset_value imm_group obiw
|
||||
generic2: i32,
|
||||
//imm_len
|
||||
generic3: i32,
|
||||
}
|
||||
|
||||
impl InstructionData {
|
||||
pub fn core_indx(&self) -> i32 {
|
||||
self.core_indx
|
||||
}
|
||||
|
||||
pub fn rd(&self) -> i32 {
|
||||
self.rd
|
||||
}
|
||||
|
||||
pub fn r1(&self) -> i32 {
|
||||
self.r1
|
||||
}
|
||||
|
||||
pub fn r2(&self) -> i32 {
|
||||
self.r2_or_imm
|
||||
}
|
||||
|
||||
pub fn imm(&self) -> i32 {
|
||||
self.r2_or_imm
|
||||
}
|
||||
|
||||
pub fn mbiw(&self) -> i32 {
|
||||
self.r2_or_imm
|
||||
}
|
||||
|
||||
pub fn offset_select(&self) -> i32 {
|
||||
self.generic1
|
||||
}
|
||||
|
||||
pub fn offset_value(&self) -> i32 {
|
||||
self.generic2
|
||||
}
|
||||
|
||||
pub fn get_core_rd_r1(&self) -> (i32, i32, i32) {
|
||||
(self.core_indx, self.rd, self.r1)
|
||||
}
|
||||
|
||||
pub fn get_core_rd_r1_r2(&self) -> (i32, i32, i32, i32) {
|
||||
(self.core_indx, self.rd, self.r1, self.r2_or_imm)
|
||||
}
|
||||
|
||||
pub fn get_core_rd_imm(&self) -> (i32, i32, i32) {
|
||||
(self.core_indx, self.rd, self.r2_or_imm)
|
||||
}
|
||||
|
||||
pub fn get_core_rd_r1_imm(&self) -> (i32, i32, i32, i32) {
|
||||
(self.core_indx, self.rd, self.r1, self.r2_or_imm)
|
||||
}
|
||||
|
||||
pub fn get_core_rd_r1_r2_immlen_offset(&self) -> (i32, i32, i32, i32, i32, i32, i32) {
|
||||
(
|
||||
self.core_indx,
|
||||
self.rd,
|
||||
self.r1,
|
||||
self.r2_or_imm,
|
||||
self.generic3,
|
||||
self.generic1,
|
||||
self.generic2,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_core_rd_r1_mbiw_immrelu_immgroup(&self) -> (i32, i32, i32, i32, i32, i32) {
|
||||
(
|
||||
self.core_indx,
|
||||
self.rd,
|
||||
self.r1,
|
||||
self.r2_or_imm,
|
||||
self.generic1,
|
||||
self.generic2,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_ibiw_obiw(&self) -> (i32, i32) {
|
||||
(self.generic1, self.generic2)
|
||||
}
|
||||
|
||||
pub fn imm_len(&self) -> i32 {
|
||||
self.generic3
|
||||
}
|
||||
|
||||
pub fn imm_core(&self) -> i32 {
|
||||
self.r2_or_imm
|
||||
}
|
||||
|
||||
pub(crate) fn get_core_immcore(&self) -> (i32, i32) {
|
||||
(self.core_indx, self.r2_or_imm)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Fixer {
|
||||
Fix(i32),
|
||||
Edit(i32),
|
||||
}
|
||||
|
||||
impl Fixer {
|
||||
fn to_fix(self) -> Self {
|
||||
match self {
|
||||
Fixer::Fix(x) => Fixer::Fix(x),
|
||||
Fixer::Edit(x) => Fixer::Fix(x),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_edit(self) -> Self {
|
||||
match self {
|
||||
Fixer::Fix(x) => Fixer::Edit(x),
|
||||
Fixer::Edit(x) => Fixer::Edit(x),
|
||||
}
|
||||
}
|
||||
|
||||
fn set(self, val: i32) -> Self {
|
||||
match self {
|
||||
Fixer::Fix(_) => Fixer::Fix(val),
|
||||
Fixer::Edit(_) => Fixer::Edit(val),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self) -> i32 {
|
||||
match self {
|
||||
Fixer::Fix(x) => *x,
|
||||
Fixer::Edit(x) => *x,
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(self) -> Self {
|
||||
match self {
|
||||
Fixer::Fix(x) => Fixer::Fix(x),
|
||||
Fixer::Edit(_) => Fixer::Edit(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fixer {
|
||||
fn default() -> Self {
|
||||
Fixer::Edit(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct InstructionDataBuilder {
|
||||
core_indx: Fixer,
|
||||
rd: Fixer,
|
||||
r1: Fixer,
|
||||
r2: Fixer,
|
||||
imm: Fixer,
|
||||
offset_select: Fixer,
|
||||
offset_value: Fixer,
|
||||
imm_len: Fixer,
|
||||
ibiw: Fixer,
|
||||
obiw: Fixer,
|
||||
mbiw: Fixer,
|
||||
imm_relu: Fixer,
|
||||
imm_group: Fixer,
|
||||
imm_core: Fixer,
|
||||
}
|
||||
|
||||
macro_rules! common_getter_setter {
|
||||
($id:ident) => {
|
||||
paste! {
|
||||
pub fn [<fix_ $id>](&mut self) -> &mut Self {
|
||||
self.[<$id>] = self.[<$id>].to_fix();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn [<edit_ $id>](&mut self) -> &mut Self {
|
||||
self.[<$id>] = self.[<$id>].to_edit();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn [<set_ $id>](&mut self, val: i32) -> &mut Self{
|
||||
self.[<$id>] = self.[<$id>].set(val);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn [<get_ $id>](&self) -> i32{
|
||||
self.[<$id>].get()
|
||||
}
|
||||
|
||||
pub fn [<clear_ $id>](&mut self){
|
||||
self.[<$id>] = self.[<$id>].clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl InstructionDataBuilder {
|
||||
common_getter_setter![core_indx];
|
||||
common_getter_setter![rd];
|
||||
common_getter_setter![r1];
|
||||
common_getter_setter![r2];
|
||||
common_getter_setter![imm];
|
||||
common_getter_setter![offset_select];
|
||||
common_getter_setter![offset_value];
|
||||
common_getter_setter![imm_len];
|
||||
common_getter_setter![ibiw];
|
||||
common_getter_setter![obiw];
|
||||
common_getter_setter![mbiw];
|
||||
common_getter_setter![imm_relu];
|
||||
common_getter_setter![imm_group];
|
||||
common_getter_setter![imm_core];
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
core_indx: Fixer::Edit(0),
|
||||
rd: Fixer::Edit(0),
|
||||
r1: Fixer::Edit(0),
|
||||
r2: Fixer::Edit(0),
|
||||
imm: Fixer::Edit(0),
|
||||
offset_select: Fixer::Edit(0),
|
||||
offset_value: Fixer::Edit(0),
|
||||
imm_len: Fixer::Edit(0),
|
||||
ibiw: Fixer::Edit(0),
|
||||
obiw: Fixer::Edit(0),
|
||||
mbiw: Fixer::Edit(0),
|
||||
imm_relu: Fixer::Edit(0),
|
||||
imm_group: Fixer::Edit(0),
|
||||
imm_core: Fixer::Edit(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.clear_core_indx();
|
||||
self.clear_rd();
|
||||
self.clear_r1();
|
||||
self.clear_r2();
|
||||
self.clear_imm();
|
||||
self.clear_offset_value();
|
||||
self.clear_offset_select();
|
||||
self.clear_imm_len();
|
||||
self.clear_ibiw();
|
||||
self.clear_obiw();
|
||||
self.clear_mbiw();
|
||||
self.clear_imm_relu();
|
||||
self.clear_imm_group();
|
||||
self.clear_imm_core();
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
pub fn build(&mut self) -> InstructionData {
|
||||
self.check_sanity();
|
||||
let inst_data = InstructionData {
|
||||
core_indx: self.get_core_indx(),
|
||||
rd: self.get_rd(),
|
||||
r1: self.get_r1(),
|
||||
r2_or_imm: self.get_r2() + self.get_imm() + self.get_mbiw() + self.get_imm_core(),
|
||||
generic1: self.get_offset_select() + self.get_ibiw() + self.get_imm_relu(),
|
||||
generic2: self.get_offset_value() + self.get_obiw() + self.get_imm_group(),
|
||||
generic3: self.get_imm_len(),
|
||||
};
|
||||
self.clear();
|
||||
inst_data
|
||||
}
|
||||
|
||||
pub fn set_rdr1r2(&mut self, rd: i32, r1: i32, r2: i32) -> &mut Self {
|
||||
self.set_rd(rd).set_r1(r1).set_r2(r2)
|
||||
}
|
||||
|
||||
pub fn set_offset_select_value(&mut self, offset_select: i32, offset_value: i32) -> &mut Self {
|
||||
self.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value)
|
||||
}
|
||||
|
||||
pub fn set_rdr1imm(&mut self, rd: i32, r1: i32, imm: i32) -> &mut Self {
|
||||
self.set_rd(rd).set_r1(r1).set_imm(imm)
|
||||
}
|
||||
|
||||
pub fn set_rdr1(&mut self, rd: i32, r1: i32) -> &mut Self {
|
||||
self.set_rd(rd).set_r1(r1)
|
||||
}
|
||||
|
||||
pub fn set_rdimm(&mut self, rd: i32, imm: i32) -> &mut Self {
|
||||
self.set_rd(rd).set_imm(imm)
|
||||
}
|
||||
|
||||
pub fn set_ibiw_obiw(&mut self, ibiw: i32, obiw: i32) -> &mut Self {
|
||||
self.set_ibiw(ibiw).set_obiw(obiw)
|
||||
}
|
||||
|
||||
pub fn set_mbiw_immrelu_immgroup(
|
||||
&mut self,
|
||||
mbiw: i32,
|
||||
imm_relu: i32,
|
||||
imm_group: i32,
|
||||
) -> &mut Self {
|
||||
self.set_mbiw(mbiw)
|
||||
.set_imm_relu(imm_relu)
|
||||
.set_imm_group(imm_group)
|
||||
}
|
||||
}
|
||||
722
backend-simulators/pim-simulator/src/lib/instruction_set/isa.rs
Normal file
722
backend-simulators/pim-simulator/src/lib/instruction_set/isa.rs
Normal file
@@ -0,0 +1,722 @@
|
||||
use crate::{
|
||||
cpu::{CPU, crossbar}, instruction_set::{
|
||||
Instruction, InstructionData, InstructionStatus, InstructionType, VectorBitWith,
|
||||
helper::add_all,
|
||||
}, memory_manager::{
|
||||
MemoryStorable,
|
||||
type_traits::{FromFloat, UpcastDestTraits, UpcastSlice},
|
||||
}, tracing::TRACER, utility::{add_offset_r1, add_offset_r2, add_offset_rd}
|
||||
};
|
||||
use aligned_vec::{AVec, ConstAlign};
|
||||
use anyhow::{Context, Result, ensure};
|
||||
|
||||
use paste::paste;
|
||||
use std::{borrow::Cow, cell::OnceCell, collections::HashMap};
|
||||
use std::{collections::HashSet, sync::LazyLock};
|
||||
|
||||
macro_rules! add_name {
|
||||
($storage:ident, $id:ident) => {
|
||||
$storage.insert($id as *const () as usize, stringify!($id));
|
||||
};
|
||||
}
|
||||
macro_rules! add_name_simd {
|
||||
($storage:ident, $id:ident) => {
|
||||
paste! {
|
||||
$storage.insert([<$id _impl>]::<f32,f32> as *const () as usize, concat!(stringify!($id), "::<f32, f32>"));
|
||||
$storage.insert([<$id _impl>]::<f64,f64> as *const () as usize, concat!(stringify!($id), "::<f64, f64>"));
|
||||
$storage.insert([<$id _impl>]::<f32,f64> as *const () as usize, concat!(stringify!($id), "::<f32, f64>"));
|
||||
$storage.insert([<$id _impl>]::<f64,f32> as *const () as usize, concat!(stringify!($id), "::<f64, f32>"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static NAMES: LazyLock<HashMap<usize, &'static str>> = LazyLock::new(|| {
|
||||
let mut hash = HashMap::new();
|
||||
add_name!(hash, sldi);
|
||||
add_name!(hash, sld);
|
||||
add_name!(hash, sadd);
|
||||
add_name!(hash, ssub);
|
||||
add_name!(hash, smul);
|
||||
add_name!(hash, saddi);
|
||||
add_name!(hash, smuli);
|
||||
add_name!(hash, setbw);
|
||||
add_name_simd!(hash, mvmul);
|
||||
add_name_simd!(hash, vvadd);
|
||||
add_name_simd!(hash, vvsub);
|
||||
add_name_simd!(hash, vvmul);
|
||||
add_name_simd!(hash, vvdmul);
|
||||
add_name_simd!(hash, vvmax);
|
||||
add_name!(hash, vvsll);
|
||||
add_name!(hash, vvsra);
|
||||
add_name_simd!(hash, vavg);
|
||||
add_name_simd!(hash, vrelu);
|
||||
add_name_simd!(hash, vtanh);
|
||||
add_name_simd!(hash, vsigm);
|
||||
add_name!(hash, vmv);
|
||||
add_name!(hash, vrsu);
|
||||
add_name!(hash, vrsl);
|
||||
add_name!(hash, ld);
|
||||
add_name!(hash, st);
|
||||
add_name!(hash, lldi);
|
||||
add_name!(hash, lmv);
|
||||
add_name!(hash, send);
|
||||
add_name!(hash, recv);
|
||||
add_name!(hash, wait);
|
||||
add_name!(hash, sync);
|
||||
hash
|
||||
});
|
||||
|
||||
pub fn functor_to_name(functor: usize) -> &'static str {
|
||||
NAMES
|
||||
.get(&functor)
|
||||
.unwrap_or_else(|| panic!("Function not found"))
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
/////////////////Scalar/register Instructions//////////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
pub fn sldi(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_sldi(cores, data);
|
||||
let (core_indx, rd, imm) = data.get_core_rd_imm();
|
||||
let core = cores.core(core_indx);
|
||||
core.set_register(rd, imm);
|
||||
TRACER.lock().unwrap().post_sldi(cores, data);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn sld(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_sld(cores, data);
|
||||
let (core_indx, rd, r1) = data.get_core_rd_r1();
|
||||
let offset_value = data.offset_value();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let host = cores.host();
|
||||
let num = host.load::<i32>((r1_val + offset_value), 4)?[0][0];
|
||||
let core = cores.core(core_indx);
|
||||
core.set_register(rd, num);
|
||||
TRACER.lock().unwrap().post_sld(cores, data);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn sadd(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_sadd(cores, data);
|
||||
let (core_indx, rd, r1, r2) = data.get_core_rd_r1_r2();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let r2_val = core.register(r2);
|
||||
core.set_register(rd, r1_val + r2_val);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn ssub(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_ssub(cores, data);
|
||||
let (core_indx, rd, r1, r2) = data.get_core_rd_r1_r2();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let r2_val = core.register(r2);
|
||||
core.set_register(rd, r1_val - r2_val);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn smul(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_smul(cores, data);
|
||||
let (core_indx, rd, r1, r2) = data.get_core_rd_r1_r2();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let r2_val = core.register(r2);
|
||||
core.set_register(rd, r1_val * r2_val);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn saddi(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_saddi(cores, data);
|
||||
let (core_indx, rd, r1, imm) = data.get_core_rd_r1_imm();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
core.set_register(rd, r1_val - imm);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn smuli(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_smuli(cores, data);
|
||||
let (core_indx, rd, r1, imm) = data.get_core_rd_r1_imm();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
core.set_register(rd, r1_val * imm);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
/////////////////Matrix/vector Instructions////////////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
macro_rules! add_simd_to_map {
|
||||
($storage:ident, $id:ident) => {
|
||||
paste! {
|
||||
let mut tmp = HashMap::new();
|
||||
tmp.insert((32_usize,32_usize), ([<$id _impl>]::<f32, f32> as InstructionType));
|
||||
tmp.insert((32_usize,64_usize), ([<$id _impl>]::<f32, f64> as InstructionType));
|
||||
tmp.insert((64_usize,32_usize), ([<$id _impl>]::<f64, f32> as InstructionType));
|
||||
tmp.insert((64_usize,64_usize), ([<$id _impl>]::<f64, f64> as InstructionType));
|
||||
//TODO WTF WHY
|
||||
tmp.insert((8_usize,8_usize), ([<$id _impl>]::<f32, f32> as InstructionType));
|
||||
$storage.insert($id as *const () as usize, tmp);
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static SIMD: LazyLock<HashMap<usize, HashMap<(usize, usize), InstructionType>>> =
|
||||
LazyLock::new(|| {
|
||||
let mut storage = HashMap::new();
|
||||
add_simd_to_map!(storage, vvadd);
|
||||
add_simd_to_map!(storage, vvsub);
|
||||
add_simd_to_map!(storage, vvmul);
|
||||
add_simd_to_map!(storage, vvdmul);
|
||||
add_simd_to_map!(storage, vvmax);
|
||||
add_simd_to_map!(storage, vavg);
|
||||
add_simd_to_map!(storage, vrelu);
|
||||
add_simd_to_map!(storage, vtanh);
|
||||
add_simd_to_map!(storage, vsigm);
|
||||
add_simd_to_map!(storage, mvmul);
|
||||
storage
|
||||
});
|
||||
|
||||
pub fn isa_simd(functor: InstructionType) -> bool {
|
||||
SIMD.contains_key(&(functor as usize))
|
||||
}
|
||||
|
||||
pub fn dispatch_simd(
|
||||
functor: InstructionType,
|
||||
vector_bit_with: VectorBitWith,
|
||||
) -> Result<InstructionType> {
|
||||
let VectorBitWith {
|
||||
vector_input_bitwith,
|
||||
vector_output_bitwith,
|
||||
} = vector_bit_with;
|
||||
let res = SIMD
|
||||
.get(&(functor as usize))
|
||||
.context("Request a non present simd")?
|
||||
.get(&(vector_input_bitwith, vector_output_bitwith))
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Function not found for the requested size input:{} output:{}",
|
||||
vector_input_bitwith, vector_output_bitwith
|
||||
)
|
||||
})?;
|
||||
Ok(*res)
|
||||
}
|
||||
|
||||
pub fn is_setbw(functor: InstructionType) -> bool {
|
||||
functor as usize == setbw as *const () as usize
|
||||
}
|
||||
|
||||
pub fn setbw(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, this instruction is resolved in the construction phase");
|
||||
}
|
||||
|
||||
pub fn mvmul(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, the real call is the generic version");
|
||||
}
|
||||
|
||||
pub(super) fn mvm_impl_internal<F, M, T>(
|
||||
cores: &mut CPU,
|
||||
data: InstructionData,
|
||||
) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T> + UpcastSlice<M>,
|
||||
[M]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
TRACER.lock().unwrap().pre_mvm::<F,M,T>(cores, data);
|
||||
let (core_indx, rd, r1, mbiw, relu, group) = data.get_core_rd_r1_mbiw_immrelu_immgroup();
|
||||
let group: usize = group.try_into().context("group can not be negative")?;
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let rd_val = core.register(rd);
|
||||
let (memory, crossbars) = core.get_memory_crossbar();
|
||||
let crossbar = crossbars.get_mut(group).unwrap();
|
||||
let crossbar_stored_bytes = crossbar.stored_bytes();
|
||||
let crossbar_byte_width = crossbar.width();
|
||||
//Fix this
|
||||
let crossbar_elem_width = crossbar_byte_width / size_of::<M>();
|
||||
ensure!(
|
||||
crossbar_byte_width & size_of::<M>() == 0,
|
||||
"M not divisor of the crosbbar size"
|
||||
);
|
||||
let crossbar_height = crossbar.height();
|
||||
let crossbar_byte_size = crossbar_byte_width * crossbar_height;
|
||||
|
||||
let loads = memory
|
||||
.reserve_load(r1_val, crossbar_height * size_of::<F>())?
|
||||
.execute_load::<F>()?;
|
||||
let load = loads[0];
|
||||
let vec: Cow<[M]> = load.up();
|
||||
let matrix = crossbar.load::<M>(crossbar_byte_size)?[0];
|
||||
let mut res = Vec::with_capacity(crossbar_elem_width);
|
||||
let mut partial :AVec<M, _> = AVec::<M, ConstAlign<64>>::with_capacity(64, vec.len());
|
||||
partial.resize(vec.len(), M::from_f32(0.0));
|
||||
|
||||
for x in 0..crossbar_elem_width {
|
||||
partial[0] = vec[0] * matrix[x];
|
||||
for y in 1..crossbar_height {
|
||||
partial[y] = vec[y] * matrix[y * crossbar_elem_width + x];
|
||||
}
|
||||
|
||||
let mut acc = add_all(partial.as_slice());
|
||||
res.push(acc);
|
||||
}
|
||||
if relu != 0 {
|
||||
res.iter_mut().for_each(|x| {
|
||||
if *x < M::from_f32(0.0) {
|
||||
*x = M::from_f32(0.0)
|
||||
}
|
||||
});
|
||||
}
|
||||
ensure!(
|
||||
res.len() == crossbar_elem_width,
|
||||
"mvm generate a vector bigger thant it's requested elements"
|
||||
);
|
||||
let res_up: Cow<[T]> = res.as_slice().up();
|
||||
core.execute_store(rd_val, res_up.as_ref());
|
||||
TRACER.lock().unwrap().post_mvm::<F,M,T>(cores, data);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub(super) fn mvmul_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T> + UpcastSlice<f32> + UpcastSlice<f64>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
[f32]: UpcastSlice<T>,
|
||||
[f64]: UpcastSlice<T>,
|
||||
{
|
||||
let mbiw = data.mbiw();
|
||||
match mbiw {
|
||||
32 => mvm_impl_internal::<F, f32, T>(cores, data),
|
||||
64 => mvm_impl_internal::<F, f64, T>(cores, data),
|
||||
//TODO i don't know why
|
||||
8 => mvm_impl_internal::<F, f32, T>(cores, data),
|
||||
n => {
|
||||
panic!("mvm size {} not handled ", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vvadd(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, the real call is the generic version");
|
||||
}
|
||||
|
||||
pub(super) fn vvadd_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
TRACER.lock().unwrap().pre_vvadd::<F,T>(cores, data);
|
||||
let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let r2_val = core.register(r2);
|
||||
let rd_val = core.register(rd);
|
||||
let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
|
||||
let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
|
||||
let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
|
||||
let imm_len: usize = imm_len.try_into().context("imm_len can not be negative")?;
|
||||
|
||||
let loads = core
|
||||
.reserve_load(r1_val, imm_len)?
|
||||
.reserve_load(r2_val, imm_len)?
|
||||
.execute_load::<F>()?;
|
||||
let (load1, load2) = (loads[0], loads[1]);
|
||||
let res: Vec<F> = load1
|
||||
.iter()
|
||||
.zip(load2.iter())
|
||||
.map(|(&a, &b)| a + b)
|
||||
.collect();
|
||||
ensure!(
|
||||
imm_len / size_of::<F>() == res.len(),
|
||||
"vvadd generate a vector bigger thant it's requested elements"
|
||||
);
|
||||
let res_up: Cow<[T]> = res.as_slice().up();
|
||||
core.execute_store(rd_val, res_up.as_ref());
|
||||
TRACER.lock().unwrap().post_vvadd::<F,T>(cores, data);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn vvsub(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, the real call is the generic version");
|
||||
}
|
||||
|
||||
pub(super) fn vvsub_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
TRACER.lock().unwrap().pre_vvsub::<F,T>(cores, data);
|
||||
let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let r2_val = core.register(r2);
|
||||
let rd_val = core.register(rd);
|
||||
let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
|
||||
let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
|
||||
let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
|
||||
let imm_len: usize = imm_len.try_into().context("imm_len can not be negative")?;
|
||||
|
||||
let loads = core
|
||||
.reserve_load(r1_val, imm_len)?
|
||||
.reserve_load(r2_val, imm_len)?
|
||||
.execute_load::<F>()?;
|
||||
let (load1, load2) = (loads[0], loads[1]);
|
||||
let res: Vec<F> = load1
|
||||
.iter()
|
||||
.zip(load2.iter())
|
||||
.map(|(&a, &b)| a - b)
|
||||
.collect();
|
||||
ensure!(
|
||||
imm_len / size_of::<F>() == res.len(),
|
||||
"vvadd generate a vector bigger thant it's requested elements"
|
||||
);
|
||||
let res_up: Cow<[T]> = res.as_slice().up();
|
||||
core.execute_store(rd_val, res_up.as_ref());
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn vvmul(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, the real call is the generic version");
|
||||
}
|
||||
|
||||
pub(super) fn vvmul_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
TRACER.lock().unwrap().pre_vvmul::<F,T>(cores, data);
|
||||
let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let r2_val = core.register(r2);
|
||||
let rd_val = core.register(rd);
|
||||
let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
|
||||
let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
|
||||
let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
|
||||
let imm_len: usize = imm_len.try_into().context("imm_len can not be negative")?;
|
||||
let loads = core
|
||||
.reserve_load(r1_val, imm_len)?
|
||||
.reserve_load(r2_val, imm_len)?
|
||||
.execute_load::<F>()?;
|
||||
let (load1, load2) = (loads[0], loads[1]);
|
||||
let res: Vec<F> = load1
|
||||
.iter()
|
||||
.zip(load2.iter())
|
||||
.map(|(&a, &b)| a * b)
|
||||
.collect();
|
||||
ensure!(
|
||||
imm_len / size_of::<F>() == res.len(),
|
||||
"vvadd generate a vector bigger thant it's requested elements"
|
||||
);
|
||||
let res_up: Cow<[T]> = res.as_slice().up();
|
||||
core.execute_store(rd_val, res_up.as_ref());
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn vvdmul(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, the real call is the generic version");
|
||||
}
|
||||
|
||||
pub(super) fn vvdmul_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
TRACER.lock().unwrap().pre_vvdmul::<F,T>(cores, data);
|
||||
let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let r2_val = core.register(r2);
|
||||
let rd_val = core.register(rd);
|
||||
let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
|
||||
let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
|
||||
let loads = core
|
||||
.reserve_load(r1_val, imm_len)?
|
||||
.reserve_load(r2_val, imm_len)?
|
||||
.execute_load::<F>()?;
|
||||
let (load1, load2) = (loads[0], loads[1]);
|
||||
let res: [F; 1] = [load1
|
||||
.iter()
|
||||
.zip(load2.iter())
|
||||
.map(|(&a, &b)| a * b)
|
||||
.reduce(|a, b| a + b)
|
||||
.unwrap()];
|
||||
|
||||
let res_up: Cow<[T]> = res.as_slice().up();
|
||||
core.execute_store(rd_val, res_up.as_ref());
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn vvmax(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, the real call is the generic version");
|
||||
}
|
||||
|
||||
pub(super) fn vvmax_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
TRACER.lock().unwrap().pre_vvmax::<F,T>(cores, data);
|
||||
let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let r2_val = core.register(r2);
|
||||
let rd_val = core.register(rd);
|
||||
let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
|
||||
let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
|
||||
let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
|
||||
|
||||
let loads = core
|
||||
.reserve_load(r1_val, imm_len)?
|
||||
.reserve_load(r2_val, imm_len)?
|
||||
.execute_load::<F>()?;
|
||||
let (load1, load2) = (loads[0], loads[1]);
|
||||
let res: Vec<F> = load1
|
||||
.iter()
|
||||
.zip(load2.iter())
|
||||
.map(|(&a, &b)| if (a > b) { a } else { b })
|
||||
.collect();
|
||||
|
||||
let res_up: Cow<[T]> = res.as_slice().up();
|
||||
core.execute_store(rd_val, res_up.as_ref());
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn vvsll(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!(
|
||||
"Shift left on floating point what does it means? who has generated this instruction???"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn vvsra(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!(
|
||||
"Shift right on floating point what does it means? who has generated this instruction???"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn vavg(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, the real call is the generic version");
|
||||
}
|
||||
|
||||
pub(super) fn vavg_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
TRACER.lock().unwrap().pre_vavg::<F,T>(cores, data);
|
||||
let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
let core = cores.core(core_indx);
|
||||
let r1_val = core.register(r1);
|
||||
let r2_val = r2;
|
||||
ensure!(r2_val == 1, "Stride different than 1 not supported");
|
||||
let rd_val = core.register(rd);
|
||||
let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
|
||||
let loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
|
||||
let load1 = loads[0];
|
||||
let len = load1.len();
|
||||
let res: [F; _] =
|
||||
[load1.iter().copied().reduce(|a: F, b: F| a + b).unwrap() / F::from_usize(len)];
|
||||
let res_up: Cow<[T]> = res.as_slice().up();
|
||||
core.execute_store(rd_val, res_up.as_ref());
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn vrelu(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, the real call is the generic version");
|
||||
}
|
||||
|
||||
pub(super) fn vrelu_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
|
||||
{
|
||||
TRACER.lock().unwrap().pre_vrelu::<F,T>(cores, data);
|
||||
let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
let core = cores.core(core_indx);
|
||||
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 loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
|
||||
let load1 = loads[0];
|
||||
let res: Vec<F> = load1
|
||||
.iter()
|
||||
.map(|&a| if (a > 0.0.into()) { a } else { 0.0.into() })
|
||||
.collect();
|
||||
|
||||
let res_up: Cow<[T]> = res.as_slice().up();
|
||||
core.execute_store(rd_val, res_up.as_ref());
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn vtanh(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, the real call is the generic version");
|
||||
}
|
||||
|
||||
pub(super) fn vtanh_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
|
||||
{
|
||||
TRACER.lock().unwrap().pre_vtanh::<F,T>(cores, data);
|
||||
let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
let core = cores.core(core_indx);
|
||||
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 loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
|
||||
let load1 = loads[0];
|
||||
let res: Vec<F> = load1.iter().map(|&a| a.tanh()).collect();
|
||||
|
||||
let res_up: Cow<[T]> = res.as_slice().up();
|
||||
core.execute_store(rd_val, res_up.as_ref());
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn vsigm(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
panic!("You are calling a placeholder, the real call is the generic version");
|
||||
}
|
||||
|
||||
pub(super) fn vsigm_impl<F, T>(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus>
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
|
||||
{
|
||||
TRACER.lock().unwrap().pre_vsigm::<F,T>(cores, data);
|
||||
let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
let core = cores.core(core_indx);
|
||||
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 loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
|
||||
let load1 = loads[0];
|
||||
let res: Vec<F> = load1.iter().map(|&a| a.sigm()).collect();
|
||||
let res_up: Cow<[T]> = res.as_slice().up();
|
||||
core.execute_store(rd_val, res_up.as_ref());
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn vmv(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn vrsu(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn vrsl(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
///Communication/synchronization Instructions/////////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
pub fn ld(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_ld(cores, data);
|
||||
let (core, rd, r1, _, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
ensure!(core != 0, "LD 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 global_memory = host.load::<u8>(r1_val, imm_len)?;
|
||||
core.execute_store(rd_val, global_memory[0])?;
|
||||
TRACER.lock().unwrap().post_ld(cores, data);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn st(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_st(cores, data);
|
||||
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::<u8>(r1_val, imm_len)?;
|
||||
host.execute_store(rd_val, local_memory[0]);
|
||||
TRACER.lock().unwrap().post_st(cores, data);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn lldi(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_lldi(cores, data);
|
||||
let (core, rd, imm) = data.get_core_rd_imm();
|
||||
let offset_value = data.offset_value();
|
||||
let imm_len = data.imm_len();
|
||||
let core = cores.core(core);
|
||||
let rd_val = core.register(rd);
|
||||
let rd_val = add_offset_rd(rd_val, 4, offset_value);
|
||||
core.memset(
|
||||
rd_val,
|
||||
imm_len,
|
||||
u8::try_from(imm).context("lldi with value bigger than u8")?,
|
||||
);
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn lmv(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_lmv(cores, data);
|
||||
let (core, rd, r1, _, imm_len, offset_select, offset_value) =
|
||||
data.get_core_rd_r1_r2_immlen_offset();
|
||||
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);
|
||||
let local_memory = core.load::<u8>(r1_val, imm_len)?;
|
||||
let tmp = local_memory[0].to_vec();
|
||||
core.execute_store(rd_val, tmp.as_slice());
|
||||
Ok(InstructionStatus::Completed)
|
||||
}
|
||||
|
||||
pub fn send(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_send(cores, data);
|
||||
Ok(InstructionStatus::Sending(data))
|
||||
}
|
||||
|
||||
pub fn recv(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
TRACER.lock().unwrap().pre_recv(cores, data);
|
||||
Ok(InstructionStatus::Reciving(data))
|
||||
}
|
||||
|
||||
pub fn wait(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
Ok(InstructionStatus::Waiting(data))
|
||||
}
|
||||
|
||||
pub fn sync(cores: &mut CPU, data: InstructionData) -> Result<InstructionStatus> {
|
||||
Ok(InstructionStatus::Sync(data))
|
||||
}
|
||||
115
backend-simulators/pim-simulator/src/lib/instruction_set/mod.rs
Normal file
115
backend-simulators/pim-simulator/src/lib/instruction_set/mod.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
use crate::{
|
||||
cpu::CPU,
|
||||
instruction_set::{
|
||||
instruction_data::InstructionData,
|
||||
isa::{dispatch_simd, functor_to_name, is_setbw, isa_simd},
|
||||
},
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use std::mem::swap;
|
||||
pub mod instruction_data;
|
||||
pub mod isa;
|
||||
pub mod helper;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Instruction {
|
||||
pub data: InstructionData,
|
||||
functor: InstructionType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub enum InstructionStatus {
|
||||
Completed,
|
||||
Waiting(InstructionData),
|
||||
Sending(InstructionData),
|
||||
Reciving(InstructionData),
|
||||
Sync(InstructionData),
|
||||
#[default]
|
||||
NotExecuted,
|
||||
}
|
||||
|
||||
impl InstructionStatus {
|
||||
#[must_use]
|
||||
pub fn is_completed(&self) -> bool {
|
||||
matches!(self, Self::Completed)
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
fn new(data: InstructionData, functor: InstructionType) -> Self {
|
||||
Self { data, functor }
|
||||
}
|
||||
|
||||
pub fn execute(&self, cpu: &mut CPU) -> InstructionStatus {
|
||||
(self.functor)(cpu, self.data)
|
||||
.with_context(|| format!("Instruction: {}", functor_to_name(self.functor as usize)))
|
||||
.with_context(|| format!("Error in core: {}", self.data.core_indx() - 1))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub type Instructions = Vec<Instruction>;
|
||||
pub type InstructionType = fn(&mut CPU, InstructionData) -> Result<InstructionStatus>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct VectorBitWith {
|
||||
pub vector_input_bitwith: usize,
|
||||
pub vector_output_bitwith: 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, 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,
|
||||
instructions: Instructions,
|
||||
}
|
||||
|
||||
impl Default for InstructionsBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionsBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vector_bit_with: VectorBitWith {
|
||||
vector_input_bitwith: 32,
|
||||
vector_output_bitwith: 32,
|
||||
},
|
||||
instructions: Instructions::new(),
|
||||
}
|
||||
}
|
||||
|
||||
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 =
|
||||
ibiw.try_into().expect("ibiw can not be negative");
|
||||
self.vector_bit_with.vector_output_bitwith =
|
||||
obiw.try_into().expect("obiw can not be negative");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isa_simd(functor)) {
|
||||
self.instructions.push(Instruction::new(
|
||||
data,
|
||||
dispatch_simd(functor, self.vector_bit_with).unwrap(),
|
||||
))
|
||||
} else {
|
||||
self.instructions.push(Instruction::new(data, functor))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(&mut self) -> Instructions {
|
||||
let mut inst = Instructions::new();
|
||||
swap(&mut self.instructions, &mut inst);
|
||||
inst
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,569 @@
|
||||
use anyhow::{Context, Result};
|
||||
use paste::paste;
|
||||
use std::{collections::HashMap, mem::offset_of, sync::LazyLock};
|
||||
|
||||
use crate::{
|
||||
instruction_set::{
|
||||
Instruction, InstructionsBuilder, instruction_data::InstructionDataBuilder, isa::*,
|
||||
},
|
||||
utility::pack_float_in_i32,
|
||||
};
|
||||
use serde_json::Value;
|
||||
type FunctorType = fn(&mut InstructionsBuilder, &mut InstructionDataBuilder, &Value) -> Result<()>;
|
||||
|
||||
macro_rules! add_to_json_map {
|
||||
($storage:ident, $id:ident) => {
|
||||
paste! {
|
||||
$storage.insert(stringify!($id).to_string(), [<json_to_ $id>] as FunctorType );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static SIMD: LazyLock<HashMap<String, FunctorType>> = LazyLock::new(|| {
|
||||
let mut storage = HashMap::new();
|
||||
add_to_json_map!(storage, sldi);
|
||||
add_to_json_map!(storage, sld);
|
||||
add_to_json_map!(storage, sadd);
|
||||
add_to_json_map!(storage, ssub);
|
||||
add_to_json_map!(storage, smul);
|
||||
add_to_json_map!(storage, saddi);
|
||||
add_to_json_map!(storage, setbw);
|
||||
add_to_json_map!(storage, mvmul);
|
||||
add_to_json_map!(storage, vvadd);
|
||||
add_to_json_map!(storage, vvsub);
|
||||
add_to_json_map!(storage, vvmul);
|
||||
add_to_json_map!(storage, vvdmul);
|
||||
add_to_json_map!(storage, vvmax);
|
||||
add_to_json_map!(storage, vvsll);
|
||||
add_to_json_map!(storage, vvsra);
|
||||
add_to_json_map!(storage, vrelu);
|
||||
add_to_json_map!(storage, vtanh);
|
||||
add_to_json_map!(storage, vsigm);
|
||||
add_to_json_map!(storage, vmv);
|
||||
add_to_json_map!(storage, vrsu);
|
||||
add_to_json_map!(storage, vrsl);
|
||||
add_to_json_map!(storage, ld);
|
||||
add_to_json_map!(storage, st);
|
||||
add_to_json_map!(storage, lldi);
|
||||
add_to_json_map!(storage, lmv);
|
||||
add_to_json_map!(storage, send);
|
||||
add_to_json_map!(storage, recv);
|
||||
add_to_json_map!(storage, wait);
|
||||
add_to_json_map!(storage, sync);
|
||||
storage
|
||||
});
|
||||
|
||||
fn json_to_offset(json: &Value) -> (i32, i32) {
|
||||
let offset_value = json.get("offset_value").unwrap().as_i64().unwrap() as i32;
|
||||
let offset_select = json.get("offset_select").unwrap().as_i64().unwrap() as i32;
|
||||
(offset_select, offset_value)
|
||||
}
|
||||
|
||||
pub fn json_to_instruction(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) {
|
||||
let json_struct = json.as_object().expect("Not an object");
|
||||
let op = json_struct.get("op").unwrap().as_str().unwrap();
|
||||
SIMD.get(op)
|
||||
.unwrap_or_else(|| panic!("Operation not found {}", op))(
|
||||
inst_builder,
|
||||
inst_data_builder,
|
||||
json,
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! json_str {
|
||||
($json:ident , $value:literal) => {
|
||||
$json.get($value).context(concat![$value, " field not present"])?.as_str().context(concat![$value, " field not str"])?
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! json_i64 {
|
||||
($json:ident , $value:literal) => {
|
||||
$json.get($value).context(concat![$value, " field not present"])?.as_i64().context(concat![$value, " field not i64"])?
|
||||
};
|
||||
}
|
||||
|
||||
fn json_to_sldi(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("sldi", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd");
|
||||
let imm = json_i64!(json, "imm");
|
||||
inst_data_builder.set_rd(rd as i32);
|
||||
inst_data_builder.set_imm(imm as i32);
|
||||
inst_builder.make_inst(sldi, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_sld(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_sadd(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_ssub(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_smul(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_saddi(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
/////////////////Matrix/vector Instructions////////////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
fn json_to_setbw(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("setbw", json_str!(json, "op"));
|
||||
let ibiw = json_i64!(json, "ibiw");
|
||||
let obiw = json_i64!(json, "obiw");
|
||||
inst_data_builder.set_ibiw(ibiw as i32);
|
||||
inst_data_builder.set_obiw(obiw as i32);
|
||||
inst_builder.make_inst(setbw, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_mvmul(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("mvmul", json_str!(json, "op"));
|
||||
let group = json_i64!(json, "group") as i32;
|
||||
let relu = json_i64!(json, "relu") as i32;
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let rs1 = json_i64!(json, "rs1") as i32;
|
||||
let mbiw = json_i64!(json, "mbiw") as i32;
|
||||
inst_data_builder
|
||||
.set_imm_group(group)
|
||||
.set_imm_relu(relu)
|
||||
.set_rd(rd)
|
||||
.set_r1(rs1)
|
||||
.set_mbiw(mbiw);
|
||||
inst_builder.make_inst(mvmul, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vvadd(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("vvadd", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let rs1 = json_i64!(json, "rs1") as i32;
|
||||
let rs2 = json_i64!(json, "rs2") as i32;
|
||||
let len = json_i64!(json, "len") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_r1(rs1)
|
||||
.set_r2(rs2)
|
||||
.set_imm_len(len)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(vvadd, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vvsub(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vvmul(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("vvmul", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let rs1 = json_i64!(json, "rs1") as i32;
|
||||
let rs2 = json_i64!(json, "rs2") as i32;
|
||||
let len = json_i64!(json, "len") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_r1(rs1)
|
||||
.set_r2(rs2)
|
||||
.set_imm_len(len)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(vvmul, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vvdmul(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vvmax(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("vvmax", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let rs1 = json_i64!(json, "rs1") as i32;
|
||||
let rs2 = json_i64!(json, "rs2") as i32;
|
||||
let len = json_i64!(json, "len") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_r1(rs1)
|
||||
.set_r2(rs2)
|
||||
.set_imm_len(len)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(vvmax, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vvsll(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vvsra(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vavg(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vrelu(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("vrelu", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let rs1 = json_i64!(json, "rs1") as i32;
|
||||
let len = json_i64!(json, "len") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_r1(rs1)
|
||||
.set_imm_len(len)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(vrelu, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vtanh(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("vtanh", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let rs1 = json_i64!(json, "rs1") as i32;
|
||||
let len = json_i64!(json, "len") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_r1(rs1)
|
||||
.set_imm_len(len)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(vtanh, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vsigm(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("vsigmoid", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let rs1 = json_i64!(json, "rs1") as i32;
|
||||
let len = json_i64!(json, "len") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_r1(rs1)
|
||||
.set_imm_len(len)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(vsigm, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vmv(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vrsu(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_vrsl(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
///Communication/synchronization Instructions/////////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
fn json_to_ld(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("ld", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let rs1 = json_i64!(json, "rs1") as i32;
|
||||
let len = json_i64!(json, "size") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_r1(rs1)
|
||||
.set_imm_len(len)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(ld, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_st(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("st", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let rs1 = json_i64!(json, "rs1") as i32;
|
||||
let len = json_i64!(json, "size") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_r1(rs1)
|
||||
.set_imm_len(len)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(st, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_lldi(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("lldi", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let imm = json_i64!(json, "imm") as f32;
|
||||
let imm = pack_float_in_i32(imm);
|
||||
let len = json_i64!(json, "len") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_imm(imm)
|
||||
.set_imm_len(len)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(lldi, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_lmv(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("lmv", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let rs1 = json_i64!(json, "rs1") as i32;
|
||||
let len = json_i64!(json, "len") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_r1(rs1)
|
||||
.set_imm_len(len)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(lmv, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_send(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("send", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let core = json_i64!(json, "core") as i32 + 1;
|
||||
let size = json_i64!(json, "size") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_imm_core(core)
|
||||
.set_imm_len(size)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(send, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_recv(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
let json = json.as_object().expect("Not an object");
|
||||
assert_eq!("recv", json_str!(json, "op"));
|
||||
let rd = json_i64!(json, "rd") as i32;
|
||||
let core = json_i64!(json, "core") as i32 + 1;
|
||||
let size = json_i64!(json, "size") as i32;
|
||||
let (offset_select, offset_value) = json_to_offset(json.get("offset").unwrap());
|
||||
inst_data_builder
|
||||
.set_rd(rd)
|
||||
.set_imm_core(core)
|
||||
.set_imm_len(size)
|
||||
.set_offset_select(offset_select)
|
||||
.set_offset_value(offset_value);
|
||||
inst_builder.make_inst(recv, inst_data_builder.build());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_wait(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_to_sync(
|
||||
inst_builder: &mut InstructionsBuilder,
|
||||
inst_data_builder: &mut InstructionDataBuilder,
|
||||
json: &Value,
|
||||
) -> Result<()> {
|
||||
todo!("Not present in the compiler");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
instruction_set::{InstructionsBuilder, instruction_data::InstructionDataBuilder},
|
||||
json_to_instruction::json_isa::json_to_instruction,
|
||||
};
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Operation not found bazza")]
|
||||
fn test_op_not_present() {
|
||||
let json = r#"{"imm":15168,"op":"bazza","rd":1}"#;
|
||||
let json = serde_json::from_str(json).unwrap();
|
||||
let mut instruction_builder = InstructionsBuilder::new();
|
||||
let mut inst_data_builder = InstructionDataBuilder::new();
|
||||
json_to_instruction(&mut instruction_builder, &mut inst_data_builder, &json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_sldi() {
|
||||
let json = r#"{"imm":15168,"op":"sldi","rd":1}"#;
|
||||
let json = serde_json::from_str(json).unwrap();
|
||||
let mut instruction_builder = InstructionsBuilder::new();
|
||||
let mut inst_data_builder = InstructionDataBuilder::new();
|
||||
json_to_instruction(&mut instruction_builder, &mut inst_data_builder, &json);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use core::panic;
|
||||
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::{
|
||||
CoreInstructionsBuilder, Executable,
|
||||
cpu::{CPU, crossbar},
|
||||
instruction_set::{
|
||||
InstructionsBuilder,
|
||||
instruction_data::{self, InstructionData, InstructionDataBuilder},
|
||||
},
|
||||
json_to_instruction::{self, json_isa},
|
||||
memory_manager::type_traits::TryToUsize,
|
||||
};
|
||||
|
||||
pub fn json_to_executor<'a>(
|
||||
config: Value,
|
||||
mut cores: impl Iterator<Item = &'a Value>,
|
||||
) -> Executable {
|
||||
let cell_precision = config.get("cell_precision").unwrap().as_i64().unwrap() as i32;
|
||||
let core_cnt = config.get("core_cnt").unwrap().as_i64().unwrap() as i32;
|
||||
let xbar_count = config.get("xbar_array_count").unwrap().as_i64().unwrap() as i32;
|
||||
let xbar_size = config.get("xbar_size").unwrap().as_array().unwrap();
|
||||
let rows_crossbar = xbar_size[0].as_i64().unwrap() as i32;
|
||||
let column_corssbar = xbar_size[1].as_i64().unwrap() as i32;
|
||||
let array_group_map = config.get("array_group_map");
|
||||
let mut cpu = CPU::new(core_cnt);
|
||||
cpu.reserve_crossbar(xbar_count, column_corssbar * 4, rows_crossbar);
|
||||
let mut core_insts_builder = CoreInstructionsBuilder::new(core_cnt as usize);
|
||||
for core_indx in 1..=core_cnt {
|
||||
let mut insts_builder = InstructionsBuilder::new();
|
||||
let mut inst_data_builder = InstructionDataBuilder::new();
|
||||
inst_data_builder.set_core_indx(core_indx).fix_core_indx();
|
||||
let json_core = cores
|
||||
.next()
|
||||
.unwrap_or_else(|| panic!("cores files less than {}", core_indx - 1));
|
||||
let json_core_insts = json_core
|
||||
.as_array()
|
||||
.unwrap_or_else(|| panic!("core{} has not a list of instruction", core_indx));
|
||||
for json_inst in json_core_insts {
|
||||
json_isa::json_to_instruction(&mut insts_builder, &mut inst_data_builder, json_inst);
|
||||
}
|
||||
core_insts_builder.set_core(core_indx, insts_builder.build());
|
||||
}
|
||||
Executable::new(cpu, core_insts_builder.build())
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
mod json_isa;
|
||||
pub mod json_to_executor;
|
||||
245
backend-simulators/pim-simulator/src/lib/memory_manager/mod.rs
Normal file
245
backend-simulators/pim-simulator/src/lib/memory_manager/mod.rs
Normal file
@@ -0,0 +1,245 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use anyhow::{Context, Result, bail, ensure};
|
||||
|
||||
use crate::memory_manager::type_traits::TryToUsize;
|
||||
|
||||
pub trait MemoryStorable: Copy {}
|
||||
pub mod type_traits;
|
||||
|
||||
impl MemoryStorable for f32 {}
|
||||
impl MemoryStorable for f64 {}
|
||||
impl MemoryStorable for i32 {}
|
||||
impl MemoryStorable for i64 {}
|
||||
impl MemoryStorable for u8 {}
|
||||
impl MemoryStorable for i8 {}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct LoadRequest {
|
||||
pub index: usize,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
unsafe fn slice_from_u8<T>(slice: &[u8]) -> Result<&[T]>
|
||||
where
|
||||
T: MemoryStorable,
|
||||
{
|
||||
let size_t = size_of::<T>();
|
||||
let size = slice.len();
|
||||
ensure!(
|
||||
size >= size_t,
|
||||
"Size {} smaller than the data type size {}",
|
||||
size,
|
||||
size_t
|
||||
);
|
||||
ensure!(
|
||||
size.is_multiple_of(size_t),
|
||||
"Size not a multiple of selected data type size"
|
||||
);
|
||||
let (prefix, slice, suffix) = unsafe { slice.align_to::<T>() };
|
||||
ensure!(
|
||||
prefix.is_empty() && suffix.is_empty(),
|
||||
"{:?} {:?} T not aligned",
|
||||
prefix,
|
||||
suffix
|
||||
);
|
||||
Ok(slice)
|
||||
}
|
||||
|
||||
unsafe fn slice_into_u8<T>(slice: &[T]) -> Result<&[u8]>
|
||||
where
|
||||
T: MemoryStorable,
|
||||
{
|
||||
ensure!(!slice.is_empty(), "Empty slice not convertable");
|
||||
let (prefix, slice, suffix) = unsafe { slice.align_to::<u8>() };
|
||||
ensure!(prefix.is_empty() && suffix.is_empty(), "T not aligned");
|
||||
Ok(slice)
|
||||
}
|
||||
|
||||
//Not thread safe
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CoreMemory {
|
||||
memory: Vec<u8>,
|
||||
load_requests: Vec<LoadRequest>,
|
||||
}
|
||||
|
||||
impl Default for CoreMemory {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreMemory {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
memory: Vec::new(),
|
||||
load_requests: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
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")?;
|
||||
let load_request = LoadRequest {
|
||||
index: address,
|
||||
size,
|
||||
};
|
||||
if self.memory.len() < address + size {
|
||||
self.memory.resize((address + size) * 2, 0);
|
||||
}
|
||||
self.load_requests.push(load_request);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn execute_load<T>(&mut self) -> Result<Vec<&[T]>>
|
||||
where
|
||||
T: MemoryStorable,
|
||||
{
|
||||
let Self {
|
||||
memory,
|
||||
load_requests,
|
||||
} = self;
|
||||
let mut res = Vec::new();
|
||||
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))?;
|
||||
res.push(memory_slice);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn load<T>(&mut self, address: impl TryToUsize, size: impl TryToUsize) -> Result<Vec<&[T]>>
|
||||
where
|
||||
T: MemoryStorable,
|
||||
{
|
||||
let address = address.try_into().expect("address can not be negative");
|
||||
let size = size.try_into().expect("size can not be negative");
|
||||
self.reserve_load(address, size)?.execute_load()
|
||||
}
|
||||
|
||||
pub fn execute_store<T>(&mut self, address: impl TryToUsize, element: &[T]) -> Result<()>
|
||||
where
|
||||
T: MemoryStorable,
|
||||
{
|
||||
let address = address.try_into().context("address can not be negative")?;
|
||||
let Self { memory, .. } = self;
|
||||
let size = std::mem::size_of_val(element);
|
||||
if memory.len() < address + size {
|
||||
memory.resize((address + size) * 2, 0);
|
||||
}
|
||||
let slice = unsafe { slice_into_u8(element) }?;
|
||||
let memory_slice = &mut memory[address..address + size];
|
||||
memory_slice.copy_from_slice(slice);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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;
|
||||
if memory.len() < address + size {
|
||||
memory.resize((address + size) * 2, 0);
|
||||
}
|
||||
memory[address..address + size].fill(val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_capacity(&mut self, size: impl TryToUsize) {
|
||||
let size = size.try_into().expect("size can not be negative");
|
||||
let Self { memory, .. } = self;
|
||||
if memory.len() < size {
|
||||
memory.resize(size, 0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_len(&self) ->usize {
|
||||
self.memory.len()
|
||||
}
|
||||
|
||||
pub(crate) fn clear(&mut self) {
|
||||
self.memory.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_slice_from_u8() {
|
||||
let buff: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
let transmuted: &[f32] = unsafe { slice_from_u8(&buff).unwrap() };
|
||||
assert!(
|
||||
transmuted[0] == 0_f32 && transmuted[1] == 0_f32,
|
||||
"Failed conversion from [00 00 00 00, 00 00 00 00] to [0_f32, 0_f32]"
|
||||
);
|
||||
|
||||
let buff = [0xdb, 0x0f, 0x49, 0x40];
|
||||
let transmuted: &[f32] = unsafe { slice_from_u8(&buff).unwrap() };
|
||||
assert!(
|
||||
transmuted[0] == std::f32::consts::PI,
|
||||
"Failed conversion from [0xdb, 0x0f, 0x49, 0x40] to [3.14]"
|
||||
);
|
||||
|
||||
let buff = [0x8f, 0xc2, 0xf5, 0x28, 0x5c, 0x8f, 0x18, 0x40];
|
||||
let transmuted: &[f64] = unsafe { slice_from_u8(&buff).unwrap() };
|
||||
assert!(
|
||||
transmuted[0] == 6.14_f64,
|
||||
"Failed conversion from [0x8f, 0xc2, 0xf5, 0x28, 0x5c, 0x8f, 0x18, 0x40] to [6.14]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice_into_u8() {
|
||||
let buff: [f32; 2] = [0_f32, 0_f32];
|
||||
let transmuted: &[u8] = unsafe { slice_into_u8(&buff).unwrap() };
|
||||
assert!(
|
||||
transmuted[0] == 0
|
||||
&& transmuted[1] == 0
|
||||
&& transmuted[2] == 0
|
||||
&& transmuted[3] == 0
|
||||
&& transmuted[4] == 0
|
||||
&& transmuted[5] == 0
|
||||
&& transmuted[6] == 0
|
||||
&& transmuted[7] == 0,
|
||||
"Failed conversion from [00 00 00 00, 00 00 00 00] to [0_f32, 0_f32]"
|
||||
);
|
||||
|
||||
let buff = [std::f32::consts::PI];
|
||||
let transmuted: &[u8] = unsafe { slice_into_u8(&buff).unwrap() };
|
||||
println!("{:?}", transmuted);
|
||||
assert!(
|
||||
transmuted[0] == 0xdb
|
||||
&& transmuted[1] == 0x0f
|
||||
&& transmuted[2] == 0x49
|
||||
&& transmuted[3] == 0x40,
|
||||
"Failed conversion from [0xdb, 0x0f, 0x49, 0x40] to [3.14]"
|
||||
);
|
||||
|
||||
let buff = [0x8f, 0xc2, 0xf5, 0x28, 0x5c, 0x8f, 0x18, 0x40];
|
||||
let transmuted: &[f64] = unsafe { slice_from_u8(&buff).unwrap() };
|
||||
assert!(
|
||||
transmuted[0] == 6.14_f64,
|
||||
"Failed conversion from [0x8f, 0xc2, 0xf5, 0x28, 0x5c, 0x8f, 0x18, 0x40] to [6.14]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_load_store() {
|
||||
let mut core_memory = CoreMemory::new();
|
||||
core_memory.execute_store(0, &[5_f32, 6_f32, 7_f32, 15_f32]);
|
||||
core_memory.reserve_load(0, 4); // Load [5_f32]
|
||||
core_memory.reserve_load(8, 8); // Load [7_f32, 15_f32]
|
||||
let loads: Vec<&[f32]> = core_memory.execute_load().unwrap();
|
||||
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];
|
||||
println!("{:?}", loads);
|
||||
assert!(loads[0] == 5_f32 && loads[1] == 12_f32 && loads[2] == 7_f32 && loads[3] == 15_f32)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::Debug,
|
||||
ops::{Add, Div, Mul, Sub},
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
pub trait FromFloat {
|
||||
fn from_f32(val :f32) -> Self;
|
||||
fn from_f64(val :f64) -> Self;
|
||||
}
|
||||
|
||||
impl FromFloat for f32 {
|
||||
fn from_f32(val :f32) -> Self {
|
||||
val
|
||||
}
|
||||
|
||||
fn from_f64(val :f64) -> Self {
|
||||
val as f32
|
||||
}
|
||||
}
|
||||
|
||||
impl FromFloat for f64 {
|
||||
fn from_f32(val :f32) -> Self {
|
||||
val as f64
|
||||
}
|
||||
|
||||
fn from_f64(val :f64) -> Self {
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasTanh {
|
||||
fn tanh(self) -> Self ;
|
||||
}
|
||||
|
||||
impl HasTanh for f32 {
|
||||
fn tanh(self) -> Self {
|
||||
self.tanh()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasTanh for f64 {
|
||||
fn tanh(self) -> Self {
|
||||
self.tanh()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasSigm {
|
||||
fn sigm(self) -> Self ;
|
||||
}
|
||||
|
||||
impl HasSigm for f32 {
|
||||
fn sigm(self) -> Self {
|
||||
let x = self;
|
||||
let e = std::f32::consts::E;
|
||||
let ex = x.powf(x);
|
||||
(ex) / (1.0+ex)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSigm for f64 {
|
||||
fn sigm(self) -> Self {
|
||||
let x = self;
|
||||
let e = std::f64::consts::E;
|
||||
let ex = x.powf(x);
|
||||
(ex) / (1.0+ex)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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
|
||||
T: TryInto<usize, Error = E>,
|
||||
E: Debug + Send + Sync + 'static + std::error::Error,
|
||||
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 }
|
||||
}
|
||||
|
||||
impl FromUsize for f64 {
|
||||
fn from_usize(v: usize) -> Self { v as f64 }
|
||||
}
|
||||
|
||||
pub trait UpcastDestTraits<T>:
|
||||
Sync
|
||||
+ Send
|
||||
+ Sized
|
||||
+ Clone
|
||||
+ Copy
|
||||
+ Debug
|
||||
+ Add<Output = T>
|
||||
+ Sub<Output = T>
|
||||
+ Mul<Output = T>
|
||||
+ Div<Output = T>
|
||||
+ PartialEq<T>
|
||||
+ PartialOrd<T>
|
||||
+ HasTanh
|
||||
+ HasSigm
|
||||
+ FromUsize
|
||||
{
|
||||
}
|
||||
|
||||
pub trait UpcastSlice<X> {
|
||||
fn up<'a>(&'a self) -> Cow<'a, [X]>
|
||||
where
|
||||
[X]: ToOwned<Owned = Vec<X>>,
|
||||
X: UpcastDestTraits<X>;
|
||||
}
|
||||
|
||||
macro_rules! upcast_impl {
|
||||
($from:ty, $to:ty) => {
|
||||
impl UpcastSlice<$to> for [$from] {
|
||||
fn up<'a>(&'a self) -> Cow<'a, [$to]>
|
||||
where
|
||||
[$to]: ToOwned<Owned = Vec<$to>>,
|
||||
{
|
||||
self.iter().map(|&x| x as $to).collect::<Vec<_>>().into()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($same:ty) => {
|
||||
impl UpcastSlice<$same> for [$same] {
|
||||
fn up<'a>(&'a self) -> Cow<'a, [$same]>
|
||||
where
|
||||
[$same]: ToOwned<Owned = Vec<$same>>,
|
||||
{
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpcastDestTraits<$same> for $same {}
|
||||
};
|
||||
}
|
||||
|
||||
upcast_impl!(f32, f64);
|
||||
upcast_impl!(f64, f32);
|
||||
upcast_impl!(f32);
|
||||
upcast_impl!(f64);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use core::panic;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::memory_manager::type_traits::{UpcastDestTraits, UpcastSlice};
|
||||
|
||||
#[test]
|
||||
fn test_same_type() {
|
||||
let elem: [f32; 4] = [1.0, 2.0, 3.0, 4.0];
|
||||
let elem_up: Cow<[f32]> = elem.up();
|
||||
if let std::borrow::Cow::Owned(_) = elem_up {
|
||||
panic!("Allocate when same element")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_type() {
|
||||
let elem: [f32; 4] = [1.0, 2.0, 3.0, 4.0];
|
||||
let elem_up: Cow<[f64]> = elem.up();
|
||||
if let std::borrow::Cow::Borrowed(_) = elem_up {
|
||||
panic!("Not allocating with different type")
|
||||
}
|
||||
}
|
||||
|
||||
fn generic_sum<A, B, T>(a: &[A], b: &[B]) -> Vec<T>
|
||||
where
|
||||
[A]: UpcastSlice<T>,
|
||||
[B]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T>,
|
||||
{
|
||||
let a_up = a.up();
|
||||
let b_up = b.up();
|
||||
let mut ret = Vec::new();
|
||||
for (a, b) in a_up.iter().zip(b_up.iter()) {
|
||||
ret.push(*a + *b);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sum_between_different_slice_in_generic() {
|
||||
let a: [f32; 4] = [1.0, 2.0, 3.0, 4.0];
|
||||
let b: [f64; 4] = [1.0, 2.0, 3.0, 4.0];
|
||||
let c: Vec<f64> = generic_sum(&a, &b);
|
||||
assert_eq!(c, vec![2.0, 4.0, 6.0, 8.0]);
|
||||
}
|
||||
}
|
||||
197
backend-simulators/pim-simulator/src/lib/pimcore.rs
Normal file
197
backend-simulators/pim-simulator/src/lib/pimcore.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use crate::{
|
||||
cpu::CPU, instruction_set::{Instruction, InstructionStatus, Instructions}, memory_manager::type_traits::TryToUsize, send_recv::{SendRecv, handle_send_recv}, tracing::TRACER
|
||||
};
|
||||
pub mod cpu;
|
||||
pub mod instruction_set;
|
||||
pub mod memory_manager;
|
||||
pub mod send_recv;
|
||||
pub mod utility;
|
||||
pub mod json_to_instruction;
|
||||
pub mod tracing;
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CoreInstructionsBuilder {
|
||||
core_instructions : Vec<CoreInstruction>
|
||||
}
|
||||
|
||||
impl CoreInstructionsBuilder {
|
||||
pub fn new(size:usize) -> Self {
|
||||
let mut core_instructions = Vec::with_capacity(size);
|
||||
for _ in 0..=size {
|
||||
core_instructions.push(CoreInstruction::empty());
|
||||
}
|
||||
Self { core_instructions }
|
||||
}
|
||||
|
||||
pub fn build(self) -> Vec<CoreInstruction> {
|
||||
self.core_instructions
|
||||
}
|
||||
|
||||
pub fn set_core(&mut self, core : impl TryToUsize, core_instruction : Instructions) -> &mut Self{
|
||||
self.core_instructions[core.try_into().expect("Set core with not valid size")] = core_instruction.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CoreInstruction {
|
||||
instructions: Instructions,
|
||||
program_counter: usize,
|
||||
}
|
||||
|
||||
impl CoreInstruction {
|
||||
fn new(instructions: Instructions, program_counter: usize) -> Self {
|
||||
Self {
|
||||
instructions,
|
||||
program_counter,
|
||||
}
|
||||
}
|
||||
|
||||
fn empty() -> Self {
|
||||
Self { instructions: Vec::new(), program_counter: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Instructions> for CoreInstruction {
|
||||
fn from(value: Instructions) -> Self {
|
||||
CoreInstruction {
|
||||
instructions: value,
|
||||
program_counter: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Executable {
|
||||
cpu: CPU,
|
||||
core_instructions: Vec<CoreInstruction>,
|
||||
send_recv : SendRecv,
|
||||
}
|
||||
|
||||
impl Executable {
|
||||
pub fn new(cpu: CPU, core_instructions: Vec<CoreInstruction>) -> Self {
|
||||
let num_core = cpu.num_core();
|
||||
let send_recv = SendRecv::new(num_core);
|
||||
assert_eq!(num_core, core_instructions.len(), "Some core doesn't have is list of istruction (required even if empty)");
|
||||
Self {
|
||||
cpu,
|
||||
core_instructions,
|
||||
send_recv
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(&mut self) {
|
||||
TRACER.lock().unwrap().init(self);
|
||||
let Self {
|
||||
cpu,
|
||||
core_instructions,
|
||||
send_recv
|
||||
} = self;
|
||||
let mut cpu_progressed = 0;
|
||||
let max_core = cpu.num_core();
|
||||
let mut index_unit = 0;
|
||||
|
||||
while (cpu_progressed > -2) {
|
||||
let mut core_result = InstructionStatus::Completed;
|
||||
while core_result.is_completed() && let Some(core_instruction) = core_instructions.get_mut(index_unit){
|
||||
core_result = InstructionStatus::NotExecuted;
|
||||
let CoreInstruction {
|
||||
instructions,
|
||||
program_counter,
|
||||
} = core_instruction;
|
||||
core_result = instructions
|
||||
.get(*program_counter)
|
||||
.map_or(InstructionStatus::default(), |inst: &Instruction| {
|
||||
inst.execute(cpu)
|
||||
});
|
||||
if core_result.is_completed() {
|
||||
cpu_progressed = 0;
|
||||
*program_counter += 1;
|
||||
}
|
||||
}
|
||||
if handle_send_recv(cpu, core_instructions, send_recv, core_result) { cpu_progressed = 0; }
|
||||
handle_wait_sync(cpu, core_instructions, core_result);
|
||||
index_unit = if index_unit + 1 >= max_core {
|
||||
cpu_progressed-=1;
|
||||
0
|
||||
} else {
|
||||
index_unit + 1
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cpu(&self) -> &CPU {
|
||||
&self.cpu
|
||||
}
|
||||
|
||||
pub fn cpu_mut(&mut self) -> &mut CPU {
|
||||
&mut self.cpu
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_wait_sync(cpu: &mut CPU, core_instructions: &mut [CoreInstruction], core_result: InstructionStatus) {
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::instruction_set::instruction_data::InstructionDataBuilder;
|
||||
use crate::instruction_set::{InstructionsBuilder, isa::*};
|
||||
|
||||
#[test]
|
||||
fn test_only_host() {
|
||||
let mut cpu = CPU::new(0);
|
||||
cpu.host()
|
||||
.execute_store(0, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
|
||||
let mut inst_builder = InstructionsBuilder::new();
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(0).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(1, 0).build());
|
||||
inst_builder.make_inst(sld, idata_build.set_rdr1(1, 1).build());
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(2, 8).build());
|
||||
inst_builder.make_inst(sld, idata_build.set_rdr1(2, 2).build());
|
||||
inst_builder.make_inst(sadd, idata_build.set_rdr1r2(2, 1, 2).build());
|
||||
let mut core_instruction = vec![inst_builder.build().into()];
|
||||
let mut executable = Executable::new(cpu, core_instruction);
|
||||
executable.execute();
|
||||
assert_eq!(executable.cpu_mut().host().register(2), 4, "Not sum to 4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_10_core_same_code() {
|
||||
let setup_core = |index: usize, cpu: &mut CPU| -> Instructions {
|
||||
cpu.core(index)
|
||||
.execute_store(0, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
|
||||
let mut inst_builder = InstructionsBuilder::new();
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(index as i32).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(1, 0).build());
|
||||
inst_builder.make_inst(sld, idata_build.set_rdr1(1, 1).build());
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(2, 8).build());
|
||||
inst_builder.make_inst(sld, idata_build.set_rdr1(2, 2).build());
|
||||
inst_builder.make_inst(sadd, idata_build.set_rdr1r2(2, 1, 2).build());
|
||||
inst_builder.build()
|
||||
};
|
||||
|
||||
let mut cpu = CPU::new(10);
|
||||
let mut core_instruction = Vec::new();
|
||||
for i in 0..cpu.num_core() {
|
||||
core_instruction.push(setup_core(i, &mut cpu).into())
|
||||
}
|
||||
|
||||
let mut executable = Executable::new(cpu, core_instruction);
|
||||
executable.execute();
|
||||
for i in 0.. executable.cpu.num_core() {
|
||||
assert_eq!(executable.cpu_mut().core(i).register(2), 4, "Core {} not sum to 4", i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
143
backend-simulators/pim-simulator/src/lib/send_recv.rs
Normal file
143
backend-simulators/pim-simulator/src/lib/send_recv.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::{
|
||||
CoreInstruction, cpu::CPU, instruction_set::InstructionStatus, tracing::TRACER,
|
||||
utility::add_offset_rd,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct SendRecvInfo {
|
||||
internal_core: usize,
|
||||
external_core: usize,
|
||||
address: usize,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
impl SendRecvInfo {
|
||||
fn new(internal_core: usize, external_core: usize, address: usize, size: usize) -> Self {
|
||||
Self {
|
||||
internal_core,
|
||||
external_core,
|
||||
address,
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SendRecv {
|
||||
sending: Box<[Option<SendRecvInfo>]>,
|
||||
receiving: Box<[Option<SendRecvInfo>]>,
|
||||
}
|
||||
|
||||
impl SendRecv {
|
||||
pub fn new(num_core: usize) -> Self {
|
||||
let sending = [Option::None].repeat(num_core);
|
||||
let reciving = [Option::None].repeat(num_core);
|
||||
Self {
|
||||
sending: sending.into(),
|
||||
receiving: reciving.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_send_recv(
|
||||
cpu: &mut CPU,
|
||||
core_instructions: &mut [CoreInstruction],
|
||||
send_recv: &mut SendRecv,
|
||||
core_result: InstructionStatus,
|
||||
) -> bool {
|
||||
let transfer_memory = |cpu: &mut CPU,
|
||||
core_instructions: &mut [CoreInstruction],
|
||||
sender: Option<SendRecvInfo>,
|
||||
receiver: Option<SendRecvInfo>| {
|
||||
if let Some(sender) = sender
|
||||
&& let Some(receiver) = receiver
|
||||
&& sender.internal_core == receiver.external_core
|
||||
&& receiver.internal_core == sender.external_core
|
||||
{
|
||||
let [sender_core, reciver_core] =
|
||||
cpu.get_multiple_cores([sender.internal_core, receiver.internal_core]);
|
||||
let memory = sender_core
|
||||
.load::<u8>(sender.address, sender.size)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Sender crash tranfering memroy from {} with size {}",
|
||||
sender.address, sender.size
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
reciver_core.execute_store(receiver.address, memory[0]);
|
||||
{
|
||||
let sender = &mut core_instructions[sender.internal_core];
|
||||
let pc = sender.program_counter;
|
||||
let inst = sender.instructions.get(pc).unwrap();
|
||||
let data = inst.data;
|
||||
TRACER.lock().unwrap().post_send(cpu, data);
|
||||
}
|
||||
{
|
||||
let recv = &mut core_instructions[receiver.internal_core];
|
||||
let pc = recv.program_counter;
|
||||
let inst = recv.instructions.get(pc).unwrap();
|
||||
let data = inst.data;
|
||||
TRACER.lock().unwrap().post_recv(cpu, data);
|
||||
}
|
||||
core_instructions[sender.internal_core].program_counter += 1;
|
||||
core_instructions[receiver.internal_core].program_counter += 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
false
|
||||
};
|
||||
|
||||
match core_result {
|
||||
InstructionStatus::Sending(instruction_data) => {
|
||||
let (core_idx, imm_core) = instruction_data.get_core_immcore();
|
||||
let r1 = instruction_data.r1();
|
||||
let imm_len = instruction_data
|
||||
.imm_len()
|
||||
.try_into()
|
||||
.expect("imm_len can not be negative");
|
||||
let offset_value = instruction_data.offset_value();
|
||||
let core = cpu.core(core_idx);
|
||||
let r1_val = core.register(r1);
|
||||
let address = add_offset_rd(r1_val, 1, offset_value);
|
||||
let sender: usize = core_idx.try_into().expect("core can not be negative");
|
||||
assert_ne!(sender, 0, "Host can not use send");
|
||||
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));
|
||||
transfer_memory(
|
||||
cpu,
|
||||
core_instructions,
|
||||
send_recv.sending[sender],
|
||||
send_recv.receiving[receiver],
|
||||
)
|
||||
}
|
||||
InstructionStatus::Reciving(instruction_data) => {
|
||||
let (core_idx, imm_core) = instruction_data.get_core_immcore();
|
||||
let rd = instruction_data.rd();
|
||||
let imm_len = instruction_data
|
||||
.imm_len()
|
||||
.try_into()
|
||||
.expect("imm_len can not be negative");
|
||||
let offset_value = instruction_data.offset_value();
|
||||
let core = cpu.core(core_idx);
|
||||
let rd_val = core.register(rd);
|
||||
let address = add_offset_rd(rd_val, 4, offset_value);
|
||||
let receiver: usize = core_idx.try_into().expect("core can not be negative");
|
||||
assert_ne!(receiver, 0, "Host can not use receive");
|
||||
let sender: usize = imm_core.try_into().expect("imm_core can not be negative");
|
||||
assert_ne!(sender, 0, "Host can not use send");
|
||||
send_recv.receiving[receiver] =
|
||||
Some(SendRecvInfo::new(receiver, sender, address, imm_len));
|
||||
transfer_memory(
|
||||
cpu,
|
||||
core_instructions,
|
||||
send_recv.sending[sender],
|
||||
send_recv.receiving[receiver],
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
289
backend-simulators/pim-simulator/src/lib/tracing/disable.rs
Normal file
289
backend-simulators/pim-simulator/src/lib/tracing/disable.rs
Normal file
@@ -0,0 +1,289 @@
|
||||
|
||||
use std::fs::File;
|
||||
|
||||
use crate::{
|
||||
cpu::CPU,
|
||||
instruction_set::instruction_data::InstructionData,
|
||||
memory_manager::{
|
||||
MemoryStorable,
|
||||
type_traits::{FromFloat, UpcastDestTraits, UpcastSlice},
|
||||
},
|
||||
tracing::Trace,
|
||||
utility::{add_offset_r1, add_offset_rd},
|
||||
};
|
||||
use std::io::Write;
|
||||
|
||||
#[cfg(not(feature = "tracing"))]
|
||||
impl Trace {
|
||||
///////////////////////////////////////////////////////////////
|
||||
/////////////////Scalar/register Instructions//////////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
pub fn pre_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 post_sld(&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 pre_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 post_smul(&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 pre_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 post_setbw(&mut self, cores: &mut CPU, data: InstructionData) {
|
||||
}
|
||||
|
||||
pub fn pre_mvm<F, M, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T> + UpcastSlice<M>,
|
||||
[M]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
self.mvm_impl::<F,M,T>(cores, data, "Pre");
|
||||
}
|
||||
|
||||
pub fn post_mvm<F, M, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T> + UpcastSlice<M>,
|
||||
[M]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
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
|
||||
[F]: UpcastSlice<T> + UpcastSlice<M>,
|
||||
[M]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn pre_vvadd<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn post_vvadd<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn pre_vvsub<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn post_vvsub<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn pre_vvmul<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn post_vvmul<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn pre_vvdmul<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn post_vvdmul<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn pre_vvmax<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn post_vvmax<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn pre_vavg<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn post_vavg<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn pre_vrelu<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn post_vrelu<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn pre_vtanh<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn post_vtanh<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn pre_vsigm<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn post_vsigm<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
|
||||
where
|
||||
[F]: UpcastSlice<T>,
|
||||
T: UpcastDestTraits<T> + MemoryStorable,
|
||||
F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
|
||||
{
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/////Communication/synchronization Instructions/////////////////
|
||||
/////////////////////////////////////////////////////////////////
|
||||
pub fn pre_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 post_st(&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 pre_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 post_send(&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) {
|
||||
}
|
||||
}
|
||||
52
backend-simulators/pim-simulator/src/lib/tracing/mod.rs
Normal file
52
backend-simulators/pim-simulator/src/lib/tracing/mod.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
mod tracing_isa;
|
||||
mod disable;
|
||||
mod pretty_print;
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
use std::fs::File;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
use crate::Executable;
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
pub struct Trace {
|
||||
out_files : Vec<File>
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
impl Trace {
|
||||
fn new() -> Self {
|
||||
Self { out_files : Vec::new()}
|
||||
}
|
||||
|
||||
|
||||
pub fn init(&mut self, executor : & Executable) {
|
||||
let cpu = executor.cpu();
|
||||
let num_core = cpu.num_core();
|
||||
for i in 0..num_core {
|
||||
let file = File::create(format!("TraceCore{}", i)).expect("Can not create file");
|
||||
self.out_files.push(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tracing"))]
|
||||
pub struct Trace {
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "tracing"))]
|
||||
impl Trace {
|
||||
fn new() -> Self {
|
||||
Self { }
|
||||
}
|
||||
|
||||
|
||||
pub fn init(&mut self, executor : & Executable) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub static TRACER: LazyLock<Mutex<Trace>> = LazyLock::new(|| { Trace::new().into()});
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
use std::{fmt::Debug, io::Write};
|
||||
|
||||
pub fn print_slice<D: Debug, F: Sized + Debug>(writer: &mut impl Write, slice: &[D], size: usize) {
|
||||
let slice = unsafe {
|
||||
let (a, b, c) = slice.align_to::<F>();
|
||||
assert_eq!(a.len(), 0);
|
||||
assert_eq!(c.len(), 0);
|
||||
b
|
||||
};
|
||||
writeln!(writer, "\t\t[");
|
||||
for elements in slice.chunks(size) {
|
||||
write!(writer, "\t\t");
|
||||
for element in elements {
|
||||
write!(writer, "{:?} ", element);
|
||||
}
|
||||
writeln!(writer);
|
||||
}
|
||||
writeln!(writer, "\t\t]");
|
||||
}
|
||||
1249
backend-simulators/pim-simulator/src/lib/tracing/tracing_isa.rs
Normal file
1249
backend-simulators/pim-simulator/src/lib/tracing/tracing_isa.rs
Normal file
File diff suppressed because it is too large
Load Diff
39
backend-simulators/pim-simulator/src/lib/utility.rs
Normal file
39
backend-simulators/pim-simulator/src/lib/utility.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::{fmt::Debug, mem::transmute};
|
||||
|
||||
use crate::memory_manager::type_traits::TryToUsize;
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn add_offset_rd(address: impl TryToUsize, offset_select : i32, offset_value : i32) -> usize
|
||||
{
|
||||
let address = address.try_into().expect("address can not be negative");
|
||||
add_offset_impl(address, offset_select, offset_value, 4)
|
||||
}
|
||||
|
||||
pub fn add_offset_r1(address: impl TryToUsize, offset_select : i32, offset_value : i32) -> usize
|
||||
{
|
||||
let address = address.try_into().expect("address can not be negative");
|
||||
add_offset_impl(address, offset_select, offset_value, 1)
|
||||
}
|
||||
|
||||
pub fn add_offset_r2(address: impl TryToUsize, offset_select : i32, offset_value : i32) -> usize
|
||||
{
|
||||
let address = address.try_into().expect("address can not be negative");
|
||||
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()
|
||||
}
|
||||
1
backend-simulators/pim-simulator/tests/A.txt
Normal file
1
backend-simulators/pim-simulator/tests/A.txt
Normal file
File diff suppressed because one or more lines are too long
1
backend-simulators/pim-simulator/tests/B.txt
Normal file
1
backend-simulators/pim-simulator/tests/B.txt
Normal file
File diff suppressed because one or more lines are too long
1
backend-simulators/pim-simulator/tests/X.txt
Normal file
1
backend-simulators/pim-simulator/tests/X.txt
Normal file
File diff suppressed because one or more lines are too long
73
backend-simulators/pim-simulator/tests/big_mul.rs
Normal file
73
backend-simulators/pim-simulator/tests/big_mul.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use std::path::Path;
|
||||
|
||||
use pimcore::{Executable, cpu::CPU, instruction_set::{InstructionsBuilder, instruction_data::InstructionDataBuilder, isa::*}};
|
||||
|
||||
fn simple_read(path: &Path) -> Vec<f32> {
|
||||
if !path.exists() {
|
||||
panic!("{:?} not exists", path)
|
||||
}
|
||||
std::fs::read_to_string(path)
|
||||
.unwrap()
|
||||
.split(',')
|
||||
.map(|s| s.trim().parse::<f32>().unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// mvmul Test
|
||||
fn mvmul_f32(err: &str)
|
||||
where
|
||||
{
|
||||
let mut cpu = CPU::new(0);
|
||||
cpu.reserve_crossbar(1, 1024 * size_of::<f32>(), 1024);
|
||||
let (memory, crossbars) = cpu.host().get_memory_crossbar();
|
||||
let matrix = simple_read(Path::new("./tests/B.txt")) ;
|
||||
|
||||
|
||||
crossbars.get_mut(0).unwrap().execute_store( &matrix).unwrap();
|
||||
let vector = simple_read(Path::new("./tests/A.txt"));
|
||||
memory.execute_store(0, &vector).unwrap();
|
||||
|
||||
let mut inst_builder = InstructionsBuilder::new();
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(0).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(1, 0).build());
|
||||
inst_builder.make_inst(
|
||||
sldi,
|
||||
idata_build.set_rdimm(3, 1024 * size_of::<f32>() as i32).build(),
|
||||
);
|
||||
inst_builder.make_inst(
|
||||
setbw,
|
||||
idata_build
|
||||
.set_ibiw_obiw(8 * size_of::<f32>() as i32, 8 * size_of::<f32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
inst_builder.make_inst(
|
||||
mvmul,
|
||||
idata_build
|
||||
.set_rdr1(3, 1)
|
||||
.set_mbiw_immrelu_immgroup(8*size_of::<f32>() as i32, 0, 0)
|
||||
.build(),
|
||||
);
|
||||
let core_instruction = vec![inst_builder.build().into()];
|
||||
let mut executable = Executable::new(cpu, core_instruction);
|
||||
executable.execute();
|
||||
|
||||
assert!(
|
||||
executable
|
||||
.cpu_mut()
|
||||
.host()
|
||||
.load::<f32>(1024 * size_of::<f32>(), 1024*size_of::<f32>()).unwrap()[0].iter().zip(
|
||||
simple_read(Path::new("./tests/X.txt")) ).all(|(&a,b) : (&f32, f32)| {a-b < 0.001}),
|
||||
"Wrong result for {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mvmul_big_test() {
|
||||
mvmul_f32("mvmul_f32");
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"adc_count":16,"array_group_map":{"core0":[0,1,2],"core1":[],"core2":[0,1,2],"core3":[],"core4":[0,1,2],"core5":[]},"cell_precision":2,"core_cnt":6,"inputs_addresses":[0],"outputs_addresses":[532],"xbar_array_count":8,"xbar_size":[64,64]}
|
||||
@@ -0,0 +1 @@
|
||||
[{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"group":0,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":260,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":260,"op":"sldi","rd":0},{"imm":260,"op":"sldi","rd":1},{"group":1,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":260,"op":"sldi","rd":2},{"len":64,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":780,"op":"sldi","rd":0},{"imm":512,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":16},{"imm":520,"op":"sldi","rd":0},{"imm":780,"op":"sldi","rd":1},{"group":2,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":520,"op":"sldi","rd":2},{"len":64,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":0,"op":"sldi","rd":0},{"core":1,"offset":{"offset_select":0,"offset_value":0},"op":"send","rd":0,"size":64}]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
[{"imm":0,"op":"sldi","rd":0},{"core":0,"offset":{"offset_select":0,"offset_value":0},"op":"recv","rd":0,"size":64},{"imm":532,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"st","rd":0,"rs1":1,"size":64}]
|
||||
@@ -0,0 +1 @@
|
||||
[{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"group":0,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":260,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":260,"op":"sldi","rd":0},{"imm":260,"op":"sldi","rd":1},{"group":1,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":260,"op":"sldi","rd":2},{"len":64,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":780,"op":"sldi","rd":0},{"imm":512,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":16},{"imm":520,"op":"sldi","rd":0},{"imm":780,"op":"sldi","rd":1},{"group":2,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":520,"op":"sldi","rd":2},{"len":64,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":0,"op":"sldi","rd":0},{"core":3,"offset":{"offset_select":0,"offset_value":0},"op":"send","rd":0,"size":64}]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
[{"imm":0,"op":"sldi","rd":0},{"core":2,"offset":{"offset_select":0,"offset_value":0},"op":"recv","rd":0,"size":64},{"imm":596,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"st","rd":0,"rs1":1,"size":64}]
|
||||
@@ -0,0 +1 @@
|
||||
[{"imm":260,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":0,"op":"sldi","rd":0},{"imm":260,"op":"sldi","rd":1},{"group":0,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":780,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":520,"op":"sldi","rd":0},{"imm":780,"op":"sldi","rd":1},{"group":1,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":520,"op":"sldi","rd":2},{"len":4,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":1040,"op":"sldi","rd":0},{"imm":512,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":16},{"imm":1040,"op":"sldi","rd":0},{"imm":1040,"op":"sldi","rd":1},{"group":2,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":1040,"op":"sldi","rd":2},{"len":4,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":0,"op":"sldi","rd":0},{"core":5,"offset":{"offset_select":0,"offset_value":0},"op":"send","rd":0,"size":4}]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
[{"imm":0,"op":"sldi","rd":0},{"core":4,"offset":{"offset_select":0,"offset_value":0},"op":"recv","rd":0,"size":4},{"imm":660,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"st","rd":0,"rs1":1,"size":4}]
|
||||
BIN
backend-simulators/pim-simulator/tests/data/Gemm/memory.bin
Normal file
BIN
backend-simulators/pim-simulator/tests/data/Gemm/memory.bin
Normal file
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"adc_count":16,"array_group_map":{"core0":[0,1,2],"core1":[],"core2":[0,1,2],"core3":[],"core4":[0,1,2],"core5":[]},"cell_precision":2,"core_cnt":6,"inputs_addresses":[0],"outputs_addresses":[528],"xbar_array_count":8,"xbar_size":[64,64]}
|
||||
@@ -0,0 +1 @@
|
||||
[{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"group":0,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":256,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":256,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"group":1,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":256,"op":"sldi","rd":2},{"len":256,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":768,"op":"sldi","rd":0},{"imm":512,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":16},{"imm":512,"op":"sldi","rd":0},{"imm":768,"op":"sldi","rd":1},{"group":2,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":512,"op":"sldi","rd":2},{"len":256,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":0,"op":"sldi","rd":0},{"core":1,"offset":{"offset_select":0,"offset_value":0},"op":"send","rd":0,"size":256}]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
[{"imm":0,"op":"sldi","rd":0},{"imm":1056,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":256,"op":"sldi","rd":0},{"core":0,"offset":{"offset_select":0,"offset_value":0},"op":"recv","rd":0,"size":256},{"imm":256,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"imm":0,"op":"sldi","rd":2},{"len":256,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":528,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"st","rd":0,"rs1":1,"size":256}]
|
||||
@@ -0,0 +1 @@
|
||||
[{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"group":0,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":256,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":256,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"group":1,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":256,"op":"sldi","rd":2},{"len":256,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":768,"op":"sldi","rd":0},{"imm":512,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":16},{"imm":512,"op":"sldi","rd":0},{"imm":768,"op":"sldi","rd":1},{"group":2,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":512,"op":"sldi","rd":2},{"len":256,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":0,"op":"sldi","rd":0},{"core":3,"offset":{"offset_select":0,"offset_value":0},"op":"send","rd":0,"size":256}]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
[{"imm":0,"op":"sldi","rd":0},{"imm":1312,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":256,"op":"sldi","rd":0},{"core":2,"offset":{"offset_select":0,"offset_value":0},"op":"recv","rd":0,"size":256},{"imm":256,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"imm":0,"op":"sldi","rd":2},{"len":256,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":784,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"st","rd":0,"rs1":1,"size":256}]
|
||||
@@ -0,0 +1 @@
|
||||
[{"imm":256,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":0,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"group":0,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":768,"op":"sldi","rd":0},{"imm":256,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":256},{"imm":512,"op":"sldi","rd":0},{"imm":768,"op":"sldi","rd":1},{"group":1,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":512,"op":"sldi","rd":2},{"len":16,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":1024,"op":"sldi","rd":0},{"imm":512,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":16},{"imm":1024,"op":"sldi","rd":0},{"imm":1024,"op":"sldi","rd":1},{"group":2,"mbiw":8,"op":"mvmul","rd":0,"relu":0,"rs1":1},{"imm":0,"op":"sldi","rd":0},{"imm":0,"op":"sldi","rd":1},{"imm":1024,"op":"sldi","rd":2},{"len":16,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":0,"op":"sldi","rd":0},{"core":5,"offset":{"offset_select":0,"offset_value":0},"op":"send","rd":0,"size":16}]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
[{"imm":0,"op":"sldi","rd":0},{"imm":1568,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"ld","rd":0,"rs1":1,"size":16},{"imm":16,"op":"sldi","rd":0},{"core":4,"offset":{"offset_select":0,"offset_value":0},"op":"recv","rd":0,"size":16},{"imm":16,"op":"sldi","rd":0},{"imm":16,"op":"sldi","rd":1},{"imm":0,"op":"sldi","rd":2},{"len":16,"offset":{"offset_select":0,"offset_value":0},"op":"vvadd","rd":0,"rs1":1,"rs2":2},{"imm":1040,"op":"sldi","rd":0},{"imm":16,"op":"sldi","rd":1},{"offset":{"offset_select":0,"offset_value":0},"op":"st","rd":0,"rs1":1,"size":16}]
|
||||
Binary file not shown.
51
backend-simulators/pim-simulator/tests/es_runner.rs
Normal file
51
backend-simulators/pim-simulator/tests/es_runner.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use std::{fs, io::BufReader, path::Path};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use pimcore::json_to_instruction::json_to_executor;
|
||||
use serde_json::Value;
|
||||
|
||||
fn collect_json_from_subfolders<P: AsRef<Path>>(root: P) -> Result<Vec<(Value, Vec<Value>)>> {
|
||||
let mut result = Vec::new();
|
||||
for entry in fs::read_dir(root)? {
|
||||
let entry = entry.context("Root not found")?;
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
let mut cores = Vec::new();
|
||||
let mut config: Option<Value> = None;
|
||||
for sub_entry in fs::read_dir(&path)
|
||||
.with_context(|| format!("File {} not readable", path.display()))?
|
||||
{
|
||||
let sub_entry =
|
||||
sub_entry.with_context(|| format!("File {} not readable", path.display()))?;
|
||||
let sub_path = sub_entry.path();
|
||||
if sub_path.is_file()
|
||||
&& sub_path.extension().and_then(|s| s.to_str()) == Some("json")
|
||||
{
|
||||
let file = fs::File::open(&sub_path)
|
||||
.with_context(|| format!("Subpath {} not opened", sub_path.display()))?;
|
||||
let reader = BufReader::new(file);
|
||||
let val: Value = serde_json::from_reader(reader).with_context(|| format!(
|
||||
"Serde reader fail for subpath {}",
|
||||
sub_path.display()
|
||||
))?;
|
||||
if sub_path.file_name().unwrap() == "config.json" {
|
||||
config = Some(val);
|
||||
} else {
|
||||
cores.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push((config.unwrap(), cores));
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_folder_tester() {
|
||||
let examples = collect_json_from_subfolders("./tests/data").unwrap();
|
||||
for example in examples {
|
||||
let (config, cores) = example;
|
||||
json_to_executor::json_to_executor(config, cores.iter()).execute();
|
||||
}
|
||||
}
|
||||
110
backend-simulators/pim-simulator/tests/placeholder.rs
Normal file
110
backend-simulators/pim-simulator/tests/placeholder.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
|
||||
use pimcore::{Executable, cpu::CPU, instruction_set::{InstructionType, InstructionsBuilder, instruction_data::InstructionDataBuilder, isa::*}};
|
||||
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Function not found for the requested size") ]
|
||||
fn wrong_size_place_holder() {
|
||||
let cpu = CPU::new(0);
|
||||
let mut inst_builder = InstructionsBuilder::new();
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(0).fix_core_indx();
|
||||
inst_builder.make_inst(
|
||||
setbw,
|
||||
idata_build
|
||||
.set_ibiw_obiw(55, 55)
|
||||
.build(),
|
||||
);
|
||||
inst_builder.make_inst(
|
||||
vvadd,
|
||||
idata_build
|
||||
.set_rdr1r2(3, 1, 2)
|
||||
.set_imm_len(8 * size_of::<f32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
let core_instruction = vec![inst_builder.build().into()];
|
||||
let mut executable = Executable::new(cpu, core_instruction);
|
||||
executable.execute();
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn place_holder(inst : InstructionType) {
|
||||
let mut cpu = CPU::new(0);
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(0).fix_core_indx();
|
||||
inst(&mut cpu, idata_build.build()).unwrap();
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "You are calling a placeholder, the real call is the generic version") ]
|
||||
fn vvadd_placeholder() {
|
||||
place_holder(vvadd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "You are calling a placeholder, the real call is the generic version") ]
|
||||
fn vvsub_placeholder() {
|
||||
place_holder(vvsub);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "You are calling a placeholder, the real call is the generic version") ]
|
||||
fn vvmul_placeholder() {
|
||||
place_holder(vvmul);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "You are calling a placeholder, the real call is the generic version") ]
|
||||
fn vvdmul_placeholder() {
|
||||
place_holder(vvdmul);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "You are calling a placeholder, the real call is the generic version") ]
|
||||
fn vvmax_placeholder() {
|
||||
place_holder(vvmax);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "You are calling a placeholder, the real call is the generic version") ]
|
||||
fn vavg_placeholder() {
|
||||
place_holder(vavg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "You are calling a placeholder, the real call is the generic version") ]
|
||||
fn vrelu_placeholder() {
|
||||
place_holder(vrelu);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "You are calling a placeholder, the real call is the generic version") ]
|
||||
fn vtanh_placeholder() {
|
||||
place_holder(vtanh);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "You are calling a placeholder, the real call is the generic version") ]
|
||||
fn vsigm_placeholder() {
|
||||
place_holder(vsigm);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "You are calling a placeholder, the real call is the generic version") ]
|
||||
fn mvmul_placeholder() {
|
||||
place_holder(mvmul);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic ]
|
||||
fn vvsll_why_inst() {
|
||||
place_holder(vvsll);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic ]
|
||||
fn vvsra_why_inst() {
|
||||
place_holder(vvsra);
|
||||
}
|
||||
1058
backend-simulators/pim-simulator/tests/simd.rs
Normal file
1058
backend-simulators/pim-simulator/tests/simd.rs
Normal file
File diff suppressed because it is too large
Load Diff
294
backend-simulators/pim-simulator/tests/sync.rs
Normal file
294
backend-simulators/pim-simulator/tests/sync.rs
Normal file
@@ -0,0 +1,294 @@
|
||||
use pimcore::{
|
||||
Executable, CoreInstructionsBuilder,
|
||||
cpu::CPU,
|
||||
instruction_set::{InstructionsBuilder, instruction_data::InstructionDataBuilder, isa::*},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn ld_test() {
|
||||
let mut cpu = CPU::new(1);
|
||||
let mut core_instruction_builder = CoreInstructionsBuilder::new(1);
|
||||
let buff: [f32; _] = [
|
||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
||||
];
|
||||
cpu.host().execute_store(0, &buff).unwrap();
|
||||
let mut inst_builder = InstructionsBuilder::new();
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(1).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(1, 0).build());
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(2, 0).build());
|
||||
inst_builder.make_inst(
|
||||
ld,
|
||||
idata_build
|
||||
.set_rdr1(1, 2)
|
||||
.set_imm_len(10 * size_of::<f32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
core_instruction_builder.set_core(1, inst_builder.build());
|
||||
let mut executable = Executable::new(cpu, core_instruction_builder.build());
|
||||
executable.execute();
|
||||
let res = executable
|
||||
.cpu_mut()
|
||||
.core(1)
|
||||
.load::<f32>(0, 10 * size_of::<f32>());
|
||||
|
||||
assert_eq!(
|
||||
res.unwrap()[0],
|
||||
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
|
||||
"LD failed to load"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn st_test() {
|
||||
let mut cpu = CPU::new(1);
|
||||
let mut core_instruction_builder = CoreInstructionsBuilder::new(1);
|
||||
let buff: [f32; _] = [
|
||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
||||
];
|
||||
cpu.core(1).execute_store(0, &buff).unwrap();
|
||||
let mut inst_builder = InstructionsBuilder::new();
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(1).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(1, 0).build());
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(2, 0).build());
|
||||
inst_builder.make_inst(
|
||||
st,
|
||||
idata_build
|
||||
.set_rdr1(1, 2)
|
||||
.set_imm_len(10 * size_of::<f32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
core_instruction_builder.set_core(1, inst_builder.build());
|
||||
let mut executable = Executable::new(cpu, core_instruction_builder.build());
|
||||
executable.execute();
|
||||
let res = executable
|
||||
.cpu_mut()
|
||||
.host()
|
||||
.load::<f32>(0, 10 * size_of::<f32>());
|
||||
|
||||
assert_eq!(
|
||||
res.unwrap()[0],
|
||||
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
|
||||
"ST failed to store"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lldi_test() {
|
||||
let cpu = CPU::new(1);
|
||||
let mut core_instruction_builder = CoreInstructionsBuilder::new(1);
|
||||
let mut inst_builder = InstructionsBuilder::new();
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(1).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(1, 0).build());
|
||||
inst_builder.make_inst(
|
||||
lldi,
|
||||
idata_build
|
||||
.set_rdimm(1, 0xff)
|
||||
.set_imm_len(10 * size_of::<i32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
core_instruction_builder.set_core(1, inst_builder.build());
|
||||
let mut executable = Executable::new(cpu, core_instruction_builder.build());
|
||||
executable.execute();
|
||||
let res = executable
|
||||
.cpu_mut()
|
||||
.core(1)
|
||||
.load::<i32>(0, 10 * size_of::<i32>());
|
||||
|
||||
assert_eq!(
|
||||
res.unwrap()[0],
|
||||
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
|
||||
"Lldi failed to memset"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lmv_test() {
|
||||
let mut cpu = CPU::new(1);
|
||||
let mut core_instruction_builder = CoreInstructionsBuilder::new(1);
|
||||
let buff: [f32; _] = [
|
||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
||||
];
|
||||
cpu.core(1).execute_store(0, &buff).unwrap();
|
||||
let mut inst_builder = InstructionsBuilder::new();
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(1).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(2, 0).build());
|
||||
inst_builder.make_inst(
|
||||
sldi,
|
||||
idata_build
|
||||
.set_rdimm(1, 8 * size_of::<f32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
inst_builder.make_inst(
|
||||
lmv,
|
||||
idata_build
|
||||
.set_rdr1(1, 2)
|
||||
.set_imm_len(8 * size_of::<f32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
core_instruction_builder.set_core(1, inst_builder.build());
|
||||
let mut executable = Executable::new(cpu, core_instruction_builder.build());
|
||||
executable.execute();
|
||||
let res = executable
|
||||
.cpu_mut()
|
||||
.core(1)
|
||||
.load::<f32>(0, 16 * size_of::<f32>());
|
||||
|
||||
assert_eq!(
|
||||
res.unwrap()[0],
|
||||
[
|
||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
|
||||
],
|
||||
"lmv failed to store"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_send_recv_test() {
|
||||
let mut cpu = CPU::new(2);
|
||||
let mut core_instruction_builder = CoreInstructionsBuilder::new(2);
|
||||
let buff: [f32; _] = [
|
||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
||||
];
|
||||
cpu.core(1).execute_store(0, &buff).unwrap();
|
||||
let mut inst_builder = InstructionsBuilder::new();
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(1).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(1, 3*size_of::<f32>() as i32).build());
|
||||
inst_builder.make_inst(
|
||||
send,
|
||||
idata_build
|
||||
.set_r1(1)
|
||||
.set_imm_core(2)
|
||||
.set_imm_len(8 * size_of::<f32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
core_instruction_builder.set_core(1, inst_builder.build());
|
||||
idata_build.set_core_indx(2).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(1, 0).build());
|
||||
inst_builder.make_inst(
|
||||
recv,
|
||||
idata_build
|
||||
.set_rd(1)
|
||||
.set_imm_core(1)
|
||||
.set_imm_len(8 * size_of::<f32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
core_instruction_builder.set_core(2, inst_builder.build());
|
||||
let mut executable = Executable::new(cpu, core_instruction_builder.build());
|
||||
executable.execute();
|
||||
let res = executable
|
||||
.cpu_mut()
|
||||
.core(2)
|
||||
.load::<f32>(0, 8 * size_of::<f32>());
|
||||
|
||||
assert_eq!(
|
||||
res.unwrap()[0],
|
||||
[
|
||||
4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0
|
||||
],
|
||||
"send_recv failed to store"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 1 -> 3
|
||||
// 2 -> 3
|
||||
// 3 <- 2
|
||||
// 4 -> 2
|
||||
// 3 <- 4
|
||||
// 2 <- 4
|
||||
// 4 -> 3
|
||||
// 3 <- 1
|
||||
|
||||
#[test]
|
||||
fn multiple_send_recv_test() {
|
||||
let mut cpu = CPU::new(4);
|
||||
let mut core_instruction_builder = CoreInstructionsBuilder::new(4);
|
||||
let buff: [f32; _] = [
|
||||
1.0, 1.0, 1.0, 1.0, 1.0
|
||||
];
|
||||
cpu.core(1).execute_store(0, &buff).unwrap();
|
||||
let buff: [f32; _] = [
|
||||
2.0, 2.0, 2.0, 2.0, 2.0
|
||||
];
|
||||
cpu.core(2).execute_store(0, &buff).unwrap();
|
||||
let buff: [f32; _] = [
|
||||
3.0, 3.0, 3.0, 3.0, 3.0
|
||||
];
|
||||
cpu.core(3).execute_store(0, &buff).unwrap();
|
||||
let buff: [f32; _] = [
|
||||
4.0, 4.0, 4.0, 4.0, 4.0
|
||||
];
|
||||
cpu.core(4).execute_store(0, &buff).unwrap();
|
||||
|
||||
let send_inst = |cpu :&mut CPU, core_instruction_builder: &mut CoreInstructionsBuilder, inst_builder: &mut InstructionsBuilder, from : i32, to : i32| {
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(from).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(1, from*size_of::<f32>() as i32).build());
|
||||
inst_builder.make_inst(
|
||||
send,
|
||||
idata_build
|
||||
.set_r1(1)
|
||||
.set_imm_core(to)
|
||||
.set_imm_len(size_of::<f32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
};
|
||||
|
||||
let recv_inst = |cpu :&mut CPU, core_instruction_builder: &mut CoreInstructionsBuilder, mut inst_builder: &mut InstructionsBuilder, to : i32, from : i32| {
|
||||
let mut idata_build = InstructionDataBuilder::new();
|
||||
idata_build.set_core_indx(to).fix_core_indx();
|
||||
inst_builder.make_inst(sldi, idata_build.set_rdimm(1, from*size_of::<f32>() as i32).build());
|
||||
inst_builder.make_inst(
|
||||
recv,
|
||||
idata_build
|
||||
.set_rd(1)
|
||||
.set_imm_core(from)
|
||||
.set_imm_len(size_of::<f32>() as i32)
|
||||
.build(),
|
||||
);
|
||||
};
|
||||
let mut inst_builder = InstructionsBuilder::new();
|
||||
|
||||
|
||||
// 1 -> 3
|
||||
send_inst(&mut cpu,&mut core_instruction_builder,&mut inst_builder,1, 3);
|
||||
core_instruction_builder.set_core(1, inst_builder.build());
|
||||
|
||||
// 2 -> 3
|
||||
// 2 <- 4
|
||||
send_inst(&mut cpu,&mut core_instruction_builder,&mut inst_builder,2, 3);
|
||||
recv_inst(&mut cpu,&mut core_instruction_builder,&mut inst_builder,2, 4);
|
||||
core_instruction_builder.set_core(2, inst_builder.build());
|
||||
|
||||
// 3 <- 2
|
||||
// 3 <- 4
|
||||
// 3 <- 1
|
||||
recv_inst(&mut cpu,&mut core_instruction_builder,&mut inst_builder,3, 2);
|
||||
recv_inst(&mut cpu,&mut core_instruction_builder,&mut inst_builder,3, 4);
|
||||
recv_inst(&mut cpu,&mut core_instruction_builder,&mut inst_builder,3, 1);
|
||||
core_instruction_builder.set_core(3, inst_builder.build());
|
||||
// 4 -> 2
|
||||
// 4 -> 3
|
||||
send_inst(&mut cpu,&mut core_instruction_builder,&mut inst_builder,4, 2);
|
||||
send_inst(&mut cpu,&mut core_instruction_builder,&mut inst_builder,4, 3);
|
||||
core_instruction_builder.set_core(4, inst_builder.build());
|
||||
|
||||
let mut executable = Executable::new(cpu, core_instruction_builder.build());
|
||||
executable.execute();
|
||||
let res = executable
|
||||
.cpu_mut()
|
||||
.core(3)
|
||||
.load::<f32>(4, 4 * size_of::<f32>());
|
||||
|
||||
assert_eq!(
|
||||
res.unwrap()[0],
|
||||
[ 1.0, 2.0, 3.0, 4.0 ],
|
||||
"send_recv failed to store"
|
||||
);
|
||||
}
|
||||
1
onnx-mlir
Submodule
1
onnx-mlir
Submodule
Submodule onnx-mlir added at f7897a0cab
Reference in New Issue
Block a user