diff --git a/.vscode/settings.json b/.vscode/settings.json index 552ed7e1..933a85b1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,8 @@ { "json.schemaDownload.enable": true, "rust-analyzer.linkedProjects": [ - "./mips/Cargo.toml", - "./Cargo.toml", - ".\\mips\\Cargo.toml" + "./mips-lib/Cargo.toml", + // "./Cargo.toml", ], "cSpell.ignoreWords": [ "Clippy", @@ -63,12 +62,47 @@ "usize", "vizia", "winit", - "xffff" + "xffff", + "ADDU", + "autowire", + "BGEZ", + "BGEZAL", + "BGTZ", + "BLEZ", + "BLTZAL", + "cntr", + "funct", + "hline", + "instrs", + "memref", + "MIPSCLK", + "muxes", + "NOTYPE", + "shamt", + "signzero", + "SIGNZEROEXTEND", + "SLLV", + "SLTI", + "SLTIU", + "SLTU", + "SRAV", + "SRLV", + "strtab", + "SUBU", + "symtype", + "syncsim", + "SYSCALL", + "vline", + "winapi", + "winuser", + "XORI", + "ZEROEXTEND" ], "[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer", "editor.formatOnSave": true - } + }, + //, // "rust-analyzer.cargo.unsetTest": [ // "syncrim" diff --git a/Cargo.toml b/Cargo.toml index 57fedf50..161828c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,11 +6,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] -members = ["mips", "riscv"] +members = [ "mips-lib", "riscv"] [dependencies] +MIPS_disassembly = "0.1.2" anyhow = "1.0.72" clap = { version = "4.3.15", features = ["derive"] } +elf = "0.7.4" fern = "0.6.2" log = "0.4.19" num_enum = "0.6.1" diff --git a/examples/and.rs b/examples/and.rs new file mode 100644 index 00000000..8877fe4c --- /dev/null +++ b/examples/and.rs @@ -0,0 +1,33 @@ +use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; +fn main() { + fern_setup(); + let cs = ComponentStore { + store: vec![ + ProbeEdit::rc_new("A", (100.0, 100.0)), + ProbeEdit::rc_new("B", (100.0, 140.0)), + And::rc_new( + "and", + (150.0, 120.0), + Input::new("A", PROBE_EDIT_OUT_ID), + Input::new("B", PROBE_EDIT_OUT_ID), + ), + Probe::rc_new("probe", (250.0, 100.0), Input::new("and", AND_OUT_ID)), + ], + }; + + let path = PathBuf::from("and.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/examples/mips_mux.rs b/examples/mips_mux.rs new file mode 100644 index 00000000..3d7c0e47 --- /dev/null +++ b/examples/mips_mux.rs @@ -0,0 +1,39 @@ +use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; +fn main() { + fern_setup(); + let cs = ComponentStore { + store: vec![ + Mux::rc_new( + "mux", + (200.0, 200.0), + Input::new("ctrl", "out"), + vec![Input::new("c0", "out"), Input::new("c1", "out")], + ), + ProbeEdit::rc_new("ctrl", (190.0, 100.0)), + Wire::rc_new( + "w0", + vec![(190.0, 110.0), (190.0, 150.0)], + Input::new("ctrl", "out"), + ), + Constant::rc_new("c0", (140.0, 170.0), 0), + Constant::rc_new("c1", (140.0, 190.0), 1), + Probe::rc_new("p_mux", (260.0, 200.0), Input::new("mux", "out")), + ], + }; + + let path = PathBuf::from("mux_edit.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/mips-lib/Cargo.toml b/mips-lib/Cargo.toml new file mode 100644 index 00000000..cef76a78 --- /dev/null +++ b/mips-lib/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "mips-lib" +version = "0.1.0" +edition = "2021" + +[dependencies] +MIPS_disassembly = "0.1.2" +anyhow = "1.0.72" +clap = { version = "4.3.15", features = ["derive"] } +elf = "0.7.4" +fern = "0.6.2" +log = "0.4.19" +num_enum = "0.6.1" +petgraph = "0.6.3" +rfd = "0.11.4" +serde = { version = "1.0.171", features = ["rc"] } +serde_derive = "1.0.171" +serde_json = "1.0.103" +typetag = "0.2.10" +egui = {version = "0.28.0"} +[dependencies.syncrim] +path = "../" +default-features = false + +[features] +default = ["gui-egui"] + +components = ["syncrim/components"] +gui-vizia = ["syncrim/gui-vizia"] +gui-egui = ["syncrim/gui-egui"] diff --git a/mips-lib/examples/mips_alu.rs b/mips-lib/examples/mips_alu.rs new file mode 100644 index 00000000..f1c7a330 --- /dev/null +++ b/mips-lib/examples/mips_alu.rs @@ -0,0 +1,46 @@ +use mips_lib::components::*; +use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; + +fn main() { + fern_setup(); + let cs = ComponentStore { + store: vec![ + ALU::rc_new( + "full_adder", + (200.0, 120.0), + Input::new("c0", "out"), + Input::new("c1", "out"), + Input::new("c2", "out"), + ), + ProbeEdit::rc_new("c0", (60.0, 100.0)), + ProbeEdit::rc_new("c1", (60.0, 140.0)), + ProbeEdit::rc_new("c2", (60.0, 180.0)), + Probe::rc_new( + "p1", + (270.0, 120.0), + Input::new("full_adder", FULL_ADD_OUT_ID), + ), + ], + }; + + let path = PathBuf::from("alu.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + { + use syncrim::autowire::autowire; + let cs = autowire(cs); + cs.save_file(&path); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + } + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/mips-lib/examples/mips_controll_unit.rs b/mips-lib/examples/mips_controll_unit.rs new file mode 100644 index 00000000..fd7cb951 --- /dev/null +++ b/mips-lib/examples/mips_controll_unit.rs @@ -0,0 +1,78 @@ +use mips_lib::components::*; +use std::path::PathBuf; +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; + +fn main() { + fern_setup(); + let cs = ComponentStore { + store: vec![ + Rc::new(ProbeEdit::new("instr", (100.0, 10.0))), + ControlUnit::rc_new("cntr", (200.0, 10.0), Input::new("instr", "out")), + Probe::rc_new( + "reg_dest", + (300.0, 10.0), + Input::new("cntr", cntr_field::REG_DEST_OUT), + ), + Probe::rc_new( + "reg_write", + (300.0, 30.0), + Input::new("cntr", cntr_field::REG_WRITE_ENABLE_OUT), + ), + Probe::rc_new( + "reg_write_src", + (300.0, 50.0), + Input::new("cntr", cntr_field::REG_WRITE_SRC_OUT), + ), + Probe::rc_new( + "alu_op", + (300.0, 70.0), + Input::new("cntr", cntr_field::ALU_OP_OUT), + ), + Probe::rc_new( + "alu_src_a", + (300.0, 90.0), + Input::new("cntr", cntr_field::ALU_SRC_A_OUT), + ), + Probe::rc_new( + "alu_src_b", + (300.0, 110.0), + Input::new("cntr", cntr_field::ALU_SRC_B_OUT), + ), + Probe::rc_new( + "extend_select", + (300.0, 130.0), + Input::new("cntr", cntr_field::EXTEND_SELECT_OUT), + ), + Probe::rc_new( + "mem_write", + (300.0, 150.0), + Input::new("cntr", cntr_field::MEM_WRITE_ENABLE_OUT), + ), + Probe::rc_new( + "branch_interrupt", + (300.0, 170.0), + Input::new("cntr", cntr_field::BRANCH_INTERRUPT_OUT), + ), + ], + }; + let path = PathBuf::from("cntr_unit.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + { + use syncrim::autowire::autowire; + let cs = autowire(cs); + cs.save_file(&path); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + } + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/mips-lib/examples/mips_im_dm_regfile.rs b/mips-lib/examples/mips_im_dm_regfile.rs new file mode 100644 index 00000000..c02edd52 --- /dev/null +++ b/mips-lib/examples/mips_im_dm_regfile.rs @@ -0,0 +1,75 @@ +use mips_lib::components::*; +use std::path::PathBuf; +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; + +fn main() { + fern_setup(); + + let cs = ComponentStore { + store: vec![ + Rc::new(PhysicalMem::new("phys_mem", (0.0, 0.0))), + ProbeEdit::rc_new("rs_addr", (60.0, 100.0)), + ProbeEdit::rc_new("rt_addr", (60.0, 140.0)), + ProbeEdit::rc_new("write_addr", (60.0, 180.0)), + ProbeEdit::rc_new("write_data", (60.0, 220.0)), + ProbeEdit::rc_new("write_enable", (60.0, 260.0)), + Probe::rc_new( + "rs", + (300.0, 120.0), + Input::new("reg_file", reg_file_fields::RS_VALUE_OUT_ID), + ), + Probe::rc_new( + "rt", + (60.0, 400.0), + Input::new("reg_file", reg_file_fields::RT_VALUE_OUT_ID), + ), + ProbeEdit::rc_new("pc", (60.0, 500.0)), + Rc::new(InstrMem::new( + "instr_mem".into(), + (200.0, 500.0), + Input::new("pc", "out"), + "phys_mem".into(), + "reg_file".into(), + )), + ProbeEdit::rc_new("data_adrs", (60.0, 660.0)), + ProbeEdit::rc_new("data_write_enable", (60.0, 700.0)), + ProbeEdit::rc_new("data_mem_op", (60.0, 740.0)), + ProbeEdit::rc_new("data_write_data", (60.0, 780.0)), + Rc::new(DataMem::new( + "data_mem".into(), + (200.0, 700.0), + Input::new("data_adrs", "out"), + Input::new("data_write_data", "out"), + Input::new("data_mem_op", "out"), + Input::new("data_write_enable", "out"), + "phys_mem".into(), + "reg_file".into(), + )), + RegFile::rc_new( + "reg_file", + (200.0, 200.0), + Input::new("rs_addr", "out"), + Input::new("rt_addr", "out"), + Input::new("write_addr", "out"), + Input::new("write_data", "out"), + Input::new("write_enable", "out"), + ), + ], + }; + + let path = PathBuf::from("add.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/mips-lib/examples/mips_instruction_splitter.rs b/mips-lib/examples/mips_instruction_splitter.rs new file mode 100644 index 00000000..04086b19 --- /dev/null +++ b/mips-lib/examples/mips_instruction_splitter.rs @@ -0,0 +1,76 @@ +use mips_lib::components::*; +use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; +fn main() { + fern_setup(); + let cs = ComponentStore { + store: vec![ + InstrSplit::rc_new( + "instruction_splitter", + (200.0, 120.0), + Input::new("c0", "out"), + ), + ProbeEdit::rc_new("c0", (60.0, 100.0)), + Probe::rc_new( + "op", + (270.0, 120.0), + Input::new("instruction_splitter", INSTRUCTION_SPLITTER_OP_ID), + ), + Probe::rc_new( + "rs", + (270.0, 140.0), + Input::new("instruction_splitter", INSTRUCTION_SPLITTER_RS_ID), + ), + Probe::rc_new( + "rt", + (270.0, 160.0), + Input::new("instruction_splitter", INSTRUCTION_SPLITTER_RT_ID), + ), + Probe::rc_new( + "rd", + (270.0, 180.0), + Input::new("instruction_splitter", INSTRUCTION_SPLITTER_RD_ID), + ), + Probe::rc_new( + "shamt", + (270.0, 200.0), + Input::new("instruction_splitter", INSTRUCTION_SPLITTER_SHAMT_ID), + ), + Probe::rc_new( + "funct", + (270.0, 220.0), + Input::new("instruction_splitter", INSTRUCTION_SPLITTER_FUNCT_ID), + ), + Probe::rc_new( + "immediate", + (270.0, 240.0), + Input::new("instruction_splitter", INSTRUCTION_SPLITTER_IMMEDIATE_ID), + ), + Probe::rc_new( + "target", + (270.0, 260.0), + Input::new("instruction_splitter", INSTRUCTION_SPLITTER_TARGET_ID), + ), + ], + }; + + let path = PathBuf::from("add.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + { + use syncrim::autowire::autowire; + let cs = autowire(cs); + cs.save_file(&path); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + } + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/mips-lib/examples/mips_jump_merge.rs b/mips-lib/examples/mips_jump_merge.rs new file mode 100644 index 00000000..bb1ac068 --- /dev/null +++ b/mips-lib/examples/mips_jump_merge.rs @@ -0,0 +1,40 @@ +use mips_lib::components::*; +use std::path::PathBuf; + +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; +fn main() { + fern_setup(); + let cs = ComponentStore { + store: vec![ + JumpMerge::rc_new( + "jump_merge", + (160.0, 120.0), + Input::new("c0", "out"), + Input::new("c1", "out"), + ), + ProbeEdit::rc_new("c0", (60.0, 100.0)), + ProbeEdit::rc_new("c1", (60.0, 140.0)), + Probe::rc_new("p1", (270.0, 120.0), Input::new("jump_merge", MERGE_OUT_ID)), + ], + }; + + let path = PathBuf::from("add.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + { + use syncrim::autowire::autowire; + let cs = autowire(cs); + cs.save_file(&path); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + } + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/mips-lib/examples/mips_pipe_example_1.rs b/mips-lib/examples/mips_pipe_example_1.rs new file mode 100644 index 00000000..441249bc --- /dev/null +++ b/mips-lib/examples/mips_pipe_example_1.rs @@ -0,0 +1,414 @@ +use mips_lib::components::*; +use std::path::PathBuf; +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; + +fn main() { + fern_setup(); + + let rc_reg_file = RegFile::rc_new( + "reg_file", + (360.0, 170.0), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RS_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RT_ID), + Input::new("reg_addr_reg", REGISTER_OUT_ID), //write address + Input::new("result_reg_EX", REGISTER_OUT_ID), //write data + Input::new("control_unit_4", cntr_field::REG_WRITE_ENABLE_OUT), + ); + + let cs = ComponentStore { + store: vec![ + Rc::new(PhysicalMem::new("phys_mem", (0.0, 0.0))), + // register that holds instr addr + Register::rc_new("pc", (170.0, 410.0), Input::new("mux_jump_merge", "out")), + // step addr from reg by 4 + Constant::rc_new("+4", (170.0, 380.0), 4), + Add::rc_new( + "pc+4", + (220.0, 380.0), + Input::new("pc", "out"), + Input::new("+4", "out"), + ), + // + // + Rc::new(InstrMem::new( + "instr_mem".into(), + (280.0, 600.0), + Input::new("pc", "out"), + "phys_mem".into(), + "reg_file".into(), + )), + // + // + // MUX to choose what instruction addr to choose from, branch jump, reg, pc+4 + Mux::rc_new( + "mux_jump_merge", + (140.0, 390.0), + Input::new("branch", BRANCH_OUT_ID), + vec![ + Input::new("pc_add_branch", ADD_OUT_ID), // describe origin + Input::new("reg_file", reg_file_fields::RS_VALUE_OUT_ID), // goes to addr, RD2 + Input::new("merge_reg", REGISTER_OUT_ID), // + Input::new("pc+4", ADD_OUT_ID), + ], + ), + // + // merges to find out jump location + JumpMerge::rc_new( + "jump_merge", + (125.0, 500.0), + Input::new("pc", REGISTER_OUT_ID), //input from reg before pc+4 + Input::new("instr_mem", INSTR_MEM_INSTRUCTION_ID), //input from instruction mem + ), + // + // + Register::rc_new("pc+4_reg", (2300.0, 5000.0), Input::new("pc+4", ADD_OUT_ID)), + // + Register::rc_new( + "InMem_reg", + (2300.0, 5200.0), + Input::new("instr_mem", INSTR_MEM_INSTRUCTION_ID), + ), + // + Register::rc_new( + "merge_reg", + (2300.0, 5300.0), + Input::new("jump_merge", MERGE_OUT_ID), + ), + // + // splits instructions from ir to fields + InstrSplit::rc_new( + "instruction_split", + (280.0, 140.0), + Input::new("InMem_reg", REGISTER_OUT_ID), + ), + // + // First CU, handles, select for sign/zero_extend and mux_write_addr + ControlUnit::rc_new( + "control_unit_1", + (280.0, 100.0), + Input::new("InMem_reg", REGISTER_OUT_ID), + ), + // + // Second CU, handles, mux_source_a, mux_source_b and the alu + ControlUnit::rc_new( + "control_unit_2", + (380.0, 100.0), + Input::new("control_EX_reg", REGISTER_OUT_ID), + ), + // + // Third CU, handles, write_back_mux, and DMs mem-read and mem-write + ControlUnit::rc_new( + "control_unit_3", + (480.0, 100.0), + Input::new("control_MEM_reg", REGISTER_OUT_ID), + ), + // + // Fourth CU, handles, WE for reg_file in the WB stage + ControlUnit::rc_new( + "control_unit_4", + (580.0, 100.0), + Input::new("control_WB_reg", REGISTER_OUT_ID), + ), + // + // + // extends immediate field + SignZeroExtend::rc_new( + "signzero_extend", + (310.0, 410.0), + Input::new("instruction_split", INSTRUCTION_SPLITTER_IMMEDIATE_ID), + Input::new("control_unit_1", cntr_field::EXTEND_SELECT_OUT), // cu tells it to either sing- or zero- extend + ), + // + // + // + // + Equal::rc_new( + "equals_operand_A", + (3200.0, 1700.0), + Input::new("reg_addr_MEM_reg", REGISTER_OUT_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RS_ID), + ), + // + Mux::rc_new( + "operand_A_mux_1", + (3200.0, 1800.0), + Input::new("equals_operand_A", EQUAL_OUT_ID), + vec![ + Input::new("reg_file", reg_file_fields::RS_VALUE_OUT_ID), + Input::new("write_back_mux", MUX_OUT_ID), + ], + ), + // + // + Equal::rc_new( + "equals_operand_B", + (3200.0, 2300.0), + Input::new("reg_addr_MEM_reg", REGISTER_OUT_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RT_ID), + ), + // + Mux::rc_new( + "operand_B_mux_1", + (3200.0, 2200.0), + Input::new("equals_operand_B", EQUAL_OUT_ID), + vec![ + Input::new("reg_file", reg_file_fields::RT_VALUE_OUT_ID), + Input::new("write_back_mux", MUX_OUT_ID), + ], + ), + // + // + Equal::rc_new( + "equals_operand_A_2", + (3300.0, 1700.0), + Input::new("reg_addr_EX_reg", REGISTER_OUT_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RS_ID), + ), + // + Mux::rc_new( + "operand_A_mux_2", + (3300.0, 1800.0), + Input::new("equals_operand_A_2", EQUAL_OUT_ID), + vec![ + Input::new("operand_A_mux_1", MUX_OUT_ID), + Input::new("alu", FULL_ADD_OUT_ID), + ], + ), + // + // + Equal::rc_new( + "equals_operand_B_2", + (3300.0, 2300.0), + Input::new("reg_addr_EX_reg", REGISTER_OUT_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RT_ID), + ), + // + Mux::rc_new( + "operand_B_mux_2", + (3300.0, 2200.0), + Input::new("equals_operand_B_2", EQUAL_OUT_ID), + vec![ + Input::new("operand_B_mux_1", MUX_OUT_ID), + Input::new("alu", FULL_ADD_OUT_ID), + ], + ), + // + // + BranchLogic::rc_new( + "branch", + (525.0, 300.0), + Input::new("instruction_split", INSTRUCTION_SPLITTER_OP_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RT_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_FUNCT_ID), + Input::new("reg_file", reg_file_fields::RS_VALUE_OUT_ID), + Input::new("reg_file", reg_file_fields::RT_VALUE_OUT_ID), + ), + // + // + Register::rc_new( + //TODO: make 2 more control units + "control_EX_reg", + (3450.0, 1400.0), + Input::new("InMem_reg", REGISTER_OUT_ID), + ), + // + Register::rc_new( + "zero_extend_reg", + (3450.0, 1600.0), + Input::new("instruction_split", INSTRUCTION_SPLITTER_SHAMT_ID), + ), + // + Register::rc_new( + //TODO: fix after adding 4 muxes + "operand_a_reg", + (3450.0, 1800.0), + Input::new("equals_operand_A_2", MUX_OUT_ID), + ), + // + Register::rc_new( + //TODO: fix after muxes + "operand_b_reg", + (3450.0, 2200.0), + Input::new("equals_operand_B_2", MERGE_OUT_ID), + ), + // + Register::rc_new( + "mux_b2_reg", + (3450.0, 5000.0), + Input::new("pc+4_reg", REGISTER_OUT_ID), + ), + Register::rc_new( + "mux_b3_reg", + (3450.0, 5100.0), + Input::new("signzero_extend", SIGNZEROEXTEND_OUT_ID), + ), + // + Register::rc_new( + "reg_addr_EX_reg", + (3450.0, 5500.0), + Input::new("mux_write_addr", MUX_OUT_ID), + ), + // + // + ZeroExtend::rc_new( + "zero_extend_for_shamt", + (550.0, 170.0), + Input::new("zero_extend_reg", REGISTER_OUT_ID), + ), + // + // + Constant::rc_new("0_a_inp", (600.0, 230.0), 0), + Mux::rc_new( + "mux_source_a", + (650.0, 220.0), + Input::new("control_unit_2", cntr_field::ALU_SRC_A_OUT), + vec![ + Input::new("zero_extend_for_shamt", SIGNZEROEXTEND_OUT_ID), + Input::new("operand_a_reg", REGISTER_OUT_ID), + Input::new("0_a_inp", "out"), + ], + ), + // + // + Mux::rc_new( + "mux_source_b", + (650.0, 300.0), + Input::new("control_unit_2", cntr_field::ALU_SRC_B_OUT), + vec![ + Input::new("operand_b_reg", REGISTER_OUT_ID), + Input::new("mux_b2_reg", REGISTER_OUT_ID), + Input::new("mux_b3_reg", REGISTER_OUT_ID), + ], + ), + // + // + // + // + ALU::rc_new( + "alu", + (720.0, 220.0), + Input::new("mux_source_a", MUX_OUT_ID), + Input::new("mux_source_b", MUX_OUT_ID), + Input::new("control_unit_2", cntr_field::ALU_OP_OUT), + ), + // + // + Rc::new(DataMem::new( + "data_mem".into(), + (600.0, 580.0), + Input::new("alu_reg", REGISTER_OUT_ID), // calculated from rs and imm + Input::new("data_MEM_reg", REGISTER_OUT_ID), + Input::new("control_unit_3", cntr_field::MEM_MODE_OUT), + Input::new("control_unit_3", cntr_field::MEM_WRITE_ENABLE_OUT), + "phys_mem".into(), + "reg_file".into(), + )), + // + // + Register::rc_new( + "control_MEM_reg", + (4200.0, 1400.0), + Input::new("control_EX_reg", REGISTER_OUT_ID), + ), + // + Register::rc_new( + "alu_reg", + (4200.0, 2000.0), + Input::new("alu", FULL_ADD_OUT_ID), + ), + // + Register::rc_new( + "data_MEM_reg", + (4200.0, 2500.0), + Input::new("operand_b_reg", REGISTER_OUT_ID), + ), + // + Register::rc_new( + "reg_addr_MEM_reg", + (4200.0, 5500.0), + Input::new("reg_addr_EX_reg", REGISTER_OUT_ID), + ), + // + // + Mux::rc_new( + "write_back_mux", + (800.0, 270.0), + Input::new("control_unit_3", cntr_field::REG_WRITE_SRC_OUT), + vec![ + Input::new("alu", FULL_ADD_OUT_ID), + Input::new("data_mem", DATA_MEM_READ_DATA_OUT_ID), + ], + ), + // + // + Register::rc_new( + "control_WB_reg", + (4400.0, 1400.0), + Input::new("control_MEM_reg", REGISTER_OUT_ID), + ), + // + Register::rc_new( + "result_reg_EX", + (4400.0, 2200.0), + Input::new("write_back_mux", MUX_OUT_ID), + ), + // + Register::rc_new( + "reg_addr_reg", + (4400.0, 5500.0), + Input::new("reg_addr_MEM_reg", REGISTER_OUT_ID), + ), + // + // + ShiftConst::rc_new( + "branch_shift", + (380.0, 460.0), + Input::new("signzero_extend", SIGNZEROEXTEND_OUT_ID), + 2, + ), + // + // + Add::rc_new( + "pc_add_branch", + (420.0, 440.0), + Input::new("pc+4", ADD_OUT_ID), + Input::new("branch_shift", SHIFT_OUT_ID), + ), + // + // + Constant::rc_new("0x_1F", (500.0, 510.0), 0x_1F), + Mux::rc_new( + "mux_write_addr", + (560.0, 500.0), + Input::new("control_unit_1", cntr_field::REG_DEST_OUT), + vec![ + Input::new("instruction_split", INSTRUCTION_SPLITTER_RT_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RD_ID), + Input::new("0x_1F", "out"), + ], + ), + rc_reg_file, + ], + }; + + let path = PathBuf::from("mips_pipe_ex.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + { + use syncrim::autowire::autowire; + let cs = autowire(cs); + cs.save_file(&path); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + } + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/mips-lib/examples/mips_reg_file.rs b/mips-lib/examples/mips_reg_file.rs new file mode 100644 index 00000000..4b8c28f2 --- /dev/null +++ b/mips-lib/examples/mips_reg_file.rs @@ -0,0 +1,50 @@ +use mips_lib::components::*; +use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; + +fn main() { + fern_setup(); + let cs = ComponentStore { + store: vec![ + ProbeEdit::rc_new("rs_addr", (60.0, 100.0)), + ProbeEdit::rc_new("rt_addr", (60.0, 140.0)), + ProbeEdit::rc_new("write_addr", (60.0, 180.0)), + ProbeEdit::rc_new("write_data", (60.0, 220.0)), + ProbeEdit::rc_new("write_enable", (60.0, 260.0)), + RegFile::rc_new( + "reg_file", + (200.0, 200.0), + Input::new("rs_addr", "out"), + Input::new("rt_addr", "out"), + Input::new("write_addr", "out"), + Input::new("write_data", "out"), + Input::new("write_enable", "out"), + ), + Probe::rc_new( + "rs", + (300.0, 120.0), + Input::new("reg_file", reg_file_fields::RS_VALUE_OUT_ID), + ), + Probe::rc_new( + "rt", + (300.0, 160.0), + Input::new("reg_file", reg_file_fields::RT_VALUE_OUT_ID), + ), + ], + }; + + let path = PathBuf::from("add.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/mips-lib/examples/mips_singel_sycle.rs b/mips-lib/examples/mips_singel_sycle.rs new file mode 100644 index 00000000..69d67d4f --- /dev/null +++ b/mips-lib/examples/mips_singel_sycle.rs @@ -0,0 +1,244 @@ +use mips_lib::components::*; +use std::path::PathBuf; +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; + +fn main() { + fern_setup(); + + let cs = ComponentStore { + store: vec![ + Rc::new(PhysicalMem::new("phys_mem", (0.0, 0.0))), + // register that holds instr addr + Register::rc_new( + "pc", + (150.0, 390.0), + Input::new("mux_jump_merge", MUX_OUT_ID), + ), + // step addr from reg by 4 + Constant::rc_new("+4", (150.0, 440.0), 4), + Add::rc_new_with_scale( + "pc+4", + (200.0, 400.0), + Input::new("+4", CONSTANT_OUT_ID), + Input::new("pc", REGISTER_OUT_ID), + 0.6, + ), + // + // + Rc::new(InstrMem::new( + "instr_mem".into(), + (250.0, 575.0), + Input::new("pc", REGISTER_OUT_ID), + "phys_mem".into(), + "reg_file".into(), + )), + // + // + // MUX to choose what intruction addr to choose from, branch jump, reg, pc+4 + Mux::rc_new_with_scale( + "mux_jump_merge", + (100.0, 390.0), + Input::new("branch", BRANCH_OUT_ID), + vec![ + Input::new("pc_add_branch", ADD_OUT_ID), //TODO: describe origin + Input::new("reg_file", reg_file_fields::RS_VALUE_OUT_ID), // goes to addr, RD2 + Input::new("jump_merge", MERGE_OUT_ID), // + Input::new("pc+4", ADD_OUT_ID), + ], + 0.6, + ), + // + // merges to find out jump location + JumpMerge::rc_new( + "jump_merge", + (125.0, 475.0), + Input::new("pc", REGISTER_OUT_ID), //input from reg before pc+4 + Input::new("instr_mem", INSTR_MEM_INSTRUCTION_ID), //input from instruction mem + ), + // + // splits intructions from ir to fields + InstrSplit::rc_new( + "instruction_split", + (200.0, 225.0), + Input::new("instr_mem", INSTR_MEM_INSTRUCTION_ID), + ), + // + // + ControlUnit::rc_new( + "control_unit", + (500.0, 50.0), + Input::new("instr_mem", INSTR_MEM_INSTRUCTION_ID), + ), + // + // + Register::rc_new( + "reg_we", + (850.0, 50.0), + Input::new("control_unit", cntr_field::REG_WRITE_ENABLE_OUT), + ), + // + // extends immediate field + SignZeroExtend::rc_new( + "signzero_extend", + (325.0, 425.0), + Input::new("instruction_split", INSTRUCTION_SPLITTER_IMMEDIATE_ID), + Input::new("control_unit", cntr_field::EXTEND_SELECT_OUT), // cu tells it to either sing- or zero- extend + ), + // + // + BranchLogic::rc_new( + "branch", + (475.0, 225.0), + Input::new("instruction_split", INSTRUCTION_SPLITTER_OP_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RT_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_FUNCT_ID), + Input::new("reg_file", reg_file_fields::RS_VALUE_OUT_ID), + Input::new("reg_file", reg_file_fields::RT_VALUE_OUT_ID), + ), + // + // + ZeroExtend::rc_new( + "zero_extend_for_chamt", + (600.0, 150.0), + Input::new("instruction_split", INSTRUCTION_SPLITTER_SHAMT_ID), + ), + // + // + Constant::rc_new("0_a_inp", (610.0, 220.0), 0), + Mux::rc_new_with_scale( + "mux_source_a", + (650.0, 195.0), + Input::new("control_unit", cntr_field::ALU_SRC_A_OUT), + vec![ + Input::new("zero_extend_for_chamt", ZEROEXTEND_OUT_ID), + Input::new("reg_file", reg_file_fields::RS_VALUE_OUT_ID), + Input::new("0_a_inp", CONSTANT_OUT_ID), + ], + 0.6, + ), + // + // + Mux::rc_new_with_scale( + "mux_source_b", + (650.0, 255.0), + Input::new("control_unit", cntr_field::ALU_SRC_B_OUT), + vec![ + Input::new("reg_file", reg_file_fields::RT_VALUE_OUT_ID), + Input::new("pc+4", ADD_OUT_ID), + Input::new("signzero_extend", SIGNZEROEXTEND_OUT_ID), + ], + 0.6, + ), + // + // + ALU::rc_new( + "alu", + (720.0, 225.0), + Input::new("mux_source_a", MUX_OUT_ID), + Input::new("mux_source_b", MUX_OUT_ID), + Input::new("control_unit", cntr_field::ALU_OP_OUT), + ), + // + // + Rc::new(DataMem::new( + "data_mem".into(), + (600.0, 575.0), + Input::new("alu", FULL_ADD_OUT_ID), // calculated from rs and imm + Input::new("reg_file", reg_file_fields::RT_VALUE_OUT_ID), + Input::new("control_unit", cntr_field::MEM_MODE_OUT), + Input::new("control_unit", cntr_field::MEM_WRITE_ENABLE_OUT), + "phys_mem".into(), + "reg_file".into(), + )), + // + // + Mux::rc_new_with_scale( + "mux_write_back", + (800.0, 225.0), + Input::new("control_unit", cntr_field::REG_WRITE_SRC_OUT), + vec![ + Input::new("alu", FULL_ADD_OUT_ID), + Input::new("data_mem", DATA_MEM_READ_DATA_OUT_ID), //TODO: data meme output + ], + 0.6, + ), + // + // + Register::rc_new( + "result_reg", + (850.0, 225.0), + Input::new("mux_write_back", MUX_OUT_ID), + ), + // + // + ShiftConst::rc_new( + "branch_shift", + (325.0, 480.0), + Input::new("signzero_extend", SIGNZEROEXTEND_OUT_ID), + 2, + ), + // + // + Add::rc_new_with_scale( + "pc_add_branch", + (400.0, 475.0), + Input::new("pc+4", ADD_OUT_ID), + Input::new("branch_shift", SHIFT_OUT_ID), + 0.6, + ), + // + // + Constant::rc_new("0x_1F", (350.0, 550.0), 0x_1F), + Mux::rc_new_with_scale( + "mux_write_addr", + (400.0, 525.0), + Input::new("control_unit", cntr_field::REG_DEST_OUT), + vec![ + Input::new("instruction_split", INSTRUCTION_SPLITTER_RT_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RD_ID), + Input::new("0x_1F", CONSTANT_OUT_ID), + ], + 0.6, + ), + // + // + Register::rc_new( + "reg_write_addr", + (850.0, 525.0), + Input::new("mux_write_addr", MUX_OUT_ID), + ), + // + // + RegFile::rc_new( + "reg_file", + (350.0, 225.0), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RS_ID), + Input::new("instruction_split", INSTRUCTION_SPLITTER_RT_ID), + Input::new("reg_write_addr", REGISTER_OUT_ID), //write address + Input::new("result_reg", REGISTER_OUT_ID), //write data + Input::new("reg_we", REGISTER_OUT_ID), + ), + ], + }; + + let path = PathBuf::from("mips_single_ex.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + { + use syncrim::autowire::autowire; + let cs = autowire(cs); + cs.save_file(&path); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + } + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/mips-lib/mips_singel_cycle.json b/mips-lib/mips_singel_cycle.json new file mode 100644 index 00000000..859fa0a6 --- /dev/null +++ b/mips-lib/mips_singel_cycle.json @@ -0,0 +1,1955 @@ +{ + "store": [ + { + "type": "Register", + "id": "reg_we", + "pos": [ + 850.0, + 50.0 + ], + "r_in": { + "id": "control_unit", + "field": "reg_write_enable" + } + }, + { + "type": "Register", + "id": "result_reg", + "pos": [ + 850.0, + 225.0 + ], + "r_in": { + "id": "mux_write_back", + "field": "mux_out" + } + }, + { + "type": "Register", + "id": "pc", + "pos": [ + 150.0, + 390.0 + ], + "r_in": { + "id": "mux_jump_merge", + "field": "mux_out" + } + }, + { + "type": "Register", + "id": "reg_write_addr", + "pos": [ + 850.0, + 525.0 + ], + "r_in": { + "id": "mux_write_addr", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "from result_reg:register_out to reg_file:write_data_in", + "pos": [ + [ + 860.0, + 225.0 + ], + [ + 870.0, + 225.0 + ], + [ + 870.0, + 373.0 + ], + [ + 278.0, + 373.0 + ], + [ + 278.0, + 335.0 + ], + [ + 284.0, + 335.0 + ] + ], + "input": { + "id": "result_reg", + "field": "register_out" + } + }, + { + "type": "Constant", + "id": "0x_1F", + "pos": [ + 350.0, + 550.0 + ], + "value": { + "data": { + "Data": 31 + }, + "fmt": { + "Hex": [ + "_32", + false + ] + } + } + }, + { + "type": "Wire", + "id": "from 0x_1F:constant_out to mux_write_addr:in2", + "pos": [ + [ + 350.0, + 550.0 + ], + [ + 388.0, + 537.0 + ] + ], + "input": { + "id": "0x_1F", + "field": "constant_out" + } + }, + { + "type": "Constant", + "id": "0_a_inp", + "pos": [ + 610.0, + 220.0 + ], + "value": { + "data": { + "Data": 0 + }, + "fmt": { + "Hex": [ + "_32", + false + ] + } + } + }, + { + "type": "Wire", + "id": "from 0_a_inp:constant_out to mux_source_a:in2", + "pos": [ + [ + 610.0, + 220.0 + ], + [ + 638.0, + 207.0 + ] + ], + "input": { + "id": "0_a_inp", + "field": "constant_out" + } + }, + { + "type": "Wire", + "id": "from reg_we:register_out to reg_file:write_enable_in", + "pos": [ + [ + 860.0, + 50.0 + ], + [ + 870.0, + 50.0 + ], + [ + 870.0, + 85.0 + ], + [ + 370.0, + 85.0 + ], + [ + 370.0, + 94.0 + ] + ], + "input": { + "id": "reg_we", + "field": "register_out" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "PhysicalMem", + "id": "phys_mem", + "pos": [ + 0.0, + 0.0 + ] + }, + { + "type": "InstrMem", + "id": "instr_mem", + "pos": [ + 250.0, + 575.0 + ], + "pc": { + "id": "pc", + "field": "register_out" + }, + "phys_mem_id": "phys_mem", + "regfile_id": "reg_file", + "mem_view": { + "visible": false, + "title": "instruction memory view", + "id": "instr_mem", + "row_offset": 0, + "max_rows": 1024, + "big_endian": true, + "format": "HexAndMips", + "show_settings": { + "symbols": true, + "sections": false, + "program_counter": false, + "registers": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true + ] + }, + "register_values": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2147483648, + 0, + 0 + ], + "dynamic_symbols": { + "PC_IM": [ + 0, + true + ] + }, + "break_points": [] + } + }, + { + "type": "JumpMerge", + "id": "jump_merge", + "pos": [ + 125.0, + 475.0 + ], + "instr_addr_in": { + "id": "pc", + "field": "register_out" + }, + "jump_addr_in": { + "id": "instr_mem", + "field": "instruction" + } + }, + { + "type": "Wire", + "id": "from jump_merge:merge_out to mux_jump_merge:in2", + "pos": [ + [ + 85.0, + 475.0 + ], + [ + 67.0, + 475.0 + ], + [ + 67.0, + 396.0 + ], + [ + 88.0, + 396.0 + ] + ], + "input": { + "id": "jump_merge", + "field": "merge_out" + } + }, + { + "type": "Wire", + "id": "from instr_mem:instruction to control_unit:control_unit_instr_in", + "pos": [ + [ + 245.0, + 344.0 + ], + [ + 245.0, + 50.0 + ], + [ + 294.0, + 50.0 + ] + ], + "input": { + "id": "instr_mem", + "field": "instruction" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "InstrSplit", + "id": "instruction_split", + "pos": [ + 200.0, + 225.0 + ], + "instruction_in": { + "id": "instr_mem", + "field": "instruction" + } + }, + { + "type": "Wire", + "id": "from instruction_split:rt_out to mux_write_addr:in0", + "pos": [ + [ + 231.0, + 236.11111 + ], + [ + 255.0, + 236.11111 + ], + [ + 255.0, + 512.5 + ], + [ + 388.0, + 513.0 + ] + ], + "input": { + "id": "instruction_split", + "field": "rt_out" + } + }, + { + "type": "Wire", + "id": "from instruction_split:shamt_out to zero_extend_for_chamt:signal_in", + "pos": [ + [ + 231.0, + 147.0 + ], + [ + 250.0, + 147.0 + ], + [ + 250.0, + 77.0 + ], + [ + 483.0, + 77.0 + ], + [ + 483.0, + 150.0 + ], + [ + 509.0, + 150.0 + ] + ], + "input": { + "id": "instruction_split", + "field": "shamt_out" + } + }, + { + "type": "Wire", + "id": "from instruction_split:immediate_out to signzero_extend:signzero_signal_in", + "pos": [ + [ + 231.0, + 258.3333 + ], + [ + 236.0, + 258.3333 + ], + [ + 236.0, + 425.0 + ], + [ + 266.5, + 425.0 + ] + ], + "input": { + "id": "instruction_split", + "field": "immediate_out" + } + }, + { + "type": "Wire", + "id": "from instruction_split:rs_out to reg_file:rs_address_in", + "pos": [ + [ + 231.0, + 213.88889 + ], + [ + 261.0, + 213.88889 + ], + [ + 261.0, + 85.0 + ], + [ + 350.0, + 85.0 + ], + [ + 350.0, + 94.0 + ] + ], + "input": { + "id": "instruction_split", + "field": "rs_out" + } + }, + { + "type": "Wire", + "id": "from instruction_split:rd_out to mux_write_addr:in1", + "pos": [ + [ + 231.0, + 280.55554 + ], + [ + 251.0, + 280.55554 + ], + [ + 251.0, + 525.0 + ], + [ + 388.0, + 525.0 + ] + ], + "input": { + "id": "instruction_split", + "field": "rd_out" + } + }, + { + "type": "ZeroExtend", + "id": "zero_extend_for_chamt", + "pos": [ + 550.0, + 150.0 + ], + "signal_in": { + "id": "instruction_split", + "field": "shamt_out" + } + }, + { + "type": "Wire", + "id": "from zero_extend_for_chamt:zero_extend_out to mux_source_a:in0", + "pos": [ + [ + 591.0, + 150.0 + ], + [ + 607.0, + 150.0 + ], + [ + 607.0, + 183.0 + ], + [ + 638.0, + 183.0 + ] + ], + "input": { + "id": "zero_extend_for_chamt", + "field": "zero_extend_out" + } + }, + { + "type": "Wire", + "id": "from instruction_split:rt_out to reg_file:rt_address_in", + "pos": [ + [ + 255.0, + 261.0 + ], + [ + 284.0, + 261.0 + ] + ], + "input": { + "id": "instruction_split", + "field": "rt_out" + } + }, + { + "type": "ControlUnit", + "id": "control_unit", + "pos": [ + 500.0, + 50.0 + ], + "a_in": { + "id": "instr_mem", + "field": "instruction" + } + }, + { + "type": "Wire", + "id": "from control_unit:alu_src_a to mux_source_a:select", + "pos": [ + [ + 644.0, + 63.5 + ], + [ + 644.0, + 171.0 + ] + ], + "input": { + "id": "control_unit", + "field": "alu_src_a" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "Wire", + "id": "from control_unit:reg_write_src to mux_write_back:select", + "pos": [ + [ + 690.0, + 63.5 + ], + [ + 690.0, + 115.0 + ], + [ + 794.0, + 115.0 + ], + [ + 794.0, + 207.0 + ] + ], + "input": { + "id": "control_unit", + "field": "reg_write_src" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "Wire", + "id": "from control_unit:extend_select to signzero_extend:signzero_ctrl_in", + "pos": [ + [ + 333.3333, + 63.5 + ], + [ + 325.0, + 404.0 + ] + ], + "input": { + "id": "control_unit", + "field": "extend_select" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "SignZeroExtend", + "id": "signzero_extend", + "pos": [ + 325.0, + 425.0 + ], + "signzero_signal_in": { + "id": "instruction_split", + "field": "immediate_out" + }, + "signzero_ctrl_in": { + "id": "control_unit", + "field": "extend_select" + } + }, + { + "type": "Wire", + "id": "from signzero_extend:sz_out to branch_shift:shift_in", + "pos": [ + [ + 384.0, + 425.0 + ], + [ + 390.0, + 425.0 + ], + [ + 390.0, + 448.0 + ], + [ + 280.5, + 448.0 + ], + [ + 280.5, + 480.0 + ], + [ + 291.5, + 480.0 + ] + ], + "input": { + "id": "signzero_extend", + "field": "sz_out" + } + }, + { + "type": "ShiftConst", + "id": "branch_shift", + "pos": [ + 325.0, + 480.0 + ], + "signal_in": { + "id": "signzero_extend", + "field": "sz_out" + }, + "shift_by": 2 + }, + { + "type": "Mux", + "id": "mux_write_addr", + "pos": [ + 400.0, + 525.0 + ], + "select": { + "id": "control_unit", + "field": "reg_dest" + }, + "m_in": [ + { + "id": "instruction_split", + "field": "rt_out" + }, + { + "id": "instruction_split", + "field": "rd_out" + }, + { + "id": "0x_1F", + "field": "constant_out" + } + ], + "scale": 0.6 + }, + { + "type": "Wire", + "id": "from mux_write_addr:mux_out to reg_write_addr:r_in", + "pos": [ + [ + 406.0, + 525.0 + ], + [ + 840.0, + 525.0 + ] + ], + "input": { + "id": "mux_write_addr", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "from control_unit:reg_dest to mux_write_addr:select", + "pos": [ + [ + 420.0, + 63.5 + ], + [ + 420.0, + 501.0 + ], + [ + 394.5, + 501.0 + ] + ], + "input": { + "id": "control_unit", + "field": "reg_dest" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "Wire", + "id": "from control_unit:alu_op to alu:alu_op_in", + "pos": [ + [ + 660.0, + 63.5 + ], + [ + 660.0, + 123.0 + ], + [ + 710.0, + 123.0 + ], + [ + 710.0, + 185.0 + ] + ], + "input": { + "id": "control_unit", + "field": "alu_op" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "Wire", + "id": "from pc:register_out to pc+4:b_in", + "pos": [ + [ + 176.0, + 390.0 + ], + [ + 188.0, + 387.5 + ] + ], + "input": { + "id": "pc", + "field": "register_out" + } + }, + { + "type": "Wire", + "id": "from pc:register_out to instr_mem:pc", + "pos": [ + [ + 160.0, + 390.0 + ], + [ + 175.79999, + 390.0 + ], + [ + 175.79999, + 520.0 + ], + [ + 210.0, + 520.0 + ], + [ + 210.0, + 541.5 + ] + ], + "input": { + "id": "pc", + "field": "register_out" + } + }, + { + "type": "Wire", + "id": "from branch_shift:shift_left_const_out to pc_add_branch:b_in", + "pos": [ + [ + 358.5, + 480.0 + ], + [ + 388.0, + 487.0 + ] + ], + "input": { + "id": "branch_shift", + "field": "shift_left_const_out" + } + }, + { + "type": "Wire", + "id": "from pc:register_out to jump_merge:merge_instr_addr_in", + "pos": [ + [ + 175.5, + 471.0 + ], + [ + 165.0, + 471.0 + ] + ], + "input": { + "id": "pc", + "field": "register_out" + } + }, + { + "type": "Wire", + "id": "from instr_mem:instruction to jump_merge:merge_jump_addr_in", + "pos": [ + [ + 245.0, + 541.5 + ], + [ + 245.0, + 483.0 + ], + [ + 165.0, + 483.0 + ] + ], + "input": { + "id": "instr_mem", + "field": "instruction" + } + }, + { + "type": "Wire", + "id": "from control_unit:mem_mode to data_mem:data_mem_op_in", + "pos": [ + [ + 630.0, + 63.5 + ], + [ + 630.0, + 541.5 + ] + ], + "input": { + "id": "control_unit", + "field": "mem_mode" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "Wire", + "id": "from control_unit:reg_write_enable to reg_we:r_in", + "pos": [ + [ + 706.0, + 50.0 + ], + [ + 840.0, + 50.0 + ] + ], + "input": { + "id": "control_unit", + "field": "reg_write_enable" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "Wire", + "id": "from control_unit:alu_src_b to mux_source_b:select", + "pos": [ + [ + 678.0, + 63.5 + ], + [ + 678.0, + 212.0 + ], + [ + 644.0, + 231.0 + ] + ], + "input": { + "id": "control_unit", + "field": "alu_src_b" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "Wire", + "id": "from reg_write_addr:register_out to reg_file:write_address_in", + "pos": [ + [ + 860.0, + 525.0 + ], + [ + 870.0, + 525.0 + ], + [ + 870.0, + 390.0 + ], + [ + 273.0, + 390.0 + ], + [ + 273.0, + 315.0 + ], + [ + 285.0, + 315.0 + ] + ], + "input": { + "id": "reg_write_addr", + "field": "register_out" + } + }, + { + "type": "RegFile", + "id": "reg_file", + "pos": [ + 350.0, + 225.0 + ], + "rs_address_in": { + "id": "instruction_split", + "field": "rs_out" + }, + "rt_address_in": { + "id": "instruction_split", + "field": "rt_out" + }, + "write_address_in": { + "id": "reg_write_addr", + "field": "register_out" + }, + "write_data_in": { + "id": "result_reg", + "field": "register_out" + }, + "write_enable_in": { + "id": "reg_we", + "field": "register_out" + } + }, + { + "type": "Wire", + "id": "from reg_file:rs_value_out to mux_source_a:in1", + "pos": [ + [ + 436.0, + 195.0 + ], + [ + 638.0, + 195.0 + ] + ], + "input": { + "id": "reg_file", + "field": "rs_value_out" + } + }, + { + "type": "Wire", + "id": "from reg_file:rt_value_out to branch:branch_rt_value_id", + "pos": [ + [ + 448.5, + 195.0 + ], + [ + 448.5, + 211.0 + ] + ], + "input": { + "id": "reg_file", + "field": "rt_value_out" + } + }, + { + "type": "Wire", + "id": "from reg_file:rs_value_out to mux_jump_merge:in1", + "pos": [ + [ + 416.0, + 195.0 + ], + [ + 436.0, + 195.0 + ], + [ + 436.0, + 72.0 + ], + [ + 67.0, + 72.0 + ], + [ + 67.0, + 384.0 + ], + [ + 88.0, + 384.0 + ] + ], + "input": { + "id": "reg_file", + "field": "rs_value_out" + } + }, + { + "type": "BranchLogic", + "id": "branch", + "pos": [ + 475.0, + 225.0 + ], + "op_in": { + "id": "instruction_split", + "field": "op_out" + }, + "rt_in": { + "id": "instruction_split", + "field": "rt_out" + }, + "funct_in": { + "id": "instruction_split", + "field": "funct_out" + }, + "rs_value": { + "id": "reg_file", + "field": "rs_value_out" + }, + "rt_value": { + "id": "reg_file", + "field": "rt_value_out" + } + }, + { + "type": "Wire", + "id": "from branch:branch_out to mux_jump_merge:select", + "pos": [ + [ + 475.0, + 211.0 + ], + [ + 475.0, + 90.0 + ], + [ + 94.0, + 90.0 + ], + [ + 94.0, + 360.0 + ] + ], + "input": { + "id": "branch", + "field": "branch_out" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "Mux", + "id": "mux_source_a", + "pos": [ + 650.0, + 195.0 + ], + "select": { + "id": "control_unit", + "field": "alu_src_a" + }, + "m_in": [ + { + "id": "zero_extend_for_chamt", + "field": "zero_extend_out" + }, + { + "id": "reg_file", + "field": "rs_value_out" + }, + { + "id": "0_a_inp", + "field": "constant_out" + } + ], + "scale": 0.6 + }, + { + "type": "Wire", + "id": "from mux_source_a:mux_out to alu:alu_a_in", + "pos": [ + [ + 656.0, + 195.0 + ], + [ + 700.0, + 195.0 + ] + ], + "input": { + "id": "mux_source_a", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "from reg_file:rt_value_out to mux_source_b:in0", + "pos": [ + [ + 416.0, + 265.0 + ], + [ + 448.5, + 265.0 + ], + [ + 448.5, + 243.0 + ], + [ + 638.0, + 243.0 + ] + ], + "input": { + "id": "reg_file", + "field": "rt_value_out" + } + }, + { + "type": "Wire", + "id": "from reg_file:rt_value_out to data_mem:data_mem_write_data_in", + "pos": [ + [ + 520.0, + 244.0 + ], + [ + 520.0, + 575.0 + ], + [ + 534.0, + 575.0 + ] + ], + "input": { + "id": "reg_file", + "field": "rt_value_out" + } + }, + { + "type": "Wire", + "id": "from reg_file:rs_value_out to branch:branch_rs_value_id", + "pos": [ + [ + 448.5, + 245.0 + ], + [ + 448.5, + 238.0 + ] + ], + "input": { + "id": "reg_file", + "field": "rs_value_out" + } + }, + { + "type": "Constant", + "id": "+4", + "pos": [ + 150.0, + 440.0 + ], + "value": { + "data": { + "Data": 4 + }, + "fmt": { + "Hex": [ + "_32", + false + ] + } + } + }, + { + "type": "Wire", + "id": "from +4:constant_out to pc+4:a_in", + "pos": [ + [ + 150.0, + 440.0 + ], + [ + 188.0, + 412.0 + ] + ], + "input": { + "id": "+4", + "field": "constant_out" + } + }, + { + "type": "Add", + "id": "pc+4", + "pos": [ + 200.0, + 400.0 + ], + "a_in": { + "id": "+4", + "field": "constant_out" + }, + "b_in": { + "id": "pc", + "field": "register_out" + }, + "scale": 0.6 + }, + { + "type": "Add", + "id": "pc_add_branch", + "pos": [ + 400.0, + 475.0 + ], + "a_in": { + "id": "pc+4", + "field": "add_out" + }, + "b_in": { + "id": "branch_shift", + "field": "shift_left_const_out" + }, + "scale": 0.6 + }, + { + "type": "Wire", + "id": "from pc_add_branch:add_out to mux_jump_merge:in0", + "pos": [ + [ + 412.0, + 475.0 + ], + [ + 422.5, + 475.0 + ], + [ + 422.5, + 365.0 + ], + [ + 213.0, + 365.0 + ], + [ + 213.0, + 356.0 + ], + [ + 75.0, + 356.0 + ], + [ + 75.0, + 372.0 + ], + [ + 88.0, + 372.0 + ] + ], + "input": { + "id": "pc_add_branch", + "field": "add_out" + } + }, + { + "type": "Mux", + "id": "mux_jump_merge", + "pos": [ + 100.0, + 390.0 + ], + "select": { + "id": "branch", + "field": "branch_out" + }, + "m_in": [ + { + "id": "pc_add_branch", + "field": "add_out" + }, + { + "id": "reg_file", + "field": "rs_value_out" + }, + { + "id": "jump_merge", + "field": "merge_out" + }, + { + "id": "pc+4", + "field": "add_out" + } + ], + "scale": 0.6 + }, + { + "type": "Wire", + "id": "from mux_jump_merge:mux_out to pc:r_in", + "pos": [ + [ + 106.0, + 390.0 + ], + [ + 140.0, + 390.0 + ] + ], + "input": { + "id": "mux_jump_merge", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "from pc+4:add_out to mux_jump_merge:in3", + "pos": [ + [ + 212.0, + 400.0 + ], + [ + 225.0, + 400.0 + ], + [ + 225.0, + 437.0 + ], + [ + 77.0, + 437.0 + ], + [ + 77.0, + 408.0 + ], + [ + 88.0, + 408.0 + ] + ], + "input": { + "id": "pc+4", + "field": "add_out" + } + }, + { + "type": "Mux", + "id": "mux_source_b", + "pos": [ + 650.0, + 255.0 + ], + "select": { + "id": "control_unit", + "field": "alu_src_b" + }, + "m_in": [ + { + "id": "reg_file", + "field": "rt_value_out" + }, + { + "id": "pc+4", + "field": "add_out" + }, + { + "id": "signzero_extend", + "field": "sz_out" + } + ], + "scale": 0.6 + }, + { + "type": "Wire", + "id": "from mux_source_b:mux_out to alu:alu_b_in", + "pos": [ + [ + 656.0, + 255.0 + ], + [ + 700.0, + 255.0 + ] + ], + "input": { + "id": "mux_source_b", + "field": "mux_out" + } + }, + { + "type": "ALU", + "id": "alu", + "pos": [ + 720.0, + 225.0 + ], + "a_in": { + "id": "mux_source_a", + "field": "mux_out" + }, + "b_in": { + "id": "mux_source_b", + "field": "mux_out" + }, + "op_in": { + "id": "control_unit", + "field": "alu_op" + } + }, + { + "type": "Wire", + "id": "from alu:alu_out to mux_write_back:in0", + "pos": [ + [ + 750.0, + 225.0 + ], + [ + 764.0, + 225.0 + ], + [ + 764.0, + 219.0 + ], + [ + 788.0, + 219.0 + ] + ], + "input": { + "id": "alu", + "field": "alu_out" + } + }, + { + "type": "Wire", + "id": "from alu:alu_out to data_mem:data_mem_address_in", + "pos": [ + [ + 740.0, + 225.0 + ], + [ + 750.0, + 225.0 + ], + [ + 750.0, + 450.0 + ], + [ + 570.0, + 450.0 + ], + [ + 570.0, + 541.5 + ] + ], + "input": { + "id": "alu", + "field": "alu_out" + } + }, + { + "type": "DataMem", + "id": "data_mem", + "pos": [ + 600.0, + 575.0 + ], + "address_input": { + "id": "alu", + "field": "alu_out" + }, + "data_input": { + "id": "reg_file", + "field": "rt_value_out" + }, + "op_input": { + "id": "control_unit", + "field": "mem_mode" + }, + "write_enable_input": { + "id": "control_unit", + "field": "mem_write_enable" + }, + "phys_mem_id": "phys_mem", + "regfile_id": "reg_file", + "mem_view": { + "visible": false, + "title": "Data memory view", + "id": "data_mem", + "row_offset": 0, + "max_rows": 1024, + "big_endian": true, + "format": "ByteAndUtf8", + "show_settings": { + "symbols": true, + "sections": false, + "program_counter": false, + "registers": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "register_values": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2147483648, + 0, + 0 + ], + "dynamic_symbols": { + "DM_ADRS": [ + 0, + false + ] + }, + "break_points": [] + } + }, + { + "type": "Wire", + "id": "from data_mem:data_out to mux_write_back:in1", + "pos": [ + [ + 666.0, + 575.0 + ], + [ + 775.0, + 575.0 + ], + [ + 775.0, + 231.0 + ], + [ + 788.0, + 231.0 + ] + ], + "input": { + "id": "data_mem", + "field": "data_out" + } + }, + { + "type": "Mux", + "id": "mux_write_back", + "pos": [ + 800.0, + 225.0 + ], + "select": { + "id": "control_unit", + "field": "reg_write_src" + }, + "m_in": [ + { + "id": "alu", + "field": "alu_out" + }, + { + "id": "data_mem", + "field": "data_out" + } + ], + "scale": 0.6 + }, + { + "type": "Wire", + "id": "from mux_write_back:mux_out to result_reg:r_in", + "pos": [ + [ + 806.0, + 225.0 + ], + [ + 840.0, + 225.0 + ] + ], + "input": { + "id": "mux_write_back", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "from pc+4:add_out to pc_add_branch:a_in", + "pos": [ + [ + 225.0, + 436.5 + ], + [ + 225.0, + 463.0 + ], + [ + 388.0, + 463.0 + ] + ], + "input": { + "id": "pc+4", + "field": "add_out" + } + }, + { + "type": "Wire", + "id": "from pc+4:add_out to mux_source_b:in1", + "pos": [ + [ + 225.0, + 400.0 + ], + [ + 477.0, + 400.0 + ], + [ + 477.0, + 256.0 + ], + [ + 638.0, + 255.0 + ] + ], + "input": { + "id": "pc+4", + "field": "add_out" + } + }, + { + "type": "Wire", + "id": "from control_unit:mem_write_enable to data_mem:data_mem_write_enable", + "pos": [ + [ + 600.0, + 63.5 + ], + [ + 600.0, + 541.5 + ] + ], + "input": { + "id": "control_unit", + "field": "mem_write_enable" + }, + "color_rgba": [ + 0, + 0, + 0, + 20 + ] + }, + { + "type": "Wire", + "id": "from signzero_extend:sz_out to mux_source_b:in2", + "pos": [ + [ + 390.0, + 425.0 + ], + [ + 541.0, + 425.0 + ], + [ + 541.0, + 268.0 + ], + [ + 638.0, + 267.0 + ] + ], + "input": { + "id": "signzero_extend", + "field": "sz_out" + } + }, + { + "type": "Wire", + "id": "from instr_mem:instruction to instruction_split:instruction_in", + "pos": [ + [ + 245.0, + 483.0 + ], + [ + 245.0, + 343.5 + ], + [ + 200.0, + 343.5 + ], + [ + 200.0, + 330.5 + ] + ], + "input": { + "id": "instr_mem", + "field": "instruction" + } + } + ] +} diff --git a/mips-lib/src/components/mips_alu.rs b/mips-lib/src/components/mips_alu.rs new file mode 100644 index 00000000..469c418b --- /dev/null +++ b/mips-lib/src/components/mips_alu.rs @@ -0,0 +1,310 @@ +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; + +pub const FULL_ADD_A_IN_ID: &str = "full_add_a_in"; +pub const FULL_ADD_B_IN_ID: &str = "full_add_b_in"; +pub const FULL_ADD_OP_IN_ID: &str = "full_add_op_in"; +pub const FULL_ADD_OUT_ID: &str = "alu_out"; +pub const FULL_ADD_OVERFLOW_OUT_ID: &str = "alu_overflow_out"; + +pub mod alu_op { + pub const ADD: u32 = 0; + pub const ADDU: u32 = 1; + pub const SUB: u32 = 2; + pub const SUBU: u32 = 3; + pub const AND: u32 = 4; + pub const OR: u32 = 5; + pub const XOR: u32 = 6; + pub const NOR: u32 = 7; + pub const SLT: u32 = 8; + pub const SLTU: u32 = 9; + pub const SLL: u32 = 10; + pub const SRL: u32 = 11; + pub const SRA: u32 = 12; + pub const LUI: u32 = 13; +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct ALU { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) a_in: Input, + pub(crate) b_in: Input, + pub(crate) op_in: Input, +} + +#[typetag::serde] +impl Component for ALU { + fn to_(&self) { + trace!("full_adder"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, _id: &str, _pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(ALU { + id: "dummy".to_string(), + pos: (0.0, 0.0), + a_in: dummy_input.clone(), + b_in: dummy_input.clone(), + op_in: dummy_input.clone(), + })) + } + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: FULL_ADD_A_IN_ID.to_string(), + input: self.a_in.clone(), + }, + &InputPort { + port_id: FULL_ADD_B_IN_ID.to_string(), + input: self.b_in.clone(), + }, + &InputPort { + port_id: FULL_ADD_OP_IN_ID.to_string(), + input: self.op_in.clone(), + }, + ], + OutputType::Combinatorial, + vec![FULL_ADD_OUT_ID, FULL_ADD_OVERFLOW_OUT_ID], + ), + ) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + FULL_ADD_A_IN_ID => self.a_in = new_input, + FULL_ADD_B_IN_ID => self.b_in = new_input, + FULL_ADD_OP_IN_ID => self.op_in = new_input, + _ => {} + } + } + + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + // get input values + let a: u32 = simulator.get_input_value(&self.a_in).try_into().unwrap(); + let b: u32 = simulator.get_input_value(&self.b_in).try_into().unwrap(); + let op: u32 = simulator.get_input_value(&self.op_in).try_into().unwrap(); + + let output: u32; + let mut overflow: u32 = 0; + + match op { + alu_op::ADD => { + // output = a.wrapping_add(b); + + let tmp = (a as i32).checked_add(b as i32); + overflow = match tmp { + Some(val) => { + output = val as u32; + 0 + } + None => { + output = a.wrapping_add(b); + 1 + } + }; + } + alu_op::ADDU => { + output = a.wrapping_add(b); + } + alu_op::SUB => { + let tmp = (a as i32).checked_sub(b as i32); + overflow = match tmp { + Some(val) => { + output = val as u32; + 0 + } + None => { + output = a.wrapping_sub(b); + 1 + } + } + } + alu_op::SUBU => { + output = a.wrapping_sub(b); + } + alu_op::AND => { + output = a & b; + } + alu_op::OR => { + output = a | b; + } + alu_op::XOR => { + output = a ^ b; + } + alu_op::NOR => { + output = !(a | b); + } + alu_op::SLT => { + output = ((a as i32) < (b as i32)) as u32; + } + alu_op::SLTU => { + output = (a < b) as u32; + } + alu_op::SLL => { + output = b.overflowing_shl(a).0; // https://doc.rust-lang.org/std/primitive.u32.html#method.overflowing_shl + } + alu_op::SRL => { + output = b.overflowing_shr(a).0; + } + alu_op::SRA => { + output = (b as i32).overflowing_shr(a).0 as u32; + } + alu_op::LUI => { + output = (a & 0x0000_ffff) | b.overflowing_shl(16).0; + } + _ => { + return Err(Condition::Error( + "undef alu operation or unimplemented instruction".to_string(), + )); + } + } + simulator.set_out_value(&self.id, FULL_ADD_OUT_ID, SignalValue::Data(output)); + simulator.set_out_value( + &self.id, + FULL_ADD_OVERFLOW_OUT_ID, + SignalValue::Data(overflow), + ); + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl ALU { + pub fn new(id: &str, pos: (f32, f32), a_in: Input, b_in: Input, op_in: Input) -> Self { + ALU { + id: id.to_string(), + pos, + a_in, + b_in, + op_in, + } + } + + pub fn rc_new(id: &str, pos: (f32, f32), a_in: Input, b_in: Input, op_in: Input) -> Rc { + Rc::new(ALU::new(id, pos, a_in, b_in, op_in)) + } +} +#[cfg(test)] +mod test { + use crate::components::cntr_field::ALU_OP_OUT; + + use super::*; + + use std::rc::Rc; + use syncrim::{ + common::{ComponentStore, Input, Simulator}, + components::ProbeOut, + }; + + #[test] + fn test_some_alu_op() { + let cs = ComponentStore { + store: vec![ + Rc::new(ProbeOut::new("op")), + Rc::new(ProbeOut::new("a")), + Rc::new(ProbeOut::new("b")), + ALU::rc_new( + "ALU", + (0.0, 0.0), + Input::new("a", "out"), + Input::new("b", "out"), + Input::new("op", "out"), + ), + ], + }; + let mut simulator = Simulator::new(cs).unwrap(); + + assert_eq!(simulator.cycle, 1); + + // outputs + let alu_val = &Input::new("ALU", FULL_ADD_OUT_ID); + + // reset + assert_eq!(simulator.get_input_value(alu_val), (0 + 0).into()); + + println!(""); + simulator.set_out_value("a", "out", 42); + simulator.set_out_value("b", "out", 1337); + simulator.set_out_value("op", "out", alu_op::ADD); + println!("sim_state {:?}", simulator.sim_state); + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 2); + assert_eq!( + simulator.get_input_value(alu_val), + (42 + 1337).into(), + "testing add (1)" + ); + + println!(""); + simulator.set_out_value("a", "out", -100i32 as u32); + simulator.set_out_value("b", "out", 1337); + simulator.set_out_value("op", "out", alu_op::ADD); + println!("sim_state {:?}", simulator.sim_state); + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!( + simulator.get_input_value(alu_val), + (1337 - 100).into(), + "testing add (2)" + ); + + println!(""); + simulator.set_out_value("a", "out", -100i32 as u32); + simulator.set_out_value("b", "out", 1337); + simulator.set_out_value("op", "out", alu_op::SUB); + println!("sim_state {:?}", simulator.sim_state); + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!( + simulator.get_input_value(alu_val), + ((-100i32 - 1337) as u32).into(), + "testing sub" + ); + + println!(""); + simulator.set_out_value("a", "out", -100i32 as u32); + simulator.set_out_value("b", "out", 1337); + simulator.set_out_value("op", "out", alu_op::SLT); + println!("sim_state {:?}", simulator.sim_state); + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!( + simulator.get_input_value(alu_val), + true.into(), + "testing SLT" + ); + + println!(""); + simulator.set_out_value("a", "out", -100i32 as u32); + simulator.set_out_value("b", "out", 1337); + simulator.set_out_value("op", "out", alu_op::SLTU); + println!("sim_state {:?}", simulator.sim_state); + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!( + simulator.get_input_value(alu_val), + false.into(), + "testing SLT" + ); + } +} diff --git a/mips-lib/src/components/mips_branch_logic.rs b/mips-lib/src/components/mips_branch_logic.rs new file mode 100644 index 00000000..9844ac1a --- /dev/null +++ b/mips-lib/src/components/mips_branch_logic.rs @@ -0,0 +1,220 @@ +// use std::fmt::Alignment; +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; +use syncrim::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; + +pub const BRANCH_OP_ID: &str = "branch_op_in"; +pub const BRANCH_RT_ID: &str = "branch_rt_in"; +pub const BRANCH_FUNCT_ID: &str = "branch_funct_in"; + +pub const BRANCH_RS_VALUE_ID: &str = "branch_rs_value_id"; +pub const BRANCH_RT_VALUE_ID: &str = "branch_rt_value_id"; + +pub const BRANCH_OUT_ID: &str = "branch_out"; + +pub const BRANCH_OFFSET: u32 = 0; +pub const BRANCH_REGISTER: u32 = 1; +pub const BRANCH_TARGET: u32 = 2; +pub const BRANCH_ADD4: u32 = 3; + +#[derive(Serialize, Deserialize, Clone)] +pub struct BranchLogic { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) op_in: Input, + pub(crate) rt_in: Input, + pub(crate) funct_in: Input, + pub(crate) rs_value: Input, + pub(crate) rt_value: Input, +} + +#[typetag::serde] +impl Component for BranchLogic { + fn to_(&self) { + trace!("branch_logic"); + } + + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: BRANCH_OP_ID.to_string(), + input: self.op_in.clone(), + }, + &InputPort { + port_id: BRANCH_RT_ID.to_string(), + input: self.rt_in.clone(), + }, + &InputPort { + port_id: BRANCH_FUNCT_ID.to_string(), + input: self.funct_in.clone(), + }, + &InputPort { + port_id: BRANCH_RS_VALUE_ID.to_string(), + input: self.rs_value.clone(), + }, + &InputPort { + port_id: BRANCH_RT_VALUE_ID.to_string(), + input: self.rt_value.clone(), + }, + ], + OutputType::Combinatorial, + vec![BRANCH_OUT_ID], + ), + ) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + BRANCH_OP_ID => self.op_in = new_input, + BRANCH_RT_ID => self.rt_in = new_input, + BRANCH_FUNCT_ID => self.funct_in = new_input, + BRANCH_RS_VALUE_ID => self.rs_value = new_input, + BRANCH_RT_VALUE_ID => self.rt_value = new_input, + _ => {} + } + } + + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + // get input values + let op: u32 = simulator.get_input_value(&self.op_in).try_into().unwrap(); + let rt: u32 = simulator.get_input_value(&self.rt_in).try_into().unwrap(); + let funct: u32 = simulator + .get_input_value(&self.funct_in) + .try_into() + .unwrap(); + let rs_value: u32 = simulator + .get_input_value(&self.rs_value) + .try_into() + .unwrap(); + let rt_value: u32 = simulator + .get_input_value(&self.rt_value) + .try_into() + .unwrap(); + + let out: u32; + + match op { + 0 => { + if (funct != 8) & (funct != 9) { + out = BRANCH_ADD4; + } else { + // JR, JARL + out = BRANCH_REGISTER; + } + } + 1 => { + if (rt != 0) & (rt != 16) { + // not BLTZ, BLTZAL + if (rt != 1) & (rt != 17) { + //not BGEZ, BGEZAL + // error + out = BRANCH_ADD4; + } else if (rs_value as i32) >= 0 { + // BGEZ, BGEZAL + out = BRANCH_OFFSET; + } else { + out = BRANCH_ADD4; + } + } else if (rs_value as i32) < 0 { + // BLTZ, BLTZAL + out = BRANCH_OFFSET; + } else { + out = BRANCH_ADD4; + } + } + 2 => { + // J + out = BRANCH_TARGET; + } + 3 => { + // JAL + out = BRANCH_TARGET; + } + 4 => { + if rs_value == rt_value { + // BEQ + out = BRANCH_OFFSET; + } else { + out = BRANCH_ADD4; + } + } + 5 => { + if rs_value == rt_value { + // BNE + out = BRANCH_ADD4; + } else { + out = BRANCH_OFFSET; + } + } + 6 => { + if (rs_value as i32) <= 0 { + // BLEZ + out = BRANCH_OFFSET; + } else { + out = BRANCH_ADD4; + } + } + 7 => { + if (rs_value as i32) > 0 { + // BGTZ + out = BRANCH_OFFSET; + } else { + out = BRANCH_ADD4; + } + } + _ => { + out = BRANCH_ADD4; + } + } + + simulator.set_out_value(&self.id, BRANCH_OUT_ID, SignalValue::Data(out)); + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl BranchLogic { + pub fn new( + id: &str, + pos: (f32, f32), + op_in: Input, + rt_in: Input, + funct_in: Input, + rs_value: Input, + rt_value: Input, + ) -> Self { + BranchLogic { + id: id.to_string(), + pos, + op_in, + rt_in, + funct_in, + rs_value, + rt_value, + } + } + + pub fn rc_new( + id: &str, + pos: (f32, f32), + op_in: Input, + rt_in: Input, + funct_in: Input, + rs_value: Input, + rt_value: Input, + ) -> Rc { + Rc::new(BranchLogic::new( + id, pos, op_in, rt_in, funct_in, rs_value, rt_value, + )) + } +} diff --git a/mips-lib/src/components/mips_control_unit.rs b/mips-lib/src/components/mips_control_unit.rs new file mode 100644 index 00000000..19de48ac --- /dev/null +++ b/mips-lib/src/components/mips_control_unit.rs @@ -0,0 +1,988 @@ +// use std::fmt::Alignment; +use log::trace; +use serde::{Deserialize, Serialize}; +use std::{any::Any, rc::Rc}; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; + +use super::alu_op; // values used in communication to the alu +use super::data_op; // values used in communication with the data memory +/// The input and output felid ids for the control unit +pub mod cntr_field { + + pub const INSTR_IN: &str = "control_unit_instr_in"; + + /// const REG_DEST_RT: u32 = 0; + /// const REG_DEST_RD: u32 = 1; + /// const REG_DEST_31: u32 = 2; + pub const REG_DEST_OUT: &str = "reg_dest"; + + /// 0 or 1 + pub const REG_WRITE_ENABLE_OUT: &str = "reg_write_enable"; + + /// const WRITE_REG_SRC_ALU:u32 = 0; + /// const WRITE_REG_SRC_MEM:u32 = 1; + pub const REG_WRITE_SRC_OUT: &str = "reg_write_src"; + + /// se module full_adder:alu_op + pub const ALU_OP_OUT: &str = "alu_op"; + + /// const ALU_SRC_A_OP:u32 = 0; + /// const ALU_SRC_A_RS:u32 = 1; + /// const ALU_SRC_A_ZERO:u32 = 3; + pub const ALU_SRC_A_OUT: &str = "alu_src_a"; + + /// const ALU_SRC_B_RT:u32 = 0; + /// const ALU_SRC_B_PC:u32 = 1; + /// const ALU_SRC_B_IMM:u32 = 2; + pub const ALU_SRC_B_OUT: &str = "alu_src_b"; + + // const EXTEND_ZERO:u32 = 0; + // const EXTEND_SIGNED:u32 = 1; + pub const EXTEND_SELECT_OUT: &str = "extend_select"; + + // 0 or 1 + pub const MEM_WRITE_ENABLE_OUT: &str = "mem_write_enable"; + + // 0 or 1, used for co-processor address stuff + pub const BRANCH_INTERRUPT_OUT: &str = "branch_interrupt"; + + // pub const CP0_MFC0 = 0; + // pub const CP0_MTC0 = 1; + // pub const CP0_RFE = 2; + // pub const CP0_SYSCALL = 3; + pub const CP0_OUT: &str = "cp0_out"; + + pub const MMU_OUT: &str = "mmu_out"; + + //TODO + // Opcode is passed to branch unit which is responsible to control branch logic + // pub const BRANCH_TYPE_OUT: &str = "branch"; + + pub const MEM_MODE_OUT: &str = "mem_mode"; +} + +// const NOP: u32 = 0; +const OP_0: u32 = 0; + +const FUNCT_SLL: u32 = 0; +const FUNCT_SRL: u32 = 0b00_0010; +const FUNCT_SRA: u32 = 0b00_0011; +const FUNCT_SLLV: u32 = 0b00_0100; +const FUNCT_SRLV: u32 = 0b00_0110; +const FUNCT_SRAV: u32 = 0b00_111; +const FUNCT_JR: u32 = 0b00_1000; +const FUNCT_JALR: u32 = 0b00_1001; +const SYSCALL: u32 = 0b00_1100; +const FUNCT_ADD: u32 = 0b10_0000; +const FUNCT_ADDU: u32 = 0b10_0001; +const FUNCT_SUB: u32 = 0b10_0010; +const FUNCT_SUBU: u32 = 0b10_0011; +const FUNCT_AND: u32 = 0b10_0100; +const FUNCT_OR: u32 = 0b10_0101; +const FUNCT_XOR: u32 = 0b10_0110; +const FUNCT_NOR: u32 = 0b10_0111; +const FUNCT_SLT: u32 = 0b10_1010; +const FUNCT_SLTU: u32 = 0b10_1011; + +const OP_1: u32 = 1; + +const B_FUNCT_BLTZ: u32 = 0; +const B_FUNCT_BGEZ: u32 = 1; +const B_FUNCT_BLTZAL: u32 = 0b1_0000; +const B_FUNCT_BGEZAL: u32 = 0b1_0001; + +const OP_J: u32 = 0b00_0010; +const OP_JAL: u32 = 0b00_0011; +const OP_BEQ: u32 = 0b00_0100; +const OP_BNE: u32 = 0b00_0101; +const OP_BLEZ: u32 = 0b00_0110; +const OP_BGTZ: u32 = 0b00_0111; + +const OP_ADDI: u32 = 0b00_1000; +const OP_ADDIU: u32 = 0b00_1001; +const OP_SLTI: u32 = 0b00_1010; +const OP_SLTIU: u32 = 0b00_1011; +const OP_ANDI: u32 = 0b00_1100; +const OP_ORI: u32 = 0b00_1101; +const OP_XORI: u32 = 0b00_1110; +const OP_LUI: u32 = 0b00_1111; + +const OP_CP0: u32 = 0b01_0000; +// const CP0_FUNCT_MFC0: u32 = 0; +// const CP0_FUNCT_MTF0: u32 = 0b0_0100; +// const CP0_FUNCT_SPECIAL: u32 = 0b1_0000; +// const CP0_FUNCT_SPECIAL_: u32 = 0b1_0000; + +const OP_LB: u32 = 0b10_0000; +const OP_LH: u32 = 0b10_0001; +const OP_LWL: u32 = 0b10_0010; +const OP_LW: u32 = 0b10_0011; +const OP_LBU: u32 = 0b10_0100; +const OP_LHU: u32 = 0b10_0101; +const OP_LWR: u32 = 0b10_0110; + +const OP_SB: u32 = 0b10_1000; +const OP_SH: u32 = 0b10_1001; +const OP_SWL: u32 = 0b10_1010; +const OP_SW: u32 = 0b10_1011; +const OP_SWR: u32 = 0b10_1110; + +/// module used to get what u32 represent. Used for communication between components +pub mod cntr_unit_signals { + pub const REG_DEST_RT: u32 = 0; + pub const REG_DEST_RD: u32 = 1; + pub const REG_DEST_31: u32 = 2; + + pub const REG_WRITE_DISABLE: u32 = 0; + pub const REG_WRITE_ENABLE: u32 = 1; + + pub const MEM_WRITE_DISABLE: u32 = 0; + pub const MEM_WRITE_ENABLE: u32 = 1; + + pub const ALU_SRC_A_SHAMT: u32 = 0; + pub const ALU_SRC_A_RS: u32 = 1; + pub const ALU_SRC_A_ZERO: u32 = 2; + + pub const ALU_SRC_B_RT: u32 = 0; + pub const ALU_SRC_B_PC: u32 = 1; + pub const ALU_SRC_B_IMM: u32 = 2; + + pub const WRITE_REG_SRC_ALU: u32 = 0; + pub const WRITE_REG_SRC_MEM: u32 = 1; + + pub const NO_BRANCH_INTERRUPT: u32 = 0; + pub const BRANCH_INTERRUPT: u32 = 1; + + pub const EXTEND_ZERO: u32 = 0; + pub const EXTEND_SIGNED: u32 = 1; + + pub const CP0_MFC0: u32 = 0; + pub const CP0_MTC0: u32 = 1; + pub const CP0_RFE: u32 = 2; + pub const CP0_SYSCALL: u32 = 3; + + pub const MMU_NORMAL: u32 = 0; + pub const MMU_CP0: u32 = 1; + pub const MMU_NOP: u32 = 2; + + // Note, it was decided to pass opcode to data mem to handle load + // and store instructions there +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct ControlUnit { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) a_in: Input, +} +#[typetag::serde] +impl Component for ControlUnit { + fn to_(&self) { + trace!("control_unit"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, _id: &str, _pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(ControlUnit { + id: "dummy".to_string(), + pos: (0.0, 0.0), + a_in: dummy_input.clone(), + })) + } + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![&InputPort { + port_id: cntr_field::INSTR_IN.to_string(), + input: self.a_in.clone(), + }], + OutputType::Combinatorial, + vec![ + cntr_field::REG_DEST_OUT, + cntr_field::REG_WRITE_ENABLE_OUT, + cntr_field::REG_WRITE_SRC_OUT, + cntr_field::ALU_OP_OUT, + cntr_field::ALU_SRC_A_OUT, + cntr_field::ALU_SRC_B_OUT, + cntr_field::EXTEND_SELECT_OUT, + cntr_field::MEM_WRITE_ENABLE_OUT, + cntr_field::BRANCH_INTERRUPT_OUT, + cntr_field::CP0_OUT, + cntr_field::MMU_OUT, + cntr_field::MEM_MODE_OUT, + ], + ), + ) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + #[allow(clippy::single_match)] + match target_port_id.as_str() { + cntr_field::INSTR_IN => self.a_in = new_input, + _ => {} + } + } + + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + let instr_in: u32 = simulator.get_input_value(&self.a_in).try_into().unwrap(); + + let op_code: u32 = (&instr_in >> 26) & 0x0000_003f; + + /// Sets the given field to the given value + macro_rules! set { + ($field:expr, $signal_val:expr) => { + simulator.set_out_value(&self.id, $field, SignalValue::Data($signal_val)) + }; + } + + /// sets the relevant fields for an i instruction + /// OP rt rs imm + /// ALU_OP and EXTEND ned to be set separately + macro_rules! set_i_instr { + () => { + // set target reg to be RT and read from alu + set!(cntr_field::REG_DEST_OUT, cntr_unit_signals::REG_DEST_RT); + set!( + cntr_field::REG_WRITE_ENABLE_OUT, + cntr_unit_signals::REG_WRITE_ENABLE + ); + set!( + cntr_field::REG_WRITE_SRC_OUT, + cntr_unit_signals::WRITE_REG_SRC_ALU + ); + + // set alu src to be RS and imm + set!(cntr_field::ALU_SRC_A_OUT, cntr_unit_signals::ALU_SRC_A_RS); + set!(cntr_field::ALU_SRC_B_OUT, cntr_unit_signals::ALU_SRC_B_IMM); + }; + } + + /// sets the relevant fields for an load operation + /// reg_src = mem + /// reg_dest = rt + /// addu rs (imm sign extended) + /// MEM MODE needs to be set separately + macro_rules! set_load_instr { + () => { + // set target reg to be RT and read from mem + set!( + cntr_field::REG_WRITE_ENABLE_OUT, + cntr_unit_signals::REG_WRITE_ENABLE + ); + set!(cntr_field::REG_DEST_OUT, cntr_unit_signals::REG_DEST_RT); + set!( + cntr_field::REG_WRITE_SRC_OUT, + cntr_unit_signals::WRITE_REG_SRC_MEM + ); + + // set alu to be addu with rs and signed imm + set!(cntr_field::ALU_OP_OUT, alu_op::ADDU); + set!(cntr_field::ALU_SRC_A_OUT, cntr_unit_signals::ALU_SRC_A_RS); + set!(cntr_field::ALU_SRC_B_OUT, cntr_unit_signals::ALU_SRC_B_IMM); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_SIGNED + ); + }; + } + macro_rules! set_store_instr { + () => { + // SET reg_write to disabled nad mem write to enable + set!( + cntr_field::REG_WRITE_ENABLE_OUT, + cntr_unit_signals::REG_WRITE_DISABLE + ); + set!( + cntr_field::MEM_WRITE_ENABLE_OUT, + cntr_unit_signals::MEM_WRITE_ENABLE + ); + + // set alu to be addu with rs and signed imm + set!(cntr_field::ALU_OP_OUT, alu_op::ADDU); + set!(cntr_field::ALU_SRC_A_OUT, cntr_unit_signals::ALU_SRC_A_RS); + set!(cntr_field::ALU_SRC_B_OUT, cntr_unit_signals::ALU_SRC_B_IMM); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_SIGNED + ); + }; + } + + set!( + cntr_field::REG_WRITE_ENABLE_OUT, + cntr_unit_signals::REG_WRITE_DISABLE + ); + set!( + cntr_field::MEM_WRITE_ENABLE_OUT, + cntr_unit_signals::MEM_WRITE_DISABLE + ); + set!( + cntr_field::BRANCH_INTERRUPT_OUT, + cntr_unit_signals::NO_BRANCH_INTERRUPT + ); + set!(cntr_field::MEM_MODE_OUT, data_op::NO_OP); + //TODO an idea would be to init all variables + // let alu_src_a : Signal; + // this would make the compiler force us to populate all paths so to not let any signal be undefined + // It would be more code, but would hopefully be more "secure" as it would stop us from forgetting a value + // another idea is to set all signals to uninitialized or dont care for better debugging + + // match the opcode + match op_code { + OP_0 => { + let funct: u32 = &instr_in & 0x0000_003f; + + set!( + cntr_field::REG_WRITE_ENABLE_OUT, + cntr_unit_signals::REG_WRITE_ENABLE + ); // overwritten by JR and SYSCALL to disabled + + set!(cntr_field::REG_DEST_OUT, cntr_unit_signals::REG_DEST_RD); // overwritten by JALR to REG_DEST_31 + set!(cntr_field::ALU_SRC_A_OUT, cntr_unit_signals::ALU_SRC_A_RS); // overwritten by JALR, SRA, SRL to shamt or zero + set!(cntr_field::ALU_SRC_B_OUT, cntr_unit_signals::ALU_SRC_B_RT); // overwritten by JALR to PC + set!( + cntr_field::REG_WRITE_SRC_OUT, + cntr_unit_signals::WRITE_REG_SRC_ALU + ); + set!( + cntr_field::BRANCH_INTERRUPT_OUT, + cntr_unit_signals::NO_BRANCH_INTERRUPT + ); // overwritten by jr and jalr + + match funct { + FUNCT_SLL => { + set!( + cntr_field::ALU_SRC_A_OUT, + cntr_unit_signals::ALU_SRC_A_SHAMT + ); + set!(cntr_field::ALU_OP_OUT, alu_op::SLL); + Ok(()) + } + FUNCT_SRL => { + set!( + cntr_field::ALU_SRC_A_OUT, + cntr_unit_signals::ALU_SRC_A_SHAMT + ); + set!(cntr_field::ALU_OP_OUT, alu_op::SRL); + Ok(()) + } + FUNCT_SRA => { + set!( + cntr_field::ALU_SRC_A_OUT, + cntr_unit_signals::ALU_SRC_A_SHAMT + ); + set!(cntr_field::ALU_OP_OUT, alu_op::SRA); + Ok(()) + } + FUNCT_SLLV => { + set!(cntr_field::ALU_OP_OUT, alu_op::SLL); + Ok(()) + } + FUNCT_SRLV => { + set!(cntr_field::ALU_OP_OUT, alu_op::SRL); + Ok(()) + } + FUNCT_SRAV => { + set!(cntr_field::ALU_OP_OUT, alu_op::SRL); + Ok(()) + } + FUNCT_JR => { + set!( + cntr_field::BRANCH_INTERRUPT_OUT, + cntr_unit_signals::BRANCH_INTERRUPT + ); + set!( + cntr_field::REG_WRITE_ENABLE_OUT, + cntr_unit_signals::REG_WRITE_DISABLE + ); + Ok(()) + } + FUNCT_JALR => { + set!( + cntr_field::BRANCH_INTERRUPT_OUT, + cntr_unit_signals::BRANCH_INTERRUPT + ); + set!(cntr_field::REG_DEST_OUT, cntr_unit_signals::REG_DEST_RD); // this is different from syncsim as there its defined as 31, but that doesn't match mips documentation; + set!(cntr_field::ALU_SRC_A_OUT, cntr_unit_signals::ALU_SRC_A_ZERO); + set!(cntr_field::ALU_SRC_B_OUT, cntr_unit_signals::ALU_SRC_B_PC); + set!(cntr_field::ALU_OP_OUT, alu_op::ADDU); + Ok(()) + } + SYSCALL => { + set!( + cntr_field::REG_WRITE_ENABLE_OUT, + cntr_unit_signals::REG_WRITE_DISABLE + ); + set!(cntr_field::CP0_OUT, cntr_unit_signals::CP0_SYSCALL); + Ok(()) + } + FUNCT_ADD => { + set!(cntr_field::ALU_OP_OUT, alu_op::ADD); + Ok(()) + } + FUNCT_ADDU => { + set!(cntr_field::ALU_OP_OUT, alu_op::ADDU); + Ok(()) + } + FUNCT_SUB => { + set!(cntr_field::ALU_OP_OUT, alu_op::SUB); + Ok(()) + } + FUNCT_SUBU => { + set!(cntr_field::ALU_OP_OUT, alu_op::SUBU); + Ok(()) + } + FUNCT_AND => { + set!(cntr_field::ALU_OP_OUT, alu_op::AND); + Ok(()) + } + FUNCT_OR => { + set!(cntr_field::ALU_OP_OUT, alu_op::OR); + Ok(()) + } + FUNCT_XOR => { + set!(cntr_field::ALU_OP_OUT, alu_op::XOR); + Ok(()) + } + FUNCT_NOR => { + set!(cntr_field::ALU_OP_OUT, alu_op::NOR); + Ok(()) + } + FUNCT_SLT => { + set!(cntr_field::ALU_OP_OUT, alu_op::SLT); + Ok(()) + } + FUNCT_SLTU => { + set!(cntr_field::ALU_OP_OUT, alu_op::SLTU); + Ok(()) + } + _ => Err(Condition::Error(format!( + "unknown funct {:#08b} for opcode 0b000000", + funct + ))), + } + } + OP_1 => { + // branch stuff, BGEZ BGEZAL BLTZ BLTZAL, + // Note many branch and jump instructions are actually pseudo instructions and will be compiled to others + // BAL => BGEZAL r0 offset + let tmp: u32 = simulator.get_input_signal(&self.a_in).try_into().unwrap(); + let b_funct: u32 = (tmp >> 16) & 0b11111; + + set!( + cntr_field::BRANCH_INTERRUPT_OUT, + cntr_unit_signals::BRANCH_INTERRUPT + ); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_SIGNED + ); + match b_funct { + B_FUNCT_BGEZ | B_FUNCT_BLTZ => Ok(()), + B_FUNCT_BGEZAL | B_FUNCT_BLTZAL => { + // save pc to reg + set!(cntr_field::REG_DEST_OUT, cntr_unit_signals::REG_DEST_31); + set!( + cntr_field::REG_WRITE_ENABLE_OUT, + cntr_unit_signals::REG_WRITE_ENABLE + ); + set!(cntr_field::ALU_OP_OUT, alu_op::ADDU); + set!(cntr_field::ALU_SRC_A_OUT, cntr_unit_signals::ALU_SRC_A_ZERO); + set!(cntr_field::ALU_SRC_B_OUT, cntr_unit_signals::ALU_SRC_B_PC); + set!( + cntr_field::REG_WRITE_SRC_OUT, + cntr_unit_signals::WRITE_REG_SRC_ALU + ); + Ok(()) + } + _ => Err(Condition::Error(format!( + "unknown funct {:#07b} for opcode 0b000001", + b_funct + ))), + } + } + OP_J => { + set!( + cntr_field::BRANCH_INTERRUPT_OUT, + cntr_unit_signals::BRANCH_INTERRUPT + ); + Ok(()) + } + OP_JAL => { + set!( + cntr_field::BRANCH_INTERRUPT_OUT, + cntr_unit_signals::BRANCH_INTERRUPT + ); + set!(cntr_field::REG_DEST_OUT, cntr_unit_signals::REG_DEST_31); + set!( + cntr_field::REG_WRITE_ENABLE_OUT, + cntr_unit_signals::REG_WRITE_ENABLE + ); + set!(cntr_field::ALU_OP_OUT, alu_op::ADDU); + set!(cntr_field::ALU_SRC_A_OUT, cntr_unit_signals::ALU_SRC_A_ZERO); + set!(cntr_field::ALU_SRC_B_OUT, cntr_unit_signals::ALU_SRC_B_PC); + set!( + cntr_field::REG_WRITE_SRC_OUT, + cntr_unit_signals::WRITE_REG_SRC_ALU + ); + Ok(()) + } + OP_BEQ | OP_BNE | OP_BLEZ | OP_BGTZ => { + set!( + cntr_field::BRANCH_INTERRUPT_OUT, + cntr_unit_signals::BRANCH_INTERRUPT + ); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_SIGNED + ); + Ok(()) + } + OP_ADDI => { + set_i_instr!(); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_SIGNED + ); + set!(cntr_field::ALU_OP_OUT, alu_op::ADD); + Ok(()) + } + OP_ADDIU => { + set_i_instr!(); + set!(cntr_field::ALU_OP_OUT, alu_op::ADDU); + Ok(()) + } + OP_SLTI => { + set_i_instr!(); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_SIGNED + ); + set!(cntr_field::ALU_OP_OUT, alu_op::SLT); + Ok(()) + } + OP_SLTIU => { + set_i_instr!(); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_ZERO + ); + set!(cntr_field::ALU_OP_OUT, alu_op::SLTU); + Ok(()) + } + OP_ANDI => { + set_i_instr!(); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_ZERO + ); + set!(cntr_field::ALU_OP_OUT, alu_op::AND); + Ok(()) + } + OP_ORI => { + set_i_instr!(); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_ZERO + ); + set!(cntr_field::ALU_OP_OUT, alu_op::OR); + Ok(()) + } + OP_XORI => { + set_i_instr!(); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_ZERO + ); + set!(cntr_field::ALU_OP_OUT, alu_op::XOR); + Ok(()) + } + OP_LUI => { + set_i_instr!(); + set!( + cntr_field::EXTEND_SELECT_OUT, + cntr_unit_signals::EXTEND_ZERO + ); + set!(cntr_field::ALU_OP_OUT, alu_op::LUI); + Ok(()) + } + OP_CP0 => Err(Condition::Error( + "CP0 instructions not yet implemented".to_string(), + )), + OP_LB => { + set!(cntr_field::MEM_MODE_OUT, data_op::LOAD_BYTE); + set_load_instr!(); + Ok(()) + } + OP_LBU => { + set!(cntr_field::MEM_MODE_OUT, data_op::LOAD_BYTE_U); + set_load_instr!(); + Ok(()) + } + OP_LH => { + set!(cntr_field::MEM_MODE_OUT, data_op::LOAD_HALF); + set_load_instr!(); + Ok(()) + } + OP_LHU => { + set!(cntr_field::MEM_MODE_OUT, data_op::LOAD_HALF_U); + set_load_instr!(); + Ok(()) + } + OP_LW => { + set!(cntr_field::MEM_MODE_OUT, data_op::LOAD_WORD); + set_load_instr!(); + Ok(()) + } + + OP_SB => { + set!(cntr_field::MEM_MODE_OUT, data_op::STORE_BYTE); + set_store_instr!(); + Ok(()) + } + OP_SH => { + set!(cntr_field::MEM_MODE_OUT, data_op::STORE_HALF); + set_store_instr!(); + Ok(()) + } + OP_SW => { + set!(cntr_field::MEM_MODE_OUT, data_op::STORE_WORD); + set_store_instr!(); + Ok(()) + } + + OP_LWL | OP_LWR | OP_SWL | OP_SWR => Err(Condition::Error( + "LWL, LWR, SWL and SWR are not implemented".to_string(), + )), + + _ => Err(Condition::Error(format!("Unknown opcode {:#08b}", op_code))), + } + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl ControlUnit { + pub fn new(id: &str, pos: (f32, f32), a_in: Input) -> Self { + ControlUnit { + id: id.to_string(), + pos, + a_in, + } + } + + pub fn rc_new(id: &str, pos: (f32, f32), a_in: Input) -> Rc { + Rc::new(ControlUnit::new(id, pos, a_in)) + } +} +#[cfg(test)] +mod test { + use syncrim::{common::ComponentStore, components::ProbeOut}; + + use super::*; + + fn setup_simulator() -> Simulator { + let cs = ComponentStore { + store: vec![ + Rc::new(ProbeOut::new("instr")), + ControlUnit::rc_new("cntr", (0.0, 0.0), Input::new("instr", "out")), + ], + }; + Simulator::new(cs).unwrap() + } + /// This tests + /// - beq t3 t0 7 => 000100 01011 01000 0000000000000111 + /// - xori $t6,$s4,32 => 001110 10100 01110 0000000000100000 + /// - sub $t0,$a0,$t0 => 000000 00100 01000 01000 00000 100010 + /// - lh $a1,14($s1) => 100001 10001 00101 0000000000001110 + /// - jal => 000011 10101010101010101010101010 + /// - jalr r10 r18 => 000000_10010_00000_01010_00000_001001 + #[test] + fn test_random_instrs() { + let mut sim = setup_simulator(); + + assert_eq!(sim.cycle, 1); + + println!("testing beq t3 t0 7 => 000100 01011 01000 0000000000000111"); + sim.set_out_value("instr", "out", 0b000100_01011_01000_0000000000000111); + println!("clock sim"); + sim.clock(); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::BRANCH_INTERRUPT_OUT)), + cntr_unit_signals::BRANCH_INTERRUPT.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_ENABLE_OUT)), + cntr_unit_signals::REG_WRITE_DISABLE.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::MEM_WRITE_ENABLE_OUT)), + cntr_unit_signals::MEM_WRITE_DISABLE.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::EXTEND_SELECT_OUT)), + cntr_unit_signals::EXTEND_SIGNED.into() + ); + + println!("testing xori $t6,$s4,32 => 001110 10100 01110 0000000000100000"); + sim.set_out_value("instr", "out", 0b001110_10100_01110_0000000000100000); + println!("clock sim"); + sim.clock(); + // no branch or mem write + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::BRANCH_INTERRUPT_OUT)), + cntr_unit_signals::NO_BRANCH_INTERRUPT.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::MEM_WRITE_ENABLE_OUT)), + cntr_unit_signals::MEM_WRITE_DISABLE.into() + ); + + // reg write, src alu and reg_dest rt, since rd is occupied by imm + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_ENABLE_OUT)), + cntr_unit_signals::REG_WRITE_ENABLE.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_SRC_OUT)), + cntr_unit_signals::WRITE_REG_SRC_ALU.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_DEST_OUT)), + cntr_unit_signals::REG_DEST_RT.into() + ); + + // ALU xor rs and imm zero extend + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::EXTEND_SELECT_OUT)), + cntr_unit_signals::EXTEND_ZERO.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_A_OUT)), + cntr_unit_signals::ALU_SRC_A_RS.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_B_OUT)), + cntr_unit_signals::ALU_SRC_B_IMM.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_OP_OUT)), + alu_op::XOR.into() + ); + + print!("testing sub $t0,$a0,$t0 => 000000 00100 01000 01000 00000 100010"); + sim.set_out_value("instr", "out", 0b000000_00100_01000_01000_00000_100010); + println!("clock sim"); + sim.clock(); + // no branch or mem write + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::BRANCH_INTERRUPT_OUT)), + cntr_unit_signals::NO_BRANCH_INTERRUPT.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::MEM_WRITE_ENABLE_OUT)), + cntr_unit_signals::MEM_WRITE_DISABLE.into() + ); + + // reg write, src alu and reg_dest rd + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_ENABLE_OUT)), + cntr_unit_signals::REG_WRITE_ENABLE.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_SRC_OUT)), + cntr_unit_signals::WRITE_REG_SRC_ALU.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_DEST_OUT)), + cntr_unit_signals::REG_DEST_RD.into(), + ); + + // ALU sub rs and rt + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_A_OUT)), + cntr_unit_signals::ALU_SRC_A_RS.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_B_OUT)), + cntr_unit_signals::ALU_SRC_B_RT.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_OP_OUT)), + alu_op::SUB.into() + ); + + print!("testing lh $a1,14($s1) => 100001 10001 00101 0000000000001110"); + sim.set_out_value("instr", "out", 0b100001_10001_00101_0000000000001110); + println!("clock sim"); + sim.clock(); + // reg write, no branch, no mem write + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::BRANCH_INTERRUPT_OUT)), + cntr_unit_signals::NO_BRANCH_INTERRUPT.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::MEM_WRITE_ENABLE_OUT)), + cntr_unit_signals::MEM_WRITE_DISABLE.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_ENABLE_OUT)), + cntr_unit_signals::REG_WRITE_ENABLE.into() + ); + + // reg dst rt, reg src mem + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_SRC_OUT)), + cntr_unit_signals::WRITE_REG_SRC_MEM.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_DEST_OUT)), + cntr_unit_signals::REG_DEST_RT.into() + ); + + // ADDU rs imm (signed) + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::EXTEND_SELECT_OUT)), + cntr_unit_signals::EXTEND_SIGNED.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_A_OUT)), + cntr_unit_signals::ALU_SRC_A_RS.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_B_OUT)), + cntr_unit_signals::ALU_SRC_B_IMM.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_OP_OUT)), + alu_op::ADDU.into() + ); + + println!("testing SW $r2 4($r3) => 101011 00011 00010 0000_0000_0000_0100"); + sim.set_out_value("instr", "out", 0b101011_00011_00010_0000_0000_0000_0100); + println!("clock sim"); + sim.clock(); + // no reg write, no branch, mem write + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::BRANCH_INTERRUPT_OUT)), + cntr_unit_signals::NO_BRANCH_INTERRUPT.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::MEM_WRITE_ENABLE_OUT)), + cntr_unit_signals::MEM_WRITE_ENABLE.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_ENABLE_OUT)), + cntr_unit_signals::REG_WRITE_DISABLE.into() + ); + + // ADDU rs imm (signed) + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::EXTEND_SELECT_OUT)), + cntr_unit_signals::EXTEND_SIGNED.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_A_OUT)), + cntr_unit_signals::ALU_SRC_A_RS.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_B_OUT)), + cntr_unit_signals::ALU_SRC_B_IMM.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_OP_OUT)), + alu_op::ADDU.into() + ); + + println!("testing jal => 000011 10101010101010101010101010 "); + sim.set_out_value("instr", "out", 0b000011_10101010101010101010101010); + println!("clock sim"); + sim.clock(); + // reg write, branch, no mem write + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::BRANCH_INTERRUPT_OUT)), + cntr_unit_signals::BRANCH_INTERRUPT.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::MEM_WRITE_ENABLE_OUT)), + cntr_unit_signals::MEM_WRITE_DISABLE.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_ENABLE_OUT)), + cntr_unit_signals::REG_WRITE_ENABLE.into() + ); + + // ALU zero + pc + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_A_OUT)), + cntr_unit_signals::ALU_SRC_A_ZERO.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_B_OUT)), + cntr_unit_signals::ALU_SRC_B_PC.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_OP_OUT)), + alu_op::ADDU.into() + ); + + // reg dst 31, reg src alu + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_SRC_OUT)), + cntr_unit_signals::WRITE_REG_SRC_ALU.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_DEST_OUT)), + cntr_unit_signals::REG_DEST_31.into() + ); + + println!("testing jalr r10 r18 => 000000_10010_00000_01010_00000_001001"); + sim.set_out_value("instr", "out", 0b000000_10010_00000_01010_00000_001001); + println!("clock sim"); + sim.clock(); + // reg write, branch, no mem write + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::BRANCH_INTERRUPT_OUT)), + cntr_unit_signals::BRANCH_INTERRUPT.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::MEM_WRITE_ENABLE_OUT)), + cntr_unit_signals::MEM_WRITE_DISABLE.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_ENABLE_OUT)), + cntr_unit_signals::REG_WRITE_ENABLE.into() + ); + + // ALU zero + pc + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_A_OUT)), + cntr_unit_signals::ALU_SRC_A_ZERO.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_SRC_B_OUT)), + cntr_unit_signals::ALU_SRC_B_PC.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::ALU_OP_OUT)), + alu_op::ADDU.into() + ); + + // reg dst rd, reg src alu + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_WRITE_SRC_OUT)), + cntr_unit_signals::WRITE_REG_SRC_ALU.into() + ); + assert_eq!( + sim.get_input_value(&Input::new("cntr", cntr_field::REG_DEST_OUT)), + cntr_unit_signals::REG_DEST_RD.into() + ); + } +} diff --git a/mips-lib/src/components/mips_dm.rs b/mips-lib/src/components/mips_dm.rs new file mode 100644 index 00000000..e18199e3 --- /dev/null +++ b/mips-lib/src/components/mips_dm.rs @@ -0,0 +1,383 @@ +#[cfg(feature = "gui-egui")] +use crate::gui_egui::mips_mem_view_window::MemViewWindow; +use core::cell::RefCell; +use std::cell::RefMut; +// use log::*; +use serde::{Deserialize, Serialize}; +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Simulator}; + +use crate::components::physical_mem::{MemOpSize, MemWriteReturn, MipsMem}; + +use super::PhysicalMem; + +pub mod data_op { + pub const NO_OP: u32 = 0; + pub const LOAD_BYTE: u32 = 1; + pub const LOAD_BYTE_U: u32 = 2; + pub const LOAD_HALF: u32 = 3; + pub const LOAD_HALF_U: u32 = 4; + pub const LOAD_WORD: u32 = 5; + + pub const STORE_BYTE: u32 = 6; + pub const STORE_HALF: u32 = 7; + pub const STORE_WORD: u32 = 8; +} + +pub const DATA_MEM_A_IN_ID: &str = "data_mem_address_in"; +pub const DATA_MEM_OP_IN_ID: &str = "data_mem_op_in"; +pub const DATA_MEM_WRITE_ENABLE_ID: &str = "data_mem_write_enable"; +pub const DATA_MEM_WD_IN_ID: &str = "data_mem_write_data_in"; + +pub const DATA_MEM_READ_DATA_OUT_ID: &str = "data_out"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct DataMem { + pub id: String, + pub pos: (f32, f32), + pub address_input: Input, + pub data_input: Input, + pub op_input: Input, + pub write_enable_input: Input, + pub phys_mem_id: String, + pub regfile_id: String, + #[cfg(feature = "gui-egui")] + pub mem_view: RefCell, +} + +impl DataMem { + #[allow(clippy::too_many_arguments)] + pub fn new( + id: String, + pos: (f32, f32), + address_input: Input, + data_input: Input, + op_input: Input, + write_enable_input: Input, + phys_mem_id: String, + regfile_id: String, + ) -> Self { + #[cfg(feature = "gui-egui")] + let mem_view = + MemViewWindow::new(id.clone(), "Data memory view".into()).set_data_view(None); + DataMem { + id, + pos, + phys_mem_id, + address_input, + data_input, + op_input, + write_enable_input, + #[cfg(feature = "gui-egui")] + mem_view: RefCell::new(mem_view), + regfile_id, + } + } + #[allow(clippy::too_many_arguments)] + pub fn rc_new( + id: String, + pos: (f32, f32), + address_input: Input, + data_input: Input, + op_input: Input, + write_enable_input: Input, + phys_mem_id: String, + regfile_id: String, + ) -> Rc { + Rc::new(DataMem::new( + id, + pos, + address_input, + data_input, + op_input, + write_enable_input, + phys_mem_id, + regfile_id, + )) + } + /// This gets a &PhysicalMem from the component named self.phys_mem_id + /// + /// # Panics + /// + /// Panics if This functions panics if phys_mem_id is not found in simulator + /// or phys_mem_id is not of type PhysicalMem + #[allow(clippy::expect_fun_call)] + fn get_phys_mem<'a>(&self, sim: &'a Simulator) -> &'a PhysicalMem { + let v = &sim.ordered_components; + let comp = v + .iter() + .find(|x| x.get_id_ports().0 == self.phys_mem_id) + .expect(&format!("cant find {} in simulator", self.phys_mem_id)); + // deref to get &dyn EguiComponent + let comp_any = (*comp).as_any(); + let phys_mem: &PhysicalMem = comp_any + .downcast_ref() + .expect("can't downcast to physical memory"); + phys_mem + } + + fn get_mut_mem<'a>(&self, sim: &'a Simulator) -> RefMut<'a, MipsMem> { + self.get_phys_mem(sim).mem.borrow_mut() + } + + fn up_hist(&self, sim: &Simulator, op: MemWriteReturn, cycle: usize) { + self.get_phys_mem(sim) + .history + .borrow_mut() + .insert(cycle, op); + } + fn up_cycle(&self, sim: &Simulator) { + let cycle = sim.cycle; + let _ = self.get_phys_mem(sim).cycle.replace(cycle); + } +} + +#[typetag::serde()] +impl Component for DataMem { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn to_(&self) { + //println!("InstrMem"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(DataMem::rc_new( + id.to_string(), + pos, + dummy_input.clone(), + dummy_input.clone(), + dummy_input.clone(), + dummy_input, + "dummy".into(), + "dummy".into(), + )) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + DATA_MEM_A_IN_ID => self.address_input = new_input, + DATA_MEM_OP_IN_ID => self.op_input = new_input, + DATA_MEM_WRITE_ENABLE_ID => self.op_input = new_input, + DATA_MEM_WD_IN_ID => self.write_enable_input = new_input, + _ => {} + } + } + + fn get_id_ports(&self) -> (String, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: DATA_MEM_A_IN_ID.to_string(), + input: self.address_input.clone(), + }, + &InputPort { + port_id: DATA_MEM_OP_IN_ID.to_string(), + input: self.op_input.clone(), + }, + &InputPort { + port_id: DATA_MEM_WD_IN_ID.to_string(), + input: self.data_input.clone(), + }, + &InputPort { + port_id: DATA_MEM_WRITE_ENABLE_ID.to_string(), + input: self.write_enable_input.clone(), + }, + ], + OutputType::Combinatorial, + vec![DATA_MEM_READ_DATA_OUT_ID], + ), + ) + } + + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + let cycle = simulator.cycle; + self.up_cycle(simulator); + // get instr at pc/4s + let address: u32 = simulator + .get_input_value(&self.address_input) + .try_into() + .unwrap(); + + // is operation load or store ect + let mem_op: u32 = simulator + .get_input_value(&self.op_input) + .try_into() + .unwrap(); + + let write_enable: bool = simulator + .get_input_value(&self.write_enable_input) + .try_into() + .unwrap(); + + let data: u32 = simulator + .get_input_value(&self.data_input) + .try_into() + .unwrap(); + + // update dynamic symbol PC_IM + #[cfg(feature = "gui-egui")] + self.mem_view + .borrow_mut() + .set_dynamic_symbol("DM_ADRS", address); + + // check if write enable and mem op match + let is_write_enable_valid = match mem_op { + data_op::LOAD_BYTE + | data_op::LOAD_BYTE_U + | data_op::LOAD_HALF + | data_op::LOAD_HALF_U + | data_op::LOAD_WORD => !write_enable, + data_op::STORE_BYTE | data_op::STORE_HALF | data_op::STORE_WORD => write_enable, + _ => true, + }; + + if !is_write_enable_valid { + return Err(Condition::Error(format!( + "write_enable ({}) doesn't match mem_op ({})", + write_enable, mem_op + ))); + } + const SIGNED: bool = true; + const UNSIGNED: bool = false; + + let ret: Result<(), Condition> = match mem_op { + data_op::NO_OP => { + simulator.set_out_value(&self.id, DATA_MEM_READ_DATA_OUT_ID, 0); + Ok(()) + } + data_op::LOAD_BYTE => { + let val = self.get_mut_mem(simulator).get_unaligned( + address, + MemOpSize::Byte, + SIGNED, + true, + ); + simulator.set_out_value(&self.id, DATA_MEM_READ_DATA_OUT_ID, val); + Ok(()) + } + data_op::LOAD_BYTE_U => { + let val = self.get_mut_mem(simulator).get_unaligned( + address, + MemOpSize::Byte, + UNSIGNED, + true, + ); + simulator.set_out_value(&self.id, DATA_MEM_READ_DATA_OUT_ID, val); + Ok(()) + } + data_op::LOAD_HALF => { + let l_ret = self + .get_mut_mem(simulator) + .get(address, MemOpSize::Half, SIGNED, true); + match l_ret { + Ok(val) => { + simulator.set_out_value(&self.id, DATA_MEM_READ_DATA_OUT_ID, val); + Ok(()) + } + Err(_) => Err(Condition::Error(format!( + "Tried to read unaligned half word, address {:#0x}", + address + ))), + } + } + data_op::LOAD_HALF_U => { + let l_ret = + self.get_mut_mem(simulator) + .get(address, MemOpSize::Half, UNSIGNED, true); + match l_ret { + Ok(val) => { + simulator.set_out_value(&self.id, DATA_MEM_READ_DATA_OUT_ID, val); + Ok(()) + } + Err(_) => Err(Condition::Error(format!( + "Tried to read unaligned half word, address {:#0x}", + address + ))), + } + } + data_op::LOAD_WORD => { + let l_ret = + self.get_mut_mem(simulator) + .get(address, MemOpSize::Word, UNSIGNED, true); + match l_ret { + Ok(val) => { + simulator.set_out_value(&self.id, DATA_MEM_READ_DATA_OUT_ID, val); + Ok(()) + } + Err(_) => Err(Condition::Error(format!( + "Tried to read unaligned word, address {:#0x}", + address + ))), + } + } + data_op::STORE_BYTE => { + let ret = self + .get_mut_mem(simulator) + .write(address, data, MemOpSize::Byte, true); + self.up_hist(simulator, ret, cycle); + simulator.set_out_value(&self.id, DATA_MEM_READ_DATA_OUT_ID, 0); + Ok(()) + } + data_op::STORE_HALF => { + let w_ret = + self.get_mut_mem(simulator) + .write_aligned(address, data, MemOpSize::Half, true); + match w_ret { + Ok(ret) => { + self.up_hist(simulator, ret, cycle); + simulator.set_out_value(&self.id, DATA_MEM_READ_DATA_OUT_ID, 0); + Ok(()) + } + Err(_) => Err(Condition::Error(format!( + "Tried to write unaligned half word, address {:#0x}", + address + ))), + } + } + data_op::STORE_WORD => { + let w_ret = + self.get_mut_mem(simulator) + .write_aligned(address, data, MemOpSize::Word, true); + match w_ret { + Ok(ret) => { + self.up_hist(simulator, ret, cycle); + simulator.set_out_value(&self.id, DATA_MEM_READ_DATA_OUT_ID, 0); + Ok(()) + } + Err(_) => Err(Condition::Error(format!( + "Tried to write unaligned word, address {:#0x}", + address + ))), + } + } + _ => Err(Condition::Error(format!("unknown mem op {}", mem_op))), + }; + // test breakpoint + match ret { + Ok(_) => match mem_op { + data_op::NO_OP => Ok(()), + _ => { + #[cfg(feature = "gui-egui")] + if self.mem_view.borrow().is_break_point(&(address & !0b11)) { + Err(Condition::Halt(format!( + "Read or write at breakpoint address {:#0x}", + address + ))) + } else { + Ok(()) + } + #[cfg(not(feature = "gui-egui"))] + Ok(()) + } + }, + Err(_) => ret, + } + } +} diff --git a/mips-lib/src/components/mips_im.rs b/mips-lib/src/components/mips_im.rs new file mode 100644 index 00000000..3c484d98 --- /dev/null +++ b/mips-lib/src/components/mips_im.rs @@ -0,0 +1,151 @@ +use core::cell::RefCell; +// use log::*; +use serde::{Deserialize, Serialize}; +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Simulator}; + +use crate::components::physical_mem::MemOpSize; +#[cfg(feature = "gui-egui")] +use crate::gui_egui::mips_mem_view_window::MemViewWindow; + +use super::PhysicalMem; + +pub const INSTR_MEM_PC_ID: &str = "pc"; + +pub const INSTR_MEM_INSTRUCTION_ID: &str = "instruction"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct InstrMem { + pub id: String, + pub pos: (f32, f32), + pub pc: Input, + // All components who deal with memory acess this + pub phys_mem_id: String, + pub regfile_id: String, + #[cfg(feature = "gui-egui")] + pub mem_view: RefCell, +} + +impl InstrMem { + pub fn new( + id: String, + pos: (f32, f32), + pc_input: Input, + phys_mem_id: String, + regfile_id: String, + ) -> InstrMem { + #[cfg(feature = "gui-egui")] + let mem_view = + MemViewWindow::new(id.clone(), "instruction memory view".into()).set_code_view(None); + InstrMem { + id, + pos, + pc: pc_input, + phys_mem_id, + #[cfg(feature = "gui-egui")] + mem_view: RefCell::new(mem_view), + regfile_id, + } + } + pub fn rc_new( + id: String, + pos: (f32, f32), + pc_input: Input, + phys_mem_id: String, + regfile_id: String, + ) -> Rc { + Rc::new(InstrMem::new(id, pos, pc_input, phys_mem_id, regfile_id)) + } +} + +#[typetag::serde()] +impl Component for InstrMem { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn to_(&self) { + //println!("InstrMem"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(InstrMem { + id: id.into(), + pos, + pc: dummy_input, + phys_mem_id: "dummy".into(), + #[cfg(feature = "gui-egui")] + mem_view: RefCell::new(MemViewWindow::new("dummy".into(), "IM dummy".into())), + regfile_id: "dummy".into(), + })) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id.as_str() == INSTR_MEM_PC_ID { + self.pc = new_input; + } + } + + fn get_id_ports(&self) -> (String, Ports) { + ( + self.id.clone(), + Ports::new( + vec![&InputPort { + port_id: INSTR_MEM_PC_ID.to_string(), + input: self.pc.clone(), + }], + OutputType::Combinatorial, + vec![INSTR_MEM_INSTRUCTION_ID], + ), + ) + } + + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + // get instr at pc/4 + let pc: u32 = simulator.get_input_value(&self.pc).try_into().unwrap(); + + // this is inside a {} to make sure our simulator borrow is returned before its used to set signal + #[allow(clippy::expect_fun_call)] + let option_instr = { + let v = &simulator.ordered_components; + let comp = v + .iter() + .find(|x| x.get_id_ports().0 == self.phys_mem_id) + .expect(&format!("cant find {} in simulator", self.phys_mem_id)); + // deref to get Rc + // deref again to get &dyn EguiComponent + let comp_any = (**comp).as_any(); + let phys_mem: &PhysicalMem = comp_any + .downcast_ref() + .expect("can't downcast to physical memory"); + phys_mem + .mem + .borrow_mut() + .get(pc, MemOpSize::Word, false, true) + }; + + // update dynamic symbol PC_IM + #[cfg(feature = "gui-egui")] + self.mem_view.borrow_mut().set_dynamic_symbol("PC_IM", pc); + + // Get a word at PC with the size of 32bits, read as big endian, + // sign extend doesn't mater since we have 32 bits so extending to 32bits does nothing + match option_instr { + Ok(instr) => { + simulator.set_out_value(&self.id, INSTR_MEM_INSTRUCTION_ID, instr); + // check if pc is at breakpoint + #[cfg(feature = "gui-egui")] + match self.mem_view.borrow().is_break_point(&pc) { + true => Err(Condition::Halt(format!("Reached breakpoint at {:#0x}", pc))), + false => Ok(()), + } + #[cfg(not(feature = "gui-egui"))] + Ok(()) + } + Err(_) => Err(Condition::Error(format!("Unaligned Read, PC = {:#0x}", pc))), + } + } +} diff --git a/mips-lib/src/components/mips_instruction_splitter.rs b/mips-lib/src/components/mips_instruction_splitter.rs new file mode 100644 index 00000000..84787486 --- /dev/null +++ b/mips-lib/src/components/mips_instruction_splitter.rs @@ -0,0 +1,136 @@ +// use std::fmt::Alignment; +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; + +pub const INSTRUCTION_SPLITTER_IN_ID: &str = "instruction_in"; + +pub const INSTRUCTION_SPLITTER_OP_ID: &str = "op_out"; +pub const INSTRUCTION_SPLITTER_RS_ID: &str = "rs_out"; +pub const INSTRUCTION_SPLITTER_RT_ID: &str = "rt_out"; +pub const INSTRUCTION_SPLITTER_RD_ID: &str = "rd_out"; +pub const INSTRUCTION_SPLITTER_SHAMT_ID: &str = "shamt_out"; +pub const INSTRUCTION_SPLITTER_FUNCT_ID: &str = "funct_out"; +pub const INSTRUCTION_SPLITTER_IMMEDIATE_ID: &str = "immediate_out"; +pub const INSTRUCTION_SPLITTER_TARGET_ID: &str = "target_out"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct InstrSplit { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) instruction_in: Input, +} + +#[typetag::serde] +impl Component for InstrSplit { + fn to_(&self) { + trace!("pc+4"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, _id: &str, _pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(InstrSplit { + id: "dummy".to_string(), + pos: (0.0, 0.0), + instruction_in: dummy_input.clone(), + })) + } + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![&InputPort { + port_id: INSTRUCTION_SPLITTER_IN_ID.to_string(), + input: self.instruction_in.clone(), + }], + OutputType::Combinatorial, + vec![ + INSTRUCTION_SPLITTER_OP_ID, + INSTRUCTION_SPLITTER_RS_ID, + INSTRUCTION_SPLITTER_RT_ID, + INSTRUCTION_SPLITTER_RD_ID, + INSTRUCTION_SPLITTER_SHAMT_ID, + INSTRUCTION_SPLITTER_FUNCT_ID, + INSTRUCTION_SPLITTER_IMMEDIATE_ID, + INSTRUCTION_SPLITTER_TARGET_ID, + ], + ), + ) + } + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + #[allow(clippy::single_match)] + match target_port_id.as_str() { + INSTRUCTION_SPLITTER_IN_ID => self.instruction_in = new_input, + _ => {} + } + } + + // propagate sign extension to output + // TODO: always extend to Signal size? (it should not matter and should be slightly cheaper) + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + // get input values + let instruction: u32 = simulator + .get_input_value(&self.instruction_in) + .try_into() + .unwrap(); + + let op = (instruction >> 26) & 0x0000_003f; + let rs = (instruction >> 21) & 0x0000_001f; + let rt = (instruction >> 16) & 0x0000_001f; + let rd = (instruction >> 11) & 0x0000_001f; + let shamt = (instruction >> 6) & 0x0000_001f; + let funct = instruction & 0x0000_003f; + let immediate = instruction & 0x0000_ffff; + let target = instruction & 0x03ff_ffff; + + simulator.set_out_value(&self.id, INSTRUCTION_SPLITTER_OP_ID, SignalValue::Data(op)); + simulator.set_out_value(&self.id, INSTRUCTION_SPLITTER_RS_ID, SignalValue::Data(rs)); + simulator.set_out_value(&self.id, INSTRUCTION_SPLITTER_RT_ID, SignalValue::Data(rt)); + simulator.set_out_value(&self.id, INSTRUCTION_SPLITTER_RD_ID, SignalValue::Data(rd)); + simulator.set_out_value( + &self.id, + INSTRUCTION_SPLITTER_SHAMT_ID, + SignalValue::Data(shamt), + ); + simulator.set_out_value( + &self.id, + INSTRUCTION_SPLITTER_FUNCT_ID, + SignalValue::Data(funct), + ); + simulator.set_out_value( + &self.id, + INSTRUCTION_SPLITTER_IMMEDIATE_ID, + SignalValue::Data(immediate), + ); + simulator.set_out_value( + &self.id, + INSTRUCTION_SPLITTER_TARGET_ID, + SignalValue::Data(target), + ); + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl InstrSplit { + pub fn new(id: &str, pos: (f32, f32), instruction_in: Input) -> Self { + InstrSplit { + id: id.to_string(), + pos, + instruction_in, + } + } + + pub fn rc_new(id: &str, pos: (f32, f32), instruction_in: Input) -> Rc { + Rc::new(InstrSplit::new(id, pos, instruction_in)) + } +} diff --git a/mips-lib/src/components/mips_jump_merge.rs b/mips-lib/src/components/mips_jump_merge.rs new file mode 100644 index 00000000..2119c65a --- /dev/null +++ b/mips-lib/src/components/mips_jump_merge.rs @@ -0,0 +1,97 @@ +// use std::fmt::Alignment; +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; +use syncrim::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; + +pub const MERGE_INSTR_ADDR_ID: &str = "merge_instr_addr_in"; +pub const MERGE_JUMP_ADDR_ID: &str = "merge_jump_addr_in"; + +pub const MERGE_OUT_ID: &str = "merge_out"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct JumpMerge { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) instr_addr_in: Input, + pub(crate) jump_addr_in: Input, +} + +#[typetag::serde] +impl Component for JumpMerge { + fn to_(&self) { + trace!("jump_merge"); + } + + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: MERGE_INSTR_ADDR_ID.to_string(), + input: self.instr_addr_in.clone(), + }, + &InputPort { + port_id: MERGE_JUMP_ADDR_ID.to_string(), + input: self.jump_addr_in.clone(), + }, + ], + OutputType::Combinatorial, + vec![MERGE_OUT_ID], + ), + ) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + MERGE_INSTR_ADDR_ID => self.instr_addr_in = new_input, + MERGE_JUMP_ADDR_ID => self.jump_addr_in = new_input, + _ => {} + } + } + + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + // get input values + let instr_addr: u32 = simulator + .get_input_value(&self.instr_addr_in) + .try_into() + .unwrap(); + let jump_addr: u32 = simulator + .get_input_value(&self.jump_addr_in) + .try_into() + .unwrap(); + + let output = (instr_addr & 0xf000_0000) | (jump_addr.overflowing_shl(2).0 & 0x0fff_ffff); + + simulator.set_out_value(&self.id, MERGE_OUT_ID, SignalValue::Data(output)); + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl JumpMerge { + pub fn new(id: &str, pos: (f32, f32), instr_addr_in: Input, jump_addr_in: Input) -> Self { + JumpMerge { + id: id.to_string(), + pos, + instr_addr_in, + jump_addr_in, + } + } + + pub fn rc_new( + id: &str, + pos: (f32, f32), + instr_addr_in: Input, + jump_addr_in: Input, + ) -> Rc { + Rc::new(JumpMerge::new(id, pos, instr_addr_in, jump_addr_in)) + } +} diff --git a/mips-lib/src/components/mips_reg_file.rs b/mips-lib/src/components/mips_reg_file.rs new file mode 100644 index 00000000..c38cb0d2 --- /dev/null +++ b/mips-lib/src/components/mips_reg_file.rs @@ -0,0 +1,224 @@ +// use std::fmt::Alignment; +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::cell::RefCell; +use std::rc::Rc; +use syncrim::common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Simulator}; + +pub mod reg_file_fields { + pub const RS_ADDRESS_IN_ID: &str = "rs_address_in"; + pub const RT_ADDRESS_IN_ID: &str = "rt_address_in"; + pub const WRITE_ADDRESS_IN_ID: &str = "write_address_in"; + pub const WRITE_DATA_IN_ID: &str = "write_data_in"; + pub const WRITE_ENABLE_IN_ID: &str = "write_enable_in"; + + pub const RT_VALUE_OUT_ID: &str = "rt_value_out"; + pub const RS_VALUE_OUT_ID: &str = "rs_value_out"; +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct RegFile { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) rs_address_in: Input, + pub(crate) rt_address_in: Input, + pub(crate) write_address_in: Input, + pub(crate) write_data_in: Input, + pub(crate) write_enable_in: Input, + + #[serde(skip)] + pub registers: RefCell<[u32; 32]>, // all 32 registers, in future, we might save the whole signal + #[serde(skip)] + history: RefCell>, // contains the value before it was modified used for unclock. + + //used for gui + #[serde(skip)] + pub show_reg_names: RefCell, + + #[serde(skip)] + pub reg_format: RefCell, +} +#[derive(Clone, Default, PartialEq, PartialOrd, Debug)] +pub enum RegFormat { + #[default] + Hex, + Bin, + DecSigned, + DecUnsigned, + UTF8BE, + UTF8LE, +} + +#[derive(Serialize, Deserialize, Clone)] +struct RegOp { + pub addr: u8, + pub data: u32, // might save whole signal in future +} + +#[typetag::serde] +impl Component for RegFile { + fn to_(&self) { + trace!("reg_file"); + } + + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: reg_file_fields::RS_ADDRESS_IN_ID.to_string(), + input: self.rs_address_in.clone(), + }, + &InputPort { + port_id: reg_file_fields::RT_ADDRESS_IN_ID.to_string(), + input: self.rt_address_in.clone(), + }, + &InputPort { + port_id: reg_file_fields::WRITE_ADDRESS_IN_ID.to_string(), + input: self.write_address_in.clone(), + }, + &InputPort { + port_id: reg_file_fields::WRITE_DATA_IN_ID.to_string(), + input: self.write_data_in.clone(), + }, + &InputPort { + port_id: reg_file_fields::WRITE_ENABLE_IN_ID.to_string(), + input: self.write_enable_in.clone(), + }, + ], + OutputType::Combinatorial, + vec![ + reg_file_fields::RS_VALUE_OUT_ID, + reg_file_fields::RT_VALUE_OUT_ID, + ], + ), + ) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + reg_file_fields::RS_ADDRESS_IN_ID => self.rs_address_in = new_input, + reg_file_fields::RT_ADDRESS_IN_ID => self.rt_address_in = new_input, + reg_file_fields::WRITE_ADDRESS_IN_ID => self.write_address_in = new_input, + reg_file_fields::WRITE_DATA_IN_ID => self.write_data_in = new_input, + reg_file_fields::WRITE_ENABLE_IN_ID => self.write_enable_in = new_input, + _ => {} + } + } + + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + //TODO check if inputs are invalid and return error + // type of signal + // address within bounds etc + + let rs_addr: usize = simulator + .get_input_value(&self.rs_address_in) + .try_into() + .unwrap(); + let rt_addr: usize = simulator + .get_input_value(&self.rt_address_in) + .try_into() + .unwrap(); + let w_addr: usize = simulator + .get_input_value(&self.write_address_in) + .try_into() + .unwrap(); + let w_data: u32 = simulator + .get_input_value(&self.write_data_in) + .try_into() + .unwrap(); + let w_enable: u32 = simulator + .get_input_value(&self.write_enable_in) + .try_into() + .unwrap(); + + //save value to history before write, no need for {} as borrows is dropped after operation? + self.history.borrow_mut().push(RegOp { + addr: w_addr as u8, + data: *self.registers.borrow().get(w_addr).unwrap(), + }); + + // write data + if w_enable == 1 && w_addr != 0 { + self.registers.borrow_mut()[w_addr] = w_data; + }; + + // update out signals, no {} since self.registers are dropped at end of function + let regs = self.registers.borrow(); + simulator.set_out_value(&self.id, reg_file_fields::RS_VALUE_OUT_ID, regs[rs_addr]); + simulator.set_out_value(&self.id, reg_file_fields::RT_VALUE_OUT_ID, regs[rt_addr]); + + Ok(()) + } + + fn un_clock(&self) { + if let Some(last_op) = self.history.borrow_mut().pop() { + let mut regs = self.registers.borrow_mut(); + regs[last_op.addr as usize] = last_op.data; + } + } + + fn reset(&self) { + *self.registers.borrow_mut() = [0; 32]; + self.registers.borrow_mut()[29] = 0x8000_0000; + *self.history.borrow_mut() = vec![]; + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl RegFile { + pub fn new( + id: &str, + pos: (f32, f32), + rs_address_in: Input, + rt_address_in: Input, + write_address_in: Input, + write_data_in: Input, + write_enable_in: Input, + ) -> Self { + let mut arr: [u32; 32] = [0; 32]; + arr[29] = 0x8000_0000; + RegFile { + id: id.to_string(), + pos, + rs_address_in, + rt_address_in, + write_address_in, + write_data_in, + write_enable_in, + registers: RefCell::new(arr), // create 32 zeros, wit 29(stack pointer) at 0x8000_0000 + history: RefCell::new(vec![]), + show_reg_names: RefCell::default(), + reg_format: RefCell::default(), + } + } + + pub fn rc_new( + id: &str, + pos: (f32, f32), + rs_address_in: Input, + rt_address_in: Input, + write_address_in: Input, + write_data_in: Input, + write_enable_in: Input, + ) -> Rc { + Rc::new(RegFile::new( + id, + pos, + rs_address_in, + rt_address_in, + write_address_in, + write_data_in, + write_enable_in, + )) + } + + pub fn get_registers(&self, i: usize) -> u32 { + self.registers.borrow()[i] + } +} diff --git a/mips-lib/src/components/mod.rs b/mips-lib/src/components/mod.rs new file mode 100644 index 00000000..95d9675a --- /dev/null +++ b/mips-lib/src/components/mod.rs @@ -0,0 +1,21 @@ +mod mips_alu; +mod mips_branch_logic; +mod mips_control_unit; +mod mips_dm; +mod mips_im; +mod mips_instruction_splitter; +mod mips_jump_merge; +mod mips_reg_file; +mod physical_mem; +mod sz_extend; + +pub use mips_alu::*; +pub use mips_branch_logic::*; +pub use mips_control_unit::*; +pub use mips_dm::*; +pub use mips_im::*; +pub use mips_instruction_splitter::*; +pub use mips_jump_merge::*; +pub use mips_reg_file::*; +pub use physical_mem::*; +pub use sz_extend::*; diff --git a/mips-lib/src/components/physical_mem.rs b/mips-lib/src/components/physical_mem.rs new file mode 100644 index 00000000..e81aa7d9 --- /dev/null +++ b/mips-lib/src/components/physical_mem.rs @@ -0,0 +1,389 @@ +use elf::{endian::AnyEndian, ElfBytes}; +use std::{ + any::Any, + cell::RefCell, + collections::{BTreeMap, HashMap}, + fs, + path::PathBuf, +}; +use syncrim::{ + common::{Component, Ports}, + signal::Id, +}; + +use serde::{Deserialize, Serialize}; + +/// Used to contain the "physical memory" which instruction memory and data memory uses +#[derive(Serialize, Deserialize, Clone)] +pub struct PhysicalMem { + pub id: String, + pub pos: (f32, f32), + #[serde(skip)] + pub mem: RefCell, + #[serde(skip)] + pub history: RefCell>, + // used for the un_clock(), this is because the simulator is not passed in un clock and we dont know what cycle we un clock to + #[serde(skip)] + pub cycle: RefCell, +} + +impl PhysicalMem { + pub fn new(id: impl Into, pos: (f32, f32)) -> Self { + Self { + id: id.into(), + pos, + mem: RefCell::default(), + history: RefCell::default(), + cycle: RefCell::default(), + } + } + + pub fn load_file(&self, path: &PathBuf) { + let data = fs::read(path).unwrap(); + self.mem.replace(MipsMem::from_sections(&data).unwrap()); + self.history.borrow_mut().clear(); + } +} + +#[typetag::serde] +impl Component for PhysicalMem { + #[doc = " returns the (id, Ports) of the component"] + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new(vec![], syncrim::common::OutputType::Combinatorial, vec![]), + ) + } + + #[doc = " any"] + fn as_any(&self) -> &dyn Any { + self + } + + fn un_clock(&self) { + *self.cycle.borrow_mut() -= 1; + if let Some(op) = self.history.borrow_mut().remove(&*self.cycle.borrow()) { + self.mem.borrow_mut().revert(op); + }; + } + + fn reset(&self) { + // dont need to reset cycle, since cycle is updated in clock + + let mut hist_vec: Vec<(usize, MemWriteReturn)> = + self.history.borrow_mut().drain().collect(); + // sort vec with largest first + hist_vec.sort_by(|(a, _), (b, _)| a.cmp(b).reverse()); + let mut mem = self.mem.borrow_mut(); + for (_, op) in hist_vec { + mem.revert(op); + } + } +} + +/// A men contains three fields. One with the memory mapped data in a BTreeMap, +/// hashmap with symbols and a hashmap with sections +#[derive(Default, Serialize, Deserialize, Clone)] +pub struct MipsMem { + symbols: HashMap, + sections: HashMap, + data: BTreeMap, +} +#[derive(Clone)] +pub enum MemOpSize { + Byte, + Half, + Word, +} +/// This struct is not ment to be cloned +#[derive(Clone)] +pub struct MemWriteReturn { + address: u32, + op_size: MemOpSize, + bytes: [u8; 4], +} +impl MemWriteReturn { + /// return the address the bytes comes from + pub fn address(&self) -> u32 { + self.address + } + pub fn op_size(&self) -> MemOpSize { + self.op_size.clone() + } + /// return the bytes before the write where [0] is at address, [1] is at address + 1 and [N] is at address + N + pub fn before_bytes(&self) -> Vec { + match self.op_size { + MemOpSize::Byte => vec![self.bytes[0]], + MemOpSize::Half => vec![self.bytes[0], self.bytes[1]], + MemOpSize::Word => vec![self.bytes[0], self.bytes[1], self.bytes[2], self.bytes[3]], + } + } +} + +impl MipsMem { + /// This function constructs a Mem struct using the elf sections to load the data. + /// This may be un-reliable as the Elf might not always contain the sections, + /// or contain un-relevant sections and no relevant ones + /// # TODO fix result error. currently panics + pub fn from_sections(elf_bytes: &[u8]) -> Result { + let mut mem: MipsMem = MipsMem { + symbols: HashMap::new(), + data: BTreeMap::new(), + sections: HashMap::new(), + }; + + let file = ElfBytes::::minimal_parse(elf_bytes).unwrap(); + // will crash if three is no str table + let sect_head_str_tab = file.section_headers_with_strtab().unwrap(); + let sections = sect_head_str_tab.0.unwrap(); + let str_tab = sect_head_str_tab.1.unwrap(); + // for each section in elf + for sect in sections { + // if section is PROG BITS and size is none zero + if sect.sh_type == 0x1 && sect.sh_size != 0 { + let v_address = sect.sh_addr as u32; + + // if the section has flag alloc(0x2), aka lives in memory + // if the section has a size larger than zero + if sect.sh_flags & 0x2 == 0x2 && sect.sh_size != 0 { + let elf_address = sect.sh_offset; // offset into elf file where data is stored (note inside of elf Segment) + let elf_end_address = elf_address + sect.sh_size; // end address of data + let sect_data = &elf_bytes[elf_address as usize..elf_end_address as usize]; + for (i, byte) in sect_data.iter().enumerate() { + mem.data.insert(v_address + i as u32, byte.to_owned()); + } + }; + + // add section to section hashmap + mem.sections.insert( + v_address, + str_tab.get(sect.sh_name as usize).unwrap().to_string(), + ); + } + } + mem.get_symbols(&file); + Ok(mem) + } + + /// This function gets the data at that location + pub fn get_unaligned( + &self, + address: u32, + size: MemOpSize, + sign_extend: bool, + big_endian: bool, + ) -> u32 { + let size_int: usize = match size { + MemOpSize::Byte => 1, + MemOpSize::Half => 2, + MemOpSize::Word => 4, + }; + let bytes: Vec = (0..size_int) + .map(|i| *self.data.get(&(address + i as u32)).unwrap_or(&0)) + .collect(); + + match size { + MemOpSize::Byte => { + if sign_extend { + // first make byte an i8 + // then when cast to i32 to sign extends + // convert to u32 as final return + bytes[0] as i8 as i32 as u32 + } else { + bytes[0] as u32 + } + } + MemOpSize::Half => { + if sign_extend { + let int_16 = if big_endian { + i16::from_be_bytes(bytes.try_into().unwrap()) + } else { + i16::from_le_bytes(bytes.try_into().unwrap()) + }; + // as i32 to get sign extended i32, and as u32 to get return type + int_16 as i32 as u32 + } else { + let uint_16: u16 = if big_endian { + u16::from_be_bytes(bytes.try_into().unwrap()) + } else { + u16::from_le_bytes(bytes.try_into().unwrap()) + }; + uint_16 as u32 + } + } + MemOpSize::Word => + { + #[allow(clippy::collapsible_else_if)] + if sign_extend { + let int_32 = if big_endian { + i32::from_be_bytes(bytes.try_into().unwrap()) + } else { + i32::from_le_bytes(bytes.try_into().unwrap()) + }; + int_32 as u32 + } else { + if big_endian { + u32::from_be_bytes(bytes.try_into().unwrap()) + } else { + u32::from_le_bytes(bytes.try_into().unwrap()) + } + } + } + } + } + + /// This function gets the data at that location, return error on miss aligned address + pub fn get( + &self, + address: u32, + size: MemOpSize, + sign_extend: bool, + big_endian: bool, + ) -> Result { + let size_int: u32 = match size { + MemOpSize::Byte => 1, + MemOpSize::Half => 2, + MemOpSize::Word => 4, + }; + if address % size_int != 0 { + Err(()) + } else { + Ok(self.get_unaligned(address, size, sign_extend, big_endian)) + } + } + + /// Will truncate the data to the given size and write the data to memory + pub fn write( + &mut self, + address: u32, + data: u32, + size: MemOpSize, + big_endian: bool, + ) -> MemWriteReturn { + match size { + MemOpSize::Byte => { + let b = self.data.insert(address, data as u8).unwrap_or(0); + MemWriteReturn { + address, + op_size: size, + bytes: [b, 0, 0, 0], + } + } + MemOpSize::Half => { + let uint_16 = data as u16; + let bytes = if big_endian { + uint_16.to_be_bytes() + } else { + uint_16.to_le_bytes() + }; + let b0 = self.data.insert(address, bytes[0]).unwrap_or(0); + let b1 = self.data.insert(address + 1, bytes[1]).unwrap_or(0); + MemWriteReturn { + address, + op_size: size, + bytes: [b0, b1, 0, 0], + } + } + MemOpSize::Word => { + let bytes = if big_endian { + data.to_be_bytes() + } else { + data.to_le_bytes() + }; + let mut b: [u8; 4] = [0; 4]; + bytes.iter().enumerate().for_each(|(i, byte)| { + b[i] = self + .data + .insert(address + i as u32, byte.to_owned()) + .unwrap_or(0); + }); + MemWriteReturn { + address, + op_size: size, + bytes: b, + } + } + } + } + /// will return error if the data is not aligned + /// will truncate the data to size and write to memory + pub fn write_aligned( + &mut self, + address: u32, + data: u32, + size: MemOpSize, + big_endian: bool, + ) -> Result { + let size_int: u32 = match size { + MemOpSize::Byte => 1, + MemOpSize::Half => 2, + MemOpSize::Word => 4, + }; + if address % size_int != 0 { + Err(()) + } else { + Ok(self.write(address, data, size, big_endian)) + } + } + + /// Gets the elf symbol table, and set the self hashmap + fn get_symbols(&mut self, elf_file: &ElfBytes) { + if let Some((sym_table, string_table)) = elf_file.symbol_table().unwrap() { + let mut sym_hash_map: HashMap = HashMap::new(); + let mut _hash_map: HashMap = HashMap::new(); + + // for each symbol entry + for sym_entry in sym_table { + let sym_name = string_table.get(sym_entry.st_name as usize).unwrap(); + + // if the symbol type is NOTYPE, bind is LOCAL and has a string add it + if sym_entry.st_symtype() == 0x0 + && sym_entry.st_bind() == 0x0 + && !sym_name.is_empty() + { + sym_hash_map.insert(sym_entry.st_value as u32, sym_name.to_string()); + } + } + self.symbols = sym_hash_map + } + } + /// consumes undo the passed related mem write operation + pub fn revert(&mut self, op: MemWriteReturn) { + match op.op_size { + MemOpSize::Byte => { + self.data + .insert(op.address, op.bytes[0]) + .expect("tried to revert an write operation that did not happen"); + } + MemOpSize::Half => { + self.data + .insert(op.address, op.bytes[0]) + .expect("tried to revert an write operation that did not happen"); + self.data + .insert(op.address + 1, op.bytes[1]) + .expect("tried to revert an write operation that did not happen"); + } + MemOpSize::Word => { + self.data + .insert(op.address, op.bytes[0]) + .expect("tried to revert an write operation that did not happen"); + self.data + .insert(op.address + 1, op.bytes[1]) + .expect("tried to revert an write operation that did not happen"); + self.data + .insert(op.address + 2, op.bytes[2]) + .expect("tried to revert an write operation that did not happen"); + self.data + .insert(op.address + 3, op.bytes[3]) + .expect("tried to revert an write operation that did not happen"); + } + } + } + + pub fn get_symbol_table(&self) -> HashMap { + self.symbols.clone() + } + pub fn get_section_table(&self) -> HashMap { + self.sections.clone() + } +} diff --git a/mips-lib/src/components/sz_extend.rs b/mips-lib/src/components/sz_extend.rs new file mode 100644 index 00000000..a639f4da --- /dev/null +++ b/mips-lib/src/components/sz_extend.rs @@ -0,0 +1,113 @@ +// use std::fmt::Alignment; +use crate::components::cntr_unit_signals; +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; +use syncrim::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; + +pub const SIGNZEROEXTEND_SIGNAL_IN_ID: &str = "signzero_signal_in"; +pub const SIGNZEROEXTEND_CTRL_IN_ID: &str = "signzero_ctrl_in"; + +pub const SIGNZEROEXTEND_OUT_ID: &str = "sz_out"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct SignZeroExtend { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) signzero_signal_in: Input, + pub(crate) signzero_ctrl_in: Input, +} + +#[typetag::serde] +impl Component for SignZeroExtend { + fn to_(&self) { + trace!("pc+4"); + } + + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: SIGNZEROEXTEND_SIGNAL_IN_ID.to_string(), + input: self.signzero_signal_in.clone(), + }, + &InputPort { + port_id: SIGNZEROEXTEND_CTRL_IN_ID.to_string(), + input: self.signzero_ctrl_in.clone(), + }, + ], + OutputType::Combinatorial, + vec![SIGNZEROEXTEND_OUT_ID], + ), + ) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + SIGNZEROEXTEND_SIGNAL_IN_ID => self.signzero_signal_in = new_input, + SIGNZEROEXTEND_CTRL_IN_ID => self.signzero_ctrl_in = new_input, + _ => {} + } + } + + // propagate sign extension to output + // TODO: always extend to Signal size? (it should not matter and should be slightly cheaper) + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + // get input values + let mut signal: u32 = simulator + .get_input_value(&self.signzero_signal_in) + .try_into() + .unwrap(); + + let ctrl: u32 = simulator + .get_input_value(&self.signzero_ctrl_in) + .try_into() + .unwrap(); + + if ctrl == cntr_unit_signals::EXTEND_SIGNED && (signal >> 15) == 1 { + signal |= 0xffff_0000; + } + + simulator.set_out_value(&self.id, SIGNZEROEXTEND_OUT_ID, SignalValue::Data(signal)); + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl SignZeroExtend { + pub fn new( + id: &str, + pos: (f32, f32), + signzero_signal_in: Input, + signzero_ctrl_in: Input, + ) -> Self { + SignZeroExtend { + id: id.to_string(), + pos, + signzero_signal_in, + signzero_ctrl_in, + } + } + + pub fn rc_new( + id: &str, + pos: (f32, f32), + signzero_signal_in: Input, + signzero_ctrl_in: Input, + ) -> Rc { + Rc::new(SignZeroExtend::new( + id, + pos, + signzero_signal_in, + signzero_ctrl_in, + )) + } +} diff --git a/mips-lib/src/gui_egui/components/full_adder.rs b/mips-lib/src/gui_egui/components/full_adder.rs new file mode 100644 index 00000000..1e33146e --- /dev/null +++ b/mips-lib/src/gui_egui/components/full_adder.rs @@ -0,0 +1,224 @@ +use crate::components::{ + alu_op, ALU, FULL_ADD_A_IN_ID, FULL_ADD_B_IN_ID, FULL_ADD_OP_IN_ID, FULL_ADD_OUT_ID, + FULL_ADD_OVERFLOW_OUT_ID, +}; +use egui::{ + pos2, vec2, Align2, Area, Color32, Order, Pos2, Rect, Response, RichText, Shape, Stroke, + TextWrapMode, Ui, Vec2, +}; +use syncrim::common::{EguiComponent, Input, Ports, Simulator}; +use syncrim::gui_egui::component_ui::{ + drag_logic, input_change_id, input_selector, pos_drag_value, properties_window, + rect_with_hover, visualize_ports, +}; +use syncrim::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use syncrim::gui_egui::gui::EguiExtra; +use syncrim::gui_egui::helper::offset_helper; +use syncrim::signal::Id; + +#[typetag::serde] +impl EguiComponent for ALU { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 41x81 + // middle: 21x 41y (0 0) + let oh: fn((f32, f32), f32, Vec2) -> Pos2 = offset_helper; + let offset_old = offset; + let mut offset = offset; + offset.x += self.pos.0 * scale; + offset.y += self.pos.1 * scale; + let s = scale; + let o = offset; + // The shape + ui.painter().add(Shape::closed_line( + vec![ + oh((-20f32, -40f32), s, o), + oh((0f32, -40f32), s, o), + oh((20f32, -20f32), s, o), + oh((20f32, 20f32), s, o), + oh((0f32, 40f32), s, o), + oh((-20f32, 40f32), s, o), + oh((-20f32, 20f32), s, o), + oh((-10f32, 0f32), s, o), + oh((-20f32, -20f32), s, o), + ], + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + + let rect = Rect { + min: oh((-20f32, -40f32), s, o), + max: oh((20f32, 40f32), s, o), + }; + let op: String = if let Some(s) = simulator { + match TryInto::::try_into(s.get_input_value(&self.op_in)).unwrap() { + alu_op::ADD => "ADD", + alu_op::ADDU => "ADDU", + alu_op::SUB => "SUB", + alu_op::SUBU => "SUBU", + alu_op::AND => "AND", + alu_op::OR => "OR", + alu_op::XOR => "XOR", + alu_op::NOR => "NOR", + alu_op::SLT => "SLT", + alu_op::SLTU => "SLTU", + alu_op::SLL => "SLL", + alu_op::SRL => "SRL", + alu_op::SRA => "SRA", + alu_op::LUI => "LUI", + _ => "UNDEF", + } + .to_string() + } else { + "no sim".to_string() + }; + + let _area = Area::new(egui::Id::from(self.id.to_string())) + .order(Order::Middle) + .current_pos(offset.to_pos2() + Vec2::new(5.0, 0.0) * scale) + .movable(false) + .enabled(true) + .interactable(false) + .pivot(Align2::CENTER_CENTER) + .constrain(false) + .show(ui.ctx(), |ui| { + ui.set_clip_rect(clip_rect); + ui.style_mut().wrap_mode = Some(TextWrapMode::Extend); + ui.label(RichText::new(format!("ALU\n{}", op)).size(scale * 12f32)) + }); + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("Adder"); + }); + match editor_mode { + EditorMode::Simulator => (), + _ => visualize_ports(ui, self.ports_location(), offset_old, scale, clip_rect), + } + Some(vec![r]) + } + + fn render_editor( + &mut self, + ui: &mut Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + id_ports: &[(syncrim::common::Id, Ports)], + grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let r_vec = ALU::render( + self, + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ) + .unwrap(); + let resp = &r_vec[0]; + let delete = drag_logic( + ui.ctx(), + resp, + &mut self.pos, + &mut context.pos_tmp, + scale, + offset, + grid, + ); + + properties_window( + ui, + self.id.clone(), + resp, + &mut context.properties_window, + |ui| { + let mut clicked_dropdown = false; + input_change_id(ui, &mut context.id_tmp, &mut self.id, id_ports); + pos_drag_value(ui, &mut self.pos); + clicked_dropdown |= input_selector( + ui, + &mut self.a_in, + crate::components::FULL_ADD_A_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.b_in, + crate::components::FULL_ADD_B_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.op_in, + crate::components::FULL_ADD_OP_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + if id == self.a_in { + Some((Pos2::from(self.pos) + vec2(-20.0, -30.0)).into()) + } else if id == self.b_in { + Some((Pos2::from(self.pos) + vec2(-20.0, 30.0)).into()) + } else if id == Input::new(&self.id, FULL_ADD_OUT_ID) { + Some((Pos2::from(self.pos) + vec2(20.0, 0.0)).into()) + } else if id == Input::new(&self.id, FULL_ADD_OVERFLOW_OUT_ID) { + Some((Pos2::from(self.pos) + vec2(20.0, 5.0)).into()) + } else if id == self.op_in { + Some((Pos2::from(self.pos) + vec2(-10.0, -40.0)).into()) + } else { + None + } + } + + fn ports_location(&self) -> Vec<(Id, egui::Pos2)> { + let own_pos: Vec2 = self.pos.into(); + vec![ + (FULL_ADD_A_IN_ID.to_string(), pos2(-20.0, -30.0) + own_pos), + (FULL_ADD_B_IN_ID.to_string(), pos2(-20.0, 30.0) + own_pos), + (FULL_ADD_OP_IN_ID.to_string(), pos2(-10.0, -40.0) + own_pos), + (FULL_ADD_OUT_ID.to_string(), pos2(20.0, 0.0) + own_pos), + ( + FULL_ADD_OVERFLOW_OUT_ID.to_string(), + pos2(-20.0, 5.0) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/mips-lib/src/gui_egui/components/mips_branch_logic.rs b/mips-lib/src/gui_egui/components/mips_branch_logic.rs new file mode 100644 index 00000000..ee44d574 --- /dev/null +++ b/mips-lib/src/gui_egui/components/mips_branch_logic.rs @@ -0,0 +1,63 @@ +use crate::components::BranchLogic; +use egui::{Rect, Response, Ui, Vec2}; +use syncrim::common::{EguiComponent, Id, Ports, Simulator}; +use syncrim::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use syncrim::gui_egui::gui::EguiExtra; +use syncrim::gui_egui::helper::basic_component_gui; + +#[typetag::serde] +impl EguiComponent for BranchLogic { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.label("Branch Logic".to_string()); + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } +} diff --git a/mips-lib/src/gui_egui/components/mips_control_unit.rs b/mips-lib/src/gui_egui/components/mips_control_unit.rs new file mode 100644 index 00000000..7f0183aa --- /dev/null +++ b/mips-lib/src/gui_egui/components/mips_control_unit.rs @@ -0,0 +1,178 @@ +use crate::components::{cntr_field, ControlUnit}; +use egui::{pos2, Pos2, Rect, Response, Ui, Vec2}; +use syncrim::common::{EguiComponent, Id, Input, Ports, Simulator}; +use syncrim::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use syncrim::gui_egui::gui::EguiExtra; +use syncrim::gui_egui::helper::basic_component_gui; + +const WIDTH: f32 = 400.0; +const HEIGHT: f32 = 15.0; + +#[typetag::serde] +impl EguiComponent for ControlUnit { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + // size of the component + let w = WIDTH * scale; + let h: f32 = HEIGHT * scale; + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.set_height(h); + ui.set_width(w); + ui.centered_and_justified(|ui| { + ui.label("Control Unit"); + }); + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + // width 50 + // height 200 + let own_pos = Vec2::new(self.pos.0, self.pos.1); + const M: f32 = 6.0; + fn out_pos(i: u32) -> Pos2 { + pos2( + -WIDTH / 2.0 + (i as f32 + 1.0) * WIDTH / 12.0, + HEIGHT / 2.0 + M, + ) + } + vec![ + ( + crate::components::cntr_field::INSTR_IN.to_string(), + Pos2::new(-WIDTH / 2.0 - M, 0.0) + own_pos, + ), + ( + crate::components::cntr_field::EXTEND_SELECT_OUT.to_string(), + out_pos(0) + own_pos, + ), + ( + crate::components::cntr_field::BRANCH_INTERRUPT_OUT.to_string(), + out_pos(1) + own_pos, + ), + ( + crate::components::cntr_field::REG_DEST_OUT.to_string(), + out_pos(2) + own_pos, + ), + ( + crate::components::cntr_field::ALU_SRC_A_OUT.to_string(), + out_pos(3) + own_pos, + ), + ( + crate::components::cntr_field::ALU_SRC_B_OUT.to_string(), + out_pos(4) + own_pos, + ), + ( + crate::components::cntr_field::MEM_WRITE_ENABLE_OUT.to_string(), + out_pos(5) + own_pos, + ), + ( + crate::components::cntr_field::MEM_MODE_OUT.to_string(), + out_pos(6) + own_pos, + ), + ( + crate::components::cntr_field::MMU_OUT.to_string(), + out_pos(7) + own_pos, + ), + ( + crate::components::cntr_field::ALU_OP_OUT.to_string(), + out_pos(8) + own_pos, + ), + ( + crate::components::cntr_field::REG_WRITE_SRC_OUT.to_string(), + out_pos(9) + own_pos, + ), + ( + crate::components::cntr_field::REG_WRITE_ENABLE_OUT.to_string(), + out_pos(10) + own_pos, + ), + ( + crate::components::cntr_field::CP0_OUT.to_string(), + out_pos(11) + own_pos, + ), + ] + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + + if id == self.a_in { + Some(loc[0]) + } else if id == Input::new(&self.id, cntr_field::EXTEND_SELECT_OUT) { + Some(loc[1]) + } else if id == Input::new(&self.id, cntr_field::BRANCH_INTERRUPT_OUT) { + Some(loc[2]) + } else if id == Input::new(&self.id, cntr_field::REG_DEST_OUT) { + Some(loc[3]) + } else if id == Input::new(&self.id, cntr_field::ALU_SRC_A_OUT) { + Some(loc[4]) + } else if id == Input::new(&self.id, cntr_field::ALU_SRC_B_OUT) { + Some(loc[5]) + } else if id == Input::new(&self.id, cntr_field::MEM_WRITE_ENABLE_OUT) { + Some(loc[6]) + } else if id == Input::new(&self.id, cntr_field::MEM_MODE_OUT) { + Some(loc[7]) + } else if id == Input::new(&self.id, cntr_field::MMU_OUT) { + Some(loc[8]) + } else if id == Input::new(&self.id, cntr_field::ALU_OP_OUT) { + Some(loc[9]) + } else if id == Input::new(&self.id, cntr_field::REG_WRITE_SRC_OUT) { + Some(loc[10]) + } else if id == Input::new(&self.id, cntr_field::REG_WRITE_ENABLE_OUT) { + Some(loc[11]) + } else if id == Input::new(&self.id, cntr_field::CP0_OUT) { + Some(loc[12]) + } else { + None + } + } +} diff --git a/mips-lib/src/gui_egui/components/mips_dm.rs b/mips-lib/src/gui_egui/components/mips_dm.rs new file mode 100644 index 00000000..02d8c0d2 --- /dev/null +++ b/mips-lib/src/gui_egui/components/mips_dm.rs @@ -0,0 +1,194 @@ +use crate::components::{DataMem, PhysicalMem, RegFile, DATA_MEM_READ_DATA_OUT_ID}; +use egui::{pos2, Pos2, Rect, Response, RichText, Ui, Vec2}; +use syncrim::common::{EguiComponent, Id, Input, Ports, Simulator}; +use syncrim::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use syncrim::gui_egui::gui::EguiExtra; +use syncrim::gui_egui::helper::basic_component_gui; + +const WIDTH: f32 = 120.0; +const HEIGHT: f32 = 55.0; + +#[typetag::serde] +impl EguiComponent for DataMem { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + // we could avoid this if we clone self in our basic_component_ui. + // but instead we let our Closure save stuff here (let path_option, mem_view_vis) + // and apply our changes when basic_component_gui returns our borrow + // this is to avoid cloning all the fields. + // avoiding to clone the fields might be premature optimization + // as instrMem.mem is a reference count and wont actually clone the underlying btree and hashmaps + // + // we save 27 bytes of clone + // and most of that clone might even be optimized away + // yes this was premature optimization + let mut mem_view_vis: bool = self.mem_view.borrow().visible; + + let r = basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.set_height(HEIGHT * scale); + ui.set_width(WIDTH * scale); + // ui.centered_and_justified(|ui| { + ui.label(RichText::new("Data memory").size(12f32 * scale)); + ui.button( + RichText::new("load file") + .size(12f32 * scale) + .strikethrough(), + ) + .on_hover_text("Use instruction memory to load a file"); + match mem_view_vis { + false => { + if ui.button("Show mem window").clicked() { + mem_view_vis = true; + } + } + true => { + ui.toggle_value(&mut mem_view_vis, "Hide mem window"); + } + }; + // }); + }); + + if let Some(sim) = &simulator { + let v = &sim.ordered_components; + #[allow(clippy::expect_fun_call)] + let comp = v + .iter() + .find(|x| x.get_id_ports().0 == self.regfile_id) + .expect(&format!("cant find {} in simulator", self.regfile_id)); + // deref to get &dyn EguiComponent + let comp_any = (*comp).as_any(); + let regfile: &RegFile = comp_any + .downcast_ref() + .expect("can't downcast to physical memory"); + self.mem_view + .borrow_mut() + .set_reg_values(*regfile.registers.borrow()); + } + + if let Some(sim) = &simulator { + let v = &sim.ordered_components; + #[allow(clippy::expect_fun_call)] + let comp = v + .iter() + .find(|x| x.get_id_ports().0 == self.phys_mem_id) + .expect(&format!("cant find {} in simulator", self.phys_mem_id)); + // deref to get &dyn EguiComponent + let comp_any = (*comp).as_any(); + let phys_mem: &PhysicalMem = comp_any + .downcast_ref() + .expect("can't downcast to physical memory"); + // {} to drop RefMut as early as possible + { + let mut mem_view = self.mem_view.borrow_mut(); + mem_view.visible = mem_view_vis; + mem_view.render(ui.ctx(), &phys_mem.mem.borrow()); + } + } + // return response from basic component gui + r + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + // width 50 + // height 200 + let own_pos = Vec2::new(self.pos.0, self.pos.1); + const M: f32 = 6.0; + fn out_pos(i: u32) -> Pos2 { + pos2( + -WIDTH / 2.0 + (i as f32 + 1.0) * WIDTH / 4.0, + -HEIGHT / 2.0 - M, + ) + } + vec![ + ( + crate::components::DATA_MEM_WD_IN_ID.to_string(), + pos2(-WIDTH / 2.0 - M, 0.0) + own_pos, + ), + ( + crate::components::DATA_MEM_A_IN_ID.to_string(), + out_pos(0) + own_pos, + ), + ( + crate::components::DATA_MEM_WRITE_ENABLE_ID.to_string(), + out_pos(1) + own_pos, + ), + ( + crate::components::DATA_MEM_OP_IN_ID.to_string(), + out_pos(2) + own_pos, + ), + ( + crate::components::DATA_MEM_READ_DATA_OUT_ID.to_string(), + pos2(WIDTH / 2.0 + M, 0.0) + own_pos, + ), + ] + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + + if id == self.data_input { + Some(loc[0]) + } else if id == self.address_input { + Some(loc[1]) + } else if id == self.write_enable_input { + Some(loc[2]) + } else if id == self.op_input { + Some(loc[3]) + } else if id == Input::new(&self.id, DATA_MEM_READ_DATA_OUT_ID) { + Some(loc[4]) + } else { + None + } + } +} diff --git a/mips-lib/src/gui_egui/components/mips_im.rs b/mips-lib/src/gui_egui/components/mips_im.rs new file mode 100644 index 00000000..adaca143 --- /dev/null +++ b/mips-lib/src/gui_egui/components/mips_im.rs @@ -0,0 +1,178 @@ +use std::path::PathBuf; + +use crate::components::InstrMem; +use crate::components::PhysicalMem; +use crate::components::RegFile; +use crate::components::INSTR_MEM_INSTRUCTION_ID; +use crate::helpers::find_component_with_type; +use egui::pos2; +use egui::Pos2; +use egui::{Rect, Response, RichText, Ui, Vec2}; +use syncrim::common::Input; +use syncrim::common::{EguiComponent, Id, Ports, Simulator}; +use syncrim::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use syncrim::gui_egui::gui::EguiExtra; +use syncrim::gui_egui::helper::basic_component_gui; + +const WIDTH: f32 = 120.0; +const HEIGHT: f32 = 55.0; + +impl InstrMem { + fn update_mem_view_register_values(&self, sim: &Simulator) { + #[allow(clippy::expect_fun_call)] + let reg: &RegFile = find_component_with_type(sim, &self.regfile_id) + .expect(&format!("can't find {} with type Regfile", self.regfile_id)); + self.mem_view + .borrow_mut() + .set_reg_values(*reg.registers.borrow()); + } +} + +#[typetag::serde] +impl EguiComponent for InstrMem { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + // we could avoid this if we clone self in our basic_component_ui. + // but instead we let our Closure save stuff here (let path_option, mem_view_vis) + // and apply our changes when basic_component_gui returns our borrow + // this is to avoid cloning all the fields. + // avoiding to clone the fields might be premature optimization + // as instrMem.mem is a reference count and wont actually clone the underlying btree and hashmaps + // + // we save 27 bytes of clone + // and most of that clone might even be optimized away + // yes this was premature optimization + let mut path_option: Option = None; + let mut mem_view_vis: bool = self.mem_view.borrow().visible; + + let r = basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + // ui.centered_and_justified(|ui| { + ui.set_height(HEIGHT * scale); + ui.set_width(WIDTH * scale); + ui.label(RichText::new("Instruction memory").size(12f32 * scale)); + if ui.button("load file").clicked() { + path_option = rfd::FileDialog::new().pick_file(); + }; + + match mem_view_vis { + false => { + if ui.button("Show mem window").clicked() { + mem_view_vis = true; + } + } + true => { + ui.toggle_value(&mut mem_view_vis, "Hide mem window"); + } + }; + // }); + }); + + // handle mem_window and load of new file + if let Some(sim) = &simulator { + self.update_mem_view_register_values(sim); + #[allow(clippy::expect_fun_call)] + let phys_mem: &PhysicalMem = find_component_with_type(sim, &self.phys_mem_id).expect( + &format!("can't find {} with type PhysicalMem", self.regfile_id), + ); + + if let Some(path) = &path_option { + phys_mem.load_file(path); + mem_view_vis = true; + }; + + let mut mem_view = self.mem_view.borrow_mut(); + mem_view.visible = mem_view_vis; + mem_view.render(ui.ctx(), &phys_mem.mem.borrow()); + } + + // this is done after handle mem_window, because our simulator need to be returned + if let Some(sim) = simulator { + if path_option.is_some() { + sim.reset(); + } + } + + // return response from basic component gui + r + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + // width 50 + // height 200 + let own_pos = Vec2::new(self.pos.0, self.pos.1); + const M: f32 = 6.0; + vec![ + ( + crate::components::INSTR_MEM_PC_ID.to_string(), + pos2(-WIDTH / 3.0, -HEIGHT / 2.0 - M) + own_pos, + ), + ( + crate::components::DATA_MEM_A_IN_ID.to_string(), + pos2(WIDTH / 3.0, -HEIGHT / 2.0 - M) + own_pos, + ), + ] + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + + if id == self.pc { + Some(loc[0]) + } else if id == Input::new(&self.id, INSTR_MEM_INSTRUCTION_ID) { + Some(loc[1]) + } else { + None + } + } +} diff --git a/mips-lib/src/gui_egui/components/mips_instruction_splitter.rs b/mips-lib/src/gui_egui/components/mips_instruction_splitter.rs new file mode 100644 index 00000000..bc7f1c63 --- /dev/null +++ b/mips-lib/src/gui_egui/components/mips_instruction_splitter.rs @@ -0,0 +1,158 @@ +use crate::components::{ + InstrSplit, INSTRUCTION_SPLITTER_FUNCT_ID, INSTRUCTION_SPLITTER_IMMEDIATE_ID, + INSTRUCTION_SPLITTER_OP_ID, INSTRUCTION_SPLITTER_RD_ID, INSTRUCTION_SPLITTER_RS_ID, + INSTRUCTION_SPLITTER_RT_ID, INSTRUCTION_SPLITTER_SHAMT_ID, INSTRUCTION_SPLITTER_TARGET_ID, +}; +use egui::{pos2, Pos2, Rect, Response, RichText, Ui, Vec2}; +use syncrim::common::{EguiComponent, Id, Input, Ports, Simulator}; +use syncrim::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use syncrim::gui_egui::gui::EguiExtra; +use syncrim::gui_egui::helper::basic_component_gui; + +const WIDTH: f32 = 50.0; +const HEIGHT: f32 = 200.0; + +#[typetag::serde] +impl EguiComponent for InstrSplit { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + // size of the component + let w = WIDTH * scale; + let h: f32 = HEIGHT * scale; + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.set_height(h); + ui.set_width(w); + ui.centered_and_justified(|ui| { + ui.label(RichText::new("instruction\n splitter").size(12f32 * scale)); + }); + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + // width 50 + // height 200 + let own_pos = Vec2::new(self.pos.0, self.pos.1); + const M: f32 = 6.0; + fn out_pos(i: u32) -> Pos2 { + pos2( + WIDTH / 2.0 + M, + -HEIGHT / 2.0 + (i as f32 + 1.0) * HEIGHT / 9.0, + ) + } + vec![ + ( + crate::components::INSTRUCTION_SPLITTER_IN_ID.to_string(), + Pos2::new(-WIDTH / 2.0 - M, 0.0) + own_pos, + ), + ( + crate::components::INSTRUCTION_SPLITTER_SHAMT_ID.to_string(), + out_pos(0) + own_pos, + ), + ( + crate::components::INSTRUCTION_SPLITTER_OP_ID.to_string(), + out_pos(1) + own_pos, + ), + ( + crate::components::INSTRUCTION_SPLITTER_FUNCT_ID.to_string(), + out_pos(2) + own_pos, + ), + ( + crate::components::INSTRUCTION_SPLITTER_RS_ID.to_string(), + out_pos(3) + own_pos, + ), + ( + crate::components::INSTRUCTION_SPLITTER_RT_ID.to_string(), + out_pos(4) + own_pos, + ), + ( + crate::components::INSTRUCTION_SPLITTER_IMMEDIATE_ID.to_string(), + out_pos(5) + own_pos, + ), + ( + crate::components::INSTRUCTION_SPLITTER_RD_ID.to_string(), + out_pos(6) + own_pos, + ), + ( + crate::components::INSTRUCTION_SPLITTER_TARGET_ID.to_string(), + out_pos(7) + own_pos, + ), + ] + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + + if id == self.instruction_in { + Some(loc[0]) + } else if id == Input::new(&self.id, INSTRUCTION_SPLITTER_SHAMT_ID) { + Some(loc[1]) + } else if id == Input::new(&self.id, INSTRUCTION_SPLITTER_OP_ID) { + Some(loc[2]) + } else if id == Input::new(&self.id, INSTRUCTION_SPLITTER_FUNCT_ID) { + Some(loc[3]) + } else if id == Input::new(&self.id, INSTRUCTION_SPLITTER_RS_ID) { + Some(loc[4]) + } else if id == Input::new(&self.id, INSTRUCTION_SPLITTER_RT_ID) { + Some(loc[5]) + } else if id == Input::new(&self.id, INSTRUCTION_SPLITTER_IMMEDIATE_ID) { + Some(loc[6]) + } else if id == Input::new(&self.id, INSTRUCTION_SPLITTER_RD_ID) { + Some(loc[7]) + } else if id == Input::new(&self.id, INSTRUCTION_SPLITTER_TARGET_ID) { + Some(loc[8]) + } else { + None + } + } +} diff --git a/mips-lib/src/gui_egui/components/mips_jump_merge.rs b/mips-lib/src/gui_egui/components/mips_jump_merge.rs new file mode 100644 index 00000000..e4cd2bde --- /dev/null +++ b/mips-lib/src/gui_egui/components/mips_jump_merge.rs @@ -0,0 +1,63 @@ +use crate::components::JumpMerge; +use egui::{Rect, Response, RichText, Ui, Vec2}; +use syncrim::common::{EguiComponent, Id, Ports, Simulator}; +use syncrim::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use syncrim::gui_egui::gui::EguiExtra; +use syncrim::gui_egui::helper::basic_component_gui; + +#[typetag::serde] +impl EguiComponent for JumpMerge { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.label(RichText::new("Jump Merge").size(12f32 * scale)); + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } +} diff --git a/mips-lib/src/gui_egui/components/mips_reg_file.rs b/mips-lib/src/gui_egui/components/mips_reg_file.rs new file mode 100644 index 00000000..b7386909 --- /dev/null +++ b/mips-lib/src/gui_egui/components/mips_reg_file.rs @@ -0,0 +1,158 @@ +use crate::components::{reg_file_fields, RegFile, RegFormat}; +use egui::{vec2, ComboBox, Pos2, Rect, Response, RichText, ScrollArea, Ui, Vec2}; +use syncrim::common::{EguiComponent, Input, Ports, Simulator}; +use syncrim::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use syncrim::gui_egui::gui::EguiExtra; +use syncrim::gui_egui::helper::basic_component_gui; +use syncrim::signal::Id; + +const REG_NAMES: [&str; 32] = [ + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", + "s7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", + "ra", +]; + +#[typetag::serde] +impl EguiComponent for RegFile { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.set_width(120f32 * scale); + ui.set_height(250f32 * scale); + ui.label("Register File"); + + // A toggle button for showing register names + ui.toggle_value(&mut self.show_reg_names.borrow_mut(), "Show names"); + + // showing the display format of the register + let mut tmp: RegFormat = self.reg_format.borrow().clone(); + ComboBox::from_id_source(&self.id) + .selected_text(format!("{:?}", tmp)) + .show_ui(ui, |ui| { + ui.selectable_value(&mut tmp, RegFormat::Hex, "Hex"); + ui.selectable_value(&mut tmp, RegFormat::DecUnsigned, "Decimal"); + ui.selectable_value(&mut tmp, RegFormat::DecSigned, "Decimal signed"); + ui.selectable_value(&mut tmp, RegFormat::Bin, "Binary"); + ui.selectable_value(&mut tmp, RegFormat::UTF8BE, "UTF-8 big endian"); + ui.selectable_value(&mut tmp, RegFormat::UTF8LE, "UTF-8 little endian"); + }); + *self.reg_format.borrow_mut() = tmp; + + ui.separator(); + + // A scroll area with all the registers in one label + ScrollArea::vertical().show(ui, |ui| { + ui.set_width(ui.available_width()); + ui.set_height(ui.available_height()); + + // for each register format the u32 and pus that formatted sting onto + // the string representing all registers + let mut str: String = "".into(); + for (i, val) in self.registers.borrow().iter().enumerate() { + // add reg name or reg number to the formatted string + str.push_str( + match *self.show_reg_names.borrow() { + true => format!("{:<4}", REG_NAMES[i]), + false => format!("r{:<3}", i), + } + .as_str(), + ); + + // add a formatted register to the string + // TODO move to separate function + str.push_str( + match *self.reg_format.borrow() { + RegFormat::Hex => format!("{:#010x}", val), + RegFormat::DecSigned => format!("{}", (*val) as i32), + RegFormat::DecUnsigned => format!("{}", val), + RegFormat::Bin => format!("{:#034b}", val), + RegFormat::UTF8BE => String::from_utf8_lossy(&val.to_be_bytes()) + .escape_debug() + .to_string(), + RegFormat::UTF8LE => String::from_utf8_lossy(&val.to_le_bytes()) + .escape_debug() + .to_string(), + } + .as_str(), + ); + str.push('\n') + } + + // push the string as monospace to the ui + ui.label(RichText::new(str).size(12f32 * scale).monospace()) + }); + }) + } + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + // components size = (120,250) + let margin = egui::style::Spacing::default().window_margin; + + // inputs + if id == self.rs_address_in { + Some((Pos2::from(self.pos) + vec2(0f32, -125.0 - margin.top)).into()) + } else if id == self.rt_address_in { + Some((Pos2::from(self.pos) + vec2(0f32, 125.0 + margin.bottom)).into()) + } else if id == self.write_enable_in { + Some((Pos2::from(self.pos) + vec2(-60.0 - margin.left, 70.0)).into()) + } else if id == self.write_address_in { + Some((Pos2::from(self.pos) + vec2(-60.0 - margin.left, 90.0)).into()) + } else if id == self.write_data_in { + Some((Pos2::from(self.pos) + vec2(-60.0 - margin.left, 110.0)).into()) + // outputs + } else if id == Input::new(&self.id, reg_file_fields::RS_VALUE_OUT_ID) { + Some((Pos2::from(self.pos) + vec2(60.0 + margin.right, 40.0)).into()) + } else if id == Input::new(&self.id, reg_file_fields::RT_VALUE_OUT_ID) { + Some((Pos2::from(self.pos) + vec2(60.0 + margin.right, -40.0)).into()) + // no match + } else { + None + } + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/mips-lib/src/gui_egui/components/mod.rs b/mips-lib/src/gui_egui/components/mod.rs new file mode 100644 index 00000000..b28720e3 --- /dev/null +++ b/mips-lib/src/gui_egui/components/mod.rs @@ -0,0 +1,10 @@ +mod full_adder; +mod mips_branch_logic; +mod mips_control_unit; +mod mips_dm; +mod mips_im; +mod mips_instruction_splitter; +mod mips_jump_merge; +mod mips_reg_file; +mod physical_mem; +mod sz_extend; diff --git a/mips-lib/src/gui_egui/components/physical_mem.rs b/mips-lib/src/gui_egui/components/physical_mem.rs new file mode 100644 index 00000000..2763b584 --- /dev/null +++ b/mips-lib/src/gui_egui/components/physical_mem.rs @@ -0,0 +1,63 @@ +use crate::components::PhysicalMem; +use egui::{Rect, Response, Ui, Vec2}; +use syncrim::common::{EguiComponent, Id, Ports, Simulator}; +use syncrim::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use syncrim::gui_egui::gui::EguiExtra; +use syncrim::gui_egui::helper::basic_component_gui; + +#[typetag::serde] +impl EguiComponent for PhysicalMem { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.label("Physical memory"); + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } +} diff --git a/mips-lib/src/gui_egui/components/sz_extend.rs b/mips-lib/src/gui_egui/components/sz_extend.rs new file mode 100644 index 00000000..e404482d --- /dev/null +++ b/mips-lib/src/gui_egui/components/sz_extend.rs @@ -0,0 +1,125 @@ +use crate::components::{SignZeroExtend, SIGNZEROEXTEND_OUT_ID}; +use egui::{pos2, Pos2, Rect, Response, RichText, Ui, Vec2}; +use syncrim::common::{EguiComponent, Id, Input, Ports, SignalValue, Simulator}; +use syncrim::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use syncrim::gui_egui::gui::EguiExtra; +use syncrim::gui_egui::helper::basic_component_gui; + +const WIDTH: f32 = 105.0; +const HEIGHT: f32 = 30.0; + +#[typetag::serde] +impl EguiComponent for SignZeroExtend { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.set_height(HEIGHT * scale); + ui.set_width(WIDTH * scale); + match &simulator { + Some(sim) => { + ui.label(match sim.get_input_value(&self.signzero_ctrl_in) { + SignalValue::Uninitialized => { + " Sign/Zero extend:\nUninitialized cntr".to_string() + } + SignalValue::Unknown => "Sign/Zero extend:\nextendUnknown".to_string(), + SignalValue::DontCare => "Sign/Zero extend:\nDon't Care".to_string(), + SignalValue::Data(v) => match v { + 0 => "Sign/Zero extend:\nZero", + 1 => "Sign/Zero extend:\nSign", + _ => "Sign/Zero extend:\nInvalid cntr", + } + .to_string(), + }); + } + + None => { + ui.label(RichText::new("Signal Extender:\nNo Sim").size(12f32 * scale)); + } + } + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + const M: f32 = 6.0; + vec![ + ( + crate::components::SIGNZEROEXTEND_SIGNAL_IN_ID.to_string(), + pos2(-WIDTH / 2.0 - M, 0.0) + own_pos, + ), + ( + crate::components::SIGNZEROEXTEND_CTRL_IN_ID.to_string(), + pos2(0.0, -HEIGHT / 2.0 - M) + own_pos, + ), + ( + crate::components::SIGNZEROEXTEND_OUT_ID.to_string(), + pos2(WIDTH / 2.0 + M, 0.0) + own_pos, + ), + ] + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + + if id == self.signzero_signal_in { + Some(loc[0]) + } else if id == self.signzero_ctrl_in { + Some(loc[1]) + } else if id == Input::new(&self.id, SIGNZEROEXTEND_OUT_ID) { + Some(loc[2]) + } else { + None + } + } +} diff --git a/mips-lib/src/gui_egui/mips_mem_view_window.rs b/mips-lib/src/gui_egui/mips_mem_view_window.rs new file mode 100644 index 00000000..0fa4f661 --- /dev/null +++ b/mips-lib/src/gui_egui/mips_mem_view_window.rs @@ -0,0 +1,602 @@ +use egui::{RichText, ScrollArea, TextWrapMode, Ui, ViewportBuilder, ViewportId}; +use std::collections::{HashMap, HashSet}; + +use crate::components::{MemOpSize, MipsMem}; +use MIPS_disassembly; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Deserialize, Serialize)] +pub struct MemViewWindow { + pub visible: bool, + title: String, + id: String, + row_offset: u32, + max_rows: u32, + /// when set to top, the given address will be displayed at the top of the scroll area + /// center, the center of the scroll area + /// bottom, the bottom of the scroll area + #[serde(skip)] + go_to_address: GoAddress, + // determents what is used as GoAddress in the top bar + #[serde(skip, default = "MemViewWindow::go_type_def")] + go_type: GoAddress, + // used when user wants to go to another address + #[serde(skip)] + custom_address: u32, + + // used for formatting the view + big_endian: bool, + format: DataFormat, + + // used to determine if section, symbols and other markers should be shown + show_settings: ShowSettings, + + // used for show register + register_values: Option<[u32; 32]>, + + // used to show pc and jump to pc + // why not a Rc? because that would cause circular dependency and a memory leak + dynamic_symbols: HashMap, + + // Added when user clicks a row, and removed when clicked again + break_points: HashSet, +} + +#[derive(PartialEq, Clone, Default)] +enum GoAddress { + Top(u32), + Center(u32), + Bottom(u32), + #[default] + None, +} +#[derive(PartialEq, Clone, Serialize, Deserialize)] +enum DataFormat { + Hex, + HexAndMips, + Bin, + DecSigned, + DecUnsigned, + Byte, + ByteAndUtf8, +} +#[derive(Clone, Serialize, Deserialize)] +struct ShowSettings { + symbols: bool, + sections: bool, + program_counter: bool, + registers: [bool; 32], +} + +const REG_NAMES: [&str; 32] = [ + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", + "s7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", + "ra", +]; + +fn set_address(adrs: &GoAddress, new_adrs: u32) -> GoAddress { + match adrs { + GoAddress::Top(_) => GoAddress::Top(new_adrs), + GoAddress::Center(_) => GoAddress::Center(new_adrs), + GoAddress::Bottom(_) => GoAddress::Bottom(new_adrs), + GoAddress::None => GoAddress::None, + } +} + +impl MemViewWindow { + fn go_type_def() -> GoAddress { + GoAddress::Top(0) + } + // creates a new memory view window with id string and the given memory + pub fn new(id: String, title: String) -> Self { + MemViewWindow { + title, + id, + visible: false, + row_offset: 0, + max_rows: 1024, + go_to_address: GoAddress::None, + go_type: GoAddress::Top(0), + custom_address: 0, + big_endian: true, // big endian is default on mips + format: DataFormat::Hex, + show_settings: ShowSettings { + symbols: true, + sections: false, + program_counter: false, + registers: [false; 32], + }, + register_values: None, + dynamic_symbols: HashMap::new(), + break_points: HashSet::new(), + } + } + + /// set register values, allows to display where they point as well as jump to them + pub fn set_reg_values(&mut self, reg_values: [u32; 32]) { + self.register_values = Some(reg_values); + } + /// Set the extra symbols address, if no symbol exist add that symbol + pub fn set_dynamic_symbol(&mut self, symbol: &str, adrs: u32) { + match self.dynamic_symbols.get_mut(symbol) { + Some((cur_adrs, _vis)) => { + *cur_adrs = adrs; + } + None => { + self.dynamic_symbols + .insert(symbol.to_string(), (adrs, false)); + } + } + } + /// Get the address of a symbol, if no such symbol exist return None + pub fn get_dynamic_symbol(&self, symbol: &str) -> Option { + self.dynamic_symbols.get(symbol).map(|(adrs, _)| *adrs) + } + + /// This sets the format to hex + mips and if possible goes to the section .text + pub fn set_code_view(mut self, mem: Option<&MipsMem>) -> MemViewWindow { + // find if value ".text" exists, if so go to that + if let Some(m) = mem { + match m.get_section_table().iter().find_map(|(adrs, name)| { + if name == ".text" { + Some(adrs) + } else { + None + } + }) { + Some(adrs) => self.go_to_address = GoAddress::Top(*adrs), + None => self.go_to_address = GoAddress::None, + }; + } + + // set + self.format = DataFormat::HexAndMips; + self.show_settings.registers[31] = true; + // add PC_IM extra symbol and set to visible + // Decided to use PC_IM, for consistence with the pipeline model + self.dynamic_symbols.insert("PC_IM".into(), (0, true)); + self + } + + /// This sets the format to byte + utf8 and if possible goes to the section .data + pub fn set_data_view(mut self, mem: Option<&MipsMem>) -> MemViewWindow { + if let Some(m) = mem { + // find if value ".text" exists + match m.get_section_table().iter().find_map(|(adrs, name)| { + if name == ".data" { + Some(adrs) + } else { + None + } + }) { + Some(adrs) => self.go_to_address = GoAddress::Top(*adrs), + None => self.go_to_address = GoAddress::Top(0x1000), + }; + } + self.format = DataFormat::ByteAndUtf8; + self + } + + pub fn is_break_point(&self, address: &u32) -> bool { + self.break_points.contains(address) + } + + pub fn render(&mut self, ctx: &egui::Context, mem: &MipsMem) { + if !self.visible { + return; + }; + + ctx.show_viewport_immediate( + ViewportId::from_hash_of(&self.id), + ViewportBuilder::default().with_title(&self.title), + |ctx, _class| { + // If window is close is sent set visible to false + // WARNING, DON'T USE CONTEXT INSIDE READER: WILL CAUSE DEADLOCK + if ctx.input(|i| i.viewport().close_requested()) { + self.visible = false + } + + // Render top panel with go to, format and show menus + self.render_top(ctx, mem); + + egui::CentralPanel::default().show(ctx, |ui| { + let h = ui.text_style_height(&egui::TextStyle::Body); + + // if self.go_to_address is none this functions does nothing but return the passed scrollArea + let scr_area = self.scroll_to_address(ui, ScrollArea::vertical()); + // +2 for the show more buttons + scr_area.show_rows(ui, h, (self.max_rows + 2) as usize, |ui, draw_range| { + ui.style_mut().wrap_mode = Some(TextWrapMode::Truncate); + ui.set_width(ui.available_width()); + for i in draw_range.clone() { + self.render_scroll_area_item(ui, i, mem); + } + }); + }) + }, + ); + } + + fn render_top(&mut self, ctx: &egui::Context, mem: &MipsMem) { + egui::TopBottomPanel::top(self.id.clone()).show(ctx, |ui| { + egui::menu::bar(ui, |ui| { + ui.menu_button("Go to", |ui| { + // used to allow the user to select if the address should be show in the top, center, bottom of the scroll area + ui.menu_button("show address at", |ui| { + ui.selectable_value(&mut self.go_type, GoAddress::Top(0), "top"); + ui.selectable_value(&mut self.go_type, GoAddress::Center(0), "center"); + ui.selectable_value(&mut self.go_type, GoAddress::Bottom(0), "bottom"); + }); + ui.separator(); + + let mut close_menu = false; + + // add submenu with a button for each symbol, which sets self.go_to_address + ui.menu_button("symbol", |ui| { + ScrollArea::vertical().show(ui, |ui| { + let because_lifetimes_sad = mem.get_symbol_table(); + let mut symbols = because_lifetimes_sad.iter().collect::>(); + symbols.sort_by(|a, b| a.0.partial_cmp(b.0).unwrap()); + + for (key, v) in symbols { + if ui.button(format!("{} {:#0x}", v, key)).clicked() { + self.go_to_address = set_address(&self.go_type, *key); + ui.close_menu(); + close_menu = true; + } + } + }); + }); + ui.menu_button("section", |ui| { + let because_lifetimes_sad = mem.get_section_table(); + let mut sections = because_lifetimes_sad.iter().collect::>(); + sections.sort_by(|a, b| a.0.partial_cmp(b.0).unwrap()); + + for (key, v) in sections { + if ui.button(format!("{} {:#0x}", v, key)).clicked() { + self.go_to_address = set_address(&self.go_type, *key); + ui.close_menu(); + close_menu = true; + } + } + }); + + // Does any PC pointer exists, make them visible in this menu for quick access + if self.dynamic_symbols.contains_key("PC_IM") + || self.dynamic_symbols.contains_key("PC_DE") + || self.dynamic_symbols.contains_key("PC_EX") + || self.dynamic_symbols.contains_key("PC_DM") + { + ui.separator(); + + if let Some((adrs, _)) = self.dynamic_symbols.get("PC_IM") { + if ui.button(format!("PC_IM ({:#0x})", adrs)).clicked() { + self.go_to_address = set_address(&self.go_type, *adrs); + ui.close_menu(); + close_menu = true; + } + } + + if let Some((adrs, _)) = self.dynamic_symbols.get("PC_DE") { + if ui.button(format!("PC_DE ({:#0x})", adrs)).clicked() { + self.go_to_address = set_address(&self.go_type, *adrs); + ui.close_menu(); + close_menu = true; + } + } + + if let Some((adrs, _)) = self.dynamic_symbols.get("PC_EX") { + if ui.button(format!("PC_EX ({:#0x})", adrs)).clicked() { + self.go_to_address = set_address(&self.go_type, *adrs); + ui.close_menu(); + close_menu = true; + } + } + + if let Some((adrs, _)) = self.dynamic_symbols.get("PC_DM") { + if ui.button(format!("PC_DM ({:#0x})", adrs)).clicked() { + self.go_to_address = set_address(&self.go_type, *adrs); + ui.close_menu(); + close_menu = true; + } + } + } + + if let Some(reg) = &self.register_values { + ui.separator(); + + let gp = reg[28]; + if ui.button(format!("Global pointer ({:#0x})", gp)).clicked() { + self.go_to_address = set_address(&self.go_type, gp) + } + let sp = reg[29]; + if ui.button(format!("Stack pointer ({:#0x})", sp)).clicked() { + self.go_to_address = set_address(&self.go_type, sp) + } + let fp = reg[30]; + if ui.button(format!("Frame pointer ({:#0x})", fp)).clicked() { + self.go_to_address = set_address(&self.go_type, fp) + } + let ra = reg[31]; + if ui.button(format!("Return address ({:#0x})", gp)).clicked() { + self.go_to_address = set_address(&self.go_type, ra) + } + + ui.separator(); + + ui.menu_button("Other Register", |ui| { + ScrollArea::vertical().show(ui, |ui| { + for (i, name) in REG_NAMES.iter().enumerate() { + let val = reg[i]; + if ui.button(format!("${} {:#0x}", name, val)).clicked() { + self.go_to_address = set_address(&self.go_type, val); + ui.close_menu(); + close_menu = true; + } + } + }) + }); + } + + ui.separator(); + ui.menu_button("Dynamic symbols", |ui| { + let mut extra_symbols = self.dynamic_symbols.iter().collect::>(); + extra_symbols.sort_by(|a, b| a.1 .0.partial_cmp(&b.1 .0).unwrap()); + for (symbol, (adrs, _)) in extra_symbols { + if ui.button(format!("{} {:#0x}", symbol, adrs)).clicked() { + self.go_to_address = set_address(&self.go_type, *adrs); + ui.close_menu(); + close_menu = true; + } + } + }); + + ui.separator(); + ui.menu_button("Other Address", |ui| { + ui.add( + egui::DragValue::new(&mut self.custom_address) + .hexadecimal(8, true, true) + .prefix("0x"), + ); + if ui.button("Go").clicked() { + self.go_to_address = set_address(&self.go_type, self.custom_address); + close_menu = true; + } + }); + + if close_menu { + ui.close_menu(); + } + }); + ui.menu_button("Format", |ui| { + ui.selectable_value(&mut self.big_endian, false, "Little Endian"); + ui.selectable_value(&mut self.big_endian, true, "Big Endian"); + ui.separator(); + ui.selectable_value(&mut self.format, DataFormat::Hex, "Hex"); + ui.selectable_value(&mut self.format, DataFormat::HexAndMips, "Hex + mips"); + ui.selectable_value(&mut self.format, DataFormat::DecSigned, "Decimal Singed"); + ui.selectable_value( + &mut self.format, + DataFormat::DecUnsigned, + "Decimal Unsigned", + ); + ui.selectable_value(&mut self.format, DataFormat::Bin, "Binary"); + ui.selectable_value(&mut self.format, DataFormat::Byte, "Bytes"); + ui.selectable_value(&mut self.format, DataFormat::ByteAndUtf8, "Bytes + UTF8"); + }); + ui.menu_button("Show", |ui| { + ui.checkbox(&mut self.show_settings.symbols, "Symbols"); + ui.checkbox(&mut self.show_settings.sections, "Sections"); + if self.register_values.is_some() { + ui.separator(); + + ui.checkbox(&mut self.show_settings.registers[28], "Global Pointer"); + ui.checkbox(&mut self.show_settings.registers[29], "Stack Pointer"); + ui.checkbox(&mut self.show_settings.registers[30], "Frame Pointer"); + ui.checkbox(&mut self.show_settings.registers[31], "Return address"); + ui.separator(); + ui.menu_button("Other register", |ui| { + ScrollArea::vertical().show(ui, |ui| { + for (i, name) in REG_NAMES.iter().enumerate() { + ui.checkbox( + &mut self.show_settings.registers[i], + format!("${}", name), + ); + } + }); + }); + } + if !self.dynamic_symbols.is_empty() { + ui.separator(); + ui.menu_button("Dynamic symbols", |ui| { + for (sym, (_, vis)) in self.dynamic_symbols.iter_mut() { + ui.checkbox(vis, sym); + } + }); + } + }); + ui.menu_button("Break points", |ui| { + if ui.button("Clear all breakpoints").clicked() { + self.break_points.clear(); + ui.close_menu(); + } + }); + }); + }); + } + /// NOTE borrows mem + fn render_scroll_area_item(&mut self, ui: &mut Ui, scroll_area_row: usize, mem: &MipsMem) { + let more_row_text = RichText::new(format!("show {} more rows", &self.max_rows / 2)); + if scroll_area_row == 0 { + if self.row_offset == 0 { + _ = ui.small_button(more_row_text.clone().strikethrough()); + } else if ui.small_button(more_row_text).clicked() { + // 4* to get memory address + // -1 because the button takes up a row + self.go_to_address = GoAddress::Top((self.row_offset - 1) * 4); + }; + } else if scroll_area_row == self.max_rows as usize + 1 { + if ui.small_button(more_row_text).clicked() { + self.go_to_address = GoAddress::Bottom((self.row_offset + self.max_rows) * 4); + }; + } else { + // -4 is to allow for space for the show more button + let address = scroll_area_row as u32 * 4 + self.row_offset * 4 - 4; + if ui + .label( + RichText::new(format!( + "{}{:#010x}\t {:015} {}", + match self.break_points.contains(&address) { + true => "BREAK ", + false => "", + }, + address, + self.format_row(address, mem), + match self.get_symbols_etc_at_address(&address, mem) { + Some(string) => format!("\t<= {}", string), + None => String::new(), + } + )) + .monospace(), + ) + .clicked() + { + // was the row clicked if so add breakpoint to address + match self.break_points.contains(&address) { + true => self.break_points.remove(&address), + false => self.break_points.insert(address), + }; + }; + } + } + /// NOTE BORROWS MEM + fn format_row(&self, adrs: u32, mem: &MipsMem) -> String { + let data_u32 = mem.get_unaligned(adrs, MemOpSize::Word, false, self.big_endian); + let bytes = mem + .get_unaligned(adrs, MemOpSize::Word, false, true) + .to_be_bytes(); + // TODO get symbol table clones the hashmap, this is infective + let sym_tab = mem.get_symbol_table(); + match self.format { + DataFormat::Hex => { + format!("{:#010x}", data_u32) + } + DataFormat::HexAndMips => { + format!( + "{:#010x} {:015}", + data_u32, + MIPS_disassembly::get_disassembly_adv( + data_u32, + adrs, + &sym_tab, + &MIPS_disassembly::MipsDisassemblyOptions::new(true, true) + ) + ) + } + DataFormat::Bin => { + format!("{:032b}", data_u32) + } + DataFormat::DecSigned => { + format!("{}", data_u32 as i32) + } + DataFormat::DecUnsigned => { + format!("{}", data_u32) + } + DataFormat::Byte => { + format!( + "{:02x} {:02x} {:02x} {:02x}", + bytes[0], bytes[1], bytes[2], bytes[3], + ) + } + DataFormat::ByteAndUtf8 => { + format!( + "{:02x} {:02x} {:02x} {:02x} \"{}\"", + bytes[0], + bytes[1], + bytes[2], + bytes[3], + String::from_utf8_lossy(&bytes).escape_debug() + ) + } + } + } + + /// Scroll the scroll area to the address + fn scroll_to_address(&mut self, ui: &mut Ui, scroll_area: ScrollArea) -> ScrollArea { + // if we should not go to an address return + if self.go_to_address == GoAddress::None { + return scroll_area; + } + + let row = match self.go_to_address { + GoAddress::Top(adrs) => adrs / 4, + GoAddress::Center(adrs) => adrs / 4, + GoAddress::Bottom(adrs) => adrs / 4, + GoAddress::None => panic!("I Should have returned not continue"), + }; + + //make address middle of rows visible rows + let mut offset = match row.checked_sub(self.max_rows / 2) { + // don't ask why +1, don't fully know, but has to to with modulo + Some(v) => v + 1, + None => 0, + }; + + // align to half max rows + offset -= offset % (self.max_rows / 2); + // update offset + self.row_offset = offset; + + // calculate scroll amount + let row_height = ui.text_style_height(&egui::TextStyle::Body); + let y_spacing = ui.style().spacing.item_spacing.y; + let top = (row - self.row_offset + 1) as f32 * (row_height + y_spacing); + + let scroll = match self.go_to_address { + GoAddress::Top(_) => top, + GoAddress::Center(_) => top - ui.available_height() / 2.0, + GoAddress::Bottom(_) => top - ui.available_height(), + GoAddress::None => panic!("I Should have returned not continue"), + }; + + self.go_to_address = GoAddress::None; + scroll_area.vertical_scroll_offset(scroll) + } + + // TODO symbol or sect might not be word aligned, + // since we check word aligned addresses we might miss the symbol/reg ect + fn get_symbols_etc_at_address(&self, adrs: &u32, mem: &MipsMem) -> Option { + let mut out_vec: Vec<&str> = vec![]; + let sym = mem.get_symbol_table(); + let sect = mem.get_section_table(); + + for (name, _) in self + .dynamic_symbols + .iter() + .filter(|(_name, (sym_adrs, vis))| sym_adrs == adrs && *vis) + { + out_vec.push(name) + } + if self.show_settings.sections && sect.contains_key(adrs) { + out_vec.push(sect.get(adrs).unwrap()) + } + if self.show_settings.symbols && sym.contains_key(adrs) { + out_vec.push(sym.get(adrs).unwrap()) + } + + if let Some(reg) = &self.register_values { + for (i, show) in self.show_settings.registers.iter().enumerate() { + if *show && (reg[i] & !0b11) == *adrs { + out_vec.push(REG_NAMES[i]) + } + } + } + + if out_vec.is_empty() { + None + } else { + Some(out_vec.join(", ")) + } + } +} diff --git a/mips-lib/src/gui_egui/mod.rs b/mips-lib/src/gui_egui/mod.rs new file mode 100644 index 00000000..f6cc5aa2 --- /dev/null +++ b/mips-lib/src/gui_egui/mod.rs @@ -0,0 +1,2 @@ +pub mod components; +pub mod mips_mem_view_window; diff --git a/mips-lib/src/helpers.rs b/mips-lib/src/helpers.rs new file mode 100644 index 00000000..2274d87e --- /dev/null +++ b/mips-lib/src/helpers.rs @@ -0,0 +1,13 @@ +use syncrim::common::Simulator; + +pub fn find_component_with_type<'a, T: 'static>(sim: &'a Simulator, id: &str) -> Option<&'a T> { + let v = &sim.ordered_components; + let o_comp = v.iter().find(|x| x.get_id_ports().0 == id); + if let Some(comp) = o_comp { + // deref to get &dyn EguiComponent + let comp_any = (*comp).as_any(); + comp_any.downcast_ref() + } else { + None + } +} diff --git a/mips-lib/src/lib.rs b/mips-lib/src/lib.rs new file mode 100644 index 00000000..24f68941 --- /dev/null +++ b/mips-lib/src/lib.rs @@ -0,0 +1,6 @@ +pub mod components; + +#[cfg(feature = "gui-egui")] +pub mod gui_egui; + +pub mod helpers; diff --git a/mips-lib/src/main.rs b/mips-lib/src/main.rs new file mode 100644 index 00000000..5a9e9892 --- /dev/null +++ b/mips-lib/src/main.rs @@ -0,0 +1,30 @@ +use clap::Parser; +use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; +use syncrim::{common::ComponentStore, fern::fern_setup}; +/// Simple program to greet a person +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Path to the model to load on startup + #[arg(short, long, default_value = "mips_singel_cycle.json")] + model: String, +} + +fn main() { + fern_setup(); + let args = Args::parse(); + let path = PathBuf::from(args.model); + + let cs = ComponentStore::load_file(&path); + + #[cfg(feature = "gui-egui")] + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); + + #[cfg(not(any(feature = "gui-vizia", feature = "gui-egui")))] + syncrim::common::Simulator::new(cs).unwrap(); +} diff --git a/mips/Cargo.toml b/mips/Cargo.toml deleted file mode 100644 index 2b675098..00000000 --- a/mips/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "mips" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -serde = "1.0.175" -serde_derive = "1.0.175" -typetag = "0.2.12" -serde_json = "1.0.103" -fern = "0.6.2" -log = "0.4.19" -num_enum = "0.6.1" - -[dependencies.syncrim] -path = "../" -default-features = false - -[features] -default = ["gui-vizia"] - -components = ["syncrim/components"] -gui-vizia = ["syncrim/gui-vizia", "components"] -gui-egui = ["syncrim/gui-egui", "components"] - -[[test]] -name = "component_tests" -required-features = ["components"] - -[[example]] -name = "mips" -required-features = ["components"] - -[[example]] -name = "reg_file" -required-features = ["components"] diff --git a/mips/README.md b/mips/README.md deleted file mode 100644 index 3929433d..00000000 --- a/mips/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# MIPS - -MIPS specific components. diff --git a/mips/examples/mips.rs b/mips/examples/mips.rs deleted file mode 100644 index 0b33da4a..00000000 --- a/mips/examples/mips.rs +++ /dev/null @@ -1,75 +0,0 @@ -// An example MIPS model - -use mips::components::*; -use std::path::PathBuf; -use syncrim::{ - common::{ComponentStore, Input}, - components::*, - fern::fern_setup, -}; - -fn main() { - fern_setup(); - let cs = ComponentStore { - store: vec![ - Add::rc_new( - "add", - (200.0, 120.0), - Input::new("c1", "out"), - Input::new("reg", "out"), - ), - Constant::rc_new("c1", (100.0, 100.0), 4), - Register::rc_new("reg", (100.0, 140.0), Input::new("add", "out")), - Wire::rc_new( - "c1_to_add_a", - vec![(110.0, 100.0), (180.0, 100.0)], - Input::new("c1", "out"), - ), - Wire::rc_new( - "reg_to_add_b", - vec![(110.0, 140.0), (180.0, 140.0)], - Input::new("reg", "out"), - ), - Wire::rc_new( - "add_to_reg", - vec![ - (220.0, 120.0), - (260.0, 120.0), - (260.0, 60.0), - (60.0, 60.0), - (60.0, 140.0), - (90.0, 140.0), - ], - Input::new("add", "out"), - ), - Wire::rc_new( - "pc_to_down", - vec![(140.0, 140.0), (140.0, 180.0), (350.0, 180.0)], - Input::new("reg", "out"), - ), - InstrMem::rc_new( - "instr_mem", - (400.0, 150.0), - Input::new("reg", "out"), - // fake instructions just to show the relation between input address and instruction - vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - ), - Wire::rc_new( - "w8", - vec![(450.0, 120.0), (520.0, 120.0)], - Input::new("instr_mem", "out"), - ), - Probe::rc_new("p1", (280.0, 160.0), Input::new("reg", "out")), - Probe::rc_new("p2", (500.0, 100.0), Input::new("instr_mem", "out")), - ], - }; - - let path = PathBuf::from("mips.json"); - cs.save_file(&path); - - #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(cs, &path).ok(); - - #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(cs, &path); -} diff --git a/mips/examples/reg_file.rs b/mips/examples/reg_file.rs deleted file mode 100644 index e02bd63f..00000000 --- a/mips/examples/reg_file.rs +++ /dev/null @@ -1,99 +0,0 @@ -use mips::components::*; -use std::path::PathBuf; -use syncrim::{ - common::{ComponentStore, Input, SignalFmt, SignalSize}, - components::*, - fern::fern_setup, -}; - -fn main() { - fern_setup(); - let cs = ComponentStore { - store: vec![ - Constant::rc_new( - "c_read_reg_1", - (100.0, 100.0), - // Format as binary - (3, SignalFmt::Binary(5)), - ), - Constant::rc_new( - "c_write_addr", - (100.0, 160.0), - // Format as hex with padding - (4, SignalFmt::Binary(5)), - ), - Constant::rc_new("c_write_enable", (100.0, 180.0), true), - Constant::rc_new( - "c_write_data", - (100.0, 220.0), - // Format as hex with padding - (42, SignalFmt::Hex(SignalSize::_32, true)), - ), - Constant::rc_new( - "c_read_reg_2", - (100.0, 300.0), - // Format as binary - (4, SignalFmt::Binary(5)), - ), - // regfile - RegFile::rc_new( - "reg_file", - (300.0, 200.0), - 200.0, - 300.0, - // ports - Input::new("c_read_reg_1", "out"), - Input::new("c_read_reg_2", "out"), - Input::new("c_write_data", "out"), - Input::new("c_write_addr", "out"), - Input::new("c_write_enable", "out"), - ), - Probe::rc_new("p_reg_a", (500.0, 100.0), Input::new("reg_file", "reg_a")), - Probe::rc_new("p_reg_b", (500.0, 300.0), Input::new("reg_file", "reg_b")), - Wire::rc_new( - "w_read_reg_1", - vec![(180.0, 100.0), (200.0, 100.0)], - Input::new("c_read_reg_1", "out"), - ), - Wire::rc_new( - "w_read_reg_2", - vec![(180.0, 300.0), (200.0, 300.0)], - Input::new("c_read_reg_2", "out"), - ), - Wire::rc_new( - "w_write_addr", - vec![(180.0, 160.0), (200.0, 160.0)], - Input::new("c_write_addr", "out"), - ), - Wire::rc_new( - "w_write_enable", - vec![(180.0, 180.0), (200.0, 180.0)], - Input::new("c_write_enable", "out"), - ), - Wire::rc_new( - "w_write_data", - vec![(180.0, 220.0), (200.0, 220.0)], - Input::new("c_write_data", "out"), - ), - Wire::rc_new( - "w_reg_a", - vec![(400.0, 100.0), (490.0, 100.0)], - Input::new("reg_file", "reg_a"), - ), - Wire::rc_new( - "w_reg_b", - vec![(400.0, 300.0), (490.0, 300.0)], - Input::new("reg_file", "reg_b"), - ), - ], - }; - - let path = PathBuf::from("reg_file.json"); - cs.save_file(&path); - - #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(cs, &path).ok(); - - #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(cs, &path); -} diff --git a/mips/src/components/instr_mem.rs b/mips/src/components/instr_mem.rs deleted file mode 100644 index c9508c52..00000000 --- a/mips/src/components/instr_mem.rs +++ /dev/null @@ -1,80 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::rc::Rc; -use syncrim::common::{ - Component, Condition, Input, InputPort, OutputType, Ports, SignalUnsigned, SignalValue, - Simulator, -}; - -pub const INSTR_MEM_PC_ID: &str = "pc"; - -pub const INSTR_MEM_OUT_ID: &str = "out"; - -#[derive(Serialize, Deserialize)] -pub struct InstrMem { - pub(crate) id: String, - pub(crate) pos: (f32, f32), - pub(crate) pc: Input, - pub(crate) instr: Vec, -} - -use log::*; - -#[typetag::serde()] -impl Component for InstrMem { - fn to_(&self) { - trace!("InstrMem"); - } - - fn get_id_ports(&self) -> (String, Ports) { - ( - self.id.clone(), - Ports { - inputs: vec![InputPort { - port_id: INSTR_MEM_PC_ID.to_string(), - input: self.pc.clone(), - }], - out_type: OutputType::Combinatorial, - outputs: vec![INSTR_MEM_OUT_ID.to_string()], - }, - ) - } - - fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { - let instr: SignalValue = - match TryInto::::try_into(simulator.get_input_value(&self.pc)) { - Ok(pc) => { - trace!("--- evaluate instr mem: pc {:?}", pc); - // get instr at pc/4 - match self.instr.get((pc / 4) as usize) { - Some(instr) => (*instr).into(), - _ => SignalValue::Unknown, - } - } - _ => SignalValue::Unknown, - }; - - // set output - trace!("--- output {:?}", instr); - simulator.set_out_value(&self.id, INSTR_MEM_OUT_ID, instr); - Ok(()) - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl InstrMem { - pub fn new(id: &str, pos: (f32, f32), pc: Input, instr: Vec) -> Self { - InstrMem { - id: id.to_string(), - pos, - pc, - instr, - } - } - - pub fn rc_new(id: &str, pos: (f32, f32), pc: Input, instr: Vec) -> Rc { - Rc::new(InstrMem::new(id, pos, pc, instr)) - } -} diff --git a/mips/src/components/mod.rs b/mips/src/components/mod.rs deleted file mode 100644 index a5fb9f4d..00000000 --- a/mips/src/components/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod instr_mem; -mod reg_file; - -pub use instr_mem::*; -pub use reg_file::*; diff --git a/mips/src/components/reg_file.rs b/mips/src/components/reg_file.rs deleted file mode 100644 index 4a776f4c..00000000 --- a/mips/src/components/reg_file.rs +++ /dev/null @@ -1,353 +0,0 @@ -use log::*; -use num_enum::TryFromPrimitive; -use serde::{Deserialize, Serialize}; -use std::ops::{Deref, Range}; -use std::{cell::RefCell, rc::Rc}; -use syncrim::common::{ - Component, Condition, Input, InputPort, OutputType, Ports, SignalUnsigned, Simulator, -}; - -#[allow(non_camel_case_types)] -#[rustfmt::skip] -#[derive(Copy, Clone, Debug, TryFromPrimitive)] -#[repr(u8)] -pub enum Reg { - zero = 0, // Constant 0 - at = 1, // Reserved for assembler - v0 = 2, // Expression evaluation and results of function - v1 = 3, // Expression evaluation and results of function - a0 = 4, // Argument 1 - a1 = 5, // Argument 2 - a2 = 6, // Argument 3 - a3 = 7, // Argument 4 - t0 = 8, // Temporary (not preserved across calls) - t1 = 9, // Temporary (not preserved across calls) - t2 = 10, // Temporary (not preserved across calls) - t3 = 11, // Temporary (not preserved across calls) - t4 = 12, // Temporary (not preserved across calls) - t5 = 13, // Temporary (not preserved across calls) - t6 = 14, // Temporary (not preserved across calls) - t7 = 15, // Temporary (not preserved across calls) - s0 = 16, // Temporary (not preserved across calls) - s1 = 17, // Temporary (not preserved across calls) - s2 = 18, // Temporary (not preserved across calls) - s3 = 19, // Temporary (not preserved across calls) - s4 = 20, // Temporary (not preserved across calls) - s5 = 21, // Temporary (not preserved across calls) - s6 = 22, // Temporary (not preserved across calls) - s7 = 23, // Temporary (not preserved across calls) - t8 = 24, // Temporary (not preserved across calls) - t9 = 25, // Temporary (not preserved across calls) - k0 = 26, // Reserved for OS kernel - k1 = 27, // Reserved for OS kernel - gp = 28, // Pointer to global area - sp = 29, // Stack pointer - fp = 30, // Frame pointer - ra = 31, // Return address (used by function calls) -} - -pub const REG_FILE_READ_ADDR1_ID: &str = "read_addr1"; -pub const REG_FILE_READ_ADDR2_ID: &str = "read_addr2"; -pub const REG_FILE_WRITE_DATA_ID: &str = "write_data"; -pub const REG_FILE_WRITE_ADDR_ID: &str = "write_addr"; -pub const REG_FILE_WRITE_ENABLE_ID: &str = "write_enable"; - -pub const REG_FILE_REG_A_OUT: &str = "reg_a"; -pub const REG_FILE_REG_B_OUT: &str = "reg_b"; - -#[derive(Serialize, Deserialize)] -pub struct RegFile { - pub(crate) id: String, - pub(crate) pos: (f32, f32), - pub(crate) width: f32, - pub(crate) height: f32, - - // ports - pub(crate) read_addr1: Input, - pub(crate) read_addr2: Input, - pub(crate) write_data: Input, - pub(crate) write_addr: Input, - pub(crate) write_enable: Input, - - // data - pub(crate) registers: RegStore, - pub(crate) history: RegHistory, -} - -#[derive(Serialize, Deserialize, Clone)] -pub struct RegOp { - read_addr1: u8, - read_addr2: u8, - write_addr2: Option<(u8, u32)>, - old_data: Option, -} -// TODO: Perhaps we want registers to be of Signal type (containing potentially Signal::Unknown) - -#[derive(Serialize, Deserialize, Clone)] -pub struct RegHistory(RefCell>); - -impl RegHistory { - pub fn new() -> Self { - RegHistory(RefCell::new(Vec::new())) - } -} - -impl Default for RegHistory { - fn default() -> Self { - Self::new() - } -} - -#[derive(Serialize, Deserialize, Clone)] -pub struct RegStore(pub Rc>); - -impl RegStore { - pub fn new() -> Self { - RegStore(Rc::new(RefCell::new([0; 32]))) - } - - pub fn full_range() -> Range { - Range { start: 0, end: 32 } - } - - pub fn lo_range() -> Range { - Range { start: 0, end: 16 } - } - - pub fn hi_range() -> Range { - Range { start: 16, end: 32 } - } -} - -impl Default for RegStore { - fn default() -> Self { - Self::new() - } -} - -impl Deref for RegStore { - type Target = RefCell<[u32; 32]>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl RegFile { - #[allow(clippy::too_many_arguments)] - pub fn new( - id: &str, - pos: (f32, f32), - width: f32, - height: f32, - read_addr1: Input, - read_addr2: Input, - write_data: Input, - write_addr: Input, - write_enable: Input, - ) -> Self { - RegFile { - id: id.to_string(), - pos, - width, - height, - - // ports - read_addr1, - read_addr2, - write_data, - write_addr, - write_enable, - - // data - registers: RegStore::new(), - history: RegHistory::new(), - } - } - - #[allow(clippy::too_many_arguments)] - pub fn rc_new( - id: &str, - pos: (f32, f32), - width: f32, - height: f32, - read_addr1: Input, - read_addr2: Input, - write_data: Input, - write_addr: Input, - write_enable: Input, - ) -> Rc { - Rc::new(RegFile::new( - id, - pos, - width, - height, - read_addr1, - read_addr2, - write_data, - write_addr, - write_enable, - )) - } - - fn read_reg(&self, simulator: &Simulator, input: &Input) -> u32 { - let read_addr: SignalUnsigned = simulator.get_input_value(input).try_into().unwrap(); - trace!("read_addr {}", read_addr); - - // mips always reads 0; - if read_addr > 0 { - self.registers.borrow()[read_addr as usize] - } else { - 0 - } - } -} - -#[typetag::serde()] -impl Component for RegFile { - fn to_(&self) { - trace!("RegFile"); - } - - fn get_id_ports(&self) -> (String, Ports) { - ( - self.id.clone(), - Ports { - inputs: vec![ - InputPort { - port_id: REG_FILE_READ_ADDR1_ID.to_string(), - input: self.read_addr1.clone(), - }, - InputPort { - port_id: REG_FILE_READ_ADDR2_ID.to_string(), - input: self.read_addr2.clone(), - }, - InputPort { - port_id: REG_FILE_WRITE_DATA_ID.to_string(), - input: self.write_data.clone(), - }, - InputPort { - port_id: REG_FILE_WRITE_ADDR_ID.to_string(), - input: self.write_addr.clone(), - }, - InputPort { - port_id: REG_FILE_WRITE_ENABLE_ID.to_string(), - input: self.write_enable.clone(), - }, - ], - out_type: OutputType::Combinatorial, - outputs: vec!["reg_a".into(), "reg_b".into()], - }, - ) - } - - fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { - if simulator.get_input_value(&self.write_enable) == (true as SignalUnsigned).into() { - let data = simulator.get_input_value(&self.write_data); - trace!("data {:?}", data); - let write_addr: SignalUnsigned = simulator - .get_input_value(&self.write_addr) - .try_into() - .unwrap(); - trace!("write_addr {}", write_addr); - self.registers.borrow_mut()[write_addr as usize] = data.try_into().unwrap(); - } - - // read after write - let reg_value_a = self.read_reg(simulator, &self.read_addr1); - trace!("reg_value {}", reg_value_a); - simulator.set_out_value(&self.id, REG_FILE_REG_A_OUT, reg_value_a); - - let reg_value_b = self.read_reg(simulator, &self.read_addr2); - trace!("reg_value {}", reg_value_b); - simulator.set_out_value(&self.id, REG_FILE_REG_B_OUT, reg_value_b); - Ok(()) - } - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::rc::Rc; - use syncrim::{ - common::{ComponentStore, Input, Simulator}, - components::ProbeOut, - }; - - // an example of integration test for a mips specific component - #[test] - fn test_reg_file() { - let cs = ComponentStore { - store: vec![ - Rc::new(ProbeOut::new("read_reg_1")), - Rc::new(ProbeOut::new("read_reg_2")), - Rc::new(ProbeOut::new("write_data")), - Rc::new(ProbeOut::new("write_addr")), - Rc::new(ProbeOut::new("write_enable")), - // regfile - Rc::new(RegFile { - id: "reg_file".to_string(), - pos: (200.0, 150.0), - width: 100.0, - height: 150.0, - - // ports - read_addr1: Input::new("read_reg_1", "out"), - read_addr2: Input::new("read_reg_2", "out"), - write_data: Input::new("write_data", "out"), - write_addr: Input::new("write_addr", "out"), - write_enable: Input::new("write_enable", "out"), - - // data - registers: RegStore::new(), - history: RegHistory::new(), - }), - ], - }; - - let mut simulator = Simulator::new(cs).unwrap(); - - assert_eq!(simulator.cycle, 1); - - // outputs - let out_reg_1 = &Input::new("reg_file", "reg_a"); - let out_reg_2 = &Input::new("reg_file", "reg_b"); - - // reset - assert_eq!(simulator.get_input_value(out_reg_1), 0.into()); - assert_eq!(simulator.get_input_value(out_reg_2), 0.into()); - - println!(""); - simulator.set_out_value("read_reg_1", "out", 0); - simulator.set_out_value("read_reg_2", "out", 1); - simulator.set_out_value("write_data", "out", 1337); - simulator.set_out_value("write_addr", "out", 1); - simulator.set_out_value("write_enable", "out", true as SignalUnsigned); - - // test write and read to reg # 1 in same cycle - println!("sim_state {:?}", simulator.sim_state); - println!(""); - simulator.clock(); - println!("sim_state {:?}", simulator.sim_state); - assert_eq!(simulator.cycle, 2); - assert_eq!(simulator.get_input_value(out_reg_1), 0.into()); - assert_eq!(simulator.get_input_value(out_reg_2), 1337.into()); - - // test write and read to reg # 0 in same cycle (red #0 should always read 0) - println!(""); - simulator.set_out_value("read_reg_1", "out", 0); - simulator.set_out_value("read_reg_2", "out", 1); - simulator.set_out_value("write_data", "out", 42); - simulator.set_out_value("write_addr", "out", 0); - simulator.set_out_value("write_enable", "out", true as SignalUnsigned); - println!(""); - simulator.clock(); - println!("sim_state {:?}", simulator.sim_state); - assert_eq!(simulator.cycle, 3); - assert_eq!(simulator.get_input_value(out_reg_1), 0.into()); - assert_eq!(simulator.get_input_value(out_reg_2), 1337.into()); - } -} diff --git a/mips/src/gui_egui/components/instr_mem.rs b/mips/src/gui_egui/components/instr_mem.rs deleted file mode 100644 index 65d74270..00000000 --- a/mips/src/gui_egui/components/instr_mem.rs +++ /dev/null @@ -1,5 +0,0 @@ -use crate::components::InstrMem; -use syncrim::common::EguiComponent; - -#[typetag::serde] -impl EguiComponent for InstrMem {} diff --git a/mips/src/gui_egui/components/mod.rs b/mips/src/gui_egui/components/mod.rs deleted file mode 100644 index a063d2de..00000000 --- a/mips/src/gui_egui/components/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod instr_mem; -pub mod reg_file; diff --git a/mips/src/gui_egui/components/reg_file.rs b/mips/src/gui_egui/components/reg_file.rs deleted file mode 100644 index 65486a76..00000000 --- a/mips/src/gui_egui/components/reg_file.rs +++ /dev/null @@ -1,5 +0,0 @@ -use crate::components::RegFile; -use syncrim::common::EguiComponent; - -#[typetag::serde] -impl EguiComponent for RegFile {} diff --git a/mips/src/gui_egui/mod.rs b/mips/src/gui_egui/mod.rs deleted file mode 100644 index f188f2c2..00000000 --- a/mips/src/gui_egui/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod components; diff --git a/mips/src/gui_vizia/components/instr_mem.rs b/mips/src/gui_vizia/components/instr_mem.rs deleted file mode 100644 index 6810c2dc..00000000 --- a/mips/src/gui_vizia/components/instr_mem.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::components::InstrMem; -use syncrim::{ - gui_vizia::{ViziaComponent, V}, - vizia::{ - prelude::*, - vg::{Color, Paint, Path}, - }, -}; - -use log::*; - -#[typetag::serde] -impl ViziaComponent for InstrMem { - // create view - fn left_view(&self, cx: &mut Context) { - trace!("---- Create Left Instr View"); - - View::build(InstMemLeft { display: false }, cx, |cx| { - Label::new(cx, "Inst Mem Left"); - }); - } - - // create view - fn view<'a>(&self, cx: &'a mut Context) -> Handle<'a, V> { - trace!("---- Create InsrMem View"); - V::new(cx, self, |cx| { - InstMem {}.build(cx, |cx| { - Label::new(cx, "Inst Mem") - .left(Percentage(20.0)) - .top(Percentage(45.0)) - .hoverable(false); - }) - }) - .left(Pixels(self.pos.0 - 50.0)) - .top(Pixels(self.pos.1 - 100.0)) - .width(Pixels(100.0)) - .height(Pixels(200.0)) - } -} - -#[derive(Lens, Clone)] -pub struct InstMemLeft { - display: bool, -} - -impl View for InstMemLeft { - fn element(&self) -> Option<&'static str> { - Some("InstMem") - } - - // TODO, what to show here -} - -pub struct InstMem {} - -impl View for InstMem { - fn element(&self) -> Option<&'static str> { - Some("InstMem") - } - - fn draw(&self, cx: &mut DrawContext<'_>, canvas: &mut Canvas) { - let bounds = cx.bounds(); - // trace!("InstMem draw {:?}", bounds); - - let mut path = Path::new(); - let mut paint = Paint::color(Color::rgbf(0.0, 1.0, 1.0)); - paint.set_line_width(cx.logical_to_physical(1.0)); - - path.move_to(bounds.left() + 0.5, bounds.top() + 0.5); - path.line_to(bounds.right() + 0.5, bounds.top() + 0.5); - path.line_to(bounds.right() + 0.5, bounds.bottom() + 0.5); - path.line_to(bounds.left() + 0.5, bounds.bottom() + 0.5); - path.line_to(bounds.left() + 0.5, bounds.top() + 0.5); - - canvas.fill_path(&path, &paint); - } -} diff --git a/mips/src/gui_vizia/components/mod.rs b/mips/src/gui_vizia/components/mod.rs deleted file mode 100644 index a063d2de..00000000 --- a/mips/src/gui_vizia/components/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod instr_mem; -pub mod reg_file; diff --git a/mips/src/gui_vizia/components/reg_file.rs b/mips/src/gui_vizia/components/reg_file.rs deleted file mode 100644 index c123e00b..00000000 --- a/mips/src/gui_vizia/components/reg_file.rs +++ /dev/null @@ -1,129 +0,0 @@ -use crate::components::{Reg, RegFile, RegStore}; -use std::{convert::TryFrom, ops::Range}; -use syncrim::{ - gui_vizia::{ViziaComponent, V}, - vizia::prelude::*, -}; - -use log::*; - -#[derive(Lens)] -pub struct RegTabs { - list: Vec<&'static str>, -} - -impl Model for RegTabs {} - -fn range_view(cx: &mut Context, range: Range) { - for i in range { - let item = - RegFileView::registers.map(move |reg| reg.borrow().get(i as usize).copied().unwrap()); - - HStack::new(cx, |cx| { - Label::new(cx, &format!("{:?}", Reg::try_from(i).unwrap())) - .width(Pixels(50.0)) - .left(Pixels(10.0)); - Label::new(cx, item); - }) - .font_size(12.0) - .size(Auto); - } -} - -#[typetag::serde] -impl ViziaComponent for RegFile { - // create view - fn left_view(&self, cx: &mut Context) { - trace!("---- Create Left Instr View"); - - View::build( - RegFileView { - registers: self.registers.clone(), - }, - cx, - |cx| { - Label::new(cx, "Register File") - .left(Pixels(10.0)) - .top(Pixels(10.0)); - - ScrollView::new(cx, 0.0, 0.0, false, true, |cx| { - range_view(cx, RegStore::full_range()); - }) - // .size(Units::Pixels(300.0)) - .class("bg-default"); - }, - ); - } - - // create view - fn view<'a>(&self, cx: &'a mut Context) -> Handle<'a, V> { - trace!("---- Create RegFile View"); - V::new(cx, self, move |cx| { - View::build( - RegFileView { - registers: self.registers.clone(), - }, - cx, - |cx| { - RegTabs { - list: vec!["Lo", "Hi", "Recent"], - } - .build(cx); - - TabView::new(cx, RegTabs::list, |cx, item| match item.get(cx) { - "Lo" => TabPair::new( - move |cx| { - Label::new(cx, item).hoverable(false); - Element::new(cx).class("indicator"); - }, - |cx| range_view(cx, RegStore::lo_range()), - ), - - "Hi" => TabPair::new( - move |cx| { - Label::new(cx, item).hoverable(false); - Element::new(cx).class("indicator"); - }, - |cx| range_view(cx, RegStore::hi_range()), - ), - - "Recent" => TabPair::new( - move |cx| { - Label::new(cx, item).hoverable(false); - Element::new(cx).class("indicator"); - }, - |cx| { - Element::new(cx) - .size(Pixels(200.0)) - .background_color(Color::blue()); - }, - ), - - _ => unreachable!(), - }) - .width(Pixels(500.0)) - .height(Pixels(300.0)); - }, - ) - }) - .overflow(Overflow::Hidden) - .left(Pixels(self.pos.0 - self.width / 2.0)) - .top(Pixels(self.pos.1 - self.height / 2.0)) - .background_color(Color::lightgrey()) - .border_width(Pixels(1.0)) - .border_color(Color::black()) - .width(Pixels(self.width)) - .height(Pixels(self.height)) - } -} - -#[derive(Lens, Clone)] -pub struct RegFileView { - registers: RegStore, -} - -impl View for RegFileView { - fn element(&self) -> Option<&'static str> { - Some("RegView") - } -} diff --git a/mips/src/gui_vizia/mod.rs b/mips/src/gui_vizia/mod.rs deleted file mode 100644 index f188f2c2..00000000 --- a/mips/src/gui_vizia/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod components; diff --git a/mips/src/lib.rs b/mips/src/lib.rs deleted file mode 100644 index facc98ee..00000000 --- a/mips/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Library specific to the MIPS architecture -pub mod components; - -#[cfg(feature = "gui-vizia")] -pub mod gui_vizia; - -#[cfg(feature = "gui-egui")] -pub mod gui_egui; diff --git a/mips/src/main.rs b/mips/src/main.rs deleted file mode 100644 index a3e56e2e..00000000 --- a/mips/src/main.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[allow(unused_imports)] -use mips::components::*; -use std::path::PathBuf; -use syncrim::{common::ComponentStore, fern::fern_setup}; -fn main() { - fern_setup(); - - let path = PathBuf::from("mips.json"); - let cs = ComponentStore::load_file(&path); - - #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(cs, &path); - - #[cfg(not(any(feature = "gui-vizia", feature = "gui-egui")))] - let _ = syncrim::common::Simulator::new(cs); -} diff --git a/mips/tests/component_tests.rs b/mips/tests/component_tests.rs deleted file mode 100644 index b6d6328e..00000000 --- a/mips/tests/component_tests.rs +++ /dev/null @@ -1,7 +0,0 @@ -// An example of a test that should panic (fail) -// Useful to assert that illegal models and/or states does not pass unnoticed -#[test] -#[should_panic(expected = "assertion failed")] -fn should_fail() { - assert!(false) -} diff --git a/riscv/autosave.json b/riscv/autosave.json deleted file mode 100644 index e8d59b48..00000000 --- a/riscv/autosave.json +++ /dev/null @@ -1 +0,0 @@ -{"store":[{"type":"Register","id":"regfile_rd_reg","pos":[1880.0,1070.0],"r_in":{"id":"decoder","field":"decoder_rd"}},{"type":"Register","id":"rf_ra_we_reg","pos":[1880.0,870.0],"r_in":{"id":"clic","field":"rf_ra_we"}},{"type":"Register","id":"regfile_we_reg","pos":[1880.0,1010.0],"r_in":{"id":"decoder","field":"decoder_wb_write_enable"}},{"type":"Register","id":"stack_depth_reg","pos":[1880.0,810.0],"r_in":{"id":"clic","field":"stack_depth_out"}},{"type":"Register","id":"wb_reg","pos":[1880.0,530.0],"r_in":{"id":"wb_mux","field":"out"}},{"type":"Register","id":"reg","pos":[490.0,700.0],"r_in":{"id":"interrupt_mux","field":"out"}},{"type":"Constant","id":"zero_c","pos":[1350.0,430.0],"value":{"data":{"Data":0},"fmt":{"Hex":["_32",false]}}},{"type":"Wire","id":"auipc_lui_imm_op_a","pos":[[815.0,370.0],[1380.0,370.0]],"input":{"id":"zero_c","field":"out"}},{"type":"Constant","id":"pc_adder_c","pos":[520.0,640.0],"value":{"data":{"Data":4},"fmt":{"Hex":["_32",false]}}},{"type":"Wire","id":"ra_we_to_rf","pos":[[1890.0,1010.0],[1940.0,1010.0],[1940.0,270.0],[1020.0,270.0],[1020.0,440.0]],"input":{"id":"regfile_we_reg","field":"out"}},{"type":"Wire","id":"blu_int_to_rf","pos":[[1890.0,870.0],[1930.0,870.0],[1930.0,280.0],[1100.0,280.0],[1100.0,440.0]],"input":{"id":"regfile_we_reg","field":"out"}},{"type":"Wire","id":"zero_c_data","pos":[[1360.0,430.0],[1380.0,430.0]],"input":{"id":"zero_c","field":"out"}},{"type":"Wire","id":"w11","pos":[[815.0,390.0],[1380.0,390.0]],"input":{"id":"zero_c","field":"out"}},{"type":"Wire","id":"pc_c","pos":[[520.0,640.0],[540.0,640.0]],"input":{"id":"pc_adder_c","field":"out"}},{"type":"Wire","id":"w11111","pos":[[815.0,410.0],[1380.0,410.0]],"input":{"id":"zero_c","field":"out"}},{"type":"Wire","id":"stack_depth_to_rf","pos":[[1890.0,810.0],[1920.0,810.0],[1920.0,290.0],[1140.0,290.0],[1140.0,440.0]],"input":{"id":"stack_depth_reg","field":"out"}},{"type":"Wire","id":"w1","pos":[[1890.0,530.0],[1910.0,530.0],[1910.0,300.0],[1180.0,300.0],[1180.0,440.0]],"input":{"id":"wb_reg","field":"out"}},{"type":"Wire","id":"curr_pc","pos":[[520.0,700.0],[520.0,1010.0],[1340.0,1010.0],[1340.0,880.0],[1380.0,880.0]],"input":{"id":"reg","field":"out"}},{"type":"InstrMem","width":100.0,"height":100.0,"id":"instr_mem","pos":[670.0,900.0],"pc":{"id":"reg","field":"out"},"range":{"start":0,"end":8192},"le":true},{"type":"Decoder","width":30.0,"height":600.0,"id":"decoder","pos":[800.0,620.0],"instruction":{"id":"instr_mem","field":"instruction"}},{"type":"Wire","id":"decoder_shamt","pos":[[815.0,840.0],[1380.0,840.0]],"input":{"id":"decoder","field":"decoder_shamt"}},{"type":"Wire","id":"decoder_store_offset_imm","pos":[[815.0,800.0],[1380.0,800.0]],"input":{"id":"decoder","field":"decoder_store_offset_imm"}},{"type":"Wire","id":"decoder_rs1","pos":[[815.0,545.0],[955.0,545.0]],"input":{"id":"decoder","field":"decoder_rs1"}},{"type":"Wire","id":"decoder_zimm","pos":[[815.0,880.0],[1300.0,880.0],[1300.0,710.0],[1470.0,710.0]],"input":{"id":"decoder","field":"decoder_zimm"}},{"type":"Wire","id":"decoder_rs2","pos":[[815.0,710.0],[815.0,685.0],[955.0,685.0]],"input":{"id":"decoder","field":"decoder_rs2"}},{"type":"Wire","id":"decoder_imm","pos":[[815.0,820.0],[1380.0,820.0]],"input":{"id":"decoder","field":"decoder_imm"}},{"type":"Wire","id":"we","pos":[[815.0,900.0],[900.0,900.0],[900.0,1060.0],[1850.0,1060.0],[1850.0,1010.0],[1870.0,1010.0]],"input":{"id":"decoder","field":"decoder_wb_write_enable"}},{"type":"Wire","id":"decoder_rd","pos":[[815.0,910.0],[890.0,910.0],[890.0,1070.0],[1870.0,1070.0]],"input":{"id":"decoder","field":"decoder_rd"}},{"type":"RegFile","id":"reg_file","pos":[1080.0,615.0],"width":250.0,"height":350.0,"stack_depth":{"id":"stack_depth_reg","field":"out"},"clic_ra_we":{"id":"rf_ra_we_reg","field":"out"},"read_addr1":{"id":"decoder","field":"decoder_rs1"},"read_addr2":{"id":"decoder","field":"decoder_rs2"},"write_data":{"id":"wb_reg","field":"out"},"write_addr":{"id":"regfile_rd_reg","field":"out"},"write_enable":{"id":"regfile_rd_reg","field":"out"},"history":[{"stack_depth":0,"read_addr1":0,"read_addr2":0,"write_addr2":null,"old_data":null,"old_ra":null}]},{"type":"Mux","id":"alu_operand_b_mux","pos":[1400.0,830.0],"select":{"id":"decoder","field":"decoder_alu_b_mux_sel"},"m_in":[{"id":"reg_file","field":"reg_b"},{"id":"decoder","field":"decoder_store_offset_imm"},{"id":"decoder","field":"decoder_imm"},{"id":"decoder","field":"decoder_shamt"},{"id":"decoder","field":"decoder_lui_auipc_imm"},{"id":"reg","field":"out"}]},{"type":"Wire","id":"alu_operand_b","pos":[[1410.0,830.0],[1440.0,830.0],[1440.0,530.0],[1460.0,530.0]],"input":{"id":"alu_operand_b_mux","field":"out"}},{"type":"Cross","id":"c0","pos":[1220.0,780.0],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Mux","id":"alu_operand_a_mux","pos":[1400.0,410.0],"select":{"id":"decoder","field":"decoder_alu_a_mux_sel"},"m_in":[{"id":"decoder","field":"decoder_lui_auipc_imm"},{"id":"decoder","field":"decoder_jal_imm"},{"id":"decoder","field":"decoder_branch_imm"},{"id":"zero_c","field":"out"},{"id":"reg_file","field":"reg_a"}]},{"type":"Wire","id":"alu_operand_a","pos":[[1410.0,410.0],[1440.0,410.0],[1440.0,470.0],[1460.0,470.0]],"input":{"id":"alu_operand_a_mux","field":"out"}},{"type":"ALU","id":"alu","pos":[1480.0,500.0],"operator_i":{"id":"decoder","field":"decoder_alu_op"},"operand_a_i":{"id":"alu_operand_a_mux","field":"out"},"operand_b_i":{"id":"alu_operand_b_mux","field":"out"}},{"type":"Wire","id":"branch_jump_target","pos":[[1500.0,500.0],[1520.0,500.0],[1520.0,240.0],[1520.0,240.0],[320.0,240.0],[320.0,240.0],[320.0,700.0],[320.0,700.0],[360.0,700.0]],"input":{"id":"alu","field":"alu_result_o"}},{"type":"Wire","id":"w111111","pos":[[1500.0,500.0],[1500.0,500.0],[1520.0,500.0]],"input":{"id":"alu","field":"alu_result_o"}},{"type":"Wire","id":"data_mem_addr","pos":[[1520.0,500.0],[1520.0,820.0],[1540.0,820.0]],"input":{"id":"alu","field":"alu_result_o"}},{"type":"Wire","id":"alu_result","pos":[[1500.0,500.0],[1820.0,500.0]],"input":{"id":"alu","field":"alu_result_o"}},{"type":"Cross","id":"c5","pos":[1520.0,500.0],"input":{"id":"alu","field":"alu_result_o"}},{"type":"Wire","id":"reg_b","pos":[[1500.0,740.0],[1690.0,740.0],[1690.0,850.0]],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Cross","id":"c1","pos":[1220.0,450.0],"input":{"id":"reg_file","field":"reg_a"}},{"type":"Cross","id":"p11","pos":[1510.0,740.0],"input":{"id":"reg_file","field":"reg_b"}},{"type":"BranchLogic","width":60.0,"height":60.0,"id":"branch_logic","pos":[1260.0,615.0],"rs1":{"id":"reg_file","field":"reg_a"},"rs2":{"id":"reg_file","field":"reg_b"},"ctrl":{"id":"decoder","field":"decoder_branch_op"},"enable":{"id":"decoder","field":"decoder_branch_instr"}},{"type":"Wire","id":"reg_a","pos":[[1220.0,450.0],[1220.0,600.0],[1230.0,600.0]],"input":{"id":"reg_file","field":"reg_a"}},{"type":"Wire","id":"rs2_data","pos":[[1205.0,780.0],[1380.0,780.0]],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Mux","id":"csr_mux","pos":[1490.0,700.0],"select":{"id":"decoder","field":"csr_data_mux"},"m_in":[{"id":"reg_file","field":"reg_a"},{"id":"decoder","field":"decoder_zimm"}]},{"type":"Wire","id":"csr_data","pos":[[1500.0,700.0],[1700.0,700.0],[1700.0,850.0]],"input":{"id":"csr_mux","field":"out"}},{"type":"Wire","id":"reg_b_data","pos":[[1220.0,780.0],[1220.0,740.0],[1510.0,740.0],[1510.0,850.0]],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Wire","id":"reg_a_data","pos":[[1300.0,450.0],[1300.0,690.0],[1470.0,690.0]],"input":{"id":"reg_file","field":"reg_a"}},{"type":"Wire","id":"reg_a_data_o","pos":[[1205.0,450.0],[1380.0,450.0]],"input":{"id":"reg_file","field":"reg_a"}},{"type":"Wire","id":"w1111111111","pos":[[1220.0,740.0],[1220.0,630.0],[1230.0,630.0]],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Wire","id":"decoder_auipc_lui_imm_to_b","pos":[[815.0,860.0],[1380.0,860.0]],"input":{"id":"decoder","field":"decoder_lui_auipc_imm"}},{"type":"Wire","id":"insn","pos":[[700.0,850.0],[700.0,640.0],[785.0,640.0]],"input":{"id":"instr_mem","field":"instruction"}},{"type":"Wire","id":"instr_addr","pos":[[520.0,780.0],[640.0,780.0],[640.0,850.0]],"input":{"id":"reg","field":"out"}},{"type":"Wire","id":"pc","pos":[[500.0,700.0],[540.0,700.0]],"input":{"id":"reg","field":"out"}},{"type":"Cross","id":"c2","pos":[520.0,780.0],"input":{"id":"reg","field":"out"}},{"type":"Add","id":"pc_adder","pos":[560.0,670.0],"a_in":{"id":"pc_adder_c","field":"out"},"b_in":{"id":"reg","field":"out"}},{"type":"Wire","id":"pc_p4","pos":[[580.0,670.0],[580.0,670.0],[610.0,670.0],[610.0,670.0],[610.0,1000.0],[610.0,1000.0],[1810.0,1000.0],[1810.0,1000.0],[1810.0,560.0],[1810.0,560.0],[1820.0,560.0]],"input":{"id":"pc_adder","field":"out"}},{"type":"Wire","id":"pc_p_4","pos":[[580.0,670.0],[600.0,670.0],[600.0,620.0],[340.0,620.0],[340.0,680.0],[360.0,680.0]],"input":{"id":"pc_adder","field":"out"}},{"type":"Mux","id":"pc_adder_mux","pos":[380.0,690.0],"select":{"id":"branch_logic","field":"out"},"m_in":[{"id":"pc_adder","field":"out"},{"id":"alu","field":"alu_result_o"}]},{"type":"Wire","id":"new_pc","pos":[[390.0,690.0],[430.0,690.0]],"input":{"id":"pc_adder_mux","field":"out"}},{"type":"Wire","id":"w111","pos":[[410.0,690.0],[410.0,1040.0],[1710.0,1040.0],[1710.0,950.0]],"input":{"id":"pc_adder_mux","field":"out"}},{"type":"Cross","id":"p1","pos":[410.0,690.0],"input":{"id":"pc_adder_mux","field":"out"}},{"type":"Cross","id":"c_pc_out","pos":[600.0,670.0],"input":{"id":"pc_adder","field":"out"}},{"type":"CLIC","id":"clic","pos":[1710.0,900.0],"width":100.0,"height":100.0,"data":{"id":"reg_file","field":"reg_b"},"addr":{"id":"alu","field":"alu_result_o"},"data_we":{"id":"decoder","field":"data_mem_ctrl"},"data_size":{"id":"decoder","field":"data_mem_size"},"csr_data":{"id":"csr_mux","field":"out"},"csr_addr":{"id":"decoder","field":"csr_addr"},"csr_ctl":{"id":"decoder","field":"csr_ctl"},"mret":{"id":"decoder","field":"mret"},"pc":{"id":"pc_adder","field":"out"},"pc_next":{"id":"pc_adder_mux","field":"out"},"history":[{"mmio_op":[[0,0],4132],"csr_op":[[833,0]],"queue_op":[]}]},{"type":"Wire","id":"w_mem_int_addr","pos":[[1680.0,850.0],[1680.0,750.0],[1530.0,750.0],[1530.0,800.0],[1540.0,800.0]],"input":{"id":"clic","field":"mem_int_addr"}},{"type":"Mux","id":"dm_addr_mux","pos":[1560.0,810.0],"select":{"id":"clic","field":"interrupt_inv"},"m_in":[{"id":"clic","field":"mem_int_addr"},{"id":"alu","field":"alu_result_o"}]},{"type":"GPIO","height":50.0,"width":250.0,"id":"gpio","pos":[1740.0,380.0],"data_i":{"id":"reg_file","field":"reg_b"},"size_i":{"id":"decoder","field":"data_mem_size"},"we_i":{"id":"decoder","field":"data_mem_ctrl"},"addr_i":{"id":"dm_addr_mux","field":"out"},"se_i":{"id":"decoder","field":"data_se"},"csr_d":{"id":"csr_mux","field":"out"},"csr_a":{"id":"decoder","field":"csr_addr"},"csr_ctl":{"id":"decoder","field":"csr_ctl"}},{"type":"LED","height":20.0,"width":20.0,"id":"led","pos":[1820.0,320.0],"input":{"id":"gpio","field":"pin_o2"}},{"type":"Wire","id":"w1111","pos":[[1570.0,810.0],[1580.0,810.0],[1580.0,850.0]],"input":{"id":"dm_addr_mux","field":"out"}},{"type":"RVMem","id":"data_memory","pos":[1550.0,900.0],"width":100.0,"height":100.0,"big_endian":false,"data":{"id":"reg_file","field":"reg_b"},"addr":{"id":"dm_addr_mux","field":"out"},"ctrl":{"id":"decoder","field":"data_mem_ctrl"},"sext":{"id":"decoder","field":"data_se"},"size":{"id":"decoder","field":"data_mem_size"},"interrupt":{"id":"clic","field":"rf_ra_we"},"range":{"start":1342177280,"end":1342185472},"history":[{"data":null,"addr":0,"size":0}]},{"type":"Wire","id":"isr_addr","pos":[[1590.0,820.0],[1610.0,820.0]],"input":{"id":"data_memory","field":"data_o"}},{"type":"Cross","id":"p","pos":[1590.0,820.0],"input":{"id":"data_memory","field":"data_o"}},{"type":"Wire","id":"mem_data_o","pos":[[1590.0,850.0],[1590.0,550.0],[1730.0,550.0]],"input":{"id":"data_memory","field":"data_o"}},{"type":"Wire","id":"new","pos":[[1610.0,800.0],[1600.0,800.0],[1600.0,760.0],[1670.0,760.0],[1670.0,850.0]],"input":{"id":"clic","field":"mepc_out"}},{"type":"Wire","id":"clic_mmio_d_o","pos":[[1710.0,850.0],[1710.0,570.0],[1730.0,570.0]],"input":{"id":"clic","field":"mmio_data_o"}},{"type":"Mux","id":"mmio_data_mux","pos":[1750.0,560.0],"select":{"id":"data_memory","field":"mmio_mux_ctl"},"m_in":[{"id":"data_memory","field":"data_o"},{"id":"clic","field":"mmio_data_o"}]},{"type":"Mux","id":"mepc_isr_mux","pos":[1630.0,810.0],"select":{"id":"clic","field":"isr_mepc_sel"},"m_in":[{"id":"clic","field":"mepc_out"},{"id":"data_memory","field":"data_o"}]},{"type":"Wire","id":"mepc_isr_addr","pos":[[1640.0,810.0],[1650.0,810.0],[1650.0,1030.0],[420.0,1030.0],[420.0,710.0],[430.0,710.0]],"input":{"id":"mepc_isr_mux","field":"out"}},{"type":"Wire","id":"clic_csr_data_o","pos":[[1730.0,850.0],[1730.0,700.0],[1800.0,700.0],[1800.0,540.0],[1820.0,540.0]],"input":{"id":"clic","field":"csr_data_o"}},{"type":"Wire","id":"clic_interrupt","pos":[[1760.0,870.0],[1870.0,870.0]],"input":{"id":"clic","field":"rf_ra_we"}},{"type":"Mux","id":"interrupt_mux","pos":[450.0,700.0],"select":{"id":"clic","field":"interrupt"},"m_in":[{"id":"pc_adder_mux","field":"out"},{"id":"mepc_isr_mux","field":"out"}]},{"type":"Mux","id":"wb_mux","pos":[1840.0,530.0],"select":{"id":"decoder","field":"decoder_wb_mux_sel"},"m_in":[{"id":"alu","field":"alu_result_o"},{"id":"mmio_data_mux","field":"out"},{"id":"clic","field":"csr_data_o"},{"id":"pc_adder","field":"out"}]},{"type":"Wire","id":"w","pos":[[1870.0,530.0],[1850.0,530.0]],"input":{"id":"wb_mux","field":"out"}},{"type":"Wire","id":"mem_wb_data","pos":[[1760.0,560.0],[1780.0,560.0],[1780.0,520.0],[1820.0,520.0]],"input":{"id":"mmio_data_mux","field":"out"}},{"type":"Wire","id":"pc_int_mux","pos":[[460.0,700.0],[480.0,700.0]],"input":{"id":"interrupt_mux","field":"out"}},{"type":"Wire","id":"w_stack_depth","pos":[[1750.0,850.0],[1750.0,810.0],[1870.0,810.0]],"input":{"id":"clic","field":"stack_depth_out"}},{"type":"Wire","id":"w1111111","pos":[[1890.0,1070.0],[1890.0,1070.0],[1950.0,1070.0],[1950.0,1070.0],[1950.0,260.0],[1950.0,260.0],[1030.0,260.0],[1017.5,260.0],[1017.5,440.0]],"input":{"id":"regfile_rd_reg","field":"out"}}]} \ No newline at end of file diff --git a/riscv/riscv.json b/riscv/riscv.json index 8f0df927..dd00f0dd 100644 --- a/riscv/riscv.json +++ b/riscv/riscv.json @@ -1 +1,1980 @@ -{"store":[{"type":"Register","id":"rf_ra_we_reg","pos":[1880.0,870.0],"r_in":{"id":"clic","field":"rf_ra_we"}},{"type":"Register","id":"stack_depth_reg","pos":[1880.0,810.0],"r_in":{"id":"clic","field":"stack_depth_out"}},{"type":"Register","id":"reg","pos":[490.0,700.0],"r_in":{"id":"interrupt_mux","field":"out"}},{"type":"Register","id":"regfile_we_reg","pos":[1880.0,1010.0],"r_in":{"id":"decoder","field":"decoder_wb_write_enable"}},{"type":"Register","id":"wb_reg","pos":[1880.0,530.0],"r_in":{"id":"wb_mux","field":"out"}},{"type":"Register","id":"regfile_rd_reg","pos":[1880.0,1070.0],"r_in":{"id":"decoder","field":"decoder_rd"}},{"type":"Constant","id":"zero_c","pos":[1350.0,430.0],"value":{"data":{"Data":0},"fmt":{"Hex":["_32",false]}}},{"type":"Wire","id":"w11111","pos":[[815.0,410.0],[1380.0,410.0]],"input":{"id":"zero_c","field":"out"}},{"type":"Wire","id":"auipc_lui_imm_op_a","pos":[[815.0,370.0],[1380.0,370.0]],"input":{"id":"zero_c","field":"out"}},{"type":"Wire","id":"stack_depth_to_rf","pos":[[1890.0,810.0],[1920.0,810.0],[1920.0,290.0],[1140.0,290.0],[1140.0,440.0]],"input":{"id":"stack_depth_reg","field":"out"}},{"type":"InstrMem","width":100.0,"height":100.0,"id":"instr_mem","pos":[670.0,900.0],"pc":{"id":"reg","field":"out"},"range":{"start":0,"end":8192},"le":true},{"type":"Decoder","width":30.0,"height":600.0,"id":"decoder","pos":[800.0,620.0],"instruction":{"id":"instr_mem","field":"instruction"}},{"type":"Wire","id":"insn","pos":[[700.0,850.0],[700.0,640.0],[785.0,640.0]],"input":{"id":"instr_mem","field":"instruction"}},{"type":"Wire","id":"curr_pc","pos":[[520.0,700.0],[520.0,1010.0],[1340.0,1010.0],[1340.0,880.0],[1380.0,880.0]],"input":{"id":"reg","field":"out"}},{"type":"Wire","id":"w11","pos":[[815.0,390.0],[1380.0,390.0]],"input":{"id":"zero_c","field":"out"}},{"type":"Wire","id":"ra_we_to_rf","pos":[[1890.0,1010.0],[1940.0,1010.0],[1940.0,270.0],[1060.0,270.0],[1060.0,440.0]],"input":{"id":"regfile_we_reg","field":"out"}},{"type":"Wire","id":"w1","pos":[[1890.0,530.0],[1910.0,530.0],[1910.0,300.0],[1180.0,300.0],[1180.0,440.0]],"input":{"id":"wb_reg","field":"out"}},{"type":"Constant","id":"pc_adder_c","pos":[520.0,640.0],"value":{"data":{"Data":4},"fmt":{"Hex":["_32",false]}}},{"type":"Wire","id":"decoder_zimm","pos":[[815.0,880.0],[1300.0,880.0],[1300.0,710.0],[1470.0,710.0]],"input":{"id":"decoder","field":"decoder_zimm"}},{"type":"Wire","id":"pc","pos":[[500.0,700.0],[540.0,700.0]],"input":{"id":"reg","field":"out"}},{"type":"Wire","id":"decoder_rs2","pos":[[815.0,685.0],[955.0,685.0]],"input":{"id":"decoder","field":"decoder_rs2"}},{"type":"RegFile","id":"reg_file","pos":[1080.0,615.0],"width":250.0,"height":350.0,"stack_depth":{"id":"stack_depth_reg","field":"out"},"clic_ra_we":{"id":"rf_ra_we_reg","field":"out"},"read_addr1":{"id":"decoder","field":"decoder_rs1"},"read_addr2":{"id":"decoder","field":"decoder_rs2"},"write_data":{"id":"wb_reg","field":"out"},"write_addr":{"id":"regfile_rd_reg","field":"out"},"write_enable":{"id":"regfile_we_reg","field":"out"},"history":[{"stack_depth":0,"read_addr1":0,"read_addr2":0,"write_addr2":null,"old_data":null,"old_ra":null}]},{"type":"Wire","id":"reg_a","pos":[[1220.0,450.0],[1220.0,600.0],[1230.0,600.0]],"input":{"id":"reg_file","field":"reg_a"}},{"type":"Wire","id":"reg_a_data","pos":[[1300.0,450.0],[1300.0,690.0],[1470.0,690.0]],"input":{"id":"reg_file","field":"reg_a"}},{"type":"Wire","id":"reg_b_data","pos":[[1220.0,780.0],[1220.0,740.0],[1510.0,740.0],[1510.0,850.0]],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Cross","id":"c1","pos":[1220.0,450.0],"input":{"id":"reg_file","field":"reg_a"}},{"type":"Wire","id":"reg_a_data_o","pos":[[1205.0,450.0],[1380.0,450.0]],"input":{"id":"reg_file","field":"reg_a"}},{"type":"Mux","id":"alu_operand_b_mux","pos":[1400.0,830.0],"select":{"id":"decoder","field":"decoder_alu_b_mux_sel"},"m_in":[{"id":"reg_file","field":"reg_b"},{"id":"decoder","field":"decoder_store_offset_imm"},{"id":"decoder","field":"decoder_imm"},{"id":"decoder","field":"decoder_shamt"},{"id":"decoder","field":"decoder_lui_auipc_imm"},{"id":"reg","field":"out"}]},{"type":"Mux","id":"alu_operand_a_mux","pos":[1400.0,410.0],"select":{"id":"decoder","field":"decoder_alu_a_mux_sel"},"m_in":[{"id":"decoder","field":"decoder_lui_auipc_imm"},{"id":"decoder","field":"decoder_jal_imm"},{"id":"decoder","field":"decoder_branch_imm"},{"id":"zero_c","field":"out"},{"id":"reg_file","field":"reg_a"}]},{"type":"Wire","id":"alu_operand_a","pos":[[1410.0,410.0],[1440.0,410.0],[1440.0,470.0],[1460.0,470.0]],"input":{"id":"alu_operand_a_mux","field":"out"}},{"type":"Wire","id":"reg_b","pos":[[1500.0,740.0],[1690.0,740.0],[1690.0,850.0]],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Wire","id":"w1111111111","pos":[[1220.0,740.0],[1220.0,630.0],[1230.0,630.0]],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Wire","id":"decoder_imm","pos":[[815.0,820.0],[1380.0,820.0]],"input":{"id":"decoder","field":"decoder_imm"}},{"type":"BranchLogic","width":60.0,"height":60.0,"id":"branch_logic","pos":[1260.0,615.0],"rs1":{"id":"reg_file","field":"reg_a"},"rs2":{"id":"reg_file","field":"reg_b"},"ctrl":{"id":"decoder","field":"decoder_branch_op"},"enable":{"id":"decoder","field":"decoder_branch_instr"}},{"type":"Wire","id":"decoder_shamt","pos":[[815.0,840.0],[1380.0,840.0]],"input":{"id":"decoder","field":"decoder_shamt"}},{"type":"Wire","id":"decoder_rs1","pos":[[815.0,545.0],[955.0,545.0]],"input":{"id":"decoder","field":"decoder_rs1"}},{"type":"Wire","id":"pc_c","pos":[[520.0,640.0],[540.0,640.0]],"input":{"id":"pc_adder_c","field":"out"}},{"type":"Wire","id":"blu_int_to_rf","pos":[[1890.0,870.0],[1930.0,870.0],[1930.0,280.0],[1100.0,280.0],[1100.0,440.0]],"input":{"id":"regfile_we_reg","field":"out"}},{"type":"Wire","id":"zero_c_data","pos":[[1360.0,430.0],[1380.0,430.0]],"input":{"id":"zero_c","field":"out"}},{"type":"Cross","id":"c2","pos":[520.0,780.0],"input":{"id":"reg","field":"out"}},{"type":"Cross","id":"p11","pos":[1510.0,740.0],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Cross","id":"c0","pos":[1220.0,780.0],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Wire","id":"alu_operand_b","pos":[[1410.0,830.0],[1440.0,830.0],[1440.0,530.0],[1460.0,530.0]],"input":{"id":"alu_operand_b_mux","field":"out"}},{"type":"Wire","id":"decoder_auipc_lui_imm_to_b","pos":[[815.0,860.0],[1380.0,860.0]],"input":{"id":"decoder","field":"decoder_lui_auipc_imm"}},{"type":"Wire","id":"rd_to_rf","pos":[[1890.0,1070.0],[1950.0,1070.0],[1950.0,260.0],[1020.0,260.0],[1020.0,440.0]],"input":{"id":"regfile_rd_reg","field":"out"}},{"type":"Wire","id":"decoder_rd","pos":[[815.0,910.0],[890.0,910.0],[890.0,1070.0],[1870.0,1070.0]],"input":{"id":"decoder","field":"decoder_rd"}},{"type":"Wire","id":"instr_addr","pos":[[520.0,780.0],[640.0,780.0],[640.0,850.0]],"input":{"id":"reg","field":"out"}},{"type":"Add","id":"pc_adder","pos":[560.0,670.0],"a_in":{"id":"pc_adder_c","field":"out"},"b_in":{"id":"reg","field":"out"}},{"type":"Wire","id":"pc_p4","pos":[[580.0,670.0],[600.0,670.0],[600.0,1000.0],[1810.0,1000.0],[1810.0,560.0],[1820.0,560.0]],"input":{"id":"pc_adder","field":"out"}},{"type":"Cross","id":"c_pc_out","pos":[600.0,670.0],"input":{"id":"pc_adder","field":"out"}},{"type":"Wire","id":"pc_p_4","pos":[[580.0,670.0],[600.0,670.0],[600.0,620.0],[340.0,620.0],[340.0,680.0],[360.0,680.0]],"input":{"id":"pc_adder","field":"out"}},{"type":"ALU","id":"alu","pos":[1480.0,500.0],"operator_i":{"id":"decoder","field":"decoder_alu_op"},"operand_a_i":{"id":"alu_operand_a_mux","field":"out"},"operand_b_i":{"id":"alu_operand_b_mux","field":"out"}},{"type":"Wire","id":"branch_jump_target","pos":[[1500.0,500.0],[1520.0,500.0],[1520.0,240.0],[1520.0,240.0],[320.0,240.0],[320.0,240.0],[320.0,700.0],[320.0,700.0],[360.0,700.0]],"input":{"id":"alu","field":"alu_result_o"}},{"type":"Mux","id":"pc_adder_mux","pos":[380.0,690.0],"select":{"id":"branch_logic","field":"out"},"m_in":[{"id":"pc_adder","field":"out"},{"id":"alu","field":"alu_result_o"}]},{"type":"Wire","id":"new_pc","pos":[[390.0,690.0],[430.0,690.0]],"input":{"id":"pc_adder_mux","field":"out"}},{"type":"Cross","id":"p1","pos":[410.0,690.0],"input":{"id":"pc_adder_mux","field":"out"}},{"type":"Wire","id":"alu_result","pos":[[1500.0,500.0],[1820.0,500.0]],"input":{"id":"alu","field":"alu_result_o"}},{"type":"Cross","id":"c5","pos":[1520.0,500.0],"input":{"id":"alu","field":"alu_result_o"}},{"type":"Wire","id":"data_mem_addr","pos":[[1520.0,500.0],[1520.0,820.0],[1540.0,820.0]],"input":{"id":"alu","field":"alu_result_o"}},{"type":"Wire","id":"rs2_data","pos":[[1205.0,780.0],[1380.0,780.0]],"input":{"id":"reg_file","field":"reg_b"}},{"type":"Wire","id":"we","pos":[[815.0,900.0],[900.0,900.0],[900.0,1060.0],[1850.0,1060.0],[1850.0,1010.0],[1870.0,1010.0]],"input":{"id":"decoder","field":"decoder_wb_write_enable"}},{"type":"Wire","id":"w111111","pos":[[1500.0,500.0],[1500.0,500.0],[1520.0,500.0]],"input":{"id":"alu","field":"alu_result_o"}},{"type":"Mux","id":"csr_mux","pos":[1490.0,700.0],"select":{"id":"decoder","field":"csr_data_mux"},"m_in":[{"id":"reg_file","field":"reg_a"},{"id":"decoder","field":"decoder_zimm"}]},{"type":"Wire","id":"csr_data","pos":[[1500.0,700.0],[1700.0,700.0],[1700.0,850.0]],"input":{"id":"csr_mux","field":"out"}},{"type":"CLIC","id":"clic","pos":[1710.0,900.0],"width":100.0,"height":100.0,"data":{"id":"reg_file","field":"reg_b"},"addr":{"id":"alu","field":"alu_result_o"},"data_we":{"id":"decoder","field":"data_mem_ctrl"},"data_size":{"id":"decoder","field":"data_mem_size"},"csr_data":{"id":"csr_mux","field":"out"},"csr_addr":{"id":"decoder","field":"csr_addr"},"csr_ctl":{"id":"decoder","field":"csr_ctl"},"mret":{"id":"decoder","field":"mret"},"pc":{"id":"pc_adder","field":"out"},"pc_next":{"id":"pc_adder_mux","field":"out"},"history":[{"mmio_op":[[0,0],4096],"csr_op":[[833,0]],"queue_op":[]}]},{"type":"Wire","id":"clic_interrupt","pos":[[1760.0,870.0],[1870.0,870.0]],"input":{"id":"clic","field":"rf_ra_we"}},{"type":"Mux","id":"dm_addr_mux","pos":[1560.0,810.0],"select":{"id":"clic","field":"interrupt_inv"},"m_in":[{"id":"clic","field":"mem_int_addr"},{"id":"alu","field":"alu_result_o"}]},{"type":"GPIO","height":50.0,"width":250.0,"id":"gpio","pos":[1740.0,380.0],"data_i":{"id":"reg_file","field":"reg_b"},"size_i":{"id":"decoder","field":"data_mem_size"},"we_i":{"id":"decoder","field":"data_mem_ctrl"},"addr_i":{"id":"dm_addr_mux","field":"out"},"se_i":{"id":"decoder","field":"data_se"},"csr_d":{"id":"csr_mux","field":"out"},"csr_a":{"id":"decoder","field":"csr_addr"},"csr_ctl":{"id":"decoder","field":"csr_ctl"}},{"type":"LED","height":20.0,"width":20.0,"id":"led","pos":[1820.0,320.0],"input":{"id":"gpio","field":"pin_o0"}},{"type":"Wire","id":"w1111","pos":[[1570.0,810.0],[1580.0,810.0],[1580.0,850.0]],"input":{"id":"dm_addr_mux","field":"out"}},{"type":"RVMem","id":"data_memory","pos":[1550.0,900.0],"width":100.0,"height":100.0,"big_endian":false,"data":{"id":"reg_file","field":"reg_b"},"addr":{"id":"dm_addr_mux","field":"out"},"ctrl":{"id":"decoder","field":"data_mem_ctrl"},"sext":{"id":"decoder","field":"data_se"},"size":{"id":"decoder","field":"data_mem_size"},"interrupt":{"id":"clic","field":"rf_ra_we"},"range":{"start":1342177280,"end":1342185472},"history":[{"data":null,"addr":0,"size":0}]},{"type":"Mux","id":"mmio_data_mux","pos":[1750.0,560.0],"select":{"id":"data_memory","field":"mmio_mux_ctl"},"m_in":[{"id":"data_memory","field":"data_o"},{"id":"clic","field":"mmio_data_o"}]},{"type":"Mux","id":"wb_mux","pos":[1840.0,530.0],"select":{"id":"decoder","field":"decoder_wb_mux_sel"},"m_in":[{"id":"alu","field":"alu_result_o"},{"id":"mmio_data_mux","field":"out"},{"id":"clic","field":"csr_data_o"},{"id":"pc_adder","field":"out"}]},{"type":"Wire","id":"w","pos":[[1870.0,530.0],[1850.0,530.0]],"input":{"id":"wb_mux","field":"out"}},{"type":"Wire","id":"mem_wb_data","pos":[[1760.0,560.0],[1780.0,560.0],[1780.0,520.0],[1820.0,520.0]],"input":{"id":"mmio_data_mux","field":"out"}},{"type":"Wire","id":"mem_data_o","pos":[[1590.0,850.0],[1590.0,550.0],[1730.0,550.0]],"input":{"id":"data_memory","field":"data_o"}},{"type":"Cross","id":"p","pos":[1590.0,820.0],"input":{"id":"data_memory","field":"data_o"}},{"type":"Wire","id":"clic_csr_data_o","pos":[[1730.0,850.0],[1730.0,700.0],[1800.0,700.0],[1800.0,540.0],[1820.0,540.0]],"input":{"id":"clic","field":"csr_data_o"}},{"type":"Wire","id":"w_mem_int_addr","pos":[[1680.0,850.0],[1680.0,750.0],[1530.0,750.0],[1530.0,800.0],[1540.0,800.0]],"input":{"id":"clic","field":"mem_int_addr"}},{"type":"Wire","id":"clic_mmio_d_o","pos":[[1710.0,850.0],[1710.0,570.0],[1730.0,570.0]],"input":{"id":"clic","field":"mmio_data_o"}},{"type":"Wire","id":"mepc_isr_addr","pos":[[1670.0,850.0],[1670.0,810.0],[1650.0,810.0],[1650.0,1030.0],[420.0,1030.0],[420.0,710.0],[430.0,710.0]],"input":{"id":"clic","field":"mepc_out"}},{"type":"Mux","id":"interrupt_mux","pos":[450.0,700.0],"select":{"id":"clic","field":"interrupt"},"m_in":[{"id":"pc_adder_mux","field":"out"},{"id":"clic","field":"mepc_out"}]},{"type":"Wire","id":"pc_int_mux","pos":[[460.0,700.0],[480.0,700.0]],"input":{"id":"interrupt_mux","field":"out"}},{"type":"Wire","id":"w111","pos":[[410.0,690.0],[410.0,1040.0],[1710.0,1040.0],[1710.0,950.0]],"input":{"id":"pc_adder_mux","field":"out"}},{"type":"Wire","id":"decoder_store_offset_imm","pos":[[815.0,800.0],[1380.0,800.0]],"input":{"id":"decoder","field":"decoder_store_offset_imm"}},{"type":"Wire","id":"w_stack_depth","pos":[[1750.0,850.0],[1750.0,810.0],[1870.0,810.0]],"input":{"id":"clic","field":"stack_depth_out"}}]} \ No newline at end of file +{ + "store": [ + { + "type": "Register", + "id": "rf_ra_we_reg", + "pos": [ + 1880.0, + 870.0 + ], + "r_in": { + "id": "clic", + "field": "rf_ra_we" + } + }, + { + "type": "Register", + "id": "stack_depth_reg", + "pos": [ + 1880.0, + 810.0 + ], + "r_in": { + "id": "clic", + "field": "stack_depth_out" + } + }, + { + "type": "Register", + "id": "reg", + "pos": [ + 490.0, + 700.0 + ], + "r_in": { + "id": "interrupt_mux", + "field": "mux_out" + } + }, + { + "type": "Register", + "id": "regfile_we_reg", + "pos": [ + 1880.0, + 1010.0 + ], + "r_in": { + "id": "decoder", + "field": "decoder_wb_write_enable" + } + }, + { + "type": "Register", + "id": "wb_reg", + "pos": [ + 1880.0, + 530.0 + ], + "r_in": { + "id": "wb_mux", + "field": "mux_out" + } + }, + { + "type": "Register", + "id": "regfile_rd_reg", + "pos": [ + 1880.0, + 1070.0 + ], + "r_in": { + "id": "decoder", + "field": "decoder_rd" + } + }, + { + "type": "Constant", + "id": "zero_c", + "pos": [ + 1350.0, + 430.0 + ], + "value": { + "data": { + "Data": 0 + }, + "fmt": { + "Hex": [ + "_32", + false + ] + } + } + }, + { + "type": "Wire", + "id": "w11111", + "pos": [ + [ + 815.0, + 410.0 + ], + [ + 1380.0, + 410.0 + ] + ], + "input": { + "id": "zero_c", + "field": "constant_out" + } + }, + { + "type": "Wire", + "id": "auipc_lui_imm_op_a", + "pos": [ + [ + 815.0, + 370.0 + ], + [ + 1380.0, + 370.0 + ] + ], + "input": { + "id": "zero_c", + "field": "constant_out" + } + }, + { + "type": "Wire", + "id": "stack_depth_to_rf", + "pos": [ + [ + 1890.0, + 810.0 + ], + [ + 1920.0, + 810.0 + ], + [ + 1920.0, + 290.0 + ], + [ + 1140.0, + 290.0 + ], + [ + 1140.0, + 440.0 + ] + ], + "input": { + "id": "stack_depth_reg", + "field": "register_out" + } + }, + { + "type": "InstrMem", + "width": 100.0, + "height": 100.0, + "id": "instr_mem", + "pos": [ + 670.0, + 900.0 + ], + "pc": { + "id": "reg", + "field": "register_out" + }, + "range": { + "start": 0, + "end": 8192 + }, + "le": true + }, + { + "type": "Decoder", + "width": 30.0, + "height": 600.0, + "id": "decoder", + "pos": [ + 800.0, + 620.0 + ], + "instruction": { + "id": "instr_mem", + "field": "instruction" + } + }, + { + "type": "Wire", + "id": "insn", + "pos": [ + [ + 700.0, + 850.0 + ], + [ + 700.0, + 640.0 + ], + [ + 785.0, + 640.0 + ] + ], + "input": { + "id": "instr_mem", + "field": "instruction" + } + }, + { + "type": "Wire", + "id": "curr_pc", + "pos": [ + [ + 520.0, + 700.0 + ], + [ + 520.0, + 1010.0 + ], + [ + 1340.0, + 1010.0 + ], + [ + 1340.0, + 880.0 + ], + [ + 1380.0, + 880.0 + ] + ], + "input": { + "id": "reg", + "field": "register_out" + } + }, + { + "type": "Wire", + "id": "w11", + "pos": [ + [ + 815.0, + 390.0 + ], + [ + 1380.0, + 390.0 + ] + ], + "input": { + "id": "zero_c", + "field": "constant_out" + } + }, + { + "type": "Wire", + "id": "ra_we_to_rf", + "pos": [ + [ + 1890.0, + 1010.0 + ], + [ + 1940.0, + 1010.0 + ], + [ + 1940.0, + 270.0 + ], + [ + 1060.0, + 270.0 + ], + [ + 1060.0, + 440.0 + ] + ], + "input": { + "id": "regfile_we_reg", + "field": "register_out" + } + }, + { + "type": "Wire", + "id": "w1", + "pos": [ + [ + 1890.0, + 530.0 + ], + [ + 1910.0, + 530.0 + ], + [ + 1910.0, + 300.0 + ], + [ + 1180.0, + 300.0 + ], + [ + 1180.0, + 440.0 + ] + ], + "input": { + "id": "wb_reg", + "field": "register_out" + } + }, + { + "type": "Constant", + "id": "pc_adder_c", + "pos": [ + 520.0, + 640.0 + ], + "value": { + "data": { + "Data": 4 + }, + "fmt": { + "Hex": [ + "_32", + false + ] + } + } + }, + { + "type": "Wire", + "id": "decoder_zimm", + "pos": [ + [ + 815.0, + 880.0 + ], + [ + 1300.0, + 880.0 + ], + [ + 1300.0, + 710.0 + ], + [ + 1470.0, + 710.0 + ] + ], + "input": { + "id": "decoder", + "field": "decoder_zimm" + } + }, + { + "type": "Wire", + "id": "pc", + "pos": [ + [ + 500.0, + 700.0 + ], + [ + 540.0, + 700.0 + ] + ], + "input": { + "id": "reg", + "field": "register_out" + } + }, + { + "type": "Wire", + "id": "decoder_rs2", + "pos": [ + [ + 815.0, + 685.0 + ], + [ + 955.0, + 685.0 + ] + ], + "input": { + "id": "decoder", + "field": "decoder_rs2" + } + }, + { + "type": "RegFile", + "id": "reg_file", + "pos": [ + 1080.0, + 615.0 + ], + "width": 250.0, + "height": 350.0, + "stack_depth": { + "id": "stack_depth_reg", + "field": "register_out" + }, + "clic_ra_we": { + "id": "rf_ra_we_reg", + "field": "register_out" + }, + "read_addr1": { + "id": "decoder", + "field": "decoder_rs1" + }, + "read_addr2": { + "id": "decoder", + "field": "decoder_rs2" + }, + "write_data": { + "id": "wb_reg", + "field": "register_out" + }, + "write_addr": { + "id": "regfile_rd_reg", + "field": "register_out" + }, + "write_enable": { + "id": "regfile_we_reg", + "field": "register_out" + }, + "history": [ + { + "stack_depth": 0, + "read_addr1": 0, + "read_addr2": 0, + "write_addr2": null, + "old_data": null, + "old_ra": null + } + ] + }, + { + "type": "Wire", + "id": "reg_a", + "pos": [ + [ + 1220.0, + 450.0 + ], + [ + 1220.0, + 600.0 + ], + [ + 1230.0, + 600.0 + ] + ], + "input": { + "id": "reg_file", + "field": "reg_a" + } + }, + { + "type": "Wire", + "id": "reg_a_data", + "pos": [ + [ + 1300.0, + 450.0 + ], + [ + 1300.0, + 690.0 + ], + [ + 1470.0, + 690.0 + ] + ], + "input": { + "id": "reg_file", + "field": "reg_a" + } + }, + { + "type": "Wire", + "id": "reg_b_data", + "pos": [ + [ + 1220.0, + 780.0 + ], + [ + 1220.0, + 740.0 + ], + [ + 1510.0, + 740.0 + ], + [ + 1510.0, + 850.0 + ] + ], + "input": { + "id": "reg_file", + "field": "reg_b" + } + }, + { + "type": "Cross", + "id": "c1", + "pos": [ + 1220.0, + 450.0 + ], + "input": { + "id": "reg_file", + "field": "reg_a" + } + }, + { + "type": "Wire", + "id": "reg_a_data_o", + "pos": [ + [ + 1205.0, + 450.0 + ], + [ + 1380.0, + 450.0 + ] + ], + "input": { + "id": "reg_file", + "field": "reg_a" + } + }, + { + "type": "Mux", + "id": "alu_operand_b_mux", + "pos": [ + 1400.0, + 830.0 + ], + "select": { + "id": "decoder", + "field": "decoder_alu_b_mux_sel" + }, + "scale": 1.0, + "m_in": [ + { + "id": "reg_file", + "field": "reg_b" + }, + { + "id": "decoder", + "field": "decoder_store_offset_imm" + }, + { + "id": "decoder", + "field": "decoder_imm" + }, + { + "id": "decoder", + "field": "decoder_shamt" + }, + { + "id": "decoder", + "field": "decoder_lui_auipc_imm" + }, + { + "id": "reg", + "field": "register_out" + } + ] + }, + { + "type": "Mux", + "id": "alu_operand_a_mux", + "pos": [ + 1400.0, + 410.0 + ], + "select": { + "id": "decoder", + "field": "decoder_alu_a_mux_sel" + }, + "scale": 1.0, + "m_in": [ + { + "id": "decoder", + "field": "decoder_lui_auipc_imm" + }, + { + "id": "decoder", + "field": "decoder_jal_imm" + }, + { + "id": "decoder", + "field": "decoder_branch_imm" + }, + { + "id": "zero_c", + "field": "constant_out" + }, + { + "id": "reg_file", + "field": "reg_a" + } + ] + }, + { + "type": "Wire", + "id": "alu_operand_a", + "pos": [ + [ + 1410.0, + 410.0 + ], + [ + 1440.0, + 410.0 + ], + [ + 1440.0, + 470.0 + ], + [ + 1460.0, + 470.0 + ] + ], + "input": { + "id": "alu_operand_a_mux", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "reg_b", + "pos": [ + [ + 1500.0, + 740.0 + ], + [ + 1690.0, + 740.0 + ], + [ + 1690.0, + 850.0 + ] + ], + "input": { + "id": "reg_file", + "field": "reg_b" + } + }, + { + "type": "Wire", + "id": "w1111111111", + "pos": [ + [ + 1220.0, + 740.0 + ], + [ + 1220.0, + 630.0 + ], + [ + 1230.0, + 630.0 + ] + ], + "input": { + "id": "reg_file", + "field": "reg_b" + } + }, + { + "type": "Wire", + "id": "decoder_imm", + "pos": [ + [ + 815.0, + 820.0 + ], + [ + 1380.0, + 820.0 + ] + ], + "input": { + "id": "decoder", + "field": "decoder_imm" + } + }, + { + "type": "BranchLogic", + "width": 60.0, + "height": 60.0, + "id": "branch_logic", + "pos": [ + 1260.0, + 615.0 + ], + "rs1": { + "id": "reg_file", + "field": "reg_a" + }, + "rs2": { + "id": "reg_file", + "field": "reg_b" + }, + "ctrl": { + "id": "decoder", + "field": "decoder_branch_op" + }, + "enable": { + "id": "decoder", + "field": "decoder_branch_instr" + } + }, + { + "type": "Wire", + "id": "decoder_shamt", + "pos": [ + [ + 815.0, + 840.0 + ], + [ + 1380.0, + 840.0 + ] + ], + "input": { + "id": "decoder", + "field": "decoder_shamt" + } + }, + { + "type": "Wire", + "id": "decoder_rs1", + "pos": [ + [ + 815.0, + 545.0 + ], + [ + 955.0, + 545.0 + ] + ], + "input": { + "id": "decoder", + "field": "decoder_rs1" + } + }, + { + "type": "Wire", + "id": "pc_c", + "pos": [ + [ + 520.0, + 640.0 + ], + [ + 540.0, + 640.0 + ] + ], + "input": { + "id": "pc_adder_c", + "field": "constant_out" + } + }, + { + "type": "Wire", + "id": "blu_int_to_rf", + "pos": [ + [ + 1890.0, + 870.0 + ], + [ + 1930.0, + 870.0 + ], + [ + 1930.0, + 280.0 + ], + [ + 1100.0, + 280.0 + ], + [ + 1100.0, + 440.0 + ] + ], + "input": { + "id": "regfile_we_reg", + "field": "register_out" + } + }, + { + "type": "Wire", + "id": "zero_c_data", + "pos": [ + [ + 1360.0, + 430.0 + ], + [ + 1380.0, + 430.0 + ] + ], + "input": { + "id": "zero_c", + "field": "constant_out" + } + }, + { + "type": "Cross", + "id": "c2", + "pos": [ + 520.0, + 780.0 + ], + "input": { + "id": "reg", + "field": "register_out" + } + }, + { + "type": "Cross", + "id": "p11", + "pos": [ + 1510.0, + 740.0 + ], + "input": { + "id": "reg_file", + "field": "reg_b" + } + }, + { + "type": "Cross", + "id": "c0", + "pos": [ + 1220.0, + 780.0 + ], + "input": { + "id": "reg_file", + "field": "reg_b" + } + }, + { + "type": "Wire", + "id": "alu_operand_b", + "pos": [ + [ + 1410.0, + 830.0 + ], + [ + 1440.0, + 830.0 + ], + [ + 1440.0, + 530.0 + ], + [ + 1460.0, + 530.0 + ] + ], + "input": { + "id": "alu_operand_b_mux", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "decoder_auipc_lui_imm_to_b", + "pos": [ + [ + 815.0, + 860.0 + ], + [ + 1380.0, + 860.0 + ] + ], + "input": { + "id": "decoder", + "field": "decoder_lui_auipc_imm" + } + }, + { + "type": "Wire", + "id": "rd_to_rf", + "pos": [ + [ + 1890.0, + 1070.0 + ], + [ + 1950.0, + 1070.0 + ], + [ + 1950.0, + 260.0 + ], + [ + 1020.0, + 260.0 + ], + [ + 1020.0, + 440.0 + ] + ], + "input": { + "id": "regfile_rd_reg", + "field": "register_out" + } + }, + { + "type": "Wire", + "id": "decoder_rd", + "pos": [ + [ + 815.0, + 910.0 + ], + [ + 890.0, + 910.0 + ], + [ + 890.0, + 1070.0 + ], + [ + 1870.0, + 1070.0 + ] + ], + "input": { + "id": "decoder", + "field": "decoder_rd" + } + }, + { + "type": "Wire", + "id": "instr_addr", + "pos": [ + [ + 520.0, + 780.0 + ], + [ + 640.0, + 780.0 + ], + [ + 640.0, + 850.0 + ] + ], + "input": { + "id": "reg", + "field": "register_out" + } + }, + { + "type": "Add", + "id": "pc_adder", + "pos": [ + 560.0, + 670.0 + ], + "scale": 1.0, + "a_in": { + "id": "pc_adder_c", + "field": "constant_out" + }, + "b_in": { + "id": "reg", + "field": "register_out" + } + }, + { + "type": "Wire", + "id": "pc_p4", + "pos": [ + [ + 580.0, + 670.0 + ], + [ + 600.0, + 670.0 + ], + [ + 600.0, + 1000.0 + ], + [ + 1810.0, + 1000.0 + ], + [ + 1810.0, + 560.0 + ], + [ + 1820.0, + 560.0 + ] + ], + "input": { + "id": "pc_adder", + "field": "add_out" + } + }, + { + "type": "Cross", + "id": "c_pc_out", + "pos": [ + 600.0, + 670.0 + ], + "input": { + "id": "pc_adder", + "field": "add_out" + } + }, + { + "type": "Wire", + "id": "pc_p_4", + "pos": [ + [ + 580.0, + 670.0 + ], + [ + 600.0, + 670.0 + ], + [ + 600.0, + 620.0 + ], + [ + 340.0, + 620.0 + ], + [ + 340.0, + 680.0 + ], + [ + 360.0, + 680.0 + ] + ], + "input": { + "id": "pc_adder", + "field": "add_out" + } + }, + { + "type": "ALU", + "id": "alu", + "pos": [ + 1480.0, + 500.0 + ], + "operator_i": { + "id": "decoder", + "field": "decoder_alu_op" + }, + "operand_a_i": { + "id": "alu_operand_a_mux", + "field": "mux_out" + }, + "operand_b_i": { + "id": "alu_operand_b_mux", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "branch_jump_target", + "pos": [ + [ + 1500.0, + 500.0 + ], + [ + 1520.0, + 500.0 + ], + [ + 1520.0, + 240.0 + ], + [ + 1520.0, + 240.0 + ], + [ + 320.0, + 240.0 + ], + [ + 320.0, + 240.0 + ], + [ + 320.0, + 700.0 + ], + [ + 320.0, + 700.0 + ], + [ + 360.0, + 700.0 + ] + ], + "input": { + "id": "alu", + "field": "alu_result_o" + } + }, + { + "type": "Mux", + "id": "pc_adder_mux", + "pos": [ + 380.0, + 690.0 + ], + "select": { + "id": "branch_logic", + "field": "out" + }, + "scale": 1.0, + "m_in": [ + { + "id": "pc_adder", + "field": "add_out" + }, + { + "id": "alu", + "field": "alu_result_o" + } + ] + }, + { + "type": "Wire", + "id": "new_pc", + "pos": [ + [ + 390.0, + 690.0 + ], + [ + 430.0, + 690.0 + ] + ], + "input": { + "id": "pc_adder_mux", + "field": "mux_out" + } + }, + { + "type": "Cross", + "id": "p1", + "pos": [ + 410.0, + 690.0 + ], + "input": { + "id": "pc_adder_mux", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "alu_result", + "pos": [ + [ + 1500.0, + 500.0 + ], + [ + 1820.0, + 500.0 + ] + ], + "input": { + "id": "alu", + "field": "alu_result_o" + } + }, + { + "type": "Cross", + "id": "c5", + "pos": [ + 1520.0, + 500.0 + ], + "input": { + "id": "alu", + "field": "alu_result_o" + } + }, + { + "type": "Wire", + "id": "data_mem_addr", + "pos": [ + [ + 1520.0, + 500.0 + ], + [ + 1520.0, + 820.0 + ], + [ + 1540.0, + 820.0 + ] + ], + "input": { + "id": "alu", + "field": "alu_result_o" + } + }, + { + "type": "Wire", + "id": "rs2_data", + "pos": [ + [ + 1205.0, + 780.0 + ], + [ + 1380.0, + 780.0 + ] + ], + "input": { + "id": "reg_file", + "field": "reg_b" + } + }, + { + "type": "Wire", + "id": "we", + "pos": [ + [ + 815.0, + 900.0 + ], + [ + 900.0, + 900.0 + ], + [ + 900.0, + 1060.0 + ], + [ + 1850.0, + 1060.0 + ], + [ + 1850.0, + 1010.0 + ], + [ + 1870.0, + 1010.0 + ] + ], + "input": { + "id": "decoder", + "field": "decoder_wb_write_enable" + } + }, + { + "type": "Wire", + "id": "w111111", + "pos": [ + [ + 1500.0, + 500.0 + ], + [ + 1500.0, + 500.0 + ], + [ + 1520.0, + 500.0 + ] + ], + "input": { + "id": "alu", + "field": "alu_result_o" + } + }, + { + "type": "Mux", + "id": "csr_mux", + "pos": [ + 1490.0, + 700.0 + ], + "select": { + "id": "decoder", + "field": "csr_data_mux" + }, + "scale": 1.0, + "m_in": [ + { + "id": "reg_file", + "field": "reg_a" + }, + { + "id": "decoder", + "field": "decoder_zimm" + } + ] + }, + { + "type": "Wire", + "id": "csr_data", + "pos": [ + [ + 1500.0, + 700.0 + ], + [ + 1700.0, + 700.0 + ], + [ + 1700.0, + 850.0 + ] + ], + "input": { + "id": "csr_mux", + "field": "mux_out" + } + }, + { + "type": "CLIC", + "id": "clic", + "pos": [ + 1710.0, + 900.0 + ], + "width": 100.0, + "height": 100.0, + "data": { + "id": "reg_file", + "field": "reg_b" + }, + "addr": { + "id": "alu", + "field": "alu_result_o" + }, + "data_we": { + "id": "decoder", + "field": "data_mem_ctrl" + }, + "data_size": { + "id": "decoder", + "field": "data_mem_size" + }, + "csr_data": { + "id": "csr_mux", + "field": "mux_out" + }, + "csr_addr": { + "id": "decoder", + "field": "csr_addr" + }, + "csr_ctl": { + "id": "decoder", + "field": "csr_ctl" + }, + "mret": { + "id": "decoder", + "field": "mret" + }, + "pc": { + "id": "pc_adder", + "field": "add_out" + }, + "pc_next": { + "id": "pc_adder_mux", + "field": "mux_out" + }, + "history": [ + { + "mmio_op": [ + [ + 0, + 0 + ], + 4096 + ], + "csr_op": [ + [ + 833, + 0 + ] + ], + "queue_op": [] + } + ] + }, + { + "type": "Wire", + "id": "clic_interrupt", + "pos": [ + [ + 1760.0, + 870.0 + ], + [ + 1870.0, + 870.0 + ] + ], + "input": { + "id": "clic", + "field": "rf_ra_we" + } + }, + { + "type": "Mux", + "id": "dm_addr_mux", + "pos": [ + 1560.0, + 810.0 + ], + "select": { + "id": "clic", + "field": "interrupt_inv" + }, + "scale": 1.0, + "m_in": [ + { + "id": "clic", + "field": "mem_int_addr" + }, + { + "id": "alu", + "field": "alu_result_o" + } + ] + }, + { + "type": "GPIO", + "height": 50.0, + "width": 250.0, + "id": "gpio", + "pos": [ + 1740.0, + 380.0 + ], + "data_i": { + "id": "reg_file", + "field": "reg_b" + }, + "size_i": { + "id": "decoder", + "field": "data_mem_size" + }, + "we_i": { + "id": "decoder", + "field": "data_mem_ctrl" + }, + "addr_i": { + "id": "dm_addr_mux", + "field": "mux_out" + }, + "se_i": { + "id": "decoder", + "field": "data_se" + }, + "csr_d": { + "id": "csr_mux", + "field": "mux_out" + }, + "csr_a": { + "id": "decoder", + "field": "csr_addr" + }, + "csr_ctl": { + "id": "decoder", + "field": "csr_ctl" + } + }, + { + "type": "LED", + "height": 20.0, + "width": 20.0, + "id": "led", + "pos": [ + 1820.0, + 320.0 + ], + "input": { + "id": "gpio", + "field": "pin_o0" + } + }, + { + "type": "Wire", + "id": "w1111", + "pos": [ + [ + 1570.0, + 810.0 + ], + [ + 1580.0, + 810.0 + ], + [ + 1580.0, + 850.0 + ] + ], + "input": { + "id": "dm_addr_mux", + "field": "mux_out" + } + }, + { + "type": "RVMem", + "id": "data_memory", + "pos": [ + 1550.0, + 900.0 + ], + "width": 100.0, + "height": 100.0, + "big_endian": false, + "data": { + "id": "reg_file", + "field": "reg_b" + }, + "addr": { + "id": "dm_addr_mux", + "field": "mux_out" + }, + "ctrl": { + "id": "decoder", + "field": "data_mem_ctrl" + }, + "sext": { + "id": "decoder", + "field": "data_se" + }, + "size": { + "id": "decoder", + "field": "data_mem_size" + }, + "interrupt": { + "id": "clic", + "field": "rf_ra_we" + }, + "range": { + "start": 1342177280, + "end": 1342185472 + }, + "history": [ + { + "data": null, + "addr": 0, + "size": 0 + } + ] + }, + { + "type": "Mux", + "id": "mmio_data_mux", + "pos": [ + 1750.0, + 560.0 + ], + "select": { + "id": "data_memory", + "field": "mmio_mux_ctl" + }, + "scale": 1.0, + "m_in": [ + { + "id": "data_memory", + "field": "data_o" + }, + { + "id": "clic", + "field": "mmio_data_o" + } + ] + }, + { + "type": "Mux", + "id": "wb_mux", + "pos": [ + 1840.0, + 530.0 + ], + "select": { + "id": "decoder", + "field": "decoder_wb_mux_sel" + }, + "scale": 1.0, + "m_in": [ + { + "id": "alu", + "field": "alu_result_o" + }, + { + "id": "mmio_data_mux", + "field": "mux_out" + }, + { + "id": "clic", + "field": "csr_data_o" + }, + { + "id": "pc_adder", + "field": "add_out" + } + ] + }, + { + "type": "Wire", + "id": "w", + "pos": [ + [ + 1870.0, + 530.0 + ], + [ + 1850.0, + 530.0 + ] + ], + "input": { + "id": "wb_mux", + "field": "out" + } + }, + { + "type": "Wire", + "id": "mem_wb_data", + "pos": [ + [ + 1760.0, + 560.0 + ], + [ + 1780.0, + 560.0 + ], + [ + 1780.0, + 520.0 + ], + [ + 1820.0, + 520.0 + ] + ], + "input": { + "id": "mmio_data_mux", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "mem_data_o", + "pos": [ + [ + 1590.0, + 850.0 + ], + [ + 1590.0, + 550.0 + ], + [ + 1730.0, + 550.0 + ] + ], + "input": { + "id": "data_memory", + "field": "data_o" + } + }, + { + "type": "Cross", + "id": "p", + "pos": [ + 1590.0, + 820.0 + ], + "input": { + "id": "data_memory", + "field": "data_o" + } + }, + { + "type": "Wire", + "id": "clic_csr_data_o", + "pos": [ + [ + 1730.0, + 850.0 + ], + [ + 1730.0, + 700.0 + ], + [ + 1800.0, + 700.0 + ], + [ + 1800.0, + 540.0 + ], + [ + 1820.0, + 540.0 + ] + ], + "input": { + "id": "clic", + "field": "csr_data_o" + } + }, + { + "type": "Wire", + "id": "w_mem_int_addr", + "pos": [ + [ + 1680.0, + 850.0 + ], + [ + 1680.0, + 750.0 + ], + [ + 1530.0, + 750.0 + ], + [ + 1530.0, + 800.0 + ], + [ + 1540.0, + 800.0 + ] + ], + "input": { + "id": "clic", + "field": "mem_int_addr" + } + }, + { + "type": "Wire", + "id": "clic_mmio_d_o", + "pos": [ + [ + 1710.0, + 850.0 + ], + [ + 1710.0, + 570.0 + ], + [ + 1730.0, + 570.0 + ] + ], + "input": { + "id": "clic", + "field": "mmio_data_o" + } + }, + { + "type": "Wire", + "id": "mepc_isr_addr", + "pos": [ + [ + 1670.0, + 850.0 + ], + [ + 1670.0, + 810.0 + ], + [ + 1650.0, + 810.0 + ], + [ + 1650.0, + 1030.0 + ], + [ + 420.0, + 1030.0 + ], + [ + 420.0, + 710.0 + ], + [ + 430.0, + 710.0 + ] + ], + "input": { + "id": "clic", + "field": "mepc_out" + } + }, + { + "type": "Mux", + "id": "interrupt_mux", + "pos": [ + 450.0, + 700.0 + ], + "select": { + "id": "clic", + "field": "interrupt" + }, + "scale": 1.0, + "m_in": [ + { + "id": "pc_adder_mux", + "field": "mux_out" + }, + { + "id": "clic", + "field": "mepc_out" + } + ] + }, + { + "type": "Wire", + "id": "pc_int_mux", + "pos": [ + [ + 460.0, + 700.0 + ], + [ + 480.0, + 700.0 + ] + ], + "input": { + "id": "interrupt_mux", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "w111", + "pos": [ + [ + 410.0, + 690.0 + ], + [ + 410.0, + 1040.0 + ], + [ + 1710.0, + 1040.0 + ], + [ + 1710.0, + 950.0 + ] + ], + "input": { + "id": "pc_adder_mux", + "field": "mux_out" + } + }, + { + "type": "Wire", + "id": "decoder_store_offset_imm", + "pos": [ + [ + 815.0, + 800.0 + ], + [ + 1380.0, + 800.0 + ] + ], + "input": { + "id": "decoder", + "field": "decoder_store_offset_imm" + } + }, + { + "type": "Wire", + "id": "w_stack_depth", + "pos": [ + [ + 1750.0, + 850.0 + ], + [ + 1750.0, + 810.0 + ], + [ + 1870.0, + 810.0 + ] + ], + "input": { + "id": "clic", + "field": "stack_depth_out" + } + } + ] +} diff --git a/src/autowire.rs b/src/autowire.rs new file mode 100644 index 00000000..824b3d1a --- /dev/null +++ b/src/autowire.rs @@ -0,0 +1,49 @@ +use std::rc::Rc; + +use crate::common::{ComponentStore, EguiComponent, Input}; +use crate::components::Wire; + +/// Adds a wire component in a straight line from the output port location of the source component to the inut port locaition of the destination component. +pub fn autowire(mut cs: ComponentStore) -> ComponentStore { + let mut wires: Vec> = vec![]; + + // for each component + for destination_component in &cs.store { + let dest_comp_id = destination_component.get_id_ports().0; + // for each port in destination component + for input_port in destination_component.get_id_ports().1.inputs.iter() { + let source_port = &input_port.input; + let dest_comp_field = &input_port.port_id; + + // find component with correct source id + #[allow(clippy::expect_fun_call)] + let source_component = cs + .store + .iter() + .find(|comp| comp.get_id_ports().0 == source_port.id) + .expect(&format!("can't find comonent with id {}", source_port.id)); + + // create wire with correct source destination and positions + + let s_id = &source_port.id; + let s_field = &source_port.field; + let d_id = &dest_comp_id; + let d_field = &dest_comp_field; + + wires.push(Wire::rc_new( + &format!("from {}:{} to {}:{}", s_id, s_field, d_id, d_field), + vec![ + source_component + .get_input_location(Input::new(s_id, s_field)) + .unwrap_or(source_component.get_pos()), + destination_component + .get_input_location(Input::new(s_id, s_field)) + .unwrap_or(destination_component.get_pos()), + ], + source_port.clone(), + )) + } + } + cs.store.append(&mut wires); + cs +} diff --git a/src/common.rs b/src/common.rs index e9573a70..ac90ca14 100644 --- a/src/common.rs +++ b/src/common.rs @@ -170,6 +170,10 @@ pub trait EguiComponent: Component { } } + fn get_input_location(&self, _id: Input) -> Option<(f32, f32)> { + None + } + fn top_padding(&self) -> f32 { todo!("Create top_padding for this EguiComponent"); } diff --git a/src/component_store.rs b/src/component_store.rs index 31aa1518..a34a9291 100644 --- a/src/component_store.rs +++ b/src/component_store.rs @@ -18,7 +18,7 @@ impl ComponentStore { } pub fn save_file(&self, path: &PathBuf) { - let json = serde_json::to_string(self).unwrap(); + let json = serde_json::to_string_pretty(self).unwrap(); trace!("json: {}", json); trace!("path {:?}", path); let mut file = File::create(path).unwrap(); diff --git a/src/components/add.rs b/src/components/add.rs index b64298ca..64ae84e3 100644 --- a/src/components/add.rs +++ b/src/components/add.rs @@ -11,7 +11,7 @@ use std::rc::Rc; pub const ADD_A_IN_ID: &str = "a_in"; pub const ADD_B_IN_ID: &str = "b_in"; -pub const ADD_OUT_ID: &str = "out"; +pub const ADD_OUT_ID: &str = "add_out"; pub const ADD_OVERFLOW_ID: &str = "overflow"; #[derive(Serialize, Deserialize, Clone)] @@ -20,6 +20,7 @@ pub struct Add { pub(crate) pos: (f32, f32), pub(crate) a_in: Input, pub(crate) b_in: Input, + pub(crate) scale: f32, } #[typetag::serde] @@ -35,6 +36,7 @@ impl Component for Add { pos: (pos.0, pos.1), a_in: dummy_input.clone(), b_in: dummy_input.clone(), + scale: 1.0, })) } fn get_id_ports(&self) -> (Id, Ports) { @@ -89,8 +91,8 @@ impl Component for Add { ); // set output - simulator.set_out_value(&self.id, "out", value); - simulator.set_out_value(&self.id, "overflow", overflow); + simulator.set_out_value(&self.id, ADD_OUT_ID, value); + simulator.set_out_value(&self.id, ADD_OVERFLOW_ID, overflow); res } @@ -114,12 +116,29 @@ impl Add { pos, a_in, b_in, + scale: 1.0, } } pub fn rc_new(id: &str, pos: (f32, f32), a_in: Input, b_in: Input) -> Rc { Rc::new(Add::new(id, pos, a_in, b_in)) } + + pub fn rc_new_with_scale( + id: &str, + pos: (f32, f32), + a_in: Input, + b_in: Input, + scale: f32, + ) -> Rc { + Rc::new(Add { + id: id.to_string(), + pos, + a_in, + b_in, + scale, + }) + } } #[cfg(test)] @@ -143,6 +162,7 @@ mod test { pos: (0.0, 0.0), a_in: Input::new("po1", "out"), b_in: Input::new("po2", "out"), + scale: 1.0, }), ], }; @@ -151,7 +171,7 @@ mod test { assert_eq!(simulator.cycle, 1); // outputs - let add_val = &Input::new("add", "out"); + let add_val = &Input::new("add", ADD_OUT_ID); let add_overflow = &Input::new("add", "overflow"); // reset diff --git a/src/components/and.rs b/src/components/and.rs new file mode 100644 index 00000000..4f0d0ae2 --- /dev/null +++ b/src/components/and.rs @@ -0,0 +1,96 @@ +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; +use crate::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; +pub const AND_A_IN_ID: &str = "a_in"; +pub const AND_B_IN_ID: &str = "b_in"; + +pub const AND_OUT_ID: &str = "and_out"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct And { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) a_in: Input, + pub(crate) b_in: Input, +} + +#[typetag::serde] +impl Component for And { + fn to_(&self) { + trace!("And"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(And { + id: id.to_string(), + pos: (pos.0, pos.1), + a_in: dummy_input.clone(), + b_in: dummy_input.clone(), + })) + } + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: AND_A_IN_ID.to_string(), + input: self.a_in.clone(), + }, + &InputPort { + port_id: AND_B_IN_ID.to_string(), + input: self.b_in.clone(), + }, + ], + OutputType::Combinatorial, + vec![AND_OUT_ID], + ), + ) + } + + // propagate addition to output + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + // get input values + let a_in: u32 = simulator.get_input_value(&self.a_in).try_into().unwrap(); + let b_in: u32 = simulator.get_input_value(&self.b_in).try_into().unwrap(); + + let result: u32 = a_in & b_in; + + simulator.set_out_value(&self.id, AND_OUT_ID, SignalValue::Data(result)); + Ok(()) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + AND_A_IN_ID => self.a_in = new_input, + AND_B_IN_ID => self.b_in = new_input, + _ => (), + } + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl And { + pub fn new(id: &str, pos: (f32, f32), a_in: Input, b_in: Input) -> Self { + And { + id: id.to_string(), + pos, + a_in, + b_in, + } + } + + pub fn rc_new(id: &str, pos: (f32, f32), a_in: Input, b_in: Input) -> Rc { + Rc::new(And::new(id, pos, a_in, b_in)) + } +} diff --git a/src/components/constant.rs b/src/components/constant.rs index 98e5713b..b3ede0f8 100644 --- a/src/components/constant.rs +++ b/src/components/constant.rs @@ -4,7 +4,7 @@ use crate::common::{Component, Condition, Id, OutputType, Ports, Signal, Simulat use log::*; use serde::{Deserialize, Serialize}; use std::{convert::Into, rc::Rc}; -pub const CONSTANT_OUT_ID: &str = "out"; +pub const CONSTANT_OUT_ID: &str = "constant_out"; use std::any::Any; #[derive(Serialize, Deserialize, Clone)] pub struct Constant { diff --git a/src/components/equals.rs b/src/components/equals.rs new file mode 100644 index 00000000..51e1c78d --- /dev/null +++ b/src/components/equals.rs @@ -0,0 +1,86 @@ +//TODO: add so it can take undefined number of inputs +use crate::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; +pub const EQUAL_A_IN_ID: &str = "a_in"; +pub const EQUAL_B_IN_ID: &str = "b_in"; + +pub const EQUAL_OUT_ID: &str = "equals_out"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct Equal { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) a_in: Input, + pub(crate) b_in: Input, +} + +#[typetag::serde] +impl Component for Equal { + fn to_(&self) { + trace!("Equal"); + } + + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: EQUAL_A_IN_ID.to_string(), + input: self.a_in.clone(), + }, + &InputPort { + port_id: EQUAL_B_IN_ID.to_string(), + input: self.b_in.clone(), + }, + ], + OutputType::Combinatorial, + vec![EQUAL_OUT_ID], + ), + ) + } + + // propagate addition to output + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + // get input values + let a_in: u32 = simulator.get_input_value(&self.a_in).try_into().unwrap(); + let b_in: u32 = simulator.get_input_value(&self.b_in).try_into().unwrap(); + + let result: u32 = (a_in == b_in) as u32; + + simulator.set_out_value(&self.id, EQUAL_OUT_ID, SignalValue::Data(result)); + Ok(()) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + EQUAL_A_IN_ID => self.a_in = new_input, + EQUAL_B_IN_ID => self.b_in = new_input, + _ => (), + } + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl Equal { + pub fn new(id: &str, pos: (f32, f32), a_in: Input, b_in: Input) -> Self { + Equal { + id: id.to_string(), + pos, + a_in, + b_in, + } + } + + pub fn rc_new(id: &str, pos: (f32, f32), a_in: Input, b_in: Input) -> Rc { + Rc::new(Equal::new(id, pos, a_in, b_in)) + } +} diff --git a/src/components/mem.rs b/src/components/mem.rs index a3f7e617..11bcbd16 100644 --- a/src/components/mem.rs +++ b/src/components/mem.rs @@ -136,21 +136,42 @@ impl Memory { Memory(Rc::new(RefCell::new(data))) } - fn align(&self, addr: usize, size: usize) -> SignalValue { + /// is one if addres is unaligend, and zero if it is. + /// # Example + /// ``` + /// use syncrim::components::Memory; + /// use syncrim::signal::SignalValue; + /// + /// let mem = Memory::default(); // creates a memory with only zeros + /// + /// let align_adrs = mem.align(0xa3f5, 4); + /// assert_eq!(SignalValue::from(1), align_adrs) + /// ``` + pub fn align(&self, addr: usize, size: usize) -> SignalValue { ((addr % size != 0) as SignalUnsigned).into() } - fn read(&self, addr: usize, size: usize, sign: bool, big_endian: bool) -> SignalValue { + /// This function reads 1, 2, 4 bytes from the memory, + /// and converts them to a SignalValue using the a big_endian and sign arguments + /// + /// # Panics + /// This function panics if the argument size is NOT 1, 2 or 4 + /// # TODO + /// - maybe make size an enum? + pub fn read(&self, addr: usize, size: usize, sign: bool, big_endian: bool) -> SignalValue { + // Read amount of bytes determined by size, and add them them a vector let data: Vec = (0..size) .map(|i| *self.0.borrow().get(&(addr + i)).unwrap_or(&0)) .collect(); + // why convert to a slice? let data = data.as_slice(); trace!("{:x?}", data); match size { 1 => { + // Loading one byte if sign { data[0] as i8 as SignalSigned as SignalUnsigned } else { @@ -158,8 +179,10 @@ impl Memory { } } 2 => { + // Loading half word, most stuff here is for tracing and debugging if sign { if big_endian { + // sign big endian trace!("read signed half word be"); let i_16 = i16::from_be_bytes(data.try_into().unwrap()); trace!("i_16 {:x?}", i_16); @@ -167,6 +190,7 @@ impl Memory { trace!("i_32 {:x?}", i_32); i_32 as SignalUnsigned } else { + // sign little endian trace!("read signed half word le"); let i_16 = i16::from_le_bytes(data.try_into().unwrap()); trace!("i_16 {:x?}", i_16); @@ -175,6 +199,7 @@ impl Memory { i_32 as SignalUnsigned } } else if big_endian { + // unsigned big endian trace!("read unsigned half word be"); let u_16 = u16::from_be_bytes(data.try_into().unwrap()); trace!("u_16 {:x?}", u_16); @@ -182,6 +207,7 @@ impl Memory { trace!("u_32 {:x?}", u_32); u_32 as SignalUnsigned } else { + // unsigned little endian trace!("read unsigned half word le"); let u_16 = u16::from_le_bytes(data.try_into().unwrap()); trace!("u_16 {:x?}", u_16); @@ -191,6 +217,7 @@ impl Memory { } } 4 => { + // Load full word if sign { if big_endian { i32::from_be_bytes(data.try_into().unwrap()) as SignalUnsigned @@ -205,10 +232,17 @@ impl Memory { } _ => panic!("illegal sized memory operation"), } - .into() + .into() // Convert to SignalValue } - fn write(&self, addr: usize, size: usize, big_endian: bool, data: SignalValue) { + /// This function writes 1, 2, 4 bytes from the memory, + /// using the a big_endian arguments + /// + /// # Panics + /// This function panics if the argument size is NOT 1, 2 or 4 + /// # TODO + /// - maybe make size an enum? + pub fn write(&self, addr: usize, size: usize, big_endian: bool, data: SignalValue) { let data: SignalUnsigned = data.try_into().unwrap(); match size { 1 => { diff --git a/src/components/mod.rs b/src/components/mod.rs index 2121b0d2..53382f69 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,6 +1,8 @@ mod add; +mod and; mod constant; mod cross; +mod equals; mod mem; mod mux; mod probe; @@ -10,11 +12,16 @@ mod probe_out; mod probe_stim; mod register; mod sext; +mod shift_left_const; +//mod sz_extend; mod wire; +mod zero_extend; pub use add::*; +pub use and::*; pub use constant::*; pub use cross::*; +pub use equals::*; pub use mem::*; pub use mux::*; pub use probe::*; @@ -24,4 +31,7 @@ pub use probe_out::*; pub use probe_stim::*; pub use register::*; pub use sext::*; +pub use shift_left_const::*; +//pub use sz_extend::*; pub use wire::*; +pub use zero_extend::*; diff --git a/src/components/mux.rs b/src/components/mux.rs index f8d83001..eae68824 100644 --- a/src/components/mux.rs +++ b/src/components/mux.rs @@ -11,7 +11,7 @@ use std::rc::Rc; pub const MUX_SELECT_ID: &str = "select"; pub const MUX_TEMPLATE_ID: &str = "in"; -pub const MUX_OUT_ID: &str = "out"; +pub const MUX_OUT_ID: &str = "mux_out"; #[derive(Serialize, Deserialize, Clone)] pub struct Mux { @@ -19,6 +19,7 @@ pub struct Mux { pub(crate) pos: (f32, f32), pub(crate) select: Input, pub(crate) m_in: Vec, + pub scale: f32, } #[typetag::serde] @@ -34,6 +35,7 @@ impl Component for Mux { pos: (pos.0, pos.1), select: dummy_input.clone(), m_in: vec![dummy_input.clone(), dummy_input.clone()], + scale: 1.0, })) } fn get_id_ports(&self) -> (Id, Ports) { @@ -86,7 +88,7 @@ impl Component for Mux { }; trace!("-----------------value:{:?}, end---------------", value); // set output - simulator.set_out_value(&self.id, "out", value); + simulator.set_out_value(&self.id, MUX_OUT_ID, value); res } @@ -116,10 +118,27 @@ impl Mux { pos, select, m_in, + scale: 1.0, } } pub fn rc_new(id: &str, pos: (f32, f32), select: Input, m_in: Vec) -> Rc { Rc::new(Mux::new(id, pos, select, m_in)) } + + pub fn rc_new_with_scale( + id: &str, + pos: (f32, f32), + select: Input, + m_in: Vec, + scale: f32, + ) -> Rc { + Rc::new(Mux { + id: id.to_string(), + pos, + select, + m_in, + scale, + }) + } } diff --git a/src/components/register.rs b/src/components/register.rs index 1752cebb..bfed7be0 100644 --- a/src/components/register.rs +++ b/src/components/register.rs @@ -8,7 +8,7 @@ use std::rc::Rc; pub const REGISTER_R_IN_ID: &str = "r_in"; -pub const REGISTER_OUT_ID: &str = "out"; +pub const REGISTER_OUT_ID: &str = "register_out"; #[derive(Serialize, Deserialize, Clone)] pub struct Register { @@ -51,7 +51,7 @@ impl Component for Register { // get input value let value = simulator.get_input_value_mut(self.id.clone(), &self.r_in); // set output - simulator.set_out_value(&self.id, "out", value); + simulator.set_out_value(&self.id, REGISTER_OUT_ID, value); trace!("eval: register id {} in {:?}", self.id, value); Ok(()) } diff --git a/src/components/sext.rs b/src/components/sext.rs index 8f36e12c..2727c6af 100644 --- a/src/components/sext.rs +++ b/src/components/sext.rs @@ -12,7 +12,7 @@ use std::rc::Rc; pub const SEXT_IN_ID: &str = "sext_in"; -pub const SEXT_OUT_ID: &str = "out"; +pub const SEXT_OUT_ID: &str = "sext_out"; #[derive(Serialize, Deserialize, Clone)] pub struct Sext { @@ -82,10 +82,10 @@ impl Component for Sext { value >>= to_shr; // set output - simulator.set_out_value(&self.id, "out", SignalValue::Data(value)); + simulator.set_out_value(&self.id, SEXT_OUT_ID, SignalValue::Data(value)); } _ => { - simulator.set_out_value(&self.id, "out", SignalValue::Unknown); + simulator.set_out_value(&self.id, SEXT_OUT_ID, SignalValue::Unknown); trace!("{} unknown input", self.id); } } @@ -150,8 +150,8 @@ mod test { assert_eq!(simulator.cycle, 1); // outputs - let sext32_out = &Input::new("sext32", "out"); - let sext16_out = &Input::new("sext16", "out"); + let sext32_out = &Input::new("sext32", SEXT_OUT_ID); + let sext16_out = &Input::new("sext16", SEXT_OUT_ID); // reset assert_eq!(simulator.get_input_value(sext32_out), 0.into()); diff --git a/src/components/shift_left_const.rs b/src/components/shift_left_const.rs new file mode 100644 index 00000000..37eeee54 --- /dev/null +++ b/src/components/shift_left_const.rs @@ -0,0 +1,92 @@ +// use std::fmt::Alignment; +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; +use crate::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; + +pub const SHIFT_SIGNAL_IN_ID: &str = "shift_in"; + +pub const SHIFT_OUT_ID: &str = "shift_left_const_out"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct ShiftConst { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) signal_in: Input, + pub shift_by: u32, +} + +#[typetag::serde] +impl Component for ShiftConst { + fn to_(&self) { + trace!("shift"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, _id: &str, _pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(ShiftConst { + id: "dummy".to_string(), + pos: (0.0, 0.0), + signal_in: dummy_input.clone(), + shift_by: 0, + })) + } + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![&InputPort { + port_id: SHIFT_SIGNAL_IN_ID.to_string(), + input: self.signal_in.clone(), + }], + OutputType::Combinatorial, + vec![SHIFT_OUT_ID], + ), + ) + } + + #[allow(clippy::single_match)] + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + SHIFT_SIGNAL_IN_ID => self.signal_in = new_input, + _ => {} + } + } + + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + // get input values + let signal_in: u32 = simulator + .get_input_value(&self.signal_in) + .try_into() + .unwrap(); + + // let output: u32 = signal_in << self.shift_by; + let output: u32 = signal_in.overflowing_shl(self.shift_by).0; + simulator.set_out_value(&self.id, SHIFT_OUT_ID, SignalValue::Data(output)); + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl ShiftConst { + pub fn new(id: &str, pos: (f32, f32), signal_in: Input, shift_by: u32) -> Self { + ShiftConst { + id: id.to_string(), + pos, + signal_in, + shift_by, + } + } + + pub fn rc_new(id: &str, pos: (f32, f32), signal_in: Input, shift_by: u32) -> Rc { + Rc::new(ShiftConst::new(id, pos, signal_in, shift_by)) + } +} diff --git a/src/components/wire.rs b/src/components/wire.rs index e0057807..b4a048a9 100644 --- a/src/components/wire.rs +++ b/src/components/wire.rs @@ -11,6 +11,15 @@ pub struct Wire { pub(crate) id: Id, pub(crate) pos: Vec<(f32, f32)>, pub(crate) input: Input, + // this is colour32, using [u8;4] instead of color + // because we are not sure if egui feature is present + // if this field does not exist in wire call basic color to generate it + // skip serializing color if color matches basic color + #[serde( + default = "Wire::basic_color", + skip_serializing_if = "Wire::is_color_basic" + )] + pub(crate) color_rgba: [u8; 4], } #[typetag::serde] @@ -46,10 +55,19 @@ impl Wire { id: id.to_string(), pos, input, + color_rgba: Wire::basic_color(), } } pub fn rc_new(id: &str, pos: Vec<(f32, f32)>, input: Input) -> Rc { Rc::new(Wire::new(id, pos, input)) } + + pub const fn basic_color() -> [u8; 4] { + [0x00, 0x00, 0x00, 0xff] + } + + pub fn is_color_basic(x: &[u8; 4]) -> bool { + &Wire::basic_color() == x + } } diff --git a/src/components/zero_extend.rs b/src/components/zero_extend.rs new file mode 100644 index 00000000..b4615ce6 --- /dev/null +++ b/src/components/zero_extend.rs @@ -0,0 +1,89 @@ +// use std::fmt::Alignment; +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; +use crate::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; + +pub const ZEROEXTEND_SIGNAL_IN_ID: &str = "signal_in"; + +pub const ZEROEXTEND_OUT_ID: &str = "zero_extend_out"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct ZeroExtend { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) signal_in: Input, +} + +#[typetag::serde] +impl Component for ZeroExtend { + fn to_(&self) { + trace!("zero_extend"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, _id: &str, _pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(ZeroExtend { + id: "dummy".to_string(), + pos: (0.0, 0.0), + signal_in: dummy_input.clone(), + })) + } + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![&InputPort { + port_id: ZEROEXTEND_SIGNAL_IN_ID.to_string(), + input: self.signal_in.clone(), + }], + OutputType::Combinatorial, + vec![ZEROEXTEND_OUT_ID], + ), + ) + } + + #[allow(clippy::single_match)] + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + ZEROEXTEND_SIGNAL_IN_ID => self.signal_in = new_input, + _ => {} + } + } + + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + // get input values + let signal_in: u32 = simulator + .get_input_value(&self.signal_in) + .try_into() + .unwrap(); + + let output: u32 = signal_in & 0x0000_FFFF; // already zero extended + + simulator.set_out_value(&self.id, ZEROEXTEND_OUT_ID, SignalValue::Data(output)); + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl ZeroExtend { + pub fn new(id: &str, pos: (f32, f32), signal_in: Input) -> Self { + ZeroExtend { + id: id.to_string(), + pos, + signal_in, + } + } + + pub fn rc_new(id: &str, pos: (f32, f32), signal_in: Input) -> Rc { + Rc::new(ZeroExtend::new(id, pos, signal_in)) + } +} diff --git a/src/fern.rs b/src/fern.rs index b8a03bef..935e7fc1 100644 --- a/src/fern.rs +++ b/src/fern.rs @@ -15,8 +15,9 @@ pub fn fern_setup() { )) }) // Add blanket level filter - - // .level(log::LevelFilter::Debug); - .level(log::LevelFilter::Trace); + .level(log::LevelFilter::Debug); + // .level(log::LevelFilter::Trace); + // .level(log::LevelFilter::Off); // - and per-module overrides #[cfg(feature = "gui-vizia")] diff --git a/src/gui_egui/components/add.rs b/src/gui_egui/components/add.rs index 4f2adf8c..a3e1f1e5 100644 --- a/src/gui_egui/components/add.rs +++ b/src/gui_egui/components/add.rs @@ -1,3 +1,5 @@ +use crate::common::Input; +use crate::components::{ADD_OUT_ID, ADD_OVERFLOW_ID}; use crate::gui_egui::component_ui::{ drag_logic, input_change_id, input_selector, pos_drag_value, properties_window, rect_with_hover, visualize_ports, @@ -32,19 +34,28 @@ impl EguiComponent for Add { offset.y += self.pos.1 * scale; let s = scale; let o = offset; + // The shape + #[rustfmt::skip] // stop formate from "compacting" our vec, doesn't affect anything else + let shape: Vec<(f32, f32)> = vec![ + (-20f32, -40f32), + (0f32, -40f32), + (20f32, -20f32), + (20f32, 20f32), + (0f32, 40f32), + (-20f32, 40f32), + (-20f32, 20f32), + (-10f32, 0f32), + (-20f32, -20f32), + ]; + + let comp_scale = self.scale; + // The shape ui.painter().add(Shape::closed_line( - vec![ - oh((-20f32, -40f32), s, o), - oh((0f32, -40f32), s, o), - oh((20f32, -20f32), s, o), - oh((20f32, 20f32), s, o), - oh((0f32, 40f32), s, o), - oh((-20f32, 40f32), s, o), - oh((-20f32, 20f32), s, o), - oh((-10f32, 0f32), s, o), - oh((-20f32, -20f32), s, o), - ], + shape + .iter() + .map(|(x, y)| oh((x * comp_scale, y * comp_scale), s, o)) + .collect(), Stroke { width: scale, color: Color32::RED, @@ -66,8 +77,8 @@ impl EguiComponent for Add { }, )); let rect = Rect { - min: oh((-20f32, -40f32), s, o), - max: oh((20f32, 40f32), s, o), + min: oh((-20.0 * self.scale, -40.0 * self.scale), s, o), + max: oh((20.0 * self.scale, 40.0 * self.scale), s, o), }; let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { ui.label(format!("Id: {}", self.id.clone())); @@ -152,23 +163,43 @@ impl EguiComponent for Add { vec![ ( crate::components::ADD_A_IN_ID.to_string(), - Pos2::new(-20f32, -20f32) + own_pos, + Pos2::new(-20f32, -20f32) * self.scale + own_pos, ), ( crate::components::ADD_B_IN_ID.to_string(), - Pos2::new(-20f32, 20f32) + own_pos, + Pos2::new(-20f32, 20f32) * self.scale + own_pos, ), ( crate::components::ADD_OUT_ID.to_string(), - Pos2::new(20f32, 0f32) + own_pos, + Pos2::new(20f32, 0f32) * self.scale + own_pos, ), ( crate::components::ADD_OVERFLOW_ID.to_string(), - Pos2::new(0f32, -40f32) + own_pos, + Pos2::new(0f32, -40f32) * self.scale + own_pos, ), ] } + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + + if id == self.a_in { + Some(loc[0]) + } else if id == self.b_in { + Some(loc[1]) + } else if id == Input::new(&self.id, ADD_OUT_ID) { + Some(loc[2]) + } else if id == Input::new(&self.id, ADD_OVERFLOW_ID) { + Some(loc[3]) + } else { + None + } + } + fn top_padding(&self) -> f32 { 40f32 } diff --git a/src/gui_egui/components/and.rs b/src/gui_egui/components/and.rs new file mode 100644 index 00000000..3c0fd900 --- /dev/null +++ b/src/gui_egui/components/and.rs @@ -0,0 +1,91 @@ +use crate::common::{EguiComponent, Id, Input, Ports, Simulator}; +use crate::components::{And, AND_A_IN_ID, AND_B_IN_ID, AND_OUT_ID}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::helper::basic_component_gui; +use egui::{pos2, Rect, Response, Ui, Vec2}; + +#[typetag::serde] +impl EguiComponent for And { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.label("And"); + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + if id == self.a_in { + Some(loc[0]) + } else if id == self.b_in { + Some(loc[1]) + } else if id == Input::new(&self.id, AND_OUT_ID) { + Some(loc[2]) + } else { + None + } + } + + fn ports_location(&self) -> Vec<(Id, egui::Pos2)> { + //size 22-14 + let m = 6f32; // margin + let pos: Vec2 = self.pos.into(); + vec![ + (AND_A_IN_ID.to_string(), pos2(-11.0 - m, -10.0) + pos), + (AND_B_IN_ID.to_string(), pos2(-11.0 - m, 10.0) + pos), + (AND_OUT_ID.to_string(), pos2(11.0 + m, 0.0) + pos), + ] + } +} diff --git a/src/gui_egui/components/equal.rs b/src/gui_egui/components/equal.rs new file mode 100644 index 00000000..58063c38 --- /dev/null +++ b/src/gui_egui/components/equal.rs @@ -0,0 +1,91 @@ +use crate::common::{EguiComponent, Id, Input, Ports, Simulator}; +use crate::components::{Equal, EQUAL_A_IN_ID, EQUAL_B_IN_ID, EQUAL_OUT_ID}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::helper::basic_component_gui; +use egui::{pos2, Rect, Response, Ui, Vec2}; + +#[typetag::serde] +impl EguiComponent for Equal { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.label("Equal"); + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + if id == self.a_in { + Some(loc[0]) + } else if id == self.b_in { + Some(loc[1]) + } else if id == Input::new(&self.id, EQUAL_OUT_ID) { + Some(loc[2]) + } else { + None + } + } + + fn ports_location(&self) -> Vec<(Id, egui::Pos2)> { + //size 22-14 + let m = 6f32; // margin + let pos: Vec2 = self.pos.into(); + vec![ + (EQUAL_A_IN_ID.to_string(), pos2(-11.0 - m, -10.0) + pos), + (EQUAL_B_IN_ID.to_string(), pos2(-11.0 - m, 10.0) + pos), + (EQUAL_OUT_ID.to_string(), pos2(11.0 + m, 0.0) + pos), + ] + } +} diff --git a/src/gui_egui/components/mod.rs b/src/gui_egui/components/mod.rs index 428041e6..6976570d 100644 --- a/src/gui_egui/components/mod.rs +++ b/src/gui_egui/components/mod.rs @@ -1,6 +1,8 @@ mod add; +mod and; mod constant; mod cross; +mod equal; mod mem; mod mux; mod probe; @@ -10,4 +12,6 @@ mod probe_out; mod probe_stim; mod register; mod sext; +mod shift_left_const; mod wire; +mod zero_extend; diff --git a/src/gui_egui/components/mux.rs b/src/gui_egui/components/mux.rs index 45b82029..1b1e41a4 100644 --- a/src/gui_egui/components/mux.rs +++ b/src/gui_egui/components/mux.rs @@ -1,5 +1,5 @@ use crate::common::{EguiComponent, Input, Ports, SignalUnsigned, Simulator}; -use crate::components::Mux; +use crate::components::{Mux, MUX_OUT_ID}; use crate::gui_egui::component_ui::{ drag_logic, input_change_id, input_selector, input_selector_removeable, pos_drag_value, properties_window, rect_with_hover, visualize_ports, @@ -44,17 +44,22 @@ impl EguiComponent for Mux { Some(s) => s.get_input_value(&self.select).try_into().unwrap_or(0), None => 0, }; + let mut shape: Vec<(f32, f32)> = vec![ + (-20f32, pa * (-10f32) - 10f32), + (0f32, pa * (-10f32) - 10f32), + (10f32, pa * (-10f32) + 10f32), + (10f32, pa * (10f32) - 10f32), + (0f32, pa * (10f32) + 10f32), + (-20f32, pa * (10f32) + 10f32), + ]; + for (x, y) in shape.iter_mut() { + *x *= self.scale; + *y *= self.scale; + } // The shape ui.painter().add(Shape::closed_line( - vec![ - oh((-20f32, pa * (-10f32) - 10f32), s, o), - oh((0f32, pa * (-10f32) - 10f32), s, o), - oh((10f32, pa * (-10f32) + 10f32), s, o), - oh((10f32, pa * (10f32) - 10f32), s, o), - oh((0f32, pa * (10f32) + 10f32), s, o), - oh((-20f32, pa * (10f32) + 10f32), s, o), - ], + shape.clone().iter().map(|point| oh(*point, s, o)).collect(), Stroke { width: scale, color: Color32::BLACK, @@ -65,11 +70,14 @@ impl EguiComponent for Mux { ui.painter().add(Shape::line_segment( [ oh( - (-20f32, ((select as f32) * 20f32) - pa * 10f32 + 10f32), + ( + -20f32 * self.scale, + (((select as f32) * 20f32) - pa * 10f32 + 10f32) * self.scale, + ), s, o, ), - oh((10f32, 0f32), s, o), + oh((10f32 * self.scale, 0f32 * self.scale), s, o), ], Stroke { width: scale, @@ -82,8 +90,16 @@ impl EguiComponent for Mux { )); let rect = Rect { - min: oh((-20f32, pa * (-10f32) - 10f32), s, o), - max: oh((10f32, pa * 10f32 + 10f32), s, o), + min: oh( + (-20f32 * self.scale, (pa * (-10f32) - 10f32) * self.scale), + s, + o, + ), + max: oh( + (10f32 * self.scale, (pa * 10f32 + 10f32) * self.scale), + s, + o, + ), }; let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { ui.label(format!("Id: {}", self.id.clone())); @@ -177,23 +193,40 @@ impl EguiComponent for Mux { } } + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + if let Some(i) = self.m_in.iter().position(|item| item == &id) { + Some(loc[i + 1]) + } else if id == self.select { + Some(loc[0]) + } else if id == Input::new(&self.id, MUX_OUT_ID) { + Some(*loc.last().unwrap()) + } else { + None + } + } + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { let own_pos = Vec2::new(self.pos.0, self.pos.1); let pa = self.m_in.len() as f32; let top = -pa * 10f32 - 10f32; let mut v = vec![( crate::components::MUX_SELECT_ID.to_string(), - Pos2::new(-10f32, top) + own_pos, + Pos2::new(-10f32, top) * self.scale + own_pos, )]; for i in 0..=self.m_in.len() - 1 { v.push(( format!("{}{}", crate::components::MUX_TEMPLATE_ID, i), - Pos2::new(-20f32, top + i as f32 * 20f32 + 20f32) + own_pos, + Pos2::new(-20f32, top + i as f32 * 20f32 + 20f32) * self.scale + own_pos, )); } v.push(( crate::components::MUX_OUT_ID.to_string(), - Pos2::new(10f32, 0f32) + own_pos, + Pos2::new(10f32, 0f32) * self.scale + own_pos, )); v } diff --git a/src/gui_egui/components/probe_edit.rs b/src/gui_egui/components/probe_edit.rs index 6ca94558..14cea32d 100644 --- a/src/gui_egui/components/probe_edit.rs +++ b/src/gui_egui/components/probe_edit.rs @@ -178,6 +178,8 @@ fn parse_signal(text: &str) -> SignalValue { if let Ok(signal) = text.parse::() { (signal as SignalUnsigned).into() + } else if let Ok(signal) = text.parse::() { + (signal as SignalUnsigned).into() } else if let Some(hex) = text.strip_prefix("0x") { if let Ok(signal) = SignalUnsigned::from_str_radix(hex, 16) { signal.into() diff --git a/src/gui_egui/components/register.rs b/src/gui_egui/components/register.rs index 2fd862ba..5707f42b 100644 --- a/src/gui_egui/components/register.rs +++ b/src/gui_egui/components/register.rs @@ -1,5 +1,5 @@ use crate::common::{EguiComponent, Input, Ports, SignalUnsigned, Simulator}; -use crate::components::Register; +use crate::components::{Register, REGISTER_OUT_ID}; use crate::gui_egui::component_ui::{ drag_logic, input_change_id, input_selector, pos_drag_value, properties_window, rect_with_hover, visualize_ports, @@ -66,7 +66,7 @@ impl EguiComponent for Register { let r: Result = s .get_input_value(&Input { id: self.id.clone(), - field: "out".to_string(), + field: REGISTER_OUT_ID.to_string(), }) .try_into(); match r { @@ -158,6 +158,28 @@ impl EguiComponent for Register { ] } + fn get_input_location(&self, input: Input) -> Option<(f32, f32)> { + let own_pos = self.get_pos(); + let input_pos_change = (-10f32, 0f32); + let output_pos_change = (10f32, 0f32); + + if input == self.r_in { + // looks input + return Some(( + own_pos.0 + input_pos_change.0, + own_pos.1 + input_pos_change.1, + )); + } else if input == Input::new(&self.id, REGISTER_OUT_ID) { + // look output + return Some(( + own_pos.0 + output_pos_change.0, + own_pos.1 + output_pos_change.1, + )); + } + + None + } + fn top_padding(&self) -> f32 { 20f32 } diff --git a/src/gui_egui/components/shift_left_const.rs b/src/gui_egui/components/shift_left_const.rs new file mode 100644 index 00000000..4acbdb4f --- /dev/null +++ b/src/gui_egui/components/shift_left_const.rs @@ -0,0 +1,99 @@ +use crate::common::{EguiComponent, Id, Input, Ports, Simulator}; +use crate::components::{ShiftConst, SHIFT_OUT_ID}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::helper::basic_component_gui; +use egui::{pos2, Pos2, Rect, Response, Ui, Vec2}; + +const HEIGHT: f32 = 14.0; +const WIDTH: f32 = 55.0; + +#[typetag::serde] +impl EguiComponent for ShiftConst { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.set_height(HEIGHT * scale); + ui.set_width(WIDTH * scale); + ui.label(format!("Shift << {}", self.shift_by)); + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + const M: f32 = 6.0; + vec![ + ( + crate::components::SHIFT_SIGNAL_IN_ID.to_string(), + pos2(-WIDTH / 2.0 - M, 0.0) + own_pos, + ), + ( + crate::components::SHIFT_OUT_ID.to_string(), + pos2(WIDTH / 2.0 + M, 0.0) + own_pos, + ), + ] + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + + if id == self.signal_in { + Some(loc[0]) + } else if id == Input::new(&self.id, SHIFT_OUT_ID) { + Some(loc[1]) + } else { + None + } + } +} diff --git a/src/gui_egui/components/sz_extend.rs b/src/gui_egui/components/sz_extend.rs new file mode 100644 index 00000000..b12fba93 --- /dev/null +++ b/src/gui_egui/components/sz_extend.rs @@ -0,0 +1,83 @@ +use crate::common::{EguiComponent, Id, Ports, SignalValue, Simulator}; +use crate::components::SignZeroExtend; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::helper::basic_component_gui; +use egui::{Rect, Response, RichText, Ui, Vec2}; + +#[typetag::serde] +impl EguiComponent for SignZeroExtend { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + match &simulator { + Some(sim) => { + ui.label(match sim.get_input_value(&self.signzero_ctrl_in) { + SignalValue::Uninitialized => { + " Sign/Zero extend:\nUninitialized cntr".to_string() + } + SignalValue::Unknown => "Sign/Zero extend:\nextendUnknown".to_string(), + SignalValue::DontCare => "Sign/Zero extend:\nDon't Care".to_string(), + SignalValue::Data(v) => match v { + 0 => "Sign/Zero extend:\nZero", + 1 => "Sign/Zero extend:\nSign", + _ => "Sign/Zero extend:\nInvalid cntr", + } + .to_string(), + }); + } + + None => { + ui.label(RichText::new("Signal Extender:\nNo Sim").size(12f32 * scale)); + } + } + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } +} diff --git a/src/gui_egui/components/wire.rs b/src/gui_egui/components/wire.rs index a75e7979..02cef4d0 100644 --- a/src/gui_egui/components/wire.rs +++ b/src/gui_egui/components/wire.rs @@ -1,15 +1,63 @@ -use crate::common::{EguiComponent, Ports, SignalUnsigned, Simulator}; +use crate::common::{EguiComponent, Ports, Simulator}; use crate::components::Wire; -use crate::gui_egui::component_ui::{ - input_change_id, input_selector, rect_with_hover, visualize_ports, -}; +use crate::gui_egui::component_ui::{input_change_id, input_selector, visualize_ports}; use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions, SnapPriority}; use crate::gui_egui::gui::EguiExtra; -use crate::gui_egui::helper::{offset_helper, shadow_small_dark}; +use crate::gui_egui::helper::{basic_on_hover, offset_helper, shadow_small_dark}; use egui::{ - Color32, DragValue, Frame, Key, KeyboardShortcut, Margin, Modifiers, PointerButton, Pos2, Rect, - Response, Rounding, Shape, Stroke, Ui, Vec2, Window, + Color32, DragValue, Frame, Key, KeyboardShortcut, Margin, Modifiers, Order, PointerButton, + Pos2, Rect, Response, Rounding, Sense, Shape, Stroke, Ui, Vec2, Window, }; + +/// if the mouse cursor is less than this distance in points away from the wire display tooltip +/// Note points is often same as pixels, but some times differ with the points_per_pixels value in egui +const TOOLTIP_DISTANCE: f32 = 5.0; + +/// Calculates the minimum distance the point is from our line going from start: Vec2 to end: Vec2 +fn min_from_line(start: Vec2, end: Vec2, point: Vec2) -> f32 { + // could probably use length_sq, but this don't need to be optimized + let length: f32 = (end - start).length(); + // if length is zero, get length between start and point + if length == 0f32 { + return (start - point).length(); + }; + + let dir_to_end: Vec2 = (end - start).normalized(); + let point_rel_to_start: Vec2 = point - start; + // dot product, + // a dot b = abs(a)*abs(b)*cos(theta) + // if abs(a)=1 we can use this to determine how far along the line our point is + let dist_along_line: f32 = dir_to_end.dot(point_rel_to_start); + + // if we are before our line start + if dist_along_line < 0f32 { + // distance to our start point + (point - start).length() // return this value + } + // if our point is after the end of our line + else if dist_along_line > length { + // distance to our end point + (point - end).length() // return this value + } + // our point is between the line + else { + // project vec a up on vec b + // theta is the angel between our vectors + // abs(a) * cos(theta) * b/abs(b) + // we can se the resemblance to a dot product + // abs(a) * abs(b) * cos(theta) * b/abs(b) # one to much abs(b) + // abs(a) * abs(b) * cos(theta) * b/(abs(b))^2 + // a dot b * b/abs(b)^2 + // this is our point projected along our line (line starts at origin (0,0)) + // if abs(b)=1, aka normalized we can simplify the math + // a dot b * b/1^2 + // a dot b * b + // a dot b is already calculated in dist along line + let proj_point_on_line: Vec2 = dist_along_line * dir_to_end; + // lets use this to calculate the orthogonal vector from our line to our point + (point_rel_to_start - proj_point_on_line).length() // return this value + } +} use log::trace; #[typetag::serde] @@ -39,49 +87,87 @@ impl EguiComponent for Wire { line_vec.push(oh(pos, s, o)); } - ui.painter().add(Shape::line( - line_vec.clone(), - Stroke { - width: scale, - color: if is_active { - Color32::RED - } else { - Color32::BLACK - }, - }, - )); - let mut r_vec = vec![]; - - for (i, _) in line_vec[1..].iter().enumerate() { - let (line_top, line_bottom) = if line_vec[i].x > line_vec[i + 1].x { - (line_vec[i + 1].x, line_vec[i].x) - } else { - (line_vec[i].x, line_vec[i + 1].x) - }; - let (line_left, line_right) = if line_vec[i].y > line_vec[i + 1].y { - (line_vec[i + 1].y, line_vec[i].y) - } else { - (line_vec[i].y, line_vec[i + 1].y) - }; - let rect = Rect { - min: Pos2::new(line_top, line_left), - max: Pos2::new(line_bottom, line_right), + let mut hovered = false; + let mut r: Vec = vec![]; + + for val in line_vec.windows(2) { + let first_pos = val[0]; + let last_pos = val[1]; + let rect = Rect::from_two_pos(first_pos, last_pos).expand(2.5); + + #[allow(clippy::single_match)] + match editor_mode { + EditorMode::Default => { + // why the fuck do i need this much code just to make sure its rendered at the correct layer + let resp = ui + .allocate_ui_at_rect(rect, |ui| { + let mut layer = ui.layer_id(); + layer.order = Order::Middle; + ui.with_layer_id(layer, |ui| { + ui.allocate_exact_size( + rect.size(), + Sense { + click: true, + drag: true, + focusable: true, + }, + ) + }) + }) + .inner + .inner + .1; + // log::debug!("{:?}", resp); + if resp.contains_pointer() { + ui.painter().rect_stroke( + resp.interact_rect, + Rounding::same(0.0), + Stroke { + width: scale, + color: Color32::RED, + }, + ); + } + r.push(resp); + } + _ => {} }; - let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { - ui.label(format!("Id: {}", self.id.clone())); - if let Some(s) = &simulator { - ui.label({ - let r: Result = - s.get_input_value(&self.input).try_into(); - match r { - Ok(data) => format!("{:#x}", data), - _ => format!("{:?}", r), - } - }); + if let Some(cursor) = ui.ctx().pointer_latest_pos() { + if min_from_line(first_pos.to_vec2(), last_pos.to_vec2(), cursor.to_vec2()) + < TOOLTIP_DISTANCE + && clip_rect.contains(cursor) + && !hovered + { + hovered = true; + egui::containers::popup::show_tooltip_at( + ui.ctx(), + ui.layer_id(), + egui::Id::new(&self.id), + (first_pos + last_pos.to_vec2()) / 2.0, + |ui| basic_on_hover(ui, self, &simulator), + ); } - }); - r_vec.push(r); + }; + } + + let sk = Stroke { + width: if hovered { scale * 3.0 } else { scale }, + color: Color32::from_rgba_unmultiplied( + self.color_rgba[0], + self.color_rgba[1], + self.color_rgba[2], + self.color_rgba[3], + ), + }; + if is_active { + ui.painter().add(Shape::line(line_vec.clone(), sk)); + } else { + Shape::dashed_line(&line_vec, sk, 10.0, 2.0) + .drain(..) + .for_each(|s| { + ui.painter().add(s); + }); } match editor_mode { @@ -89,7 +175,7 @@ impl EguiComponent for Wire { _ => visualize_ports(ui, self.ports_location(), offset_old, scale, clip_rect), } - Some(r_vec) + Some(r) } fn render_editor( @@ -180,27 +266,42 @@ impl EguiComponent for Wire { id_ports, self.id.clone(), ); + let mut c: Color32 = Color32::from_rgba_unmultiplied( + self.color_rgba[0], + self.color_rgba[1], + self.color_rgba[2], + self.color_rgba[3], + ); + ui.color_edit_button_srgba(&mut c); + self.color_rgba = c.to_array(); let mut i = 0; + let mut to_insert: Option<(usize, (f32, f32))> = None; let mut first_item = true; self.pos.retain_mut(|seg_pos| { let mut delete = false; ui.horizontal(|ui| { ui.label(format!("Segment {}:", i)); ui.label("pos x"); - ui.add(DragValue::new(&mut seg_pos.0)); + ui.add(DragValue::new(&mut seg_pos.0).speed(0.5)); ui.label("pos y"); - ui.add(DragValue::new(&mut seg_pos.1)); + ui.add(DragValue::new(&mut seg_pos.1).speed(0.5)); if first_item { first_item = false; } else if ui.button("🗙").clicked() { delete = true; } + if ui.button("NEW").clicked() { + to_insert = Some((i, *seg_pos)); + } }); i += 1; !delete }); + if let Some((i, pos)) = to_insert { + self.pos.insert(i, pos) + }; if ui.button("+ Add new segment").clicked() { self.pos.push(*self.pos.last().unwrap()); diff --git a/src/gui_egui/components/zero_extend.rs b/src/gui_egui/components/zero_extend.rs new file mode 100644 index 00000000..3d82f06f --- /dev/null +++ b/src/gui_egui/components/zero_extend.rs @@ -0,0 +1,99 @@ +use crate::common::{EguiComponent, Id, Input, Ports, Simulator}; +use crate::components::{ZeroExtend, ZEROEXTEND_OUT_ID}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::helper::basic_component_gui; +use egui::{pos2, Pos2, Rect, Response, Ui, Vec2}; + +const HEIGHT: f32 = 14.0; +const WIDTH: f32 = 70.0; + +#[typetag::serde] +impl EguiComponent for ZeroExtend { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + basic_component_gui(self, &simulator, ui.ctx(), offset, scale, clip_rect, |ui| { + ui.set_height(HEIGHT * scale); + ui.set_width(WIDTH * scale); + ui.label("Zero Extend"); + }) + } + + fn render_editor( + &mut self, + ui: &mut egui::Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: egui::Vec2, + scale: f32, + clip_rect: egui::Rect, + _id_ports: &[(Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + self.render( + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ); + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } + + fn top_padding(&self) -> f32 { + 20f32 + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + const M: f32 = 6.0; + vec![ + ( + crate::components::ZEROEXTEND_SIGNAL_IN_ID.to_string(), + pos2(-WIDTH / 2.0 - M, 0.0) + own_pos, + ), + ( + crate::components::ZEROEXTEND_OUT_ID.to_string(), + pos2(WIDTH / 2.0 + M, 0.0) + own_pos, + ), + ] + } + + fn get_input_location(&self, id: Input) -> Option<(f32, f32)> { + let loc = self + .ports_location() + .iter() + .map(|(_, loc)| <(f32, f32)>::from(loc)) + .collect::>(); + + if id == self.signal_in { + Some(loc[0]) + } else if id == Input::new(&self.id, ZEROEXTEND_OUT_ID) { + Some(loc[1]) + } else { + None + } + } +} diff --git a/src/gui_egui/editor.rs b/src/gui_egui/editor.rs index a5a4d9fc..ff1da204 100644 --- a/src/gui_egui/editor.rs +++ b/src/gui_egui/editor.rs @@ -86,6 +86,7 @@ impl Default for Library { pos: (0.0, 0.0), a_in: dummy_input.clone(), b_in: dummy_input.clone(), + scale: 1.0, }), Rc::new(Constant { id: "c".to_string(), @@ -127,6 +128,7 @@ impl Default for Library { pos: (0.0, 0.0), select: dummy_input.clone(), m_in: vec![dummy_input.clone(), dummy_input.clone()], + scale: 1.0, }), Rc::new(Register { id: "reg".to_string(), @@ -357,7 +359,11 @@ impl Editor { _ => e.components.retain_mut(|c| { let old_key = c.as_ref().get_id_ports().0; let mut context = e.contexts.remove(&old_key).unwrap(); - let render_return = (*Rc::get_mut(c).unwrap()).render_editor( + let debug_id = c.get_id_ports().0; + #[allow(clippy::expect_fun_call)] + let render_return = (*Rc::get_mut(c) + .expect(&format!("More than one reference exist to {}, can't get mut, therefore not render editor", debug_id))) + .render_editor( ui, &mut context, None, diff --git a/src/gui_egui/editor_wire_mode.rs b/src/gui_egui/editor_wire_mode.rs index 70b79559..6cb58810 100644 --- a/src/gui_egui/editor_wire_mode.rs +++ b/src/gui_egui/editor_wire_mode.rs @@ -101,11 +101,7 @@ pub fn last_click(e: &mut Editor, closest_uw: CloseToComponent) { #[allow(ambiguous_wide_pointer_comparisons)] if !Rc::ptr_eq(&in_c.comp, &out_c.comp) { let comp = if is_input_in_comp_start { out_c } else { in_c }; - e.components.push(Rc::new(Wire { - id: id.to_string(), - pos: pos_v, - input: i.clone(), - })); + e.components.push(Rc::new(Wire::new(id, pos_v, i.clone()))); e.contexts.insert( id.to_string(), EguiExtra { diff --git a/src/gui_egui/gui.rs b/src/gui_egui/gui.rs index 342d0ccf..7b68456b 100644 --- a/src/gui_egui/gui.rs +++ b/src/gui_egui/gui.rs @@ -149,7 +149,7 @@ impl Gui { } }); let cpr = central_panel.response.interact(Sense::drag()); - if cpr.dragged_by(PointerButton::Middle) { + if cpr.dragged_by(PointerButton::Primary) { self.pan += cpr.drag_delta(); } if central_panel.response.hovered() { diff --git a/src/gui_egui/helper.rs b/src/gui_egui/helper.rs index 2652fffe..fa098a52 100644 --- a/src/gui_egui/helper.rs +++ b/src/gui_egui/helper.rs @@ -1,6 +1,9 @@ -use crate::common::{Components, Ports}; +use crate::common::{Components, EguiComponent, Input, Ports, SignalValue, Simulator}; use crate::gui_egui::editor::{EditorMode, SnapPriority}; -use egui::{Color32, Pos2, Rect, Sense, Vec2}; +use egui::{ + Align2, Area, Color32, Context, InnerResponse, Order, Pos2, Rect, Response, Sense, + TextWrapMode, Ui, Vec2, +}; use epaint::Shadow; pub fn offset_reverse_helper_pos2(xy: Pos2, scale: f32, offset: Vec2) -> Pos2 { @@ -116,3 +119,201 @@ pub fn shadow_small_dark() -> Shadow { color: Color32::BLACK, } } + +pub fn component_area( + id: String, + ctx: &Context, + pos: impl Into, + content: impl FnOnce(&mut Ui) -> R, +) -> InnerResponse { + Area::new(egui::Id::from(id)) + .order(Order::Middle) + .current_pos(pos) + .movable(false) + .enabled(true) + .interactable(false) + .pivot(Align2::CENTER_CENTER) + .constrain(false) + .show(ctx, content) +} + +/// This renders a basic component +/// Use Content to add label or other graphical info +/// and if desired implement a custom on hover function. +/// The default hover function displays component id and in/out signals formatet as hex +/// +/// # Arguments +/// - size: if size is (0f32,0f32) the component will be as large as its content +/// - content: Note the function don't size the components, +/// that is the responsibility of the content closure +/// - on_hover: if this is some this overides the on hover function and displays that instead +/// +/// # Example +/// This renders a box with the size of 100 by 20, this is scaled with the passed scaled. +/// It is also moved according to the offset argument. +/// +/// In the box the label "Jump Merge" is displayed. +/// +/// And an possible default on hover label might be +/// +/// id: jump_merge +/// +/// merge_instr_addr_in <- reg:out (0x00000000) +/// +/// merge_jump_addr_in <- c1:out (0x00000000) +/// +/// out-> 0x00000000 +/// +/// ``` +/// # use std::any::Any; +/// # use egui::{Ui, Vec2, Rect, Response}; +/// # use syncrim::common::{Ports, EguiComponent, Component, Simulator}; +/// # use syncrim::gui_egui::{EguiExtra, editor::EditorMode}; +/// # use serde::{Deserialize, Serialize}; +/// # #[derive(Serialize, Deserialize, Clone)] +/// # struct JumpMerge {tmp: u32} +/// # impl Component for JumpMerge { +/// # fn get_id_ports(&self) -> (std::string::String, Ports) { todo!() } +/// # fn as_any(&self) -> &(dyn Any + 'static) { todo!() } +/// # fn typetag_name(&self) -> &'static str { todo!() } +/// # fn typetag_deserialize(&self) { todo!() } +/// # } +/// +/// use syncrim::gui_egui::helper::basic_component_gui_with_on_hover; +/// +/// # #[typetag::serde] +/// impl EguiComponent for JumpMerge { +/// fn render( +/// &self, +/// ui: &mut Ui, +/// _context: &mut EguiExtra, +/// simulator: Option<&mut Simulator>, +/// offset: Vec2, +/// scale: f32, +/// clip_rect: Rect, +/// _editor_mode: EditorMode, +/// ) -> Option> { +/// // size of the component +/// let width = 100f32; +/// let height: f32 = 20f32; +/// basic_component_gui_with_on_hover( +/// self, +/// ui.ctx(), +/// offset, +/// scale, +/// clip_rect, +/// |ui| { +/// ui.label("Jump Merge"); +/// }, +/// |ui| { +/// ui.label("i am hovered"); +/// }, +/// ) +/// } +/// } +/// ``` +pub fn basic_component_gui_with_on_hover( + component: &dyn EguiComponent, + ctx: &Context, + offset: impl Into, + scale: f32, + clip_rect: Rect, + content: impl FnOnce(&mut Ui), + on_hover: impl FnOnce(&mut Ui), +) -> Option> { + let offset: Vec2 = offset.into(); + + let r = component_area( + component.get_id_ports().0, + ctx, + Pos2::from(component.get_pos()) * scale + offset, + |ui| { + ui.set_clip_rect(clip_rect); + + ui.style_mut().wrap_mode = Some(TextWrapMode::Extend); + + for (_text_style, font) in ui.style_mut().text_styles.iter_mut() { + font.size *= scale; + } + ui.spacing_mut().button_padding *= scale; + ui.spacing_mut().item_spacing *= scale; + ui.spacing_mut().combo_height *= scale; + ui.spacing_mut().combo_width *= scale; + ui.spacing_mut().icon_width *= scale; + ui.spacing_mut().icon_width_inner *= scale; + ui.spacing_mut().icon_spacing *= scale; + ui.spacing_mut().interact_size *= scale; + + let mut group = egui::containers::Frame::group(ui.style()); + group.inner_margin *= scale; + group.rounding *= scale; + // group.fill = Color32::LIGHT_RED; // Use this ween component background is implemented, probably when we implement dark mode + group + .show(ui, |ui| { + content(ui); + }) + .response + }, + ) + .inner; + + r.clone().on_hover_ui(on_hover); + Some(vec![r]) +} + +pub fn basic_component_gui( + component: &dyn EguiComponent, + simulator: &Option<&mut Simulator>, + ctx: &Context, + offset: impl Into, + scale: f32, + clip_rect: Rect, + content: impl FnOnce(&mut Ui), +) -> Option> { + basic_component_gui_with_on_hover(component, ctx, offset, scale, clip_rect, content, |ui| { + basic_on_hover(ui, component, simulator) + }) +} + +/// example +/// r.on_hover(|ui| { +/// basic_on_hover(ui,self,simulator) +/// }) +pub fn basic_on_hover( + ui: &mut Ui, + component: &dyn EguiComponent, + simulator: &Option<&mut Simulator>, +) { + ui.set_max_width(200.0); + ui.label(format!("id: {}", component.get_id_ports().0)); + if let Some(sim) = simulator { + ui.separator(); + for port in component.get_id_ports().1.inputs { + ui.label(format!( + "{} <- {}:{} ({})", + port.port_id, + port.input.id, + port.input.field, + match sim.get_input_value(&port.input) { + SignalValue::Uninitialized => "Uninitialized".to_string(), + SignalValue::Unknown => "Unknown".to_string(), + SignalValue::DontCare => "don't care".to_string(), + SignalValue::Data(v) => format!("{:#010x}", v), + }, + )); + } + ui.separator(); + for port_id in component.get_id_ports().1.outputs { + ui.label(format!( + "{} -> {}", + port_id, + match sim.get_input_value(&Input::new(&component.get_id_ports().0, &port_id)) { + SignalValue::Uninitialized => "Uninitialized".to_string(), + SignalValue::Unknown => "Unknown".to_string(), + SignalValue::DontCare => "Don't care".to_string(), + SignalValue::Data(v) => format!("{:#010x}", v), + }, + )); + } + }; +} diff --git a/src/gui_egui/menu.rs b/src/gui_egui/menu.rs index 47c7ae99..61c3f6c6 100644 --- a/src/gui_egui/menu.rs +++ b/src/gui_egui/menu.rs @@ -4,7 +4,6 @@ use crate::gui_egui::{ keymap, }; use egui::{menu, Button, DragValue, KeyboardShortcut, Response, Ui}; - pub(crate) struct Menu {} impl Menu { diff --git a/src/lib.rs b/src/lib.rs index 1578e478..30025954 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,8 @@ pub mod components; #[cfg(feature = "gui-vizia")] pub mod gui_vizia; +#[cfg(feature = "gui-egui")] +pub mod autowire; // Egui frontend #[cfg(feature = "gui-egui")] pub mod gui_egui; diff --git a/src/simulator.rs b/src/simulator.rs index a72b1d5c..036601b4 100644 --- a/src/simulator.rs +++ b/src/simulator.rs @@ -170,12 +170,12 @@ impl Simulator { halt_on_warning: false, running_state: RunningState::Stopped, component_condition: vec![], + running_state_history: vec![], + component_condition_history: vec![], // used for determine active components sinks, inputs_read: HashMap::new(), active: HashSet::new(), - running_state_history: vec![], - component_condition_history: vec![], }; trace!("sim_state {:?}", simulator.sim_state); @@ -199,6 +199,10 @@ impl Simulator { .id_field_index .get(&(input.id.clone(), input.field.clone())) .unwrap_or_else(|| { + error!( + "Component {:?}, field {:?} not found.", + input.id, input.field + ); panic!( "Component {:?}, field {:?} not found.", input.id, input.field @@ -599,6 +603,6 @@ mod test { let simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); - let _ = simulator.get_input_fmt(&Input::new("c", "out")); + let _ = simulator.get_input_fmt(&Input::new("c", CONSTANT_OUT_ID)); } }