diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d59641aa..6107f558 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,7 +2,7 @@ name: Rust on: push: - branches: ["master"] + branches: ["master", "egui_generic"] pull_request: branches: ["master"] @@ -42,4 +42,4 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: rustfmt - run: cargo fmt --all -- --check \ No newline at end of file + run: cargo fmt --all -- --check diff --git a/.vscode/settings.json b/.vscode/settings.json index 41f07beb..552ed7e1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,21 +14,38 @@ "Swatinem", "Textbox", "Tpdf", + "bgeu", + "bltu", "blueviolet", + "clic", + "clicintattr", + "clicintctl", + "clicintie", + "clicintip", + "csrstore", "darkgray", "eframe", "egui", "epaint", "graphviz", "hoverable", + "imem", + "interactable", + "jalr", "librust", "lightcoral", "lightgray", "lightgreen", + "lsbzero", "menubutton", + "mepc", + "mintthresh", + "mmio", + "mret", "petgraph", "println", "regfile", + "regs", "repr", "rgbaf", "rgbf", @@ -39,6 +56,7 @@ "struct", "stylesheet", "syncrim", + "szext", "toposort", "trik", "typetag", diff --git a/Cargo.toml b/Cargo.toml index cf3989bd..1ffb1a49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,18 +29,18 @@ optional = true [dependencies.egui] optional = true -version = "0.22.0" +version = "0.23.0" [dependencies.eframe] optional = true -version = "0.22.0" +version = "0.23.0" [dependencies.epaint] optional = true -version = "0.22.0" +version = "0.23.0" [features] -default = ["gui-vizia"] +default = ["gui-egui"] components = [] gui-vizia = ["vizia", "components"] gui-egui = ["egui", "eframe", "epaint", "components"] diff --git a/empty.json b/empty.json new file mode 100644 index 00000000..1428d803 --- /dev/null +++ b/empty.json @@ -0,0 +1,3 @@ +{ + "store": [] +} diff --git a/examples/add.rs b/examples/add.rs index 45d5b0d9..b0486381 100644 --- a/examples/add.rs +++ b/examples/add.rs @@ -1,4 +1,6 @@ use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; use syncrim::{ common::{ComponentStore, Input}, components::*, @@ -40,8 +42,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/add_edit.rs b/examples/add_edit.rs index ebb16edd..74978e2b 100644 --- a/examples/add_edit.rs +++ b/examples/add_edit.rs @@ -1,4 +1,6 @@ use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; use syncrim::{ common::{ComponentStore, Input}, components::*, @@ -40,8 +42,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/add_mux.rs b/examples/add_mux.rs index f2601558..d6700952 100644 --- a/examples/add_mux.rs +++ b/examples/add_mux.rs @@ -1,10 +1,11 @@ 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 { @@ -107,8 +108,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/add_reg.rs b/examples/add_reg.rs index bdbe2a7a..4081e3be 100644 --- a/examples/add_reg.rs +++ b/examples/add_reg.rs @@ -1,10 +1,11 @@ use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; use syncrim::{ common::{ComponentStore, Input, SignalFmt, SignalSize}, components::*, fern::fern_setup, }; - fn main() { fern_setup(); @@ -66,8 +67,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/add_reg_compound_wire.rs b/examples/add_reg_compound_wire.rs index 3fdabf08..07bfdc3d 100644 --- a/examples/add_reg_compound_wire.rs +++ b/examples/add_reg_compound_wire.rs @@ -1,10 +1,11 @@ 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 { @@ -48,8 +49,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/data_mem.rs b/examples/data_mem.rs index 21a25078..37585b42 100644 --- a/examples/data_mem.rs +++ b/examples/data_mem.rs @@ -1,10 +1,11 @@ use std::{ops::Range, path::PathBuf}; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; use syncrim::{ common::{ComponentStore, Input, SignalUnsigned}, components::*, fern::fern_setup, }; - fn main() { fern_setup(); let cs = ComponentStore { @@ -79,8 +80,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/mux_edit.rs b/examples/mux_edit.rs index 61055fa1..513b86cf 100644 --- a/examples/mux_edit.rs +++ b/examples/mux_edit.rs @@ -1,10 +1,11 @@ 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 { @@ -63,8 +64,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/probe_constant.rs b/examples/probe_constant.rs index 94a1885e..e62fe703 100644 --- a/examples/probe_constant.rs +++ b/examples/probe_constant.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; use syncrim::{common::ComponentStore, components::*, fern::fern_setup}; - fn main() { fern_setup(); @@ -15,8 +16,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/probe_edit.rs b/examples/probe_edit.rs index 6f24a398..0764ea6f 100644 --- a/examples/probe_edit.rs +++ b/examples/probe_edit.rs @@ -1,10 +1,11 @@ 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 { @@ -18,8 +19,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/probe_stim.rs b/examples/probe_stim.rs index 843e04ce..ddcb92a3 100644 --- a/examples/probe_stim.rs +++ b/examples/probe_stim.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; +#[cfg(feature = "gui-egui")] +use syncrim::gui_egui::editor::Library; use syncrim::{common::ComponentStore, components::*, fern::fern_setup}; - fn main() { fern_setup(); let cs = ComponentStore { @@ -11,8 +12,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/probe_stim_assert.rs b/examples/probe_stim_assert.rs index 194bc8c7..7a265c79 100644 --- a/examples/probe_stim_assert.rs +++ b/examples/probe_stim_assert.rs @@ -1,10 +1,11 @@ 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 { @@ -23,8 +24,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/examples/sext.rs b/examples/sext.rs index 35d53c68..84cf8484 100644 --- a/examples/sext.rs +++ b/examples/sext.rs @@ -1,10 +1,11 @@ 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 { @@ -21,7 +22,7 @@ fn main() { vec![(220.0, 100.0), (250.0, 100.0)], Input::new("sxt0", "out"), ), - Probe::rc_new("p1", (260.0, 100.0), Input::new("sxt0", "out")), + Cross::rc_new("p1", (260.0, 100.0), Input::new("sxt0", "out")), ], }; @@ -29,8 +30,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/mips/examples/mips.rs b/mips/examples/mips.rs index 67e6333b..0b33da4a 100644 --- a/mips/examples/mips.rs +++ b/mips/examples/mips.rs @@ -68,8 +68,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/mips/examples/reg_file.rs b/mips/examples/reg_file.rs index 56ef3f62..e02bd63f 100644 --- a/mips/examples/reg_file.rs +++ b/mips/examples/reg_file.rs @@ -92,8 +92,8 @@ fn main() { cs.save_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + syncrim::gui_egui::gui(cs, &path).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } diff --git a/mips/src/components/instr_mem.rs b/mips/src/components/instr_mem.rs index 23a32e19..c9508c52 100644 --- a/mips/src/components/instr_mem.rs +++ b/mips/src/components/instr_mem.rs @@ -1,9 +1,14 @@ use serde::{Deserialize, Serialize}; use std::rc::Rc; use syncrim::common::{ - Component, Condition, Input, OutputType, Ports, SignalUnsigned, SignalValue, Simulator, + 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, @@ -24,9 +29,12 @@ impl Component for InstrMem { ( self.id.clone(), Ports { - inputs: vec![self.pc.clone()], + inputs: vec![InputPort { + port_id: INSTR_MEM_PC_ID.to_string(), + input: self.pc.clone(), + }], out_type: OutputType::Combinatorial, - outputs: vec!["out".into()], + outputs: vec![INSTR_MEM_OUT_ID.to_string()], }, ) } @@ -47,9 +55,13 @@ impl Component for InstrMem { // set output trace!("--- output {:?}", instr); - simulator.set_out_value(&self.id, "out", instr); + simulator.set_out_value(&self.id, INSTR_MEM_OUT_ID, instr); Ok(()) } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } impl InstrMem { diff --git a/mips/src/components/reg_file.rs b/mips/src/components/reg_file.rs index ac7d6552..4a776f4c 100644 --- a/mips/src/components/reg_file.rs +++ b/mips/src/components/reg_file.rs @@ -3,7 +3,9 @@ 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, OutputType, Ports, SignalUnsigned, Simulator}; +use syncrim::common::{ + Component, Condition, Input, InputPort, OutputType, Ports, SignalUnsigned, Simulator, +}; #[allow(non_camel_case_types)] #[rustfmt::skip] @@ -44,6 +46,15 @@ pub enum Reg { 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, @@ -203,11 +214,26 @@ impl Component for RegFile { self.id.clone(), Ports { inputs: vec![ - self.read_addr1.clone(), - self.read_addr2.clone(), - self.write_addr.clone(), - self.write_data.clone(), - self.write_enable.clone(), + 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()], @@ -230,13 +256,16 @@ impl Component for RegFile { // 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_a", 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_b", 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)] @@ -279,7 +308,7 @@ mod test { ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); diff --git a/mips/src/main.rs b/mips/src/main.rs index 0366ca74..a3e56e2e 100644 --- a/mips/src/main.rs +++ b/mips/src/main.rs @@ -6,8 +6,11 @@ fn main() { fern_setup(); let path = PathBuf::from("mips.json"); - let _cs = ComponentStore::load_file(&path); + let cs = ComponentStore::load_file(&path); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&_cs, &path); + syncrim::gui_vizia::gui(cs, &path); + + #[cfg(not(any(feature = "gui-vizia", feature = "gui-egui")))] + let _ = syncrim::common::Simulator::new(cs); } diff --git a/riscv/Cargo.toml b/riscv/Cargo.toml index c43f1db2..606d0d43 100644 --- a/riscv/Cargo.toml +++ b/riscv/Cargo.toml @@ -10,20 +10,28 @@ serde = "1.0.167" serde_derive = "1.0.167" typetag = "0.2.9" serde_json = "1.0.100" -riscv-elf-parse = {git = 'https://github.com/onsdagens/riscv-elf-parse'} -clap = {version="4.3.11",features=["derive"]} +riscv-elf-parse = { git = 'https://github.com/onsdagens/riscv-elf-parse/' } +clap = { version = "4.3.11", features = ["derive"] } log = "0.4.19" -num_enum = "0.6.1" +num_enum = "0.7.2" fern = "0.6.2" xmas-elf = "0.9.0" -asm_riscv = "0.1.0" +egui = "0.23.0" +asm_riscv = { git = 'https://github.com/onsdagens/wari' } +gimli = "0.27.3" +object = "0.31.1" +memmap2 = "0.7.1" +riscv_asm_strings = { git = 'https://github.com/perlindgren/riscv_asm_strings' } +priority-queue = { version = "1.3.2", features = ["serde"] } +riscv-rt = "0.11.0" +egui_extras = "0.23.0" [dependencies.syncrim] path = "../" default-features = false [features] -default = ["gui-vizia"] +default = ["gui-egui"] components = ["syncrim/components"] gui-vizia = ["syncrim/gui-vizia", "components"] diff --git a/riscv/README.md b/riscv/README.md index 90f917c5..8e2cbef6 100644 --- a/riscv/README.md +++ b/riscv/README.md @@ -2,13 +2,30 @@ RISCV specific components. +For now this requires nightly to compile the included assembly. + +## Initializing the model with assembly + ```cargo run --example riscv``` Runs the simulation with default settings. This means compiling the assembly source file ``./asm.s``, linking it using ``./memory.x`` , and initializing the instruction and data memory accordingly. This is all done using cargo, meaning no dependencies except the ``riscv32i-unknown-none-elf`` target. It may be installed via ``rustup target add riscv32i-unknown-none-elf`` -A sample ``asm.s`` is provided, but any instructions except opcodes ``CSR`` and ``MISC-MEM`` (so CSR read/writes and FENCE instructions) are supported, so experiment! +A sample ``asm.s`` is provided, but any instructions except opcode ``MISC-MEM`` (so FENCE instructions) are supported, so experiment! To provide your own source file or linker script, use ``asm-path=$ASM_PATH`` and ``ls-path=$LS_PATH`` respectively. The default values are ``asm.s`` and ``memory.x``. To skip compilation and linking, the ``use-elf`` flag can be used along with ``elf-path=$ELF_PATH`` to provide the simulation with an already compiled ELF file. + +## Initializing the model with Rust + +```cargo run --example riscv -- --rust``` +Runs the simulation with the Rust flag. + +This means compiling the crate ``riscv-basic`` in ``./``, linking it using ``./memory.x`` and initializing the instruction and data memory. + +Similarly to the assembly example, this is all done automatically using cargo, meaning no additional dependencies except the ``riscv32i-unknown-none-elf`` target. + +A sample application is provided, in this case a simple [``RTIC``](https://github.com/rtic-rs/rtic/) application which spawns a task ``foo``, that spawns another task ``baz`` . ``baz`` does nothing and returns, and upon that ``foo`` loops infintely. + +The Rust support for the ``CLIC`` and the ``RTIC`` support for CLIC-equipped RISC-V CPUs is in an early work in progress stage, driven by the development of the SyncRim RISC-V model. diff --git a/riscv/asm.s b/riscv/asm.s index 03f5000d..9954955e 100644 --- a/riscv/asm.s +++ b/riscv/asm.s @@ -1,319 +1,800 @@ -.option arch, rv32i -# ---------------------------------------------------------- -# Group 1's "underlag" for Lab 1 -# Pseudo-instructions may be used for Lab 1. -# ---------------------------------------------------------- - - - -# Group 1's Codeword Generator Subroutine (pseudocode) -# (remember: "seed" is a global variable, UNSIGNED INTEGER; -# you may implement local variables in registers or on the stack; -# result returned in v0; preserve all except t regs) -# -# FUNCTION codgen(): UNSIGNED INTEGER; -# LOCAL SIGNED INTEGER n; -# LOCAL UNSIGNED INTEGER x, y; -# BEGIN -# n := [count the number of 0's in word "seed"]; -# x := [rotate "seed" left by 30 bits]; -# y := [shift "seed" right-ARITHMETIC by 6 bits]; -# seed := x XOR y XOR n; -# RETURN( seed XOR 0x464b713e ); -# END; -# -# hint: if "seed" is initialized to 0x3e944b9f, -# the first five calls will generate these values: -# 0x891432f9, 0x4aa1dccc, 0xc54270fa, 0x9885155f, 0xce83d1b8, ... -# your code is to be written farther down (see comment below). - - -# Group 1's Recursive Decoding Subroutine (pseudocode) -# (for "decode", all four local variables must be implemented ON THE -# STACK, and NOT in registers; implement the code literally,. -# no optimizations. We're trying to teach you something. -# remember: result returned in v0; preserve all except t regs) -# -# FUNCTION decode( wordarr, bytearr ): UNSIGNED INTEGER; -# (wordarr, bytearr passed by reference) -# LOCAL UNSIGNED INTEGER m, r, x, y; -# BEGIN -# x := ONE'S-COMPLEMENT of codgen(); -# IF ([contents of word at "wordarr"] = 0) THEN -# [byte pointed to by "bytearr"] := 0; -# r := x; -# ELSE -# y := decode( wordarr+, bytearr+ ); # "k+" means "successor in k" -# m := ( x - y ) - [contents of word at "wordarr"]; -# [byte pointed to by "bytearr"] := [the eight bits at "m"<20:13>]; -# r := TWO'S-COMPLEMENT OF codgen(); -# r := x + y + m + r + 5; -# ENDIF; -# RETURN( r ); -# END; - - -# ---------------------------------------------------------- -# The following are the ONLY lines that may appear in the -# ".data" section of the code. You may add NO other lines. -# NO additional global variables. -# ---------------------------------------------------------- - - - .data -test_word: .word 0xDEADBEEF -some_string: .string "Hi! :)" -.align 4 -abc: .word 0x9fdd9158 # string "abc", encoded - .word 0x85715808 - .word 0xac73323a - .word 0 -plain: .space 132 # room for 132 characters - - .align 4 -seed: .word 0 # 32-bit UNSIGNED INTEGER. - -coded: .word 0x015e7a47 # the real encoded data - .word 0x2ef84ebb - .word 0x177a8db4 - .word 0x1b722ff9 - .word 0x5dc7cff0 - .word 0x5dc9dea6 - .word 0x1da0c15a - .word 0xe4c236a2 - .word 0x3d16b0d0 - .word 0x1f397842 - .word 0xaae0d2ba - .word 0x11246674 - .word 0x0845317f - .word 0xd5512dad - .word 0xb6184977 - .word 0xd293a53e - .word 0x7d9c2716 - .word 0xd917eae6 - .word 0xd8852384 - .word 0x286e46f9 - .word 0xce566029 - .word 0xcefe7daf - .word 0x62d726d4 - .word 0x0dbaeb2d - .word 0x95f57c60 - .word 0xed515141 - .word 0x29b77d0f - .word 0x9f7b8d0c - .word 0x45a8395a - .word 0xfead2b72 - .word 0x883d434c - .word 0xed8ddf60 - .word 0xe51e65e4 - .word 0x19bf6bb1 - .word 0xfeb505ec - .word 0x662aa23c - .word 0xf6827cf8 - .word 0xd1dc7a5c - .word 0x4fa5b066 - .word 0x7ddd25a4 - .word 0xa8ba8e8a - .word 0x72846227 - .word 0xf8f636fb - .word 0x2b389a9c - .word 0xe4038bf6 - .word 0x6e169877 - .word 0xad028132 - .word 0x84dbfe8c - .word 0x243762ff - .word 0x59c8f80c - .word 0xb6e0db4b - .word 0xedb8cab7 - .word 0xcd4b39f6 - .word 0xaf263741 - .word 0x18d9965f - .word 0x1ab1f037 - .word 0x5b458792 - .word 0xc94d960d - .word 0xd45cedea - .word 0x2160aca3 - .word 0x93c77766 - .word 0x2d66e105 - .word 0x9ff74d4f - .word 0x6dc22f21 - .word 0x6b03d689 - .word 0x5fc48de0 - .word 0x1138f000 - .word 0xccb58e57 - .word 0xf9c8e200 - .word 0x7ab26e3c - .word 0xc61dcb3e - .word 0x6aefccb0 - .word 0x7a452f05 - .word 0xa5cf0731 - .word 0xa249383f - .word 0x628fe534 - .word 0xcad81710 - .word 0x7f616276 - .word 0x3ce18308 - .word 0xed4857ff - .word 0xd1e5b1d1 - .word 0xc2e84dc2 - .word 0xaa003742 - .word 0xaf637488 - .word 0x831afc48 - .word 0x287a69a0 - .word 0x6e04546e - .word 0x13dffa07 - .word 0x3232fb10 - .word 0xd69e2e09 - .word 0x355d8dc7 - .word 0xef902301 - .word 0x9a89ac15 - .word 0x967dc900 - .word 0x08dc2b1c - .word 0x6b5be690 - .word 0x894b0e02 - .word 0xe26af9af - .word 0xa6fd3b23 - .word 0xfcf213e5 - .word 0x85217608 - .word 0x7fd3be8b - .word 0xa2e757fb - .word 0x3717a341 - .word 0x85ee426d - .word 0x394bb856 - .word 0x12ac98c3 - .word 0xec7d4ab5 - .word 0x721b6989 - .word 0x30e36360 - .word 0xaa018403 - .word 0x9ee61196 - .word 0xa8697adc - .word 0x51e9d65a - .word 0x11023594 - .word 0xc4c4b36b - .word 0xda80bf7a - .word 0xbd5a645e - .word 0x18cea918 - .word 0xa723dda8 - .word 0x0126c05e - .word 0x2962d48a - .word 0xd5f7d312 - .word 0xb8947041 - .word 0x7c1e2e9a - .word 0x945eeac3 - .word 0x7110fb1c - .word 0xa7bc72cc - .word 0xdf47dfbb - .word 0x09a1c6c8 - .word 0xc2e41061 - .word 0 - - -# ---------------------------------------------------------- -# The following is the main program. You may not change this. -# You may only add your subroutines AFTER the "infinite end loop" instruction here. -# You MUST have two subroutines named "codgen" and "decode". -# BOTH must adhere to our calling conventions; -# both MUST preserve all registers except v-regs and t-regs; -# we are going to TEST for this when we run your code. -# ---------------------------------------------------------- - - - .text + .option norvc + .text +init: + la sp, _stack_start # set stack pointer + csrwi 0x350, 2 # set stack_depth + csrsi 0x0, 4 # enable GPIO 2 + csrci 0x1, 4 # GPIO 2 to output + csrsi 0x5, 4 # GPIO 2 toggle + csrsi 0x5, 4 # GPIO 2 toggle + csrsi 0x3, 4 # GPIO 2 set + csrsi 0x4, 4 # GPIO 2 clear + + csrsi 0x6, 4 # set GPIO 2 + csrsi 0x6, 4 # set GPIO 2 again + csrsi 0x3, 4 # set GPIO 2 via set reg should do nothing + csrsi 0x4, 4 # clear GPIO 2 via clear reg + csrsi 0x6, 4 # set GPIO 2 again + csrsi 0x5, 4 # toggle GPIO 2 off + csrsi 0x5, 4 # toggle GPIO 2 on + csrci 0x6, 4 # clear GPIO 2 main: - addi t0, t1, 0 - la t0, test_word - li t1, 0xFFFFFFFF - sw t1, 0(t0) - sw t1, 4(t0) - sw t1, 8(t0) - sw t1, 12(t0) - li t0, 0x500000F4 # address should be outide of memview range - sw t1, 0(t0) - sw t1, 4(t0) - sw t1, 8(t0) - sw t1, 12(t0) - li s0, 0x0e0657c1 # initialize "seed" - la s1, seed # initialize "seed" - sw s0, 0(s1) - la a0, coded # address of start of coded words - la a1, plain # address of start of decoded bytes - jal ra, codgen - jal ra, codgen - jal ra, codgen - jal ra, codgen - jal ra, codgen - jal ra, codgen - jal ra, decode # outer call to recursive "decode" -end: - j end # infinite loop; plaintext now in "plain". - - -# ---------------------------------------------------------- -# Group 1's assembly code for Function CodGen : -# ---------------------------------------------------------- - - # your activation record diagram here. - -# Group 1's Codeword Generator Subroutine (pseudocode) -# (remember: "seed" is a global variable, UNSIGNED INTEGER; -# you may implement local variables in registers or on the stack; -# result returned in v0; preserve all except t regs) -# -# FUNCTION codgen(): UNSIGNED INTEGER; -# LOCAL SIGNED INTEGER n; -# LOCAL UNSIGNED INTEGER x, y; -# BEGIN -# n := [count the number of 0's in word "seed"]; -# x := [rotate "seed" left by 30 bits]; -# y := [shift "seed" right-ARITHMETIC by 6 bits]; -# seed := x XOR y XOR n; -# RETURN( seed XOR 0x464b713e ); -# END; -# -# hint: if "seed" is initialized to 0x3e944b9f, -# the first five calls will generate these values: -# 0x891432f9, 0x4aa1dccc, 0xc54270fa, 0x9885155f, 0xce83d1b8, ... -# your code is to be written farther down (see comment below). - -#t1 = n -#t2 = x -#t3 = y -codgen: - la s1, seed - lw s0, 0(s1) - li t0, 1 - li t1, 0 -loop: - beq t0, x0, continue - and t2, t0, s0 - bne t2, x0, skip - addi t1, t1, 1 -skip: - sll t0, t0, 1 - jal x0, loop -continue: - slli t2, s0, 30 - srli t3, s0, 2 - or t2, t2, t3 #rotate - srai t3, s0, 6 #shift - addi s0, t2, 0 - xor s0, s0, t3 - xor s0, s0, t1 - sw s0, 0(s1) #update seed - jalr x0, ra - - - - - - - -# ---------------------------------------------------------- -# Group 1's assembly code for Function DeCode : -# ---------------------------------------------------------- - - # your activation record diagram here. - -decode: # your code here. - - -# end of file. + # setup for individual interrupts done using MMIO + # we are already at priority 0 + la t1, 0x01000100 # priority 1, enabled, not pended, will store at interrupt 1 + la t2, 0x02000100 # priority 2, enabled, not pended, will store at interrupt 2 + la t3, 0x03000100 # ... + la t4, 0x04000100 + la t5, 0x05000100 + la t6, 0x06000100 + la a1, 0x07000100 + la a2, 0x08000100 + la a3, 0x09000100 + csrw 0xB01, t1 # configure interrupt 1 via CSR + csrw 0xB02, t2 # configure interrupt 2 via CSR + csrw 0xB03, t3 + csrw 0xB04, t4 + csrw 0xB05, t5 + csrw 0xB06, t6 + csrw 0xB07, a1 + csrw 0xB08, a2 + # csrw 0xB09, a3 + la t1, 0x00005008 + la t2, 50 + #fire interrupt at 50 + sw t2, 0(t1) + # setup for vector tables and vector table pointers + la t0, .clic_vec # load 0 latency vector table address + csrw 0x351, t0 # store 0 latency vector table address at super_mtvec + la t0, .vector_table # load vanilla vector table address + csrw mtvec, t0 # store vanilla vector table address at mtvec + csrwi 0x347, 0 # set interrupt prio threshold to 0 + csrwi mstatus, 8 # enable global interrupts + li t1, 0x1 # 1 for pending + csrrs zero, 0xB02, t1 # pend interrupt 2, it should now be dispatched + #jal ra, helper # enable interrupts via helper +stop: + j stop # finished loop + +helper: + nop + nop + csrwi mstatus, 8 # mock an incoming interrupt in some helper. + nop + nop + jr ra + +isr_0: + j isr_0 # panic loop, we should never end up here + +isr_1: #interrupt 1 + #li a0, 1 + #csrrc zero, 0xB01, a0 # clear pending flag + jr ra # return + +isr_2: #interrupt 2 + li a0, 1 + #csrrc zero, 0xB02, a0 # unpend self + csrrs zero, 0xB01, a0 # pend interrupt 1 + #nop + #nop + #jal ra, helper + #nop + #nop + csrrs zero, 0xB04, a0 # pend interrupt 4 + nop # waste a cycle so we can see the return + jr ra # return +isr_3: #interrupt 3 + li a0, 1 + #csrrc zero, 0xB03, a0 # unpend self + jr ra # return +isr_4: #interrupt 4 + li a0, 1 + #csrrc zero, 0xB04, a0 # unpend self + la a1, EXIT_VAR + lw t0, 0(a1) # t0 = EXIT_VAR + bnez t0, isr4_exit # if EXIT_VAR==0 + sb a0, 0(a1) # store a 1 at EXIT_VAR + csrrs zero, 0xB06, a0 # pend interrupt 6 +isr4_exit: + jr ra # return +isr_5: #interrupt 5 + li a0, 1 + #csrrc zero, 0xB05, a0 # unpend self + jr ra # return +isr_6: #interrupt 6 + li a0, 1 + #csrrc zero, 0xB06, a0 # unpend self + csrrs zero, 0xB08, a0 # pend interrupt 8 + csrrs zero, 0xB03, a0 # pend interrupt 3 + jr ra # return +isr_7: #interrupt 7 + li a0, 1 + #csrrc zero, 0xB07, a0 # unpend self + jr ra # return +isr_8: #interrupt 8 + li a0, 1 + #csrrc zero, 0xB08, a0 # unpend self + csrrs zero, 0xB04, a0 # pend interrupt 4 + csrrs zero, 0xB05, a0 # pend interrupt 5 + jr ra # return +isr_9: + la t0, 0x5000 # timer base + lw t1, 0(t0) # timer lo + lw t2, 4(t0) # timer hi + addi t1, t1, 100 # queue self in 100 cycles + sw t1, 8(t0) + sw t2, 12(t0) + nop + nop + jr ra + .section .trap, "ax" +trap_0: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 3(a0) # load prio config register of interrupt 0 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_0 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt + +trap_1: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 7(a0) # load prio config register of interrupt 1 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + jal ra, isr_1 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt + +trap_2: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 11(a0) # load prio config register of interrupt 2 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_2 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt +trap_3: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 15(a0) # load prio config register of interrupt 3 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_3 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt + +trap_4: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 19(a0) # load prio config register of interrupt 4 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_4 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt +trap_5: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 23(a0) # load prio config register of interrupt 5 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + jal ra, isr_5 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt + +trap_6: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 27(a0) # load prio config register of interrupt 6 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_6 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt +trap_7: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 31(a0) # load prio config register of interrupt 7 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_7 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt +trap_8: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 35(a0) # load prio config register of interrupt 8 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_8 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt +trap_9: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 39(a0) # load prio config register of interrupt 8 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_9 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt + + .data +EXIT_VAR: .word 0x00000000 + .section .vector_table, "aw" + .word trap_0 + .word trap_1 + .word trap_2 + .word trap_3 + .word trap_4 + .word trap_5 + .word trap_6 + .word trap_7 + .word trap_8 + .word trap_9 + .word 0x20212223 + .word 0x24252627 + .word 0x28292A2B + .word 0x2C2D2E2F + .word 0x30313233 + .word 0x34353637 + .word 0x38393A3B + .word 0x3C3D3E3F + .word 0x40414243 + .word 0x44454647 + .word 0x48494A4B + .word 0x4C4D4E4F + .word 0x50515253 + .word 0x54555657 + .word 0x58595A5B + .word 0x5C5D5E5F + .word 0x60616263 + .word 0x64656667 + .word 0x68696A6B + .word 0x6C6D6E6F + .word 0x70717273 + .word 0x74757677 + .word 0x78797A7B + .word 0x7C7D7E7F + + .section .clic_vec, "aw" + .word isr_0 + .word isr_1 + .word isr_2 + .word isr_3 + .word isr_4 + .word isr_5 + .word isr_6 + .word isr_7 + .word isr_8 + .word isr_9 + .word 0x20212223 + .word 0x24252627 + .word 0x28292A2B + .word 0x2C2D2E2F + .word 0x30313233 + .word 0x34353637 + .word 0x38393A3B + .word 0x3C3D3E3F + .word 0x40414243 + .word 0x44454647 + .word 0x48494A4B + .word 0x4C4D4E4F + .word 0x50515253 + .word 0x54555657 + .word 0x58595A5B + .word 0x5C5D5E5F + .word 0x60616263 + .word 0x64656667 + .word 0x68696A6B + .word 0x6C6D6E6F + .word 0x70717273 + .word 0x74757677 + .word 0x78797A7B + .word 0x7C7D7E7F + + + diff --git a/riscv/asm.s.bak b/riscv/asm.s.bak new file mode 100644 index 00000000..7b761d7e --- /dev/null +++ b/riscv/asm.s.bak @@ -0,0 +1,685 @@ + .option norvc + .text +init: + la t0, 0x60000000 # GPIO addr + la t1, 0b100 # poke pin 2 + la t2, 0b1000 # poke pin 3 + sw t1, 0(t0) # enable pin 2 + sw t1, 12(t0) # set pin 2 high + sw t2, 12(t0) # poke pin 3 should do nothing + nop + nop + sw t1, 16(t0) # clear pin 2 + sw t2, 16(t0) # poke pin 3 should do nothing + nop + nop + sw t1, 20(t0) # toggle pin 2 should end up high + sw t2, 20(t0) # poke pin 3 should do nothing + nop + nop + sw t1, 20(t0) # toggle pin 2 should end up low + sw t2, 20(t0) # poke pin 3 should do nothing + +stop: + j stop # finished loop + +isr_0: + j isr_0 # panic loop, we should never end up here + +isr_1: #interrupt 1 + #li a0, 1 + #csrrc zero, 0xB01, a0 # clear pending flag + jr ra # return + +isr_2: #interrupt 2 + li a0, 1 + #csrrc zero, 0xB02, a0 # unpend self + csrrs zero, 0xB01, a0 # pend interrupt 1 + csrrs zero, 0xB04, a0 # pend interrupt 4 + nop # waste a cycle so we can see the return + jr ra # return +isr_3: #interrupt 3 + li a0, 1 + #csrrc zero, 0xB03, a0 # unpend self + jr ra # return +isr_4: #interrupt 4 + li a0, 1 + #csrrc zero, 0xB04, a0 # unpend self + la a1, EXIT_VAR + lw t0, 0(a1) # t0 = EXIT_VAR + bnez t0, isr4_exit # if EXIT_VAR==0 + sb a0, 0(a1) # store a 1 at EXIT_VAR + csrrs zero, 0xB06, a0 # pend interrupt 6 +isr4_exit: + jr ra # return +isr_5: #interrupt 5 + li a0, 1 + #csrrc zero, 0xB05, a0 # unpend self + jr ra # return +isr_6: #interrupt 6 + li a0, 1 + #csrrc zero, 0xB06, a0 # unpend self + csrrs zero, 0xB08, a0 # pend interrupt 8 + csrrs zero, 0xB03, a0 # pend interrupt 3 + jr ra # return +isr_7: #interrupt 7 + li a0, 1 + #csrrc zero, 0xB07, a0 # unpend self + jr ra # return +isr_8: #interrupt 8 + li a0, 1 + #csrrc zero, 0xB08, a0 # unpend self + csrrs zero, 0xB04, a0 # pend interrupt 4 + csrrs zero, 0xB05, a0 # pend interrupt 5 + jr ra # return + + .section .trap, "ax" +trap_0: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 3(a0) # load prio config register of interrupt 0 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_0 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt + +trap_1: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 7(a0) # load prio config register of interrupt 1 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + jal ra, isr_1 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt + +trap_2: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 11(a0) # load prio config register of interrupt 2 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_2 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt +trap_3: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 15(a0) # load prio config register of interrupt 3 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_3 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt + +trap_4: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 19(a0) # load prio config register of interrupt 4 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_4 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt +trap_5: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 23(a0) # load prio config register of interrupt 5 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + jal ra, isr_5 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt + +trap_6: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 27(a0) # load prio config register of interrupt 6 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_6 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt +trap_7: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 31(a0) # load prio config register of interrupt 7 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_7 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt +trap_8: + addi sp, sp, -0x4c # allocate space for the context on the stack + sw a0, 0x10(sp) # start by pushing a0 we it to stack CSRs and set threshold + csrrs a0, mstatus, x0 # read and stack mstatus + sw a0, 0x00(sp) + csrrs a0, mepc, x0 # read and stack mepc + sw a0, 0x04(sp) +#_STORE_PRIO SUBROUTINE + csrr a0, 0x347 # load current threshold + sw a0, 0x08(sp) # store old threshold on stack + li a0, 0x1000 # base address for the CLIC MMIO + lb a0, 35(a0) # load prio config register of interrupt 8 + csrw 0x347, a0 # set the priority + csrrsi x0, mstatus, 8 # enable interrupts (end of critical section) +#END + sw ra, 0x0c(sp) # stack the caller saved registers + sw a1, 0x14(sp) + sw a2, 0x18(sp) + sw a3, 0x1c(sp) + sw a4, 0x20(sp) + sw a5, 0x24(sp) + sw a6, 0x28(sp) + sw a7, 0x2c(sp) + sw t0, 0x30(sp) + sw t1, 0x34(sp) + sw t2, 0x38(sp) + sw t3, 0x3c(sp) + sw t4, 0x40(sp) + sw t5, 0x44(sp) + sw t6, 0x48(sp) + + jal ra, isr_8 # call into the user defined handler + + lw a0, 0x00(sp) # restore CSRs and caller saved registers + csrrw x0, mstatus, a0 + lw a0, 0x04(sp) + csrrw x0, mepc, a0 + lw ra, 0x0c(sp) + lw a0, 0x10(sp) + lw a1, 0x14(sp) + lw a2, 0x18(sp) + lw a3, 0x1c(sp) + lw a4, 0x20(sp) + lw a5, 0x24(sp) + lw a6, 0x28(sp) + lw a7, 0x2c(sp) + lw t0, 0x30(sp) + lw t1, 0x34(sp) + lw t2, 0x38(sp) + lw t3, 0x3c(sp) + lw t4, 0x40(sp) + lw t5, 0x44(sp) + #CS + csrci mstatus, 0x8 + lw t6, 0x08(sp) # load the old threshold from the stack + csrw 0x347, t6 # set the priority + lw t6, 0x48(sp) + addi sp, sp, 0x4c + mret # return from interrupt + + + .data +EXIT_VAR: .word 0x00000000 + .section .vector_table, "aw" + .word trap_0 + .word trap_1 + .word trap_2 + .word trap_3 + .word trap_4 + .word trap_5 + .word trap_6 + .word trap_7 + .word trap_8 + .word 0x20212223 + .word 0x24252627 + .word 0x28292A2B + .word 0x2C2D2E2F + .word 0x30313233 + .word 0x34353637 + .word 0x38393A3B + .word 0x3C3D3E3F + .word 0x40414243 + .word 0x44454647 + .word 0x48494A4B + .word 0x4C4D4E4F + .word 0x50515253 + .word 0x54555657 + .word 0x58595A5B + .word 0x5C5D5E5F + .word 0x60616263 + .word 0x64656667 + .word 0x68696A6B + .word 0x6C6D6E6F + .word 0x70717273 + .word 0x74757677 + .word 0x78797A7B + .word 0x7C7D7E7F + + .section .clic_vec, "aw" + .word isr_0 + .word isr_1 + .word isr_2 + .word isr_3 + .word isr_4 + .word isr_5 + .word isr_6 + .word isr_7 + .word isr_8 + .word 0x20212223 + .word 0x24252627 + .word 0x28292A2B + .word 0x2C2D2E2F + .word 0x30313233 + .word 0x34353637 + .word 0x38393A3B + .word 0x3C3D3E3F + .word 0x40414243 + .word 0x44454647 + .word 0x48494A4B + .word 0x4C4D4E4F + .word 0x50515253 + .word 0x54555657 + .word 0x58595A5B + .word 0x5C5D5E5F + .word 0x60616263 + .word 0x64656667 + .word 0x68696A6B + .word 0x6C6D6E6F + .word 0x70717273 + .word 0x74757677 + .word 0x78797A7B + .word 0x7C7D7E7F + + + diff --git a/riscv/asm_branch.s b/riscv/asm_branch.s new file mode 100644 index 00000000..d9882219 --- /dev/null +++ b/riscv/asm_branch.s @@ -0,0 +1,12 @@ + .option norvc + .text +init: + la sp, _stack_start # set stack pointer + li t1, 2 +l: addi t1, t1, -1 + bne t1, zero, l +s: j s + + + + diff --git a/riscv/asm_break.s b/riscv/asm_break.s new file mode 100644 index 00000000..431d61d4 --- /dev/null +++ b/riscv/asm_break.s @@ -0,0 +1,99 @@ + .option norvc + .text +init: + la sp, _stack_start # set stack pointer + csrwi 0x350, 2 # set stack_depth +main: + # setup for individual interrupts done using MMIO + # we are already at priority 0 + la t2, 0x02000100 # priority 2, enabled, not pended, will store at interrupt 2 + csrw 0xB02, t2 # configure interrupt 2 via CSR + # csrw 0xB09, a3 + # setup for vector tables and vector table pointers + la t0, .clic_vec # load 0 latency vector table address + csrw 0x351, t0 # store 0 latency vector table address at super_mtvec + la t0, .vector_table # load vanilla vector table address + csrw mtvec, t0 # store vanilla vector table address at mtvec + csrwi 0x347, 0 # set interrupt prio threshold to 0 + csrrsi zero, 0xB02, 0x1 # pend interrupt 2 + jal ra, helper # enable interrupts via helper +stop: + j stop # finished loop + +helper: + nop + nop + csrwi mstatus, 8 # mock an incoming interrupt in some helper. + nop + nop + jr ra + +isr_2: #interrupt 2 + li a0, 1 + csrrc zero, 0xB02, a0 # unpend self + csrrs zero, 0xB01, a0 # pend interrupt 1 + nop + nop + addi sp, sp, -4 + sw ra, 0(sp) + jal ra, helper # since interrupt occured at the CSRRW instruction in helper, hitting that instruction now will cause stack_depth to increase erroneously. + lw ra, 0(sp) + addi sp, sp, 4 + nop # make it obvious that helper call never returns here.... + nop + jr ra # return + + .section .vector_table, "aw" + .word 0x20212223 + .word 0x24252627 + .word 0x28292A2B + .word 0x2C2D2E2F + .word 0x30313233 + .word 0x34353637 + .word 0x38393A3B + .word 0x3C3D3E3F + .word 0x40414243 + .word 0x44454647 + .word 0x48494A4B + .word 0x4C4D4E4F + .word 0x50515253 + .word 0x54555657 + .word 0x58595A5B + .word 0x5C5D5E5F + .word 0x60616263 + .word 0x64656667 + .word 0x68696A6B + .word 0x6C6D6E6F + .word 0x70717273 + .word 0x74757677 + .word 0x78797A7B + .word 0x7C7D7E7F + + .section .clic_vec, "aw" + .word 0x00000000 + .word 0x00000000 + .word isr_2 + .word 0x20212223 + .word 0x24252627 + .word 0x28292A2B + .word 0x2C2D2E2F + .word 0x30313233 + .word 0x34353637 + .word 0x38393A3B + .word 0x3C3D3E3F + .word 0x40414243 + .word 0x44454647 + .word 0x48494A4B + .word 0x4C4D4E4F + .word 0x50515253 + .word 0x54555657 + .word 0x58595A5B + .word 0x5C5D5E5F + .word 0x60616263 + .word 0x64656667 + .word 0x68696A6B + .word 0x6C6D6E6F + .word 0x70717273 + .word 0x74757677 + .word 0x78797A7B + .word 0x7C7D7E7F diff --git a/riscv/asm_gpio.s b/riscv/asm_gpio.s new file mode 100644 index 00000000..37faab97 --- /dev/null +++ b/riscv/asm_gpio.s @@ -0,0 +1,99 @@ + .option norvc + .text +init: + la sp, _stack_start # set stack pointer + csrwi 0x350, 2 # set stack_depth +main: + csrwi 0x300, 8 # enable global interrupts + la t1, isr_2 # address to isr_2 + csrw 0xB02, t1 # write above to interrupt 2 vector + la t1, isr_0 # address to isr_0 + csrw 0xB00, t1 # write above to interrupt 0 vector + la t1, 0b11010 # prio 0b110, enable 0b1, pend 0b0 + csrw 0xB22, t1 # write above to interrupt 2 + la t1, 0b1 # pend 0b1 + csrs 0xB22, t1 # write above to interrupt 2 + li t2, 0b11110000 # cmp value 0b1111 = 15, interrupt every 15 cycles , prescaler 0b0000 + csrw 0x400, t2 # write above to timer CSR + la t1, 0b11110 # prio 0b111, enable, 0b1, pend 0b0 + csrw 0xB20, t1 # write above to interrupt 0 (timer interrupt) +stop: + j stop # wfi + +isr_0: #timer interrupt + la t0, .toggled # &static mut toggled state + lw t2, 0(t0) # deref toggled + li t1, 1 + bnez t2, led_off # if toggled == 1 then led_off else led_on +led_on: + csrs 0x0, t1 # set bit 0 (t1 = 1) in GPIO CSR (LED on) + sw t1, 0(t0) # store 1 at toggled state var + csrr t3, 0xB40 # load timestamp + jr ra # return (if is unrolled to save some cycles) +led_off: + csrc 0x0, t1 # clear bit 0 (t1 = 1) in GPIO CSR (LED off) + sw zero, 0(t0) # store 0 at toggled state var + csrr t3, 0xB40 # load timestamp + jr ra # return (if is unrolled to save some cycles) + +isr_2: #interrupt 2 + csrr t3, 0xB42 # read time stamp + jr ra # return + + .data +.toggled: .word 0x0 + + .section .vector_table, "aw" + .word 0x20212223 + .word 0x24252627 + .word 0x28292A2B + .word 0x2C2D2E2F + .word 0x30313233 + .word 0x34353637 + .word 0x38393A3B + .word 0x3C3D3E3F + .word 0x40414243 + .word 0x44454647 + .word 0x48494A4B + .word 0x4C4D4E4F + .word 0x50515253 + .word 0x54555657 + .word 0x58595A5B + .word 0x5C5D5E5F + .word 0x60616263 + .word 0x64656667 + .word 0x68696A6B + .word 0x6C6D6E6F + .word 0x70717273 + .word 0x74757677 + .word 0x78797A7B + .word 0x7C7D7E7F + + .section .clic_vec, "aw" + .word 0x00000000 + .word 0x00000000 + .word isr_2 + .word 0x20212223 + .word 0x24252627 + .word 0x28292A2B + .word 0x2C2D2E2F + .word 0x30313233 + .word 0x34353637 + .word 0x38393A3B + .word 0x3C3D3E3F + .word 0x40414243 + .word 0x44454647 + .word 0x48494A4B + .word 0x4C4D4E4F + .word 0x50515253 + .word 0x54555657 + .word 0x58595A5B + .word 0x5C5D5E5F + .word 0x60616263 + .word 0x64656667 + .word 0x68696A6B + .word 0x6C6D6E6F + .word 0x70717273 + .word 0x74757677 + .word 0x78797A7B + .word 0x7C7D7E7F diff --git a/riscv/asm_mem.s b/riscv/asm_mem.s new file mode 100644 index 00000000..4996e3c3 --- /dev/null +++ b/riscv/asm_mem.s @@ -0,0 +1,37 @@ + .option norvc + .text +init: + la sp, _stack_start # set stack pointer + la a0, b + # test signed + lb t0, 0(a0) + lb t1, 1(a0) + lb t2, 2(a0) + lb t3, 3(a0) + lh t4, 0(a0) + lh t5, 2(a0) + lw t6, 0(a0) + + # test unsigned + lbu t0, 0(a0) + lbu t1, 1(a0) + lbu t2, 2(a0) + lbu t3, 3(a0) + lhu t4, 0(a0) + lhu t5, 2(a0) + sb t0, 4(a0) + sb t1, 5(a0) + sb t2, 6(a0) + sb t3, 7(a0) + sh t4, 8(a0) + sh t5, 10(a0) +s: j s + +.data +b: .byte 0x01, 0xf2, 0xf3, 0xf4 + +w: .word 0x01020304 + .word 0x12345678 + .word 0xF1F2F3F4 + + diff --git a/riscv/asm_timer.s b/riscv/asm_timer.s new file mode 100644 index 00000000..8429a4d4 --- /dev/null +++ b/riscv/asm_timer.s @@ -0,0 +1,81 @@ + .option norvc + .text +init: la sp, _stack_start # set stack pointer +main: csrwi 0x300, 8 # enable global interrupts + la t1, isr_0 + srl t1, t1, 2 + csrw 0xB00, t1 # setup isr_0 address + + li t2, 0b11110000 # interrupt every 15 cycles, cmp value 0b1111 = 15, prescaler 0b0000 + csrw 0x400, t2 # timer.counter_top CSR + la t1, 0b1110 # prio 0b11, enable, 0b1, pend 0b0 + csrw 0xB20, t1 # write above to interrupt 0 (timer interrupt) +stop: j stop # wait for interrupt + +isr_0: la t0, .toggled # &static mut toggled state + lw t1, 0(t0) # deref toggled + xori t1, t1, 1 # toggle bit 0 + csrw 0x0, t1 # set bit 0 (t1 = 1) in GPIO CSR (LED on/off) + sw t1, 0(t0) # store toggled value + csrr t3, 0xB40 # read captured timestamp + sw t3, 4(t0) # store timestamp + jr ra # return + + .data +.toggled: .word 0x0 # state + .word 0x0 # time-stamp + + .section .vector_table, "aw" + .word 0x20212223 + .word 0x24252627 + .word 0x28292A2B + .word 0x2C2D2E2F + .word 0x30313233 + .word 0x34353637 + .word 0x38393A3B + .word 0x3C3D3E3F + .word 0x40414243 + .word 0x44454647 + .word 0x48494A4B + .word 0x4C4D4E4F + .word 0x50515253 + .word 0x54555657 + .word 0x58595A5B + .word 0x5C5D5E5F + .word 0x60616263 + .word 0x64656667 + .word 0x68696A6B + .word 0x6C6D6E6F + .word 0x70717273 + .word 0x74757677 + .word 0x78797A7B + .word 0x7C7D7E7F + + .section .clic_vec, "aw" + .word 0x00000000 + .word 0x00000000 + .word 0x00000000 + .word 0x20212223 + .word 0x24252627 + .word 0x28292A2B + .word 0x2C2D2E2F + .word 0x30313233 + .word 0x34353637 + .word 0x38393A3B + .word 0x3C3D3E3F + .word 0x40414243 + .word 0x44454647 + .word 0x48494A4B + .word 0x4C4D4E4F + .word 0x50515253 + .word 0x54555657 + .word 0x58595A5B + .word 0x5C5D5E5F + .word 0x60616263 + .word 0x64656667 + .word 0x68696A6B + .word 0x6C6D6E6F + .word 0x70717273 + .word 0x74757677 + .word 0x78797A7B + .word 0x7C7D7E7F diff --git a/riscv/autosave.json b/riscv/autosave.json new file mode 100644 index 00000000..e8d59b48 --- /dev/null +++ b/riscv/autosave.json @@ -0,0 +1 @@ +{"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/decode.s b/riscv/decode.s new file mode 100644 index 00000000..961186d3 --- /dev/null +++ b/riscv/decode.s @@ -0,0 +1,313 @@ +# ---------------------------------------------------------- +# Group 2's "underlag" for Lab 1 +# Pseudo-instructions may be used for Lab 1. +# ---------------------------------------------------------- + + + +# Group 2's Codeword Generator Subroutine (pseudocode) +# (remember: "seed" is a global variable, UNSIGNED INTEGER; +# you may implement local variables in registers or on the stack; +# result returned in v0; preserve all except t regs) +# + + +# Group 2's Recursive Decoding Subroutine (pseudocode) +# (for "decode", all four local variables must be implemented ON THE +# STACK, and NOT in registers; implement the code literally,. +# no optimizations. We're trying to teach you something. +# remember: result returned in v0; preserve all except t regs) +# + + +# ---------------------------------------------------------- +# The following are the ONLY lines that may appear in the +# ".data" section of the code. You may add NO other lines. +# NO additional global variables. +# ---------------------------------------------------------- + + + .data +plain: .space 111 # room for 111 characters + + .align 4 +seed: .word 0 # 32-bit UNSIGNED INTEGER. + +abc: .word 0x62ab2a56 # string "abc", encoded + .word 0xa1cf643c + .word 0x0c0bd91f + .word 0 +coded: .word 0x5544a9f1 # the real encoded data + .word 0x386f1fb5 + .word 0x6bd5c5b1 + .word 0xef886d1c + .word 0x561172ce + .word 0x13e22576 + .word 0x4b0390b5 + .word 0xfacda2ad + .word 0xcee130b8 + .word 0x29ac9cb7 + .word 0xa8258c56 + .word 0xdce741c3 + .word 0x4cbeae38 + .word 0xf5bc91a9 + .word 0xac281d11 + .word 0x5a10218e + .word 0x812caa68 + .word 0xcab07b57 + .word 0x65af2c34 + .word 0x4117a997 + .word 0xb3ff2ae1 + .word 0x521d6972 + .word 0x555fee5a + .word 0x1e77fb36 + .word 0xdbf7cec3 + .word 0x8b658749 + .word 0xeb29b7f7 + .word 0xeb56972e + .word 0x79f20c4e + .word 0x5036c13d + .word 0x15372870 + .word 0xdda2bcfc + .word 0xc19b7aff + .word 0x0a2a5a2f + .word 0x43d11c6f + .word 0x52ad0b67 + .word 0xe29c2a65 + .word 0xcf3580f4 + .word 0xa8688de2 + .word 0x3cf2ee9c + .word 0x472b57ae + .word 0x58003d45 + .word 0xd7ee1305 + .word 0xa46d7481 + .word 0xbc01cee9 + .word 0x63dcd64f + .word 0x7cd54d8d + .word 0x0f4bd5c0 + .word 0x42275377 + .word 0xe337ec2f + .word 0xc1ec063f + .word 0x98db8d51 + .word 0x7fe97be0 + .word 0xa7e62ab6 + .word 0x488db1e2 + .word 0xa571d3fa + .word 0xa144d15a + .word 0x42c8fe01 + .word 0xd4b380d2 + .word 0xd39b07e8 + .word 0xfb3b6e05 + .word 0x70ec0b75 + .word 0xcf3fb946 + .word 0xe1fd07ce + .word 0xe66bc017 + .word 0x86b77f45 + .word 0x94020298 + .word 0x99fa4fed + .word 0x935abbb0 + .word 0x9621b3b4 + .word 0x16cba096 + .word 0x8442f7fd + .word 0x11d3db46 + .word 0x9400c70f + .word 0x884437c4 + .word 0x613738ad + .word 0x4a1acd6b + .word 0xc92a7ac6 + .word 0x6e54c848 + .word 0x17f26190 + .word 0xfc43a849 + .word 0x7e9aa283 + .word 0x82d8564d + .word 0x521d6327 + .word 0x669f610b + .word 0x717d55bb + .word 0x10c1bbf4 + .word 0xb11a0ea1 + .word 0xbf88f4f1 + .word 0x50b6e0f6 + .word 0x8249659b + .word 0x2dd11935 + .word 0xec0160ff + .word 0x4a6c930e + .word 0x56bf5a1a + .word 0xa8841626 + .word 0x3471228e + .word 0x81658237 + .word 0xde2f964d + .word 0xafaf998d + .word 0x2a33e5e1 + .word 0x2264385f + .word 0x3fd44413 + .word 0x94935835 + .word 0x009d8e04 + .word 0x1d30d325 + .word 0x88fa11c5 + .word 0xb7c00535 + .word 0x2070f791 + .word 0x574892ce + .word 0 + + +# ---------------------------------------------------------- +# The following is the main program. You may not change this. +# You may only add your subroutines AFTER the "infinite end loop" instruction here. +# You MUST have two subroutines named "codgen" and "decode". +# BOTH must adhere to our calling conventions; +# both MUST preserve all registers except v-regs and t-regs; +# we are going to TEST for this when we run your code. +# ---------------------------------------------------------- + + + .text + .set noreorder, norvc #do not reorder, do not generate compressed instructions +main: + la sp, _stack_start + li s0, 0x177023a6 # initialize "seed" test 0x51d40b5c real 0x177023a6 + la s1, seed # initialize "seed" + sw s0, 0(s1) + la a0, coded # address of start of coded words + la a1, plain # address of start of decoded bytes + jal decode # outer call to recursive "decode" + // jal codgen + // jal codgen + // jal codgen + // jal codgen + // jal codgen +end: + j end # infinite loop; plaintext now in "plain". + + +# ---------------------------------------------------------- +# Group 2's assembly code for Function CodGen : +# ---------------------------------------------------------- +# FUNCTION codgen(): UNSIGNED INTEGER; +# LOCAL SIGNED INTEGER n; +# LOCAL UNSIGNED INTEGER x, y; +# BEGIN +# n := [count the number of 0's in word "seed"]; +# x := [multiply "seed" by the constant 2]; +# y := [divide "seed" (unsigned !) by the constant 64]; +# seed := x + y + n; [ignore overflow condition] +# RETURN( seed XOR 0x290995cf ); +# END; +# +# hint: if "seed" is initialized to 0x51d40b5c, +# the first five calls will generate these values: +# 0x8de6f338, 0x657b1e5b, 0xb31f74a7, 0x1f9f8ba8, 0x470f0099, ... +# your code is to be written farther down (see comment below). + # your activation record diagram here. + +codgen: # your code here. + li t0, 0 #result + li t1, 32 #iteration counter + add a0, s0, zero +count: + beqz t1, continue + andi t2, a0, 1 + srl a0, a0, 1 + addi t1, t1, -1 + bne t2, zero, count +increment: + addi t0, t0, 1 + j count +continue: + sll t1, s0, 1 + srl t2, s0, 6 + add s0, zero, t1 + add s0, s0, t2 + add s0, s0, t0 + li t0, 0x290995cf + xor a0, s0, t0 + jr ra + + +# ---------------------------------------------------------- +# Group 2's assembly code for Function DeCode : +# ---------------------------------------------------------- + + # your activation record diagram here. +# FUNCTION decode( wordarr, bytearr ): UNSIGNED INTEGER; +# (wordarr, bytearr passed by reference) +# LOCAL UNSIGNED INTEGER m, r, x, y; +# BEGIN +# x := ONE'S-COMPLEMENT of codgen(); +# IF ([contents of word at "wordarr"] = 0) THEN +# [byte pointed to by "bytearr"] := 0; +# r := x; +# ELSE +# y := decode( wordarr+, bytearr+ ); # "k+" means "successor in k" +# m := ( x XOR y ) + [contents of word at "wordarr"]; +# [byte pointed to by "bytearr"] := [the eight bits at "m"<9:2>]; +# r := TWO'S-COMPLEMENT OF codgen(); +# r := x + y + m + r + 5; +# ENDIF; +# RETURN( r ); +# END; +#ptr bytearr -24fp +#ptr wordarr -20fp +# m -16fp +# r -12fp +# x -8fp +# y -4fp +# fp 0fp +# ra 4fp +decode: # your code here. + addi sp, sp, -4 + sw ra, 0(sp) + addi sp, sp, -4 + sw tp, 0(sp) #use thread poitner as frame pointer for convenience (fp = s0) + add tp, sp, zero #new frame pointer + addi sp, sp, -24 #space for 6 word vars + sw a0, -20(tp) + sw a1, -24(tp) + + jal codgen + not a0, a0 + sw a0, -8(tp) # store x + lw t0, -20(tp) # wordarr + lw t0, 0(t0) # contents wordarr + bne t0, zero, else +then: + lw t1, -24(tp) #bytearr + sb zero, 0(t1) #bytearr = 0 + j return +else: + lw a0, -20(tp) + addi a0, a0, 4 + lw a1, -24(tp) + addi a1, a1, 1 + jal decode + sw a0, -4(tp) #y onto stack + lw t0, -8(tp) #t0 = x + lw t1, -20(tp)#wordarr ptr + lw t1, 0(t1) #wordarr contents + xor t2, a0, t0 #t2 = m = y xor x + add t2, t2, t1 #t2 = m = (x xor y) + *wordarr + sw t2, -16(tp) + lw t1, -24(tp) + srl t2, t2, 2 + sb t2, 0(t1) + jal codgen + not a0, a0 + addi a0, a0, 1 + lw t0, -4(tp) + lw t1, -8(tp) + #lw t2, -12(fp) + lw t3, -16(tp) + add a0, a0, t1 + add a0, a0, t0 + add a0, a0, t3 + addi a0, a0, 5 +return: + add sp, tp, zero + lw tp, 0(sp) + addi sp, sp, 4 + lw ra, 0(sp) + addi sp, sp, 4 + jr ra + + +# end of file. + diff --git a/riscv/decode_link.x b/riscv/decode_link.x new file mode 100644 index 00000000..27e1cd56 --- /dev/null +++ b/riscv/decode_link.x @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x0; + .text : + { + KEEP(*(.text)); + } + . = 0x50000000; + .data : + { + KEEP(*(.data)); + } + . = 0x50000100; + .vector_table : + { + KEEP(*(.vector_table)); + } +} +PROVIDE(_stack_start = 0x50002000); diff --git a/riscv/examples/empty.rs b/riscv/examples/empty.rs new file mode 100644 index 00000000..fc6d3ceb --- /dev/null +++ b/riscv/examples/empty.rs @@ -0,0 +1,100 @@ +use clap::Parser; +use std::path::PathBuf; +use syncrim::common::ComponentStore; + +#[derive(Parser, Debug)] +struct Args { + /// Use a pre-compiled elf file instead of compiling one + #[arg(short, long, default_value = "false")] + use_elf: bool, + /// Path to the pre-compiled elf file + #[arg(short, long, default_value = "")] + elf_path: String, + /// Path to the assembly source file + #[arg(short, long, default_value = "asm.s")] + asm_path: String, + /// Path to the linker script + #[arg(short, long, default_value = "memory.x")] + ls_path: String, +} + +fn main() { + let path = PathBuf::from("riscv.json"); + let cs = ComponentStore::load_file(&path); + #[cfg(feature = "gui-egui")] + { + use riscv::components::*; + use std::{ + cell::RefCell, + collections::{BTreeMap, HashMap, HashSet}, + ops::Range, + rc::Rc, + }; + use syncrim::common::Input; + let dummy = Input::new("id", "field"); + let lib = ComponentStore { + store: vec![ + Rc::new(InstrMem { + width: INSTR_MEM_WIDTH, + height: INSTR_MEM_HEIGHT, + id: "dummy_instr_mem".to_string(), + pos: (0.0, 0.0), + pc: dummy.clone(), + bytes: BTreeMap::new(), + breakpoints: Rc::new(RefCell::new(HashSet::new())), + le: true, + range: Range { start: 0, end: 0 }, + symbols: HashMap::new(), + }), + Rc::new(ALU { + id: "dummy_alu".to_string(), + pos: (0.0, 0.0), + operator_i: dummy.clone(), + operand_a_i: dummy.clone(), + operand_b_i: dummy.clone(), + }), + Rc::new(BranchLogic { + width: BRANCH_LOGIC_WIDTH, + height: BRANCH_LOGIC_HEIGHT, + id: "dummy_blu".to_string(), + pos: (0.0, 0.0), + rs1: dummy.clone(), + rs2: dummy.clone(), + ctrl: dummy.clone(), + int: dummy.clone(), + mret: dummy.clone(), + enable: dummy.clone(), + }), + Rc::new(Decoder { + width: DECODER_WIDTH, + height: DECODER_HEIGHT, + id: "dummy_decoder".to_string(), + pos: (0.0, 0.0), + instruction: dummy.clone(), + }), + Rc::new(LSBZero { + height: LSB_ZERO_HEIGHT, + width: LSB_ZERO_WIDTH, + id: "dummy_lsbzero".to_string(), + pos: (0.0, 0.0), + data_i: dummy.clone(), + }), + Rc::new(RegFile::dummy()), + Rc::new(SZExt { + height: SIGN_ZERO_EXT_HEIGHT, + width: SIGN_ZERO_EXT_WIDTH, + id: "dummy_szext".to_string(), + pos: (0.0, 0.0), + data_i: dummy.clone(), + sel_i: dummy.clone(), + }), + ], + }; + let mut component_vec = lib.store.clone(); + component_vec.append(&mut syncrim::gui_egui::editor::Library::default().0.clone()); + syncrim::gui_egui::gui(cs, &path, syncrim::gui_egui::editor::Library(component_vec)).ok(); + } + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(cs, &path); +} diff --git a/riscv/examples/riscv.rs b/riscv/examples/riscv.rs index e854d4ba..11eb4b06 100644 --- a/riscv/examples/riscv.rs +++ b/riscv/examples/riscv.rs @@ -4,13 +4,15 @@ use fern; use riscv::components::*; use riscv_elf_parse; use std::{ - cell::RefCell, collections::BTreeMap, fs, ops::Range, path::PathBuf, process::Command, rc::Rc, + cell::RefCell, + collections::{BTreeMap, HashSet}, + fs, + ops::Range, + path::PathBuf, + process::Command, + rc::Rc, }; -use syncrim::{ - common::{ComponentStore, Input}, - components::*, -}; -use xmas_elf::ElfFile; +use syncrim::common::{ComponentStore, Input}; #[derive(Parser, Debug)] struct Args { @@ -26,219 +28,204 @@ struct Args { /// Path to the linker script #[arg(short, long, default_value = "memory.x")] ls_path: String, + #[arg(short, long, default_value = "false")] + rust: bool, } fn main() { fern_setup_riscv(); let args = Args::parse(); - let memory = if !args.use_elf { + let memory = if !args.use_elf && !args.rust { elf_from_asm(&args); let bytes = fs::read("./output").expect("The elf file could not be found"); - riscv_elf_parse::Memory::new_from_file(&bytes, true) - } else { + riscv_elf_parse::Memory::new_from_file(&bytes, false) + } else if args.use_elf && !args.rust { let bytes = fs::read(format!("{}", args.elf_path)).expect("The elf file could not be found"); - riscv_elf_parse::Memory::new_from_file(&bytes, true) + riscv_elf_parse::Memory::new_from_file(&bytes, false) + } else { + compile_rust_crate(); + let bytes = fs::read("./output").expect("The elf file could not be found"); + riscv_elf_parse::Memory::new_from_file(&bytes, false) }; - println!("{}", memory); let mut instr_mem = BTreeMap::new(); let mut data_mem = BTreeMap::new(); + let mut breakpoints = HashSet::new(); + + breakpoints.insert(0x0000_0008); //init data memory with 0's let range = Range { start: 0x5000_0000u32, - end: 0x5000_0500u32, + end: 0x5000_2000u32, + }; + let instr_range = Range { + start: 0x0000_0000usize, + end: 0x0000_1000usize, }; for address in range.clone() { data_mem.insert(address as usize, 0); } - for element in memory.bytes { + for address in instr_range.clone() { + instr_mem.insert(address as usize, 0); + } + for element in memory.bytes.clone() { if element.0 < 0x5000_0000 { instr_mem.insert(element.0, element.1); } else { data_mem.insert(element.0, element.1); } } - - let cs = ComponentStore { - store: vec![ - Add::rc_new( - "pc_adder", - (150.0, 120.0), - Input::new("pc_adder_c", "out"), - Input::new("reg", "out"), - ), - Constant::rc_new("pc_adder_c", (100.0, 100.0), 4), - Register::rc_new("reg", (100.0, 140.0), Input::new("pc_adder_mux", "out")), - Mux::rc_new( - "pc_adder_mux", - (100.0, 120.0), - Input::new("branch_logic", "out"), - vec![ - Input::new("pc_adder", "out"), - Input::new("jalr_stripper", "out"), - Input::new("branch_adder", "out"), - ], - ), - Add::rc_new( - "jalr_adder", - (100.0, 200.0), - Input::new("reg_file", "reg_a"), - Input::new("jalr_se", "out"), - ), - Rc::new(BranchLogic { - id: "branch_logic".to_string(), - pos: (725.0, 300.0), - rs1: Input::new("reg_file", "reg_a"), - rs2: Input::new("reg_file", "reg_b"), - ctrl: Input::new("decoder", "branch_logic_ctl"), - enable: Input::new("decoder", "branch_logic_enable"), - }), - Rc::new(LSBZero { - id: "jalr_stripper".to_string(), - pos: (600.0, 1000.0), - data_i: Input::new("jalr_adder", "out"), - }), - Sext::rc_new( - "jalr_se", - (900.0, 900.0), - Input::new("decoder", "jalr_imm"), - 12, - 32, - ), - Mux::rc_new( - "branch_adder_mux", - (500.0, 1000.0), - Input::new("decoder", "pc_imm_sel"), - vec![ - Input::new("jal_imm_sext", "out"), - Input::new("branch_imm_sext", "out"), - ], - ), - Add::rc_new( - "branch_adder", - (50.0, 400.0), - Input::new("reg", "out"), - Input::new("branch_adder_mux", "out"), - ), - Sext::rc_new( - "jal_imm_sext", - (500.0, 1000.0), - Input::new("decoder", "big_imm"), - 21, - 32, - ), - Sext::rc_new( - "branch_imm_sext", - (500.0, 1000.0), - Input::new("decoder", "branch_imm"), - 13, - 32, - ), - Rc::new(InstrMem { - id: "instr_mem".to_string(), - pos: (180.0, 400.0), - pc: Input::new("reg", "out"), - bytes: instr_mem, - }), - Rc::new(Decoder { - id: "decoder".to_string(), - pos: (300.0, 150.0), - instruction: Input::new("instr_mem", "instruction"), - }), - Register::rc_new( - "regfile_we_reg", - (450.0, 50.0), - Input::new("decoder", "regfile_we"), - ), - Register::rc_new( - "regfile_rd_reg", - (480.0, 50.0), - Input::new("decoder", "regfile_rd"), - ), - Rc::new(SZExt { - id: "imm_szext".to_string(), - pos: (450.0, 1000.0), - data_i: Input::new("decoder", "sign_zero_ext_data"), - sel_i: Input::new("decoder", "sign_zero_ext_sel"), - }), - Rc::new(RegFile { - id: "reg_file".into(), - pos: (450.0, 150.0), - width: 100.0, - height: 100.0, - read_addr1: Input::new("decoder", "regfile_rs1"), - read_addr2: Input::new("decoder", "regfile_rs2"), - write_data: Input::new("wb_mux", "out"), - write_addr: Input::new("regfile_rd_reg", "out"), - write_enable: Input::new("regfile_we_reg", "out"), - registers: RegStore::new(Rc::new(RefCell::new([0; 32]))), - history: RegHistory::new(), - }), - Mem::rc_new_from_bytes( - "data_memory", - (700.0, 600.0), - 100.0, - 100.0, - false, - Input::new("reg_file", "reg_b"), - Input::new("alu", "result_o"), - Input::new("decoder", "data_mem_ctrl"), - Input::new("decoder", "data_se"), - Input::new("decoder", "data_mem_size"), - data_mem, - range, - ), - Constant::rc_new("zero_c", (680.0, 150.0), 0), - Mux::rc_new( - "alu_operand_a_mux", - (700.0, 150.0), - Input::new("decoder", "alu_operand_a_sel"), - vec![ - Input::new("reg_file", "reg_a"), - Input::new("decoder", "imm_a_mux_data"), - Input::new("zero_c", "out"), - ], - ), - Mux::rc_new( - "alu_operand_b_mux", - (700.0, 300.0), - Input::new("decoder", "alu_operand_b_sel"), - vec![ - Input::new("reg_file", "reg_b"), - Input::new("imm_szext", "out"), - Input::new("pc_adder", "out"), - Input::new("reg", "out"), - ], - ), - Rc::new(ALU { - id: "alu".to_string(), - pos: (800.0, 225.0), - operator_i: Input::new("decoder", "alu_operator"), - operand_a_i: Input::new("alu_operand_a_mux", "out"), - operand_b_i: Input::new("alu_operand_b_mux", "out"), - }), - Mux::rc_new( - "wb_mux", - (900.0, 225.0), - Input::new("decoder", "wb_mux"), - vec![ - Input::new("alu", "result_o"), - Input::new("data_memory", "data"), - ], - ), - ], - }; - let path = PathBuf::from("riscv.json"); - cs.save_file(&path); - + let mut cs = ComponentStore::load_file(&path); + let mut i = 0; + let mut store = cs.store.clone(); + for component in store.clone() { + // if the component is a data memory + if component.get_id_ports().0 == "data_memory" { + // pull the trait object from the Component vector + let comp = store.remove(i); + // and downcast it to an InstrMem, own by cloning + let mut data_mem_comp: RVMem = comp + .as_any() + .downcast_ref::() + .expect(&format!("Downcast failed for {:?}", comp.to_())) + .clone(); + // replace the memory contents with ELF contents + data_mem_comp.memory = Memory::new(data_mem.clone()); + // also, set the initial state for reset + data_mem_comp.init_state = data_mem; + // repush the mutated RVMem to the Component vector + store.push(Rc::new(data_mem_comp)); + //satisfy borrow checker + break; + } + i += 1 + } + let mut i = 0; + for component in store.clone() { + // if the component is an instr mem + if component.get_id_ports().0 == "instr_mem" { + // pull the trait object from the Component vector + let comp = store.remove(i); + // and downcast it to an InstrMem, own by cloning + let mut instr_mem_comp: InstrMem = comp + .as_any() + .downcast_ref::() + .expect(&format!("Downcast failed for {:?}", comp.to_())) + .clone(); + // replace the memory contents with ELF contents + instr_mem_comp.bytes = instr_mem; + // replace the symbols with ELF symbols + instr_mem_comp.symbols = memory.symbols; + // repush the mutated InstrMem to the Component vector + store.push(Rc::new(instr_mem_comp)); + //satisfy borrow checker + break; + } + i += 1 + } + cs.store = store; #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&cs, &path).ok(); + { + use std::collections::HashMap; + let dummy = Input::new("id", "field"); + let lib = ComponentStore { + store: vec![ + Rc::new(InstrMem { + width: INSTR_MEM_WIDTH, + height: INSTR_MEM_HEIGHT, + id: "dummy_instr_mem".to_string(), + pos: (0.0, 0.0), + pc: dummy.clone(), + bytes: BTreeMap::new(), + range: Range { + start: 0, + end: 0x1000, + }, + breakpoints: Rc::new(RefCell::new(HashSet::new())), + symbols: HashMap::new(), + le: true, + }), + Rc::new(ALU { + id: "dummy_alu".to_string(), + pos: (0.0, 0.0), + operator_i: dummy.clone(), + operand_a_i: dummy.clone(), + operand_b_i: dummy.clone(), + }), + Rc::new(BranchLogic { + width: BRANCH_LOGIC_WIDTH, + height: BRANCH_LOGIC_HEIGHT, + id: "dummy_blu".to_string(), + pos: (0.0, 0.0), + rs1: dummy.clone(), + rs2: dummy.clone(), + ctrl: dummy.clone(), + enable: dummy.clone(), + // int: dummy.clone(), + // mret: dummy.clone(), + }), + Rc::new(Decoder { + width: DECODER_WIDTH, + height: DECODER_HEIGHT, + id: "dummy_decoder".to_string(), + pos: (0.0, 0.0), + instruction: dummy.clone(), + }), + Rc::new(LSBZero { + height: LSB_ZERO_HEIGHT, + width: LSB_ZERO_WIDTH, + id: "dummy_lsbzero".to_string(), + pos: (0.0, 0.0), + data_i: dummy.clone(), + }), + Rc::new(RegFile::dummy()), + Rc::new(SZExt { + height: SIGN_ZERO_EXT_HEIGHT, + width: SIGN_ZERO_EXT_WIDTH, + id: "dummy_szext".to_string(), + pos: (0.0, 0.0), + data_i: dummy.clone(), + sel_i: dummy.clone(), + }), + Rc::new(WBCtl { + height: WB_CTL_HEIGHT, + width: WB_CTL_WIDTH, + id: "dummy_wbctl".to_string(), + pos: (0.0, 0.0), + clic_i: dummy.clone(), + dec_i: dummy.clone(), + }), + Rc::new(GPIO { + height: GPIO_HEIGHT, + width: GPIO_WIDTH, + id: "dummy_gpio".to_string(), + pos: (0.0, 0.0), + data_i: dummy.clone(), + addr_i: dummy.clone(), + size_i: dummy.clone(), + we_i: dummy.clone(), + se_i: dummy.clone(), + csr_d: dummy.clone(), + csr_a: dummy.clone(), + csr_ctl: dummy.clone(), + csrstore: GPIOCsrStore::default(), + pins: Pins::default(), + memory: Memory::default(), + }), + ProbeLabel::rc_new("dummy_labeled_probe", (0.0, 0.0), dummy.clone()), + ], + }; + let mut component_vec = lib.store.clone(); + component_vec.append(&mut syncrim::gui_egui::editor::Library::default().0.clone()); + let _ = + syncrim::gui_egui::gui(cs, &path, syncrim::gui_egui::editor::Library(component_vec)); + } #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&cs, &path); + syncrim::gui_vizia::gui(cs, &path); } #[allow(unused_imports)] use log::LevelFilter; @@ -253,17 +240,12 @@ fn fern_setup_riscv() { message )) }) - // Add blanket level filter - - // .level(log::LevelFilter::Debug); - .level_for( - "syncrim::gui_vizia::components::mem", - log::LevelFilter::Trace, - ) + .level_for("riscv::components::clic", log::LevelFilter::Trace) .level(log::LevelFilter::Error); // - and per-module overrides #[cfg(feature = "gui-vizia")] - let f = f.level_for("riscv::components::instr_mem", LevelFilter::Trace); + let f = f.level_for("riscv::gui_egui::components::instr_mem", LevelFilter::Trace); f // Output to stdout, files, and other Dispatch configurations @@ -309,18 +291,43 @@ fn elf_from_asm(args: &Args) { .unwrap() }; let _ = if cfg!(target_os = "windows") { - Command::new("cmd") + match Command::new("cmd") + .current_dir(".\\riscv_asm\\") + .args(["/C", "cargo clean"]) + .status() + { + Ok(_) => {} + Err(_) => { + panic!("cargo clean unsuccessful") + } + } + match Command::new("cmd") .current_dir(".\\riscv_asm\\") .args(["/C", "cargo build --release"]) .status() - .unwrap() + { + Ok(_) => {} + Err(_) => { + panic!("cargo build unsuccessful") + } + } } else { - Command::new("sh") + match Command::new("sh") .current_dir("./riscv_asm/") .arg("-c") .arg(format!("cargo build --release")) .status() - .unwrap() + { + Ok(exit_status) => match exit_status.success() { + true => {} + false => { + panic!("cargo build unsuccessful") + } + }, //25856 + Err(_) => { + panic!() + } + } }; let _ = if cfg!(target_os = "windows") { Command::new("cmd") @@ -342,3 +349,54 @@ fn elf_from_asm(args: &Args) { .unwrap() }; } + +fn compile_rust_crate() { + let _ = if cfg!(target_os = "windows") { + match Command::new("cmd") + .current_dir(".\\riscv_basic\\") + .args(["/C", "cargo build --release"]) + .status() + { + Ok(_) => {} + Err(_) => { + panic!("cargo build unsuccessful") + } + } + } else { + match Command::new("sh") + .current_dir("./riscv_basic/") + .arg("-c") + .arg(format!("cargo build --release")) + .status() + { + Ok(exit_status) => match exit_status.success() { + true => {} + false => { + panic!("cargo build unsuccessful") + } + }, //25856 + Err(_) => { + panic!() + } + } + }; + let _ = if cfg!(target_os = "windows") { + Command::new("cmd") + .current_dir(".\\riscv_basic\\") + .args([ + "/C", + "move /y .\\target\\riscv32i-unknown-none-elf\\release\\riscv_basic ..\\output", + ]) + .status() + .unwrap() + } else { + Command::new("sh") + .current_dir("./") + .arg("-c") + .arg(format!( + "mv ./riscv_basic/target/riscv32i-unknown-none-elf/release/riscv_basic ./output" + )) + .status() + .unwrap() + }; +} diff --git a/riscv/expanded.rs b/riscv/expanded.rs new file mode 100644 index 00000000..e69de29b diff --git a/riscv/memory.x b/riscv/memory.x index 609bbf4c..88e3535d 100644 --- a/riscv/memory.x +++ b/riscv/memory.x @@ -6,9 +6,28 @@ SECTIONS KEEP(*(.text)); } + . = 0x200; + .trap : + { + KEEP(*(.trap)); + } + + + . = 0x50000000; .data : { KEEP(*(.data)); - } -} \ No newline at end of file + } + . = 0x50000100; + .vector_table : + { + KEEP(*(.vector_table)); + } + . = 0x50000200; + .clic_vec : + { + KEEP(*(.clic_vec)); + } +} +PROVIDE(_stack_start = 0x50000500); diff --git a/riscv/riscv.json b/riscv/riscv.json new file mode 100644 index 00000000..8f0df927 --- /dev/null +++ b/riscv/riscv.json @@ -0,0 +1 @@ +{"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 diff --git a/riscv/riscv_asm/.cargo/config.toml b/riscv/riscv_asm/.cargo/config.toml index a48af91e..6bc1dda7 100644 --- a/riscv/riscv_asm/.cargo/config.toml +++ b/riscv/riscv_asm/.cargo/config.toml @@ -2,6 +2,7 @@ rustflags = [ # LLD (shipped with the Rust toolchain) is used as the default linker + "-g", "-C", "link-arg=-Tmemory.x", ] diff --git a/riscv/riscv_basic/.cargo/config.toml b/riscv/riscv_basic/.cargo/config.toml new file mode 100644 index 00000000..411a6c63 --- /dev/null +++ b/riscv/riscv_basic/.cargo/config.toml @@ -0,0 +1,11 @@ +[target.riscv32i-unknown-none-elf] + +rustflags = [ + # LLD (shipped with the Rust toolchain) is used as the default linker + "-g", + "-C", "link-arg=-Tmemory.x", + "-C", "link-arg=-Tlink.x", +] + +[build] +target = "riscv32i-unknown-none-elf" diff --git a/riscv/riscv_basic/Cargo.toml b/riscv/riscv_basic/Cargo.toml new file mode 100644 index 00000000..f6dbd4f8 --- /dev/null +++ b/riscv/riscv_basic/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "riscv_basic" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +hippomenes-core = {git="https://github.com/onsdagens/hippomenes-core"} +hippomenes-rt = {git="https://github.com/onsdagens/hippomenes-core"} +rtic = {git="https://github.com/onsdagens/rtic", branch="hippomenes", features=["riscv-clic-backend"]} + + +[workspace] diff --git a/riscv/riscv_basic/assembly.objdump b/riscv/riscv_basic/assembly.objdump new file mode 100644 index 00000000..b83e1ec7 --- /dev/null +++ b/riscv/riscv_basic/assembly.objdump @@ -0,0 +1,97 @@ + +riscv_basic: file format elf32-littleriscv + +Disassembly of section .text: + +00000000 <_start>: + 0: auipc gp, 0x50001 + 4: addi gp, gp, -0x7fc + +00000008 <.Lpcrel_hi1>: + 8: auipc t1, 0x50004 + c: addi t1, t1, -0x8 + 10: andi sp, t1, -0x10 + +00000014 <.Lpcrel_hi2>: + 14: auipc t0, 0x50000 + 18: addi t0, t0, -0x10 + +0000001c <.Lpcrel_hi3>: + 1c: auipc t2, 0x50000 + 20: addi t2, t2, -0x18 + +00000024 <.Lpcrel_hi4>: + 24: auipc t1, 0x50000 + 28: addi t1, t1, -0x20 + 2c: bgeu t0, t2, 0x44 <.Lline_table_start0+0x44> + 30: lw t3, 0x0(t1) + 34: addi t1, t1, 0x4 + 38: sw t3, 0x0(t0) + 3c: addi t0, t0, 0x4 + 40: bltu t0, t2, 0x30 <.Lline_table_start0+0x30> + +00000044 <.Lpcrel_hi5>: + 44: auipc t0, 0x50000 + 48: addi t0, t0, -0x40 + +0000004c <.Lpcrel_hi6>: + 4c: auipc t2, 0x50000 + 50: addi t2, t2, -0x48 + 54: bgeu t0, t2, 0x64 <.Lline_table_start0+0x64> + 58: sw zero, 0x0(t0) + 5c: addi t0, t0, 0x4 + 60: bltu t0, t2, 0x58 <.Lline_table_start0+0x58> + 64: auipc ra, 0x0 + 68: jalr 0x70(ra) <.Lline_table_start0+0xd4> + 6c: j 0x90 <.Lline_table_start0+0x90> + +00000070 : + 70: j 0x70 <.Lline_table_start0+0x70> + +00000074 : + 74: lui a0, 0x50000 + 78: lbu a1, 0x0(a0) + 7c: xori a1, a1, 0x1 + 80: sb a1, 0x0(a0) + 84: csrrw a0, 0x0, a1 + 88: csrr a0, 0xb40 + 8c: ret + +00000090
: + 90: addi sp, sp, -0x10 + 94: sw ra, 0xc(sp) + 98: li a0, 0x8 + 9c: csrc mstatus, a0 + a0: li a0, 0xc + a4: csrs 0xb20, a0 + a8: csrsi 0xb20, 0x2 + ac: auipc ra, 0x0 + b0: jalr 0xc(ra) <.Lline_table_start0+0xb8> + b4: j 0xb4 <.Lline_table_start0+0xb4> + +000000b8 : + b8: li a0, 0xf0 + bc: csrrw a1, 0x400, a0 + c0: lui a0, 0x50000 + c4: sb zero, 0x0(a0) + c8: li a0, 0x8 + cc: csrs mstatus, a0 + d0: ret + +000000d4 <_setup_interrupts>: + d4: lui a0, 0x0 + d8: addi a0, a0, 0x74 + dc: srli a0, a0, 0x2 + e0: csrrw a1, mcycle, a0 + e4: lui a0, 0x0 + e8: addi a0, a0, 0x108 + ec: srli a0, a0, 0x2 + f0: csrrw a1, 0xb01, a0 + f4: lui a0, 0x0 + f8: addi a0, a0, 0x108 + fc: srli a0, a0, 0x2 + 100: csrrw a1, minstret, a0 + 104: ret + +00000108 : + 108: j 0x108 <.Lline_table_start0+0x108> diff --git a/riscv/riscv_basic/expanded.rs b/riscv/riscv_basic/expanded.rs new file mode 100644 index 00000000..c2089555 --- /dev/null +++ b/riscv/riscv_basic/expanded.rs @@ -0,0 +1,342 @@ +#![feature(prelude_import)] +#![no_std] +#![no_main] +#[prelude_import] +use core::prelude::rust_2021::*; +#[macro_use] +extern crate core; +extern crate compiler_builtins as _; +use core::panic::PanicInfo; +use hippomenes_rt as _; +/// The RTIC application module +pub mod app { + /// Always include the device crate which contains the vector table + use hippomenes_core as you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml; + /// Holds the maximum priority level for use by async HAL drivers. + #[no_mangle] + static RTIC_ASYNC_MAX_LOGICAL_PRIO: u8 = 2u8; + use hippomenes_core::{gpio, interrupt0, timer}; + /// User code end + ///Shared resources + struct Shared { + time_stamp: usize, + toggled: bool, + } + ///Local resources + struct Local {} + /// Execution context + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] + pub struct __rtic_internal_init_Context<'a> { + #[doc(hidden)] + __rtic_internal_p: ::core::marker::PhantomData<&'a ()>, + /// Core peripherals + pub core: rtic::export::Peripherals, + /// Device peripherals (PAC) + pub device: hippomenes_core::Peripherals, + /// Critical section token for init + pub cs: rtic::export::CriticalSection<'a>, + } + impl<'a> __rtic_internal_init_Context<'a> { + #[inline(always)] + #[allow(missing_docs)] + pub unsafe fn new(core: rtic::export::Peripherals) -> Self { + __rtic_internal_init_Context { + __rtic_internal_p: ::core::marker::PhantomData, + device: hippomenes_core::Peripherals::steal(), + cs: rtic::export::CriticalSection::new(), + core, + } + } + } + #[allow(non_snake_case)] + ///Initialization function + pub mod init { + #[doc(inline)] + pub use super::__rtic_internal_init_Context as Context; + } + #[inline(always)] + #[allow(non_snake_case)] + fn init(_: init::Context) -> (Shared, Local) { + timer::Bits::write(0b1111 << 4); + let toggled = false; + let time_stamp = 0; + (Shared { time_stamp, toggled }, Local {}) + } + impl<'a> __rtic_internal_idleSharedResources<'a> { + #[inline(always)] + #[allow(missing_docs)] + pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self { + __rtic_internal_idleSharedResources { + time_stamp: shared_resources::time_stamp_that_needs_to_be_locked::new( + priority, + ), + __rtic_internal_marker: core::marker::PhantomData, + priority: priority, + } + } + } + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] + ///Shared resources `idle` has access to + pub struct __rtic_internal_idleSharedResources<'a> { + #[allow(missing_docs)] + pub time_stamp: shared_resources::time_stamp_that_needs_to_be_locked<'a>, + #[doc(hidden)] + pub __rtic_internal_marker: core::marker::PhantomData<&'a ()>, + pub priority: &'a rtic::export::Priority, + } + /// Execution context + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] + pub struct __rtic_internal_idle_Context<'a> { + #[doc(hidden)] + __rtic_internal_p: ::core::marker::PhantomData<&'a ()>, + /// Shared Resources this task has access to + pub shared: idle::SharedResources<'a>, + pub priority: &'a rtic::export::Priority, + } + impl<'a> __rtic_internal_idle_Context<'a> { + #[inline(always)] + #[allow(missing_docs)] + pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self { + __rtic_internal_idle_Context { + __rtic_internal_p: ::core::marker::PhantomData, + priority, + shared: idle::SharedResources::new(priority), + } + } + } + #[allow(non_snake_case)] + ///Idle loop + pub mod idle { + #[doc(inline)] + pub use super::__rtic_internal_idleSharedResources as SharedResources; + #[doc(inline)] + pub use super::__rtic_internal_idle_Context as Context; + } + #[allow(non_snake_case)] + fn idle(mut cx: idle::Context) -> ! { + use rtic::Mutex as _; + use rtic::mutex::prelude::*; + loop { + let time_stamp = cx.shared.time_stamp.lock(|time_stamp| *time_stamp); + } + } + #[allow(non_snake_case)] + #[no_mangle] + unsafe fn Interrupt0() { + const PRIORITY: u8 = 3u8; + rtic::export::run( + PRIORITY, + || { + timer_task( + timer_task::Context::new(&rtic::export::Priority::new(PRIORITY)), + ) + }, + ); + } + impl<'a> __rtic_internal_timer_taskSharedResources<'a> { + #[inline(always)] + #[allow(missing_docs)] + pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self { + __rtic_internal_timer_taskSharedResources { + time_stamp: shared_resources::time_stamp_that_needs_to_be_locked::new( + priority, + ), + toggled: shared_resources::toggled_that_needs_to_be_locked::new( + priority, + ), + __rtic_internal_marker: core::marker::PhantomData, + priority: priority, + } + } + } + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] + ///Shared resources `timer_task` has access to + pub struct __rtic_internal_timer_taskSharedResources<'a> { + #[allow(missing_docs)] + pub time_stamp: shared_resources::time_stamp_that_needs_to_be_locked<'a>, + #[allow(missing_docs)] + pub toggled: shared_resources::toggled_that_needs_to_be_locked<'a>, + #[doc(hidden)] + pub __rtic_internal_marker: core::marker::PhantomData<&'a ()>, + pub priority: &'a rtic::export::Priority, + } + /// Execution context + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] + pub struct __rtic_internal_timer_task_Context<'a> { + #[doc(hidden)] + __rtic_internal_p: ::core::marker::PhantomData<&'a ()>, + /// Shared Resources this task has access to + pub shared: timer_task::SharedResources<'a>, + pub priority: &'a rtic::export::Priority, + } + impl<'a> __rtic_internal_timer_task_Context<'a> { + #[inline(always)] + #[allow(missing_docs)] + pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self { + __rtic_internal_timer_task_Context { + __rtic_internal_p: ::core::marker::PhantomData, + priority, + shared: timer_task::SharedResources::new(priority), + } + } + } + #[allow(non_snake_case)] + ///Hardware task + pub mod timer_task { + #[doc(inline)] + pub use super::__rtic_internal_timer_taskSharedResources as SharedResources; + #[doc(inline)] + pub use super::__rtic_internal_timer_task_Context as Context; + } + #[allow(non_snake_case)] + fn timer_task(mut cx: timer_task::Context) { + use rtic::Mutex as _; + use rtic::mutex::prelude::*; + cx.shared + .toggled + .lock(|toggled| { + *toggled = !*toggled; + gpio::Bits::write(*toggled as usize); + }); + cx.shared + .time_stamp + .lock(|time_stamp| { + *time_stamp = interrupt0::Timestamp::Bits::read(); + }); + } + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + #[link_section = ".uninit.rtic0"] + static __rtic_internal_shared_resource_time_stamp: rtic::RacyCell< + core::mem::MaybeUninit, + > = rtic::RacyCell::new(core::mem::MaybeUninit::uninit()); + impl<'a> rtic::Mutex for shared_resources::time_stamp_that_needs_to_be_locked<'a> { + type T = usize; + #[inline(always)] + fn lock( + &mut self, + f: impl FnOnce(&mut usize) -> RTIC_INTERNAL_R, + ) -> RTIC_INTERNAL_R { + /// Priority ceiling + const CEILING: u8 = 3u8; + unsafe { + rtic::export::lock( + __rtic_internal_shared_resource_time_stamp.get_mut() as *mut _, + self.priority, + CEILING, + f, + ) + } + } + } + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + #[link_section = ".uninit.rtic1"] + static __rtic_internal_shared_resource_toggled: rtic::RacyCell< + core::mem::MaybeUninit, + > = rtic::RacyCell::new(core::mem::MaybeUninit::uninit()); + impl<'a> rtic::Mutex for shared_resources::toggled_that_needs_to_be_locked<'a> { + type T = bool; + #[inline(always)] + fn lock( + &mut self, + f: impl FnOnce(&mut bool) -> RTIC_INTERNAL_R, + ) -> RTIC_INTERNAL_R { + /// Priority ceiling + const CEILING: u8 = 3u8; + unsafe { + rtic::export::lock( + __rtic_internal_shared_resource_toggled.get_mut() as *mut _, + self.priority, + CEILING, + f, + ) + } + } + } + mod shared_resources { + #[doc(hidden)] + #[allow(non_camel_case_types)] + pub struct time_stamp_that_needs_to_be_locked<'a> { + __rtic_internal_p: ::core::marker::PhantomData<&'a ()>, + pub priority: &'a rtic::export::Priority, + } + impl<'a> time_stamp_that_needs_to_be_locked<'a> { + #[inline(always)] + pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self { + time_stamp_that_needs_to_be_locked { + __rtic_internal_p: ::core::marker::PhantomData, + priority, + } + } + } + #[doc(hidden)] + #[allow(non_camel_case_types)] + pub struct toggled_that_needs_to_be_locked<'a> { + __rtic_internal_p: ::core::marker::PhantomData<&'a ()>, + pub priority: &'a rtic::export::Priority, + } + impl<'a> toggled_that_needs_to_be_locked<'a> { + #[inline(always)] + pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self { + toggled_that_needs_to_be_locked { + __rtic_internal_p: ::core::marker::PhantomData, + priority, + } + } + } + } + #[doc(hidden)] + #[no_mangle] + unsafe extern "C" fn main() -> ! { + rtic::export::assert_send::(); + rtic::export::assert_send::(); + rtic::export::interrupt::disable(); + let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal() + .into(); + const _: () = if (15usize) <= 3u8 as usize { + { + ::core::panicking::panic_fmt( + format_args!( + "Maximum priority used by interrupt vector \'Interrupt0\' is more than supported by hardware", + ), + ); + }; + }; + rtic::export::enable( + you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::Interrupt0, + 3u8, + ); + #[inline(never)] + fn __rtic_init_resources(f: F) + where + F: FnOnce(), + { + f(); + } + __rtic_init_resources(|| { + let (shared_resources, local_resources) = init( + init::Context::new(core.into()), + ); + __rtic_internal_shared_resource_time_stamp + .get_mut() + .write(core::mem::MaybeUninit::new(shared_resources.time_stamp)); + __rtic_internal_shared_resource_toggled + .get_mut() + .write(core::mem::MaybeUninit::new(shared_resources.toggled)); + rtic::export::interrupt::enable(); + }); + idle(idle::Context::new(&rtic::export::Priority::new(0u8))) + } +} +#[panic_handler] +fn panic(_: &PanicInfo) -> ! { + loop {} +} diff --git a/riscv/riscv_basic/memory.x b/riscv/riscv_basic/memory.x new file mode 100644 index 00000000..3dd45dd8 --- /dev/null +++ b/riscv/riscv_basic/memory.x @@ -0,0 +1,34 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 2M + RAM : ORIGIN = 0x50000000, LENGTH = 16K +} + +REGION_ALIAS("REGION_TEXT", FLASH); +REGION_ALIAS("REGION_RODATA", RAM); +REGION_ALIAS("REGION_DATA", RAM); +REGION_ALIAS("REGION_BSS", RAM); +REGION_ALIAS("REGION_HEAP", RAM); +REGION_ALIAS("REGION_STACK", RAM); + +PROVIDE(Interrupt0 = DefaultInterruptHandler); +PROVIDE(Interrupt1 = DefaultInterruptHandler); +PROVIDE(Interrupt2 = DefaultInterruptHandler); +PROVIDE(Interrupt3 = DefaultInterruptHandler); +PROVIDE(Interrupt4 = DefaultInterruptHandler); +PROVIDE(Interrupt5 = DefaultInterruptHandler); +PROVIDE(Interrupt6 = DefaultInterruptHandler); +PROVIDE(Interrupt7 = DefaultInterruptHandler); +PROVIDE(Interrupt8 = DefaultInterruptHandler); +PROVIDE(MTIME = DefaultInterruptHandler); +SECTIONS{ + /* ### .uninit */ + .uninit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __suninit = .; + *(.uninit .uninit.*); + . = ALIGN(4); + __euninit = .; + } > RAM +} diff --git a/riscv/riscv_basic/rustfmt.toml b/riscv/riscv_basic/rustfmt.toml new file mode 100644 index 00000000..28781c75 --- /dev/null +++ b/riscv/riscv_basic/rustfmt.toml @@ -0,0 +1 @@ +tab_spaces=2 diff --git a/riscv/riscv_basic/src/main.rs b/riscv/riscv_basic/src/main.rs new file mode 100644 index 00000000..b5050305 --- /dev/null +++ b/riscv/riscv_basic/src/main.rs @@ -0,0 +1,56 @@ +#![no_main] +#![no_std] +use core::panic::PanicInfo; +use hippomenes_rt as _; +#[rtic::app(device = hippomenes_core)] +mod app { + use hippomenes_core::{interrupt1, interrupt2}; + #[shared] + struct Shared { + r: u8, + l: u8, + } + + #[local] + struct Local {} + + #[init] + fn init(cx: init::Context) -> (Shared, Local) { + rtic::export::pend(interrupt1::Interrupt1); + rtic::export::pend(interrupt2::Interrupt2); + let r = 9; + let l = 10; + (Shared { r, l }, Local {}) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + loop {} + } + + #[task(binds = Interrupt1, priority = 1, shared = [r,l])] + fn i1(mut cx: i1::Context) { + cx.shared.r.lock(|r| { + cx.shared.l.lock(|l| { + *l = *l + *r; + }); + }); + } + #[task(binds = Interrupt2, priority = 2, shared=[l])] + fn i2(mut cx: i2::Context) { + cx.shared.l.lock(|l| { + *l += 2; + }); + } + #[task(binds = Interrupt3, priority = 3, shared = [r])] + fn i3(mut cx: i3::Context) { + cx.shared.r.lock(|r| { + *r += 1; + }); + } +} + +#[panic_handler] +fn panic(_: &PanicInfo) -> ! { + loop {} +} diff --git a/riscv/src/components/alu.rs b/riscv/src/components/alu.rs index 162c7a80..37c93108 100644 --- a/riscv/src/components/alu.rs +++ b/riscv/src/components/alu.rs @@ -1,10 +1,22 @@ use log::trace; use serde::{Deserialize, Serialize}; +#[cfg(feature = "gui-egui")] +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; use syncrim::{ - common::{Component, Condition, Input, OutputType, Ports, SignalValue, Simulator}, + common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, + }, signal::SignalSigned, }; +pub const ALU_OPERATOR_I_ID: &str = "operator_i"; +pub const ALU_OPERAND_A_I_ID: &str = "operand_a_i"; +pub const ALU_OPERAND_B_I_ID: &str = "operand_b_i"; + +pub const ALU_RESULT_O_ID: &str = "alu_result_o"; + #[derive(Serialize, Deserialize)] pub struct ALU { pub id: String, @@ -21,31 +33,63 @@ impl Component for ALU { fn to_(&self) { println!("ALU"); } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy = Input::new("dummy", "out"); + Box::new(Rc::new(ALU { + id: id.to_string(), + pos: (pos.0, pos.1), + operator_i: dummy.clone(), + operand_a_i: dummy.clone(), + operand_b_i: dummy.clone(), + })) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + ALU_OPERAND_A_I_ID => self.operand_a_i = new_input, + ALU_OPERAND_B_I_ID => self.operand_b_i = new_input, + ALU_OPERATOR_I_ID => self.operator_i = new_input, + _ => (), + } + } + fn get_id_ports(&self) -> (String, Ports) { ( self.id.clone(), - Ports { - inputs: vec![ - self.operator_i.clone(), - self.operand_a_i.clone(), - self.operand_b_i.clone(), + Ports::new( + vec![ + &InputPort { + port_id: ALU_OPERATOR_I_ID.to_string(), + input: self.operator_i.clone(), + }, + &InputPort { + port_id: ALU_OPERAND_A_I_ID.to_string(), + input: self.operand_a_i.clone(), + }, + &InputPort { + port_id: ALU_OPERAND_B_I_ID.to_string(), + input: self.operand_b_i.clone(), + }, //self.operand_c_i.clone(), ], - out_type: OutputType::Combinatorial, - outputs: vec![ - "result_o".into(), + OutputType::Combinatorial, + vec![ + ALU_RESULT_O_ID, + //"result_o".into(), //"comparison_result_o".into(), //"ready_o".into(), ], - }, + ), ) } + #[allow(non_snake_case)] fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { let operator_i = match simulator.get_input_value(&self.operator_i) { SignalValue::Data(data) => data, _ => { - simulator.set_out_value(&self.id, "result_o", SignalValue::Unknown); + simulator.set_out_value(&self.id, ALU_RESULT_O_ID, SignalValue::Unknown); return Ok(()); } }; @@ -128,10 +172,15 @@ impl Component for ALU { _ => {} } trace!("ALU result_o:{:08x}", result_o); - simulator.set_out_value(&self.id, "result_o", result_o); + simulator.set_out_value(&self.id, ALU_RESULT_O_ID, result_o); Ok(()) } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } + #[cfg(test)] mod test { use super::*; @@ -159,11 +208,11 @@ mod test { ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs - let alu_out = &Input::new("alu", "result_o"); + let alu_out = &Input::new("alu", "alu_result_o"); simulator.set_out_value("operator_i", "out", 1); //add simulator.set_out_value("operand_a_i", "out", -41i32 as u32); diff --git a/riscv/src/components/branch_logic.rs b/riscv/src/components/branch_logic.rs index 237002c8..8c9498a1 100644 --- a/riscv/src/components/branch_logic.rs +++ b/riscv/src/components/branch_logic.rs @@ -1,9 +1,27 @@ use log::trace; use serde::{Deserialize, Serialize}; -use syncrim::common::{Component, Condition, Input, OutputType, Ports, SignalValue, Simulator}; +#[cfg(feature = "gui-egui")] +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 BRANCH_LOGIC_RS1_ID: &str = "rs1"; +pub const BRANCH_LOGIC_RS2_ID: &str = "rs2"; +pub const BRANCH_LOGIC_CTRL_ID: &str = "ctrl"; +pub const BRANCH_LOGIC_ENABLE_ID: &str = "enable"; +//pub const BRANCH_LOGIC_MRET_ID: &str = "mret"; +pub const BRANCH_LOGIC_OUT_ID: &str = "out"; + +pub const BRANCH_LOGIC_HEIGHT: f32 = 60.0; +pub const BRANCH_LOGIC_WIDTH: f32 = 60.0; #[derive(Serialize, Deserialize)] pub struct BranchLogic { + pub width: f32, + pub height: f32, pub id: String, pub pos: (f32, f32), @@ -12,6 +30,8 @@ pub struct BranchLogic { pub ctrl: Input, pub enable: Input, + // pub mret: Input, + // pub int: Input, } #[typetag::serde()] @@ -19,21 +39,67 @@ impl Component for BranchLogic { fn to_(&self) { println!("BranchLogic"); } + + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy = Input::new("dummy", "out"); + Box::new(Rc::new(BranchLogic { + width: 60.0, + height: 60.0, + id: id.to_string(), + pos: (pos.0, pos.1), + rs1: dummy.clone(), + rs2: dummy.clone(), + ctrl: dummy.clone(), + enable: dummy.clone(), + // mret: dummy.clone(), + // int: dummy.clone(), + })) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + BRANCH_LOGIC_RS1_ID => self.rs1 = new_input, + BRANCH_LOGIC_RS2_ID => self.rs2 = new_input, + BRANCH_LOGIC_CTRL_ID => self.ctrl = new_input, + BRANCH_LOGIC_ENABLE_ID => self.enable = new_input, + // BRANCH_LOGIC_MRET_ID => self.mret = new_input, + _ => (), + } + } + fn get_id_ports(&self) -> (String, Ports) { ( self.id.clone(), - Ports { - inputs: vec![ - self.rs1.clone(), - self.rs2.clone(), - self.ctrl.clone(), - self.enable.clone(), + Ports::new( + vec![ + &InputPort { + port_id: BRANCH_LOGIC_RS1_ID.to_string(), + input: self.rs1.clone(), + }, + &InputPort { + port_id: BRANCH_LOGIC_RS2_ID.to_string(), + input: self.rs2.clone(), + }, + &InputPort { + port_id: BRANCH_LOGIC_CTRL_ID.to_string(), + input: self.ctrl.clone(), + }, + &InputPort { + port_id: BRANCH_LOGIC_ENABLE_ID.to_string(), + input: self.enable.clone(), + }, + // &InputPort { + // port_id: BRANCH_LOGIC_MRET_ID.to_string(), + // input: self.mret.clone(), + // }, ], - out_type: OutputType::Combinatorial, - outputs: vec!["out".into()], - }, + OutputType::Combinatorial, + vec![BRANCH_LOGIC_OUT_ID], + ), ) } + #[allow(non_snake_case)] fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { let enable: u32 = simulator.get_input_value(&self.enable).try_into().unwrap(); @@ -51,7 +117,7 @@ impl Component for BranchLogic { let rs1: u32 = rs1.try_into().unwrap(); let rs2: u32 = rs2.try_into().unwrap(); if rs1 == rs2 { - out = SignalValue::from(2); + out = SignalValue::from(1); trace!("beq ok"); } else { out = SignalValue::from(0); @@ -62,7 +128,7 @@ impl Component for BranchLogic { let rs1: u32 = rs1.try_into().unwrap(); let rs2: u32 = rs2.try_into().unwrap(); if rs1 != rs2 { - out = SignalValue::from(2); + out = SignalValue::from(1); trace!("bne ok"); } else { out = SignalValue::from(0); @@ -73,7 +139,7 @@ impl Component for BranchLogic { let rs1: u32 = rs1.try_into().unwrap(); let rs2: u32 = rs2.try_into().unwrap(); if (rs1 as i32) < (rs2 as i32) { - out = SignalValue::from(2); + out = SignalValue::from(1); trace!("blt ok"); } else { out = SignalValue::from(0); @@ -84,7 +150,7 @@ impl Component for BranchLogic { let rs1: u32 = rs1.try_into().unwrap(); let rs2: u32 = rs2.try_into().unwrap(); if rs1 as i32 >= rs2 as i32 { - out = SignalValue::from(2); + out = SignalValue::from(1); trace!("bge ok"); } else { out = SignalValue::from(0); @@ -95,7 +161,7 @@ impl Component for BranchLogic { let rs1: u32 = rs1.try_into().unwrap(); let rs2: u32 = rs2.try_into().unwrap(); if rs1 < rs2 { - out = SignalValue::from(2); + out = SignalValue::from(1); trace!("bltu ok"); } else { out = SignalValue::from(0); @@ -107,7 +173,7 @@ impl Component for BranchLogic { let rs2: u32 = rs2.try_into().unwrap(); if rs1 >= rs2 { trace!("bgeu ok"); - out = SignalValue::from(2); + out = SignalValue::from(1); } else { trace!("bgeu failed"); out = SignalValue::from(0); @@ -119,7 +185,7 @@ impl Component for BranchLogic { } //jalr 0b010 => { trace!("jal ok"); - out = SignalValue::from(2); //jal + out = SignalValue::from(1); //jal } _ => { trace!("no control transfer"); @@ -135,6 +201,10 @@ impl Component for BranchLogic { simulator.set_out_value(&self.id, "out", out); Ok(()) } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } #[cfg(test)] mod test { @@ -154,18 +224,24 @@ mod test { Rc::new(ProbeOut::new("rs2")), Rc::new(ProbeOut::new("ctrl")), Rc::new(ProbeOut::new("enable")), + Rc::new(ProbeOut::new("int")), + Rc::new(ProbeOut::new("mret")), Rc::new(BranchLogic { + width: 0.0, + height: 0.0, id: "blu".to_string(), pos: (0.0, 0.0), rs1: Input::new("rs1", "out"), rs2: Input::new("rs2", "out"), ctrl: Input::new("ctrl", "out"), enable: Input::new("enable", "out"), + // int: Input::new("int", "out"), + // mret: Input::new("mret", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs @@ -191,7 +267,7 @@ mod test { simulator.set_out_value("rs1", "out", 42); simulator.set_out_value("rs2", "out", 1337); simulator.clock(); - assert_eq!(simulator.get_input_value(blu_out), 2.into()); + assert_eq!(simulator.get_input_value(blu_out), 1.into()); println!(""); simulator.set_out_value("ctrl", "out", 0b001); @@ -209,18 +285,23 @@ mod test { Rc::new(ProbeOut::new("rs2")), Rc::new(ProbeOut::new("ctrl")), Rc::new(ProbeOut::new("enable")), + Rc::new(ProbeOut::new("int")), + Rc::new(ProbeOut::new("mret")), Rc::new(BranchLogic { + width: 0.0, + height: 0.0, id: "blu".to_string(), pos: (0.0, 0.0), rs1: Input::new("rs1", "out"), rs2: Input::new("rs2", "out"), ctrl: Input::new("ctrl", "out"), enable: Input::new("enable", "out"), + // int: Input::new("int", "out"), + // mret: Input::new("mret", "out"), }), ], }; - - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs @@ -246,7 +327,7 @@ mod test { simulator.set_out_value("rs1", "out", 42); simulator.set_out_value("rs2", "out", 42); simulator.clock(); - assert_eq!(simulator.get_input_value(blu_out), 2.into()); + assert_eq!(simulator.get_input_value(blu_out), 1.into()); println!(""); simulator.set_out_value("ctrl", "out", 0b000); //beq @@ -264,18 +345,24 @@ mod test { Rc::new(ProbeOut::new("rs2")), Rc::new(ProbeOut::new("ctrl")), Rc::new(ProbeOut::new("enable")), + Rc::new(ProbeOut::new("int")), + Rc::new(ProbeOut::new("mret")), Rc::new(BranchLogic { + width: 0.0, + height: 0.0, id: "blu".to_string(), pos: (0.0, 0.0), rs1: Input::new("rs1", "out"), rs2: Input::new("rs2", "out"), ctrl: Input::new("ctrl", "out"), enable: Input::new("enable", "out"), + // int: Input::new("int", "out"), + // mret: Input::new("mret", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs @@ -301,7 +388,7 @@ mod test { simulator.set_out_value("rs1", "out", 41); simulator.set_out_value("rs2", "out", 42); simulator.clock(); - assert_eq!(simulator.get_input_value(blu_out), 2.into()); + assert_eq!(simulator.get_input_value(blu_out), 1.into()); println!(""); simulator.set_out_value("ctrl", "out", 0b100); @@ -326,18 +413,24 @@ mod test { Rc::new(ProbeOut::new("rs2")), Rc::new(ProbeOut::new("ctrl")), Rc::new(ProbeOut::new("enable")), + Rc::new(ProbeOut::new("int")), + Rc::new(ProbeOut::new("mret")), Rc::new(BranchLogic { + width: 0.0, + height: 0.0, id: "blu".to_string(), pos: (0.0, 0.0), rs1: Input::new("rs1", "out"), rs2: Input::new("rs2", "out"), ctrl: Input::new("ctrl", "out"), enable: Input::new("enable", "out"), + // int: Input::new("int", "out"), + // mret: Input::new("mret", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs @@ -363,7 +456,7 @@ mod test { simulator.set_out_value("rs1", "out", 42); simulator.set_out_value("rs2", "out", 42); simulator.clock(); - assert_eq!(simulator.get_input_value(blu_out), 2.into()); + assert_eq!(simulator.get_input_value(blu_out), 1.into()); println!(""); simulator.set_out_value("ctrl", "out", 0b101); @@ -371,7 +464,7 @@ mod test { simulator.set_out_value("rs1", "out", 43); simulator.set_out_value("rs2", "out", 42); simulator.clock(); - assert_eq!(simulator.get_input_value(blu_out), 2.into()); + assert_eq!(simulator.get_input_value(blu_out), 1.into()); println!(""); simulator.set_out_value("ctrl", "out", 0b101); simulator.set_out_value("enable", "out", 1); //enabled @@ -388,18 +481,24 @@ mod test { Rc::new(ProbeOut::new("rs2")), Rc::new(ProbeOut::new("ctrl")), Rc::new(ProbeOut::new("enable")), + Rc::new(ProbeOut::new("int")), + Rc::new(ProbeOut::new("mret")), Rc::new(BranchLogic { + width: 0.0, + height: 0.0, id: "blu".to_string(), pos: (0.0, 0.0), rs1: Input::new("rs1", "out"), rs2: Input::new("rs2", "out"), ctrl: Input::new("ctrl", "out"), enable: Input::new("enable", "out"), + // int: Input::new("int", "out"), + // mret: Input::new("mret", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs @@ -425,7 +524,7 @@ mod test { simulator.set_out_value("rs1", "out", 1); simulator.set_out_value("rs2", "out", 42); simulator.clock(); - assert_eq!(simulator.get_input_value(blu_out), 2.into()); + assert_eq!(simulator.get_input_value(blu_out), 1.into()); println!(""); simulator.set_out_value("ctrl", "out", 0b110); @@ -450,18 +549,24 @@ mod test { Rc::new(ProbeOut::new("rs2")), Rc::new(ProbeOut::new("ctrl")), Rc::new(ProbeOut::new("enable")), + Rc::new(ProbeOut::new("int")), + Rc::new(ProbeOut::new("mret")), Rc::new(BranchLogic { + width: 0.0, + height: 0.0, id: "blu".to_string(), pos: (0.0, 0.0), rs1: Input::new("rs1", "out"), rs2: Input::new("rs2", "out"), ctrl: Input::new("ctrl", "out"), enable: Input::new("enable", "out"), + // int: Input::new("int", "out"), + // mret: Input::new("mret", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs @@ -495,21 +600,21 @@ mod test { simulator.set_out_value("rs1", "out", -1i32 as u32); simulator.set_out_value("rs2", "out", 42); simulator.clock(); - assert_eq!(simulator.get_input_value(blu_out), 2.into()); + assert_eq!(simulator.get_input_value(blu_out), 1.into()); println!(""); simulator.set_out_value("ctrl", "out", 0b111); simulator.set_out_value("enable", "out", 1); //enabled simulator.set_out_value("rs1", "out", 43); simulator.set_out_value("rs2", "out", 42); simulator.clock(); - assert_eq!(simulator.get_input_value(blu_out), 2.into()); + assert_eq!(simulator.get_input_value(blu_out), 1.into()); println!(""); simulator.set_out_value("ctrl", "out", 0b111); simulator.set_out_value("enable", "out", 1); //enabled simulator.set_out_value("rs1", "out", 42); simulator.set_out_value("rs2", "out", 42); simulator.clock(); - assert_eq!(simulator.get_input_value(blu_out), 2.into()); + assert_eq!(simulator.get_input_value(blu_out), 1.into()); } #[test] fn test_jalr() { @@ -519,18 +624,24 @@ mod test { Rc::new(ProbeOut::new("rs2")), Rc::new(ProbeOut::new("ctrl")), Rc::new(ProbeOut::new("enable")), + Rc::new(ProbeOut::new("int")), + Rc::new(ProbeOut::new("mret")), Rc::new(BranchLogic { + width: 0.0, + height: 0.0, id: "blu".to_string(), pos: (0.0, 0.0), rs1: Input::new("rs1", "out"), rs2: Input::new("rs2", "out"), ctrl: Input::new("ctrl", "out"), enable: Input::new("enable", "out"), + // int: Input::new("int", "out"), + // mret: Input::new("mret", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs @@ -558,18 +669,24 @@ mod test { Rc::new(ProbeOut::new("rs2")), Rc::new(ProbeOut::new("ctrl")), Rc::new(ProbeOut::new("enable")), + Rc::new(ProbeOut::new("int")), + Rc::new(ProbeOut::new("mret")), Rc::new(BranchLogic { + width: 0.0, + height: 0.0, id: "blu".to_string(), pos: (0.0, 0.0), rs1: Input::new("rs1", "out"), rs2: Input::new("rs2", "out"), ctrl: Input::new("ctrl", "out"), enable: Input::new("enable", "out"), + // int: Input::new("int", "out"), + // mret: Input::new("mret", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs @@ -587,6 +704,6 @@ mod test { simulator.set_out_value("rs1", "out", SignalValue::Unknown); simulator.set_out_value("rs2", "out", SignalValue::Unknown); simulator.clock(); - assert_eq!(simulator.get_input_value(blu_out), 2.into()); + assert_eq!(simulator.get_input_value(blu_out), 1.into()); } } diff --git a/riscv/src/components/clic.rs b/riscv/src/components/clic.rs index a50a1d6b..35253674 100644 --- a/riscv/src/components/clic.rs +++ b/riscv/src/components/clic.rs @@ -1,9 +1,49 @@ -use crate::common::{Component, Condition, Id, Input, OutputType, Ports, Signal, Simulator}; -use num_enum::IntoPrimitive; -use num_enum::TryFromPrimitive; +use log::trace; use serde::{Deserialize, Serialize}; - -use std::{cell::RefCell, collections::HashMap, convert::TryFrom}; +use syncrim::common::InputPort; +use syncrim::{ + common::{Component, Condition, Id, Input, OutputType, Ports, Simulator}, + signal::{SignalSigned, SignalUnsigned, SignalValue}, +}; + +use priority_queue::PriorityQueue; + +use std::{cell::RefCell, collections::HashMap}; +const CLIC_TIMESTAMP_BASE: u32 = 0xB40; +const CLIC_TIMESTAMP_PRESCALER: usize = 0x0; +pub const CLIC_CSR_ADDR_ID: &str = "csr_addr"; +pub const CLIC_CSR_CTL_ID: &str = "csr_ctl"; +pub const CLIC_CSR_DATA_ID: &str = "csr_data"; +pub const CLIC_DATA_ID: &str = "data"; +pub const CLIC_ADDR_ID: &str = "addr"; +pub const CLIC_DATA_WE_ID: &str = "data_we"; +pub const CLIC_MRET_ID: &str = "mret"; +pub const CLIC_PC_ID: &str = "pc"; +pub const CLIC_PC_NEXT_ID: &str = "pc_next"; +pub const CLIC_DATA_SIZE_ID: &str = "size"; +pub const CLIC_CSR_DATA_OUT_ID: &str = "csr_data_o"; +pub const CLIC_MMIO_DATA_OUT_ID: &str = "mmio_data_o"; +pub const CLIC_MEM_INT_ADDR_ID: &str = "mem_int_addr"; +pub const CLIC_INTERRUPT_ID: &str = "interrupt"; +pub const CLIC_INTERRUPT_INV_ID: &str = "interrupt_inv"; +pub const CLIC_WRITE_RA_ENABLE_ID: &str = "write_ra_enable"; +pub const CLIC_PC_ADDR_OUT_ID: &str = "pc_addr_out"; +pub const CLIC_MEPC_OUT_ID: &str = "mepc_out"; +pub const CLIC_MEPC_ISR_MUX: &str = "isr_mepc_sel"; +pub const CLIC_RF_RA_WE: &str = "rf_ra_we"; +// pub const CLIC_REG_FILE_WRITE_ID: &str = "reg_file_write"; +pub const CLIC_STACK_DEPTH_OUT_ID: &str = "stack_depth_out"; + +pub const TIMER_WIDTH: u32 = 16; +pub const TIMER_PRES_WIDTH: u32 = 4; +pub const TIMER_ADDR: u32 = 0x400; +#[derive(Serialize, Deserialize)] +struct CLICOp { + pub mmio_op: Option<([u32; 2], u32)>, + pub csr_op: Option>, + //op removed = true, op added = false + pub queue_op: Vec<(u32, u8, bool)>, +} #[derive(Serialize, Deserialize)] pub struct CLIC { @@ -13,152 +53,1029 @@ pub struct CLIC { pub height: f32, // configuration - pub big_endian: bool, + //pub big_endian: bool, - // ports + // mmio ports pub data: Input, pub addr: Input, - pub ctrl: Input, - pub sign: Input, - pub size: Input, + pub data_we: Input, + pub data_size: Input, + + //CSR ports + pub csr_data: Input, + pub csr_addr: Input, + //1 = write, 2 = set, 3 = clear + pub csr_ctl: Input, + + //MRET specific signal + pub mret: Input, + + //PC input for MEPC update + pub pc: Input, + pub pc_next: Input, + + //internal state + #[serde(skip)] + pub csrstore: RefCell>, //address, val + #[serde(skip)] + pub mmio: RefCell>, //address, val + #[serde(skip)] + pub queue: RefCell>, //prio, id's + #[serde(skip)] + pub clic_stack: RefCell>, + #[serde(skip)] + pub mtime: RefCell, + #[serde(skip)] + pub monotonic: RefCell, + #[serde(skip)] + pub mtimecomp: RefCell, + // #[serde(skip)] + // pub stack_depth: RefCell, //current register stack depth + history: RefCell>, +} +#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +pub struct MMIOEntry { + clicintip: u8, + clicintie: u8, + clicintattr: u8, + clicintctl: u8, +} - // memory - pub memory: Memory, - // later history... tbd +//it would be nice to have a bitfield type instead of wildly using u32s and hoping for the best +#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +pub struct TimerCSR { + pub counter_top: u32, + pub prescaler: u32, } -#[derive(Serialize, Deserialize, Debug)] -pub struct CLIC { - bytes: RefCell>, +impl Into for u32 { + fn into(self) -> TimerCSR { + trace!("TIMER CSR: {}", self); + TimerCSR { + counter_top: (self & ((2_u32.pow(TIMER_WIDTH) - 1) << TIMER_PRES_WIDTH)) + >> TIMER_PRES_WIDTH, + prescaler: self & (2_u32.pow(TIMER_PRES_WIDTH) - 1), + } + } +} + +impl Into for TimerCSR { + fn into(self) -> u32 { + self.counter_top << TIMER_PRES_WIDTH | self.prescaler + } +} + +impl From for MMIOEntry { + fn from(val: u32) -> Self { + MMIOEntry { + clicintip: (val & 0b11111111) as u8, + clicintie: ((val >> 8) & 0b11111111) as u8, + clicintattr: ((val >> 16) & 0b11111111) as u8, + clicintctl: ((val >> 24) & 0b11111111) as u8, + } + } } -impl Default for Memory { - fn default() -> Self { - Self::new() +impl From for u32 { + fn from(val: MMIOEntry) -> u32 { + val.clicintip as u32 + | ((val.clicintie as u32) << 8) + | ((val.clicintattr as u32) << 16) + | ((val.clicintctl as u32) << 24) + } +} +impl From for usize { + fn from(val: MMIOEntry) -> usize { + val.clicintip as usize + | ((val.clicintie as usize) << 8) + | ((val.clicintattr as usize) << 16) + | ((val.clicintctl as usize) << 24) + } +} +impl CLIC { + #[allow(clippy::too_many_arguments)] + pub fn new( + id: Id, + pos: (f32, f32), + width: f32, + height: f32, + data: Input, + addr: Input, + data_we: Input, + data_size: Input, + csr_data: Input, + csr_addr: Input, + // lines: Vec, + csr_ctl: Input, + mret: Input, + pc: Input, + pc_next: Input, + ) -> Self { + CLIC { + id, + pos, + width, + height, + data, + addr, + data_we, + data_size, + csr_data, + csr_addr, + mret, + pc, + pc_next, + csrstore: { + let mut csrstore = HashMap::new(); + csrstore.insert(0x300, 0); //mstatus + csrstore.insert(0x305, 0b11); //mtvec, we only support vectored + csrstore.insert(0x307, 0); //mtvt + csrstore.insert(0x340, 0); //mscratch + csrstore.insert(0x341, 0); //mepc + csrstore.insert(0x342, 0); //mcause + csrstore.insert(0x343, 0); //mtval + csrstore.insert(0x345, 0); //mnxti + csrstore.insert(0xFB1, 0); //mintstatus + csrstore.insert(0x347, 0); //mintthresh + csrstore.insert(0x348, 0); //mscratchcsw + csrstore.insert(0x349, 0); //mscratchcswl + csrstore.insert(0xF14, 0); //mhartid + csrstore.insert(0x350, 7); //stack_depth + for i in 0xB00..0xBC0 { + csrstore.insert(i, 0); //set up individual interrupt config CSRs + } + RefCell::new(csrstore) + }, + monotonic: RefCell::new(0), + mmio: { + let mut mmio = HashMap::new(); + for i in 0x1000..0x10C0 { + //for now, we support 0xBF interrupts to give ourselves a + //continuous CSR range + mmio.insert(i, 0); + } + RefCell::new(mmio) + }, + queue: RefCell::new(PriorityQueue::new()), + // lines: lines, + csr_ctl, + clic_stack: RefCell::new(Vec::new()), + history: RefCell::new(vec![]), + mtime: 0.into(), + mtimecomp: 0.into(), + // stack_depth: 0.into(), + } } } -impl Memory { - pub fn new() -> Self { - Memory { - bytes: RefCell::new(HashMap::new()), +#[typetag::serde()] +impl Component for CLIC { + fn reset(&self) { + self.csrstore.swap({ + let mut csrstore = HashMap::new(); + csrstore.insert(0x300, 0); //mstatus + csrstore.insert(0x305, 0b11); //mtvec, we only support vectored + csrstore.insert(0x307, 0); //mtvt + csrstore.insert(0x340, 0); //mscratch + csrstore.insert(0x341, 0); //mepc + csrstore.insert(0x342, 0); //mcause + csrstore.insert(0x343, 0); //mtval + csrstore.insert(0x345, 0); //mnxti + csrstore.insert(0xFB1, 0); //mintstatus + csrstore.insert(0x347, 0); //mintthresh + csrstore.insert(0x348, 0); //mscratchcsw + csrstore.insert(0x349, 0); //mscratchcswl + csrstore.insert(0xF14, 0); //mhartid + csrstore.insert(0x350, 7); //stack_depth, vanilla clic config + csrstore.insert(0x351, 0); //super mtvec + csrstore.insert(0x400, 0); //timer + for i in 0xB00..=0xBBF { + csrstore.insert(i, 0); //set up individual interrupt config CSRs + } + for i in 0xD00..=0xDBF { + csrstore.insert(i, 0); //set up timestamping CSRs + } + &RefCell::new(csrstore) + }); + self.mmio.swap({ + let mut mmio = HashMap::new(); + for i in 0x1000..0x5010 { + mmio.insert(i, 0); + } + &RefCell::new(mmio) + }); + self.queue.swap(&RefCell::new(PriorityQueue::new())); + self.history.swap(&RefCell::new(vec![])); + self.clic_stack.swap(&RefCell::new(Vec::new())); + self.monotonic.swap(&RefCell::new(0)); + } + + fn to_(&self) { + println!("CLIC"); + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + CLIC_CSR_ADDR_ID => self.csr_addr = new_input, + CLIC_CSR_CTL_ID => self.csr_ctl = new_input, + CLIC_CSR_DATA_ID => self.csr_data = new_input, + CLIC_DATA_ID => self.data = new_input, + CLIC_ADDR_ID => self.addr = new_input, + CLIC_DATA_WE_ID => self.data_we = new_input, + CLIC_MRET_ID => self.mret = new_input, + CLIC_PC_ID => self.pc = new_input, + CLIC_DATA_SIZE_ID => self.data_size = new_input, + _ => (), } } - fn align(&self, addr: usize, size: usize) -> Signal { - (addr % size != 0) as Signal + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: CLIC_CSR_ADDR_ID.to_string(), + input: self.csr_addr.clone(), + }, + &InputPort { + port_id: CLIC_CSR_CTL_ID.to_string(), + input: self.csr_ctl.clone(), + }, + &InputPort { + port_id: CLIC_CSR_DATA_ID.to_string(), + input: self.csr_data.clone(), + }, + &InputPort { + port_id: CLIC_DATA_ID.to_string(), + input: self.data.clone(), + }, + &InputPort { + port_id: CLIC_ADDR_ID.to_string(), + input: self.addr.clone(), + }, + &InputPort { + port_id: CLIC_DATA_WE_ID.to_string(), + input: self.data_we.clone(), + }, + &InputPort { + port_id: CLIC_MRET_ID.to_string(), + input: self.mret.clone(), + }, + &InputPort { + port_id: CLIC_PC_ID.to_string(), + input: self.pc.clone(), + }, + &InputPort { + port_id: CLIC_PC_NEXT_ID.to_string(), + input: self.pc_next.clone(), + }, + &InputPort { + port_id: CLIC_DATA_SIZE_ID.to_string(), + input: self.data_size.clone(), + }, + ], + OutputType::Combinatorial, + vec![ + // CLIC_REG_FILE_WRITE_ID, + CLIC_CSR_DATA_OUT_ID, + CLIC_MMIO_DATA_OUT_ID, + CLIC_MEM_INT_ADDR_ID, + CLIC_PC_ADDR_OUT_ID, + CLIC_INTERRUPT_ID, + CLIC_INTERRUPT_INV_ID, + CLIC_MEPC_OUT_ID, + CLIC_STACK_DEPTH_OUT_ID, + CLIC_MEPC_ISR_MUX, + CLIC_RF_RA_WE, + ], + ), + ) } - fn read(&self, addr: usize, size: usize, sign: bool, big_endian: bool) -> Signal { + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + //get inputs + let csr_ctl: u32 = simulator + .get_input_value(&self.csr_ctl) + .try_into() + .unwrap_or(0); + let csr_addr: u32 = simulator + .get_input_value(&self.csr_addr) + .try_into() + .unwrap_or(0); + let csr_data: u32 = simulator + .get_input_value(&self.csr_data) + .try_into() + .unwrap_or(0); + let mmio_addr: u32 = simulator + .get_input_value(&self.addr) + .try_into() + .unwrap_or(0); + let mmio_data: u32 = simulator + .get_input_value(&self.data) + .try_into() + .unwrap_or(0); + let mret: u32 = simulator + .get_input_value(&self.mret) + .try_into() + .unwrap_or(0); + let pc: u32 = simulator.get_input_value(&self.pc).try_into().unwrap(); + let pc_next: u32 = simulator.get_input_value(&self.pc_next).try_into().unwrap(); + let data_size: u32 = simulator + .get_input_value(&self.data_size) + .try_into() + .unwrap_or(0); + let mmio_we: u32 = simulator.get_input_value(&self.data_we).try_into().unwrap(); + + // define outputs + let csr_out; + let mut blu_int = false; // default to pc + //let mut int_mux_ctl = 0; // default to pc + let mut mem_int_addr = SignalValue::Uninitialized; + let mut rf_ra_we = SignalValue::Data(0); + let mut isr_mepc_select = SignalValue::Uninitialized; + let mut pc_out_signal = SignalValue::Uninitialized; + + // get state + //csr store + let mut csrstore = self.csrstore.borrow_mut(); + // operation history for reversing + let mut history = self.history.borrow_mut(); + // interrupt priority queue + let mut queue = self.queue.borrow_mut(); + // super-clic threshold/return address stack + let mut clic_stack = self.clic_stack.borrow_mut(); + + // init a history entry for this cycle + let mut history_entry = CLICOp { + csr_op: None, + mmio_op: None, + queue_op: vec![], + }; + //dispatched interrupt id, used to unpend in csr store + let mut dispatched_interrupt_id = None; + let mut monotonic = self.monotonic.borrow_mut(); + *monotonic += 1; + // handle CSR op if there was any + csr_out = self.csr_op( + &mut csrstore, + &mut history_entry, + &mut queue, + csr_ctl, + csr_data, + csr_addr, + ); + + // with CSR IO handled, get all of the neccessary CSR values + let mut stack_depth = *csrstore.get(&0x350).unwrap() as i32; + let mut mstatus = *csrstore.get(&0x300).unwrap(); + let mut mintthresh = *csrstore.get(&0x347).unwrap(); + let mtvec = *csrstore.get(&0x305).unwrap(); + let super_mtvec = *csrstore.get(&0x351).unwrap(); + + let mut mepc = *csrstore.get(&0x341).unwrap(); + + // INTERRUPT RETURN + // super-clic + let mut return_from_interrupt = None; + + if let Some((old_threshold, current_mepc)) = clic_stack.last() { + let (old_threshold, current_mepc) = (*old_threshold, *current_mepc); + // trace!("clic stack {:#x?}", clic_stack); + trace!( + "pc {:#X}, pc_next {:#X}, current_mepc {:#X}", + pc, + pc_next, + current_mepc + ); + // check if we are about to return from interrupt in super-clic mode + // by comparing against magic number + //if pc_next == current_mepc { + if pc_next == 0xFFFF_FFFF { + let _ = clic_stack.pop(); + // set old threshold + mintthresh = old_threshold as usize; + stack_depth += 1; + + return_from_interrupt = Some(current_mepc); + isr_mepc_select = SignalValue::Data(0); + blu_int = true; + pc_out_signal = SignalValue::Data(mepc as u32); + } + } + // vanilla clic (MRET) + if mret == 1 { + //we are changing mstatus and stack_depth, push to history + if mstatus >> 7 & 1 == 1 { + mstatus |= 0x8; //if mpie then set mie + } else { + mstatus &= !0x8; //if not mpie, ensure not mie + } + mstatus |= 0b1 << 7; //mpie is set on mret + trace!("mret"); + // select mepc on the mux since mret + isr_mepc_select = SignalValue::Data(0); + // select interrupt mux on the PC adder mux + blu_int = true; + stack_depth += 1; + pc_out_signal = SignalValue::Data(mepc as u32); + } + // END INTERRUPT RETURN + // handle mmio + let mmio_data = self.mmio_op( + mmio_addr, + mmio_we, + data_size, + mmio_data, + &mut history_entry, + &mut queue, + &mut csrstore, + ); + + let mut mtime = self.mtime.borrow_mut(); + let timer_t: TimerCSR = (*csrstore.get(&(TIMER_ADDR as usize)).unwrap_or(&0) as u32).into(); + let mtimecomp = timer_t.counter_top; + if *mtime << timer_t.prescaler >= mtimecomp as u64 { + // set pending bit of interrupt 0, call it the timer interrupt + //self.csr_op(&mut csrstore, &mut history_entry,&mut queue, 2, 1, 0xB09); + trace!("COUNTER_TOP:{}", mtimecomp); + self.mmio_op( + 0x1000, + 2, + 1, + 1, + &mut history_entry, + &mut queue, + &mut csrstore, + ); + *mtime = 0; + } else { + /*self.mmio_op( + 0x1000, + 2, + 1, + 0, + &mut history_entry, + &mut queue, + &mut csrstore, + );*/ + *mtime += 1; + // clear pending bit if compare fails we shouldn't do this... + //self.csr_op(&mut csrstore, &mut history_entry, &mut queue, 3, 1, 0xB09); + } + //Interrupt dispatch + + if mstatus & 8 == 8 { + //if MIE is set + if !queue.is_empty() { + let (interrupt_id, interrupt_priority) = queue.peek().unwrap(); // peek highest prio interrupt + let (interrupt_id, interrupt_priority) = (*interrupt_id, *interrupt_priority); + if interrupt_priority as usize > mintthresh { + let _ = queue.pop().unwrap(); //if above threshold, pop it + //now dispatch + //make memory output contents of mtvec + id*4 to branch mux + //set interrupt signal on branch control + history_entry + .queue_op + .push((interrupt_id, interrupt_priority, true)); + // mepc + + let new_mepc = if mret == 1 { + mepc + } else if let Some(mepc_current) = return_from_interrupt { + mepc_current as usize + } else { + // take into account any jumps + pc_next as usize + }; + //vanilla mode + if (stack_depth) <= 0 { + mstatus = (mstatus & !0x8) | 0b1 << 7; //clear interrupt enable, set mpie + mem_int_addr = + SignalValue::Data((mtvec as u32 + (interrupt_id) * 4) & !0b11); + } else { + // super clic + clic_stack.push((mintthresh as u32, new_mepc as u32)); + mintthresh = interrupt_priority as usize; + mem_int_addr = + SignalValue::Data((super_mtvec as u32 + (interrupt_id) * 4) & !0b11); + } + // write to csr + mepc = new_mepc; + blu_int = true; + rf_ra_we = SignalValue::Data(1); + stack_depth -= 1; + trace!("STACK DEPTH: {}", stack_depth); + isr_mepc_select = SignalValue::Data(0); + pc_out_signal = SignalValue::Data( + (*csrstore.get(&(0xB00 + interrupt_id as usize)).unwrap() as u32) << 2, + ); + + trace!( + "interrupt dispatched id:{} prio:{}", + interrupt_id, + interrupt_priority + ); + csrstore.insert( + (CLIC_TIMESTAMP_BASE + interrupt_id) as usize, + ((*monotonic as u32) >> CLIC_TIMESTAMP_PRESCALER) as usize, + ); + dispatched_interrupt_id = Some(interrupt_id); + } + } + } + // END INTERRUPT_DISPATCH + // tracing... + for entry in csrstore.clone().into_iter() { + if entry.0 >= 0xB20 && entry.0 <= 0xB2A {} + } + // trace!("CSR OUT:{:08x}", csr_out); + trace!("QUEUE:{:?}", queue); + simulator.set_out_value( + &self.id, + CLIC_STACK_DEPTH_OUT_ID, + SignalValue::Data(stack_depth as u32), + ); + trace!("clic_stack:{:x?}", clic_stack); + let blu_int_value: SignalValue = blu_int.into(); + let blu_int_inv_value: SignalValue = (!blu_int).into(); + + // write the new CSR values back to the csr store + self.csr_op( + &mut csrstore, + &mut history_entry, + &mut queue, + 1, + stack_depth as u32, + 0x350, + ); + self.csr_op( + &mut csrstore, + &mut history_entry, + &mut queue, + 1, + mstatus as u32, + 0x300, + ); + self.csr_op( + &mut csrstore, + &mut history_entry, + &mut queue, + 1, + mintthresh as u32, + 0x347, + ); + self.csr_op( + &mut csrstore, + &mut history_entry, + &mut queue, + 1, + mtvec as u32, + 0x305, + ); + self.csr_op( + &mut csrstore, + &mut history_entry, + &mut queue, + 1, + super_mtvec as u32, + 0x351, + ); + self.csr_op( + &mut csrstore, + &mut history_entry, + &mut queue, + 1, + mepc as u32, + 0x341, + ); + + if let Some(interrupt_id) = dispatched_interrupt_id { + self.csr_op( + &mut csrstore, + &mut history_entry, + &mut queue, + 3, + 0x1, + 0xB20 + interrupt_id, + ); + } + history.push(history_entry); + simulator.set_out_value(&self.id, "mem_int_addr", mem_int_addr); + simulator.set_out_value(&self.id, CLIC_INTERRUPT_ID, blu_int_value); + simulator.set_out_value(&self.id, CLIC_INTERRUPT_INV_ID, blu_int_inv_value); + // simulator.set_out_value(&self.id, CLIC_INTERRUPT_MUX, blu_int); + simulator.set_out_value(&self.id, "csr_data_o", csr_out as u32); + simulator.set_out_value( + &self.id, + "mmio_data_o", + if let Some(data) = mmio_data { + data + } else { + SignalValue::Data(0) + }, + ); + simulator.set_out_value(&self.id, "mepc_out", pc_out_signal); + simulator.set_out_value(&self.id, CLIC_MEPC_ISR_MUX, isr_mepc_select); + simulator.set_out_value(&self.id, CLIC_RF_RA_WE, rf_ra_we); + // simulator.set_out_value(&self.id, "mret_out", mret_sig); + trace!("MINTTHRESH {}", mintthresh); + trace!("CLIC_INTERRUPT_ID {:?}", blu_int); + Ok(()) + } + + fn un_clock(&self) { + // TODO: Add super-clic stack ops + let mut entry = self.history.borrow_mut().pop().unwrap(); + if let Some(mut ops) = entry.csr_op { + while let Some(op) = ops.pop() { + self.csrstore.borrow_mut().insert(op.0, op.1 as usize); + } + } + if let Some(op) = entry.mmio_op { + self.write( + op.1.try_into().unwrap(), + 4, + false, + SignalValue::Data(op.0[0]), + ); + self.write( + (op.1 + 4).try_into().unwrap(), + 4, + false, + SignalValue::Data(op.0[1]), + ); + } + + while let Some(e) = entry.queue_op.pop() { + //readd + if e.2 { + self.queue.borrow_mut().push(e.0, e.1); + } + //remove + else { + self.queue.borrow_mut().remove(&e.0); + } + } + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl CLIC { + fn mmio_op( + &self, + addr: SignalUnsigned, + we: SignalUnsigned, + data_size: SignalUnsigned, + data: SignalUnsigned, + history_entry: &mut CLICOp, + queue: &mut PriorityQueue, + csrstore: &mut HashMap, + ) -> Option { + let mut mmio_data = None; + let offset = addr % 4; + trace!("mmio_op {:x} {:x} {:x} {:x}", addr, we, data_size, data); + if (0x1000..=0x5000).contains(&addr) { + //if within our mmio range + if we == 2 { + //trace!("clic mmio write"); + + let old_entries: [u32; 2] = [ + self.read(addr as usize - offset as usize, 4_usize, false, false) + .try_into() + .unwrap(), + self.read(addr as usize - offset as usize + 4, 4_usize, false, false) + .try_into() + .unwrap(), + ]; + history_entry.mmio_op = Some((old_entries, addr - offset)); + let mut mask: u64 = 0; + for i in 0..data_size { + mask |= 0xFF << (i * 8); + } + mask <<= offset; + let mmio_entries: [MMIOEntry; 2] = [ + (old_entries[0] ^ (((data << (offset * 8)) ^ old_entries[0]) & mask as u32)) + .into(), + (old_entries[1] + ^ (((data.checked_shr((4 - offset) * 8)).unwrap_or(0) << (offset * 8)) + ^ old_entries[1]) + & mask.checked_shr(32).unwrap_or(0) as u32) + .into(), + ]; + /* trace!( + "CSRSTORE INSERT {:?}, {:?}, addr: {:x}, {:x}", + mmio_entries[0], + mmio_entries[1], + (addr as usize - offset as usize - 0x1000_usize) / 4 + 0xB00, + (addr as usize - offset as usize + 4 - 0x1000_usize) / 4 + 0xB00, + );*/ + + csrstore.insert( + (addr as usize - offset as usize - 0x1000_usize) / 4 + 0xB20, + mmio_entries[0].into(), + ); + csrstore.insert( + (addr as usize - offset as usize + 4 - 0x1000_usize) / 4 + 0xB20, + mmio_entries[1].into(), + ); + for (i, mmio_entry) in mmio_entries.into_iter().enumerate() { + if mmio_entry.clicintie == 1 && mmio_entry.clicintip == 1 { + //enqueue self if pending status and enable status are 1, this changes prio dynamically with prio change also. + history_entry.queue_op.push(( + ((addr - offset + 4u32 * i as u32 - 0x1000) / 4), + mmio_entry.clicintctl, + false, + )); + + /* trace!( + "MMIO QUEUE INTERRUPT {:x}", + ((addr - offset + 4u32 * i as u32 - 0x1000) / 4) + );*/ + queue.push( + (addr - offset + 4u32 * i as u32 - 0x1000) / 4, + mmio_entry.clicintctl, + ); + } + if mmio_entry.clicintie != 1 || mmio_entry.clicintip != 1 { + //dequeue self if pending or enabled status is 0 + if queue + .remove(&(((addr - offset + 4u32 * i as u32 - 0x1000) / 4) - 0x20)) + .is_some() + { + history_entry.queue_op.push(( + ((addr - offset + 4u32 * i as u32 - 0x1000) / 4), + mmio_entry.clicintctl, + true, + )); + }; + } + } + self.write( + addr as usize, + data_size as usize, + false, + SignalValue::Data(data), + ); + trace!("write: {:08x} addr: {:08x}", data, addr); + } else if we == 1 { + mmio_data = Some(self.read(addr as usize, data_size as usize, false, false)); + } + } else if (0x5000..=0x500F).contains(&addr) { + if we == 1 { + mmio_data = Some(self.read(addr as usize, data_size as usize, false, false)); + } else if we == 2 { + self.write( + addr as usize, + data_size as usize, + false, + SignalValue::Data(data), + ); + } + } + mmio_data + } + + fn csr_op( + &self, + csrstore: &mut HashMap, + history_entry: &mut CLICOp, + queue: &mut PriorityQueue, + csr_ctl: SignalUnsigned, + csr_data: SignalUnsigned, + csr_addr: SignalUnsigned, + ) -> u32 { + // handle CSR operations + // trace!("CSR OP"); + let mut val = 0; + let mut csr_data = csr_data.clone(); + match csr_ctl { + 0 => {} + //write + 1 => { + if csrstore.contains_key(&(csr_addr as usize)) { + //mtvec write + if csr_addr == 0x305 { + csr_data |= 0b11; //hardwire to vectored mode + } + // if not mhartid, mhartid is RO + if csr_addr != 0xf14 { + val = *csrstore.get(&(csr_addr as usize)).unwrap(); + if 0xB20 <= csr_addr && csr_addr <= 0xB39 { + csr_data = ((csr_data & (0b11100)) << 22) + | ((csr_data & 0b10) << 7) + | (csr_data & 0b1); + //val = ((val & (0b11100)) << 22) | ((val & 0b10) << 7) | (val & 0b1); + } + + csrstore.insert(csr_addr as usize, csr_data as usize); + history_entry.csr_op = Some(vec![(csr_addr as usize, val as u32)]); + } + // interrupt config write, mirror in mmio + // trace!("CSR_ADDR_NEW:{:x}", csr_addr); + if 0xB20 <= csr_addr && csr_addr <= 0xB39 { + // trace!("ok do thing"); + self.mmio_op( + 0x1000 + ((csr_addr - 0xb20) * 4), + 2, + 4, + csr_data, + history_entry, + queue, + csrstore, + ); + } + } + } + //set + 2 => { + if csrstore.contains_key(&(csr_addr as usize)) { + if csr_addr == 0x305 { + //mtvec set + csr_data |= 0b11; //hardwire to vectored mode + } + if csr_addr != 0xf14 { + //mhartid RO + val = *csrstore.get(&(csr_addr as usize)).unwrap(); + if 0xB20 <= csr_addr && csr_addr <= 0xB39 { + csr_data = ((csr_data & (0b11100)) << 22) + | ((csr_data & 0b10) << 7) + | (csr_data & 0b1); + //val = ((val & (0b11100)) << 22) | ((val & 0b10) << 7) | (val & 0b1); + } + csrstore.insert(csr_addr as usize, (csr_data as usize) | val); + history_entry.csr_op = Some(vec![(csr_addr as usize, val as u32)]); + + //interrupt config CSR + // trace!("SET CSR: {:x}, curr val: {:x}", csr_addr, val); + if 0xB20 <= csr_addr && csr_addr <= 0xB39 { + self.mmio_op( + 0x1000 + ((csr_addr - 0xb20) * 4), + 2, + 4, + (csr_data) | val as u32, + history_entry, + queue, + csrstore, + ); + } + } + } + } + //clear + 3 => { + // trace!("csr clear"); + if csrstore.contains_key(&(csr_addr as usize)) { + // trace!("ADDR:{:x}", csr_addr); + if csr_addr == 0x305 { + //mtvec clear + csr_data |= !0b11; //hardwire to vectored mode + } + if csr_addr != 0xf14 { + //mhartid RO + val = *csrstore.get(&(csr_addr as usize)).unwrap(); + //trace!("val:{:x}, csr_data:{:x}", val, csr_data); + // trace!("{:x}", (val as u32 & !csr_data)); + if 0xB20 <= csr_addr && csr_addr <= 0xB39 { + csr_data = ((csr_data & (0b11100)) << 22) + | ((csr_data & 0b10) << 7) + | (csr_data & 0b1); + // val = ((val & (0b11100)) << 22) | ((val & 0b10) << 7) | (val & 0b1); + } + csrstore.insert(csr_addr as usize, val & !(csr_data as usize)); + history_entry.csr_op = Some(vec![(csr_addr as usize, val as u32)]); + if 0xB20 <= csr_addr && csr_addr <= 0xB39 { + self.mmio_op( + 0x1000 + ((csr_addr - 0xb20) * 4), + 2, + 4, + (val as u32) & !csr_data, + history_entry, + queue, + csrstore, + ); + } + } + } + } + _ => {} + } + val as u32 + } + + fn read(&self, addr: usize, size: usize, sign: bool, big_endian: bool) -> SignalValue { let data: Vec = (0..size) - .map(|i| *self.bytes.borrow().get(&(addr + i)).unwrap_or(&0)) + .map(|i| *self.mmio.borrow().get(&(addr + i)).unwrap_or(&0)) .collect(); - let data = data.as_slice(); - println!("{:x?}", data); match size { 1 => { if sign { - data[0] as i8 as Signal + data[0] as i8 as SignalSigned as SignalUnsigned } else { - data[0] as Signal + data[0] as SignalUnsigned } } 2 => { if sign { if big_endian { - println!("read signed half word be"); + trace!("read signed half word be"); let i_16 = i16::from_be_bytes(data.try_into().unwrap()); - println!("i_16 {:x?}", i_16); + trace!("i_16 {:x?}", i_16); let i_32 = i_16 as i32; - println!("i_32 {:x?}", i_32); - i_32 as Signal + trace!("i_32 {:x?}", i_32); + i_32 as SignalUnsigned } else { - println!("read signed half word le"); + trace!("read signed half word le"); let i_16 = i16::from_le_bytes(data.try_into().unwrap()); - println!("i_16 {:x?}", i_16); + trace!("i_16 {:x?}", i_16); let i_32 = i_16 as i32; - println!("i_32 {:x?}", i_32); - i_32 as Signal + trace!("i_32 {:x?}", i_32); + i_32 as SignalUnsigned } } else if big_endian { - println!("read unsigned half word be"); + trace!("read unsigned half word be"); let u_16 = u16::from_be_bytes(data.try_into().unwrap()); - println!("u_16 {:x?}", u_16); + trace!("u_16 {:x?}", u_16); let u_32 = u_16 as u32; - println!("u_32 {:x?}", u_32); - u_32 as Signal + trace!("u_32 {:x?}", u_32); + u_32 as SignalUnsigned } else { - println!("read unsigned half word le"); + trace!("read unsigned half word le"); let u_16 = u16::from_le_bytes(data.try_into().unwrap()); - println!("u_16 {:x?}", u_16); + trace!("u_16 {:x?}", u_16); let u_32 = u_16 as u32; - println!("u_32 {:x?}", u_32); - u_32 as Signal + trace!("u_32 {:x?}", u_32); + u_32 as SignalUnsigned } } 4 => { if sign { if big_endian { - i32::from_be_bytes(data.try_into().unwrap()) as Signal + i32::from_be_bytes(data.try_into().unwrap()) as SignalUnsigned } else { - i32::from_le_bytes(data.try_into().unwrap()) as Signal + i32::from_le_bytes(data.try_into().unwrap()) as SignalUnsigned } } else if big_endian { - u32::from_be_bytes(data.try_into().unwrap()) as Signal + u32::from_be_bytes(data.try_into().unwrap()) as SignalUnsigned } else { - u32::from_le_bytes(data.try_into().unwrap()) as Signal + u32::from_le_bytes(data.try_into().unwrap()) as SignalUnsigned } } _ => panic!("illegal sized memory operation"), } + .into() } - fn write(&self, addr: usize, size: usize, big_endian: bool, data: Signal) { + fn write(&self, addr: usize, size: usize, big_endian: bool, data: SignalValue) { + let data: SignalUnsigned = data.try_into().unwrap(); match size { 1 => { - println!("write byte"); - self.bytes.borrow_mut().insert(addr, data as u8); + trace!("write byte"); + self.mmio.borrow_mut().insert(addr, data as u8); } 2 => { if big_endian { - println!("write half word be"); + trace!("write half word be"); (data as u16) .to_be_bytes() .iter() .enumerate() .for_each(|(i, bytes)| { - self.bytes.borrow_mut().insert(addr + i, *bytes); + self.mmio.borrow_mut().insert(addr + i, *bytes); }) } else { - println!("write half word le"); + trace!("write half word le"); (data as u16) .to_le_bytes() .iter() .enumerate() .for_each(|(i, bytes)| { - self.bytes.borrow_mut().insert(addr + i, *bytes); + self.mmio.borrow_mut().insert(addr + i, *bytes); }) } } 4 => { if big_endian { - println!("write word be"); + trace!("write word be"); data.to_be_bytes() .iter() .enumerate() .for_each(|(i, bytes)| { - self.bytes.borrow_mut().insert(addr + i, *bytes); + self.mmio.borrow_mut().insert(addr + i, *bytes); }) } else { - println!("write word le"); + trace!("write word le"); data.to_le_bytes() .iter() .enumerate() .for_each(|(i, bytes)| { - self.bytes.borrow_mut().insert(addr + i, *bytes); + self.mmio.borrow_mut().insert(addr + i, *bytes); }) } } @@ -168,362 +1085,3 @@ impl Memory { }; } } - -#[derive(Copy, Clone, Debug, IntoPrimitive, TryFromPrimitive)] -#[repr(u8)] // Unfortunately Rust does not allow Signal here, we need to cast manually -pub enum MemCtrl { - None, - Read, - Write, -} - -#[typetag::serde()] -impl Component for Mem { - fn to_(&self) { - println!("Mem"); - } - - fn to_string(&self) -> String { - "".to_string() - } - fn get_id_ports(&self) -> (Id, Ports) { - ( - self.id.clone(), - Ports::new( - vec![&self.data, &self.addr, &self.ctrl], - OutputType::Combinatorial, - vec!["data", "err"], - ), - ) - } - - fn evaluate(&self, simulator: &mut Simulator) { - let data = simulator.get_input_val(&self.data); - let addr = simulator.get_input_val(&self.addr) as usize; - let ctrl = MemCtrl::try_from(simulator.get_input_val(&self.ctrl) as u8).unwrap(); - let size = simulator.get_input_val(&self.size) as usize; - let sign = simulator.get_input_val(&self.sign) != 0; - - match ctrl { - MemCtrl::Read => { - println!("read addr {:?} size {:?}", addr, size); - let value = self.memory.read(addr, size, sign, self.big_endian); - simulator.set_out_val(&self.id, "data", value); - let value = self.memory.align(addr, size); - println!("align {}", value); - simulator.set_out_val(&self.id, "err", value); // align - } - MemCtrl::Write => { - println!("write addr {:?} size {:?}", addr, size); - self.memory.write(addr, size, self.big_endian, data); - let value = self.memory.align(addr, size); - println!("align {}", value); - simulator.set_out_val(&self.id, "err", value); // align - } - MemCtrl::None => { - println!("no read/write"); - } - } - - println!("memory {:?}", self.memory); - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::common::ComponentStore; - use crate::components::ProbeOut; - use std::rc::Rc; - - #[test] - fn test_mem_be() { - let cs = ComponentStore { - store: vec![ - Rc::new(ProbeOut::new("data")), - Rc::new(ProbeOut::new("addr")), - Rc::new(ProbeOut::new("ctrl")), - Rc::new(ProbeOut::new("size")), - Rc::new(ProbeOut::new("sign")), - Rc::new(Mem { - id: "mem".into(), - pos: (0.0, 0.0), - width: 0.0, - height: 0.0, - - // configuration - big_endian: true, // i.e., big endian - - // ports - data: Input::new("data", "out"), - addr: Input::new("addr", "out"), - ctrl: Input::new("ctrl", "out"), - size: Input::new("size", "out"), - sign: Input::new("sign", "out"), - - // memory - memory: Memory { - bytes: RefCell::new(HashMap::new()), - }, - }), - ], - }; - - let mut clock = 0; - let mut simulator = Simulator::new(&cs, &mut clock); - - assert_eq!(clock, 1); - - // outputs - let out = &Input::new("mem", "data"); - let err = &Input::new("mem", "err"); - - // reset - assert_eq!(simulator.get_input_val(out), 0); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - - simulator.set_out_val("data", "out", 0xf0); - simulator.set_out_val("addr", "out", 4); - simulator.set_out_val("ctrl", "out", MemCtrl::Write as Signal); - simulator.set_out_val("size", "out", 1); - println!("sim_state {:?}", simulator.sim_state); - - println!(""); - simulator.clock(&mut clock); - println!("sim_state {:?}", simulator.sim_state); - - assert_eq!(clock, 2); - assert_eq!(simulator.get_input_val(out), 0); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - - simulator.set_out_val("ctrl", "out", MemCtrl::Read as Signal); - simulator.set_out_val("size", "out", 1); - - simulator.clock(&mut clock); - - assert_eq!(clock, 3); - assert_eq!(simulator.get_input_val(out), 0xf0); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("size", "out", 1); - simulator.set_out_val("sign", "out", true as Signal); - - simulator.clock(&mut clock); - assert_eq!(clock, 4); - assert_eq!(simulator.get_input_val(out), 0xffff_fff0); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("size", "out", 2); - simulator.set_out_val("sign", "out", true as Signal); - - simulator.clock(&mut clock); - assert_eq!(clock, 5); - assert_eq!(simulator.get_input_val(out), 0xffff_f000); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("size", "out", 4); - simulator.set_out_val("sign", "out", true as Signal); - - simulator.clock(&mut clock); - assert_eq!(clock, 6); - assert_eq!(simulator.get_input_val(out), 0xf000_0000); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("addr", "out", 5); - - simulator.clock(&mut clock); - assert_eq!(clock, 7); - assert_eq!(simulator.get_input_val(err), true as Signal); - - println!(""); - simulator.set_out_val("addr", "out", 6); - - simulator.clock(&mut clock); - assert_eq!(clock, 8); - assert_eq!(simulator.get_input_val(err), true as Signal); - - println!(""); - simulator.set_out_val("addr", "out", 7); - - simulator.clock(&mut clock); - assert_eq!(clock, 9); - assert_eq!(simulator.get_input_val(err), true as Signal); - - println!(""); - simulator.set_out_val("addr", "out", 8); - - simulator.clock(&mut clock); - assert_eq!(clock, 10); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("addr", "out", 9); - simulator.set_out_val("size", "out", 2); - simulator.clock(&mut clock); - assert_eq!(clock, 11); - assert_eq!(simulator.get_input_val(err), true as Signal); - - println!(""); - simulator.set_out_val("addr", "out", 10); - - simulator.clock(&mut clock); - assert_eq!(clock, 12); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("addr", "out", 10); - simulator.set_out_val("data", "out", 0x1234); - simulator.set_out_val("ctrl", "out", MemCtrl::Write as Signal); - simulator.clock(&mut clock); - assert_eq!(clock, 13); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("ctrl", "out", MemCtrl::Read as Signal); - simulator.set_out_val("size", "out", 1); - simulator.clock(&mut clock); - assert_eq!(clock, 14); - assert_eq!(simulator.get_input_val(out), 0x12 as Signal); - - println!(""); - simulator.set_out_val("addr", "out", 11); - simulator.clock(&mut clock); - assert_eq!(clock, 15); - assert_eq!(simulator.get_input_val(out), 0x34 as Signal); - - println!("test done") - } - - #[test] - fn test_mem_le() { - let cs = ComponentStore { - store: vec![ - Rc::new(ProbeOut::new("data")), - Rc::new(ProbeOut::new("addr")), - Rc::new(ProbeOut::new("ctrl")), - Rc::new(ProbeOut::new("size")), - Rc::new(ProbeOut::new("sign")), - Rc::new(Mem { - id: "mem".into(), - pos: (0.0, 0.0), - width: 0.0, - height: 0.0, - - // configuration - big_endian: false, // i.e., little endian - - // ports - data: Input::new("data", "out"), - addr: Input::new("addr", "out"), - ctrl: Input::new("ctrl", "out"), - size: Input::new("size", "out"), - sign: Input::new("sign", "out"), - - // memory - memory: Memory { - bytes: RefCell::new(HashMap::new()), - }, - // later history... tbd - }), - ], - }; - - let mut clock = 0; - let mut simulator = Simulator::new(&cs, &mut clock); - - assert_eq!(clock, 1); - - // outputs - let out = &Input::new("mem", "data"); - let err = &Input::new("mem", "err"); - - // reset - assert_eq!(simulator.get_input_val(out), 0); - assert_eq!(simulator.get_input_val(err), false as Signal); - - // println!(""); - - simulator.set_out_val("data", "out", 0xf0); - simulator.set_out_val("addr", "out", 4); - simulator.set_out_val("ctrl", "out", MemCtrl::Write as Signal); - simulator.set_out_val("size", "out", 1); // byte - - println!("sim_state {:?}", simulator.sim_state); - - println!(""); - simulator.clock(&mut clock); - println!("sim_state {:?}", simulator.sim_state); - - assert_eq!(clock, 2); - assert_eq!(simulator.get_input_val(out), 0); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - - simulator.set_out_val("ctrl", "out", MemCtrl::Read as Signal); - simulator.set_out_val("size", "out", 1); - - simulator.clock(&mut clock); - - assert_eq!(clock, 3); - assert_eq!(simulator.get_input_val(out), 0xf0); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("size", "out", 1); - simulator.set_out_val("sign", "out", true as Signal); - - simulator.clock(&mut clock); - assert_eq!(clock, 4); - assert_eq!(simulator.get_input_val(out), 0xffff_fff0); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("size", "out", 2); - simulator.set_out_val("sign", "out", true as Signal); - - simulator.clock(&mut clock); - assert_eq!(clock, 5); - assert_eq!(simulator.get_input_val(out), 0x0000_00f0); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("size", "out", 4); - simulator.set_out_val("sign", "out", true as Signal); - simulator.clock(&mut clock); - assert_eq!(clock, 6); - assert_eq!(simulator.get_input_val(out), 0x0000_00f0); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("addr", "out", 10); // b - simulator.set_out_val("data", "out", 0x1234); - simulator.set_out_val("ctrl", "out", MemCtrl::Write as Signal); - simulator.set_out_val("size", "out", 2); - - simulator.clock(&mut clock); - assert_eq!(clock, 7); - assert_eq!(simulator.get_input_val(err), false as Signal); - - println!(""); - simulator.set_out_val("ctrl", "out", MemCtrl::Read as Signal); - simulator.set_out_val("size", "out", 1); - simulator.clock(&mut clock); - assert_eq!(clock, 8); - assert_eq!(simulator.get_input_val(out), 0x34 as Signal); - - println!(""); - simulator.set_out_val("addr", "out", 11); - simulator.clock(&mut clock); - assert_eq!(clock, 9); - assert_eq!(simulator.get_input_val(out), 0x12 as Signal); - } -} diff --git a/riscv/src/components/csr.rs b/riscv/src/components/csr.rs index 303fb9bd..349c84f0 100644 --- a/riscv/src/components/csr.rs +++ b/riscv/src/components/csr.rs @@ -1,7 +1,13 @@ use std::{cell::RefCell, collections::HashMap}; use serde::{Deserialize, Serialize}; -use syncrim::common::{Component, Condition, Input, OutputType, Ports, Simulator}; +use syncrim::common::{Component, Condition, Input, InputPort, OutputType, Ports, Simulator}; + +pub const CSR_ADDRESS_ID: &str = "address"; +pub const CSR_DATA_ID: &str = "data"; +pub const CSR_WE_ID: &str = "we"; + +pub const CSR_OUTPUT_ID: &str = "output"; #[derive(Serialize, Deserialize)] pub struct CSR { @@ -64,11 +70,24 @@ impl Component for CSR { fn get_id_ports(&self) -> (String, Ports) { ( self.id.clone(), - Ports { - inputs: vec![self.address.clone(), self.data.clone(), self.we.clone()], - out_type: OutputType::Combinatorial, - outputs: vec!["output".into()], - }, + Ports::new( + vec![ + &InputPort { + port_id: CSR_ADDRESS_ID.to_string(), + input: self.address.clone(), + }, + &InputPort { + port_id: CSR_DATA_ID.to_string(), + input: self.data.clone(), + }, + &InputPort { + port_id: CSR_WE_ID.to_string(), + input: self.we.clone(), + }, + ], + OutputType::Combinatorial, + vec![CSR_OUTPUT_ID], + ), ) } @@ -79,4 +98,8 @@ impl Component for CSR { //simulator.set_out_val(&self.id, "instruction", we); } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/riscv/src/components/decoder.rs b/riscv/src/components/decoder.rs index ba64c15e..6bcd95ab 100644 --- a/riscv/src/components/decoder.rs +++ b/riscv/src/components/decoder.rs @@ -1,52 +1,167 @@ use log::trace; use serde::{Deserialize, Serialize}; -use syncrim::common::{Component, Condition, Input, OutputType, Ports, SignalValue, Simulator}; +#[cfg(feature = "gui-egui")] +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalValue, Simulator, +}; use syncrim::components::MemCtrl; +pub const DECODER_INSTRUCTION_ID: &str = "instruction"; + +pub const DECODER_WB_MUX_SEL_ID: &str = "decoder_wb_mux_sel"; +pub const DECODER_ALU_A_MUX_SEL_ID: &str = "decoder_alu_a_mux_sel"; +pub const DECODER_ALU_B_MUX_SEL_ID: &str = "decoder_alu_b_mux_sel"; +pub const DECODER_ALU_OP_ID: &str = "decoder_alu_op"; +// reg file signals +pub const DECODER_RD_ID: &str = "decoder_rd"; +pub const DECODER_RS1_ID: &str = "decoder_rs1"; +pub const DECODER_RS2_ID: &str = "decoder_rs2"; +pub const DECODER_WB_WRITE_ENABLE_ID: &str = "decoder_wb_write_enable"; +//unsure here +pub const DECODER_SIGN_ZERO_EXT_SEL_ID: &str = "sign_zero_ext_sel"; + +//this is imm +// consolidate these as immediate +//pub const DECODER_IMM_ID: &str = "decoder_imm"; +pub const DECODER_PC_IMM_SEL_ID: &str = "pc_imm_sel"; + +pub const DECODER_LUI_AUIPC_IMM_ID: &str = "decoder_lui_auipc_imm"; +pub const DECODER_SHAMT_ID: &str = "decoder_shamt"; +pub const DECODER_IMM_ID: &str = "decoder_imm"; +pub const DECODER_STORE_OFFSET_IMM_ID: &str = "decoder_store_offset_imm"; +pub const DECODER_ZIMM_ID: &str = "decoder_zimm"; +pub const DECODER_JAL_IMM_ID: &str = "decoder_jal_imm"; +pub const DECODER_BRANCH_IMM_ID: &str = "decoder_branch_imm"; +pub const DECODER_IMM_SEL_ID: &str = "decoder_imm_sel"; +//"pc_se_data".into(), +//"pc_mux_sel".into(), +pub const DECODER_DATA_MEM_SIZE_ID: &str = "data_mem_size"; +pub const DECODER_DATA_SE_ID: &str = "data_se"; +pub const DECODER_DATA_MEM_CTRL_ID: &str = "data_mem_ctrl"; + +pub const DECODER_BRANCH_OP: &str = "decoder_branch_op"; +pub const DECODER_BRANCH_INSTR: &str = "decoder_branch_instr"; +pub const DECODER_BRANCH_ALWAYS: &str = "decoder_branch_always"; + +pub const DECODER_MRET_ID: &str = "mret"; +pub const DECODER_MEPC_ID: &str = "mepc"; +pub const DECODER_CSR_CTL_ID: &str = "csr_ctl"; +pub const DECODER_CSR_DATA_MUX_ID: &str = "csr_data_mux"; +pub const DECODER_CSR_ADDR_ID: &str = "csr_addr"; + +pub const DECODER_HEIGHT: f32 = 600.0; +pub const DECODER_WIDTH: f32 = 30.0; + #[derive(Serialize, Deserialize)] pub struct Decoder { + pub width: f32, + pub height: f32, pub id: String, pub pos: (f32, f32), pub instruction: Input, } +#[repr(u8)] +pub enum ImmSel { + LuiAuipc = 0, + Shamt = 1, + Imm = 2, + StoreImm = 3, + Zimm = 4, + JalImm = 5, +} + +impl Into for ImmSel { + fn into(self) -> SignalValue { + SignalValue::Data(self as u32) + } +} + +fn sign_zero_extend(sign: bool, width: u8, val: u32) -> u32 { + assert!(width > 0); + if sign { + let sign_bit = val >> (width - 1); + let mask = !(2u32.pow(width as u32) - 1); + //println!("MASK: {:08x}", mask); + if sign_bit == 1 { + val | mask + } else { + val + } + } else { + val + } +} #[typetag::serde()] impl Component for Decoder { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn to_(&self) { println!("Decoder"); } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(Decoder { + width: DECODER_WIDTH, + height: DECODER_HEIGHT, + id: id.to_string(), + pos: (pos.0, pos.1), + instruction: dummy_input, + })) + } + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id.as_str() == DECODER_INSTRUCTION_ID { + self.instruction = new_input + } + } fn get_id_ports(&self) -> (String, Ports) { ( self.id.clone(), - Ports { - inputs: vec![self.instruction.clone()], - out_type: OutputType::Combinatorial, - outputs: vec![ - "wb_mux".into(), - "alu_operand_a_sel".into(), - "alu_operand_b_sel".into(), - "alu_operator".into(), - "regfile_rd".into(), - "regfile_rs1".into(), - "regfile_rs2".into(), - "regfile_we".into(), - "sign_zero_ext_sel".into(), - "sign_zero_ext_data".into(), - "imm_a_mux_data".into(), - //"pc_se_data".into(), - //"pc_mux_sel".into(), - "data_mem_size".into(), - "data_se".into(), - "data_mem_ctrl".into(), - "pc_imm_sel".into(), - "big_imm".into(), - "branch_imm".into(), - "branch_logic_ctl".into(), - "branch_logic_enable".into(), - "jalr_imm".into(), + Ports::new( + vec![&InputPort { + port_id: DECODER_INSTRUCTION_ID.to_string(), + input: self.instruction.clone(), + }], + OutputType::Combinatorial, + vec![ + DECODER_WB_MUX_SEL_ID, + DECODER_ALU_A_MUX_SEL_ID, + DECODER_ALU_B_MUX_SEL_ID, + DECODER_ALU_OP_ID, + DECODER_RD_ID, + DECODER_RS1_ID, + DECODER_RS2_ID, + DECODER_WB_WRITE_ENABLE_ID, + DECODER_SIGN_ZERO_EXT_SEL_ID, + DECODER_DATA_MEM_SIZE_ID, + DECODER_DATA_SE_ID, + DECODER_DATA_MEM_CTRL_ID, + DECODER_BRANCH_OP, + DECODER_BRANCH_INSTR, + DECODER_MEPC_ID, + DECODER_MRET_ID, + DECODER_CSR_CTL_ID, + DECODER_CSR_DATA_MUX_ID, + DECODER_CSR_ADDR_ID, + DECODER_PC_IMM_SEL_ID, + DECODER_BRANCH_ALWAYS, + DECODER_IMM_ID, + DECODER_LUI_AUIPC_IMM_ID, + DECODER_SHAMT_ID, + DECODER_STORE_OFFSET_IMM_ID, + DECODER_ZIMM_ID, + DECODER_JAL_IMM_ID, + DECODER_IMM_SEL_ID, + DECODER_BRANCH_IMM_ID, ], - }, + ), ) } #[allow(non_snake_case)] @@ -55,6 +170,8 @@ impl Component for Decoder { .get_input_value(&self.instruction) .try_into() .unwrap(); + + //constant instruction field values let opcode = instruction & 0b1111111; let funct3 = (instruction & (0b111 << 12)) >> 12; let funct7 = (instruction & (0b1111111 << 25)) >> 25; @@ -66,51 +183,65 @@ impl Component for Decoder { | ((instruction & (0b1 << 20)) >> (20 - 11)) | (instruction & (0b11111111 << 12))) & 0b1111_1111_1111_1111_1111_1111_1111_1110; + let branch_imm = ((instruction & (0b1 << 31)) >> 19) + | ((instruction & (0b111111 << 25)) >> 20) + | ((instruction & (0b1111 << 8)) >> 7) + | ((instruction & (0b1 << 7)) << 4); //no idea why this is encoded this way but the ISA is what it is let imm_store = ((instruction & (0b11111 << 7)) >> 7) | ((instruction & (0b1111111 << 25)) >> 20); - - let mut wb_mux = SignalValue::Uninitialized; - let mut alu_operand_a_sel = SignalValue::Uninitialized; - let mut alu_operand_b_sel = SignalValue::Uninitialized; - let mut regfile_rd = SignalValue::Uninitialized; - let mut regfile_rs1 = SignalValue::Uninitialized; - let mut regfile_rs2 = SignalValue::Uninitialized; - let mut regfile_we = SignalValue::from(0); //this must be 0 - let mut alu_operator = SignalValue::Uninitialized; - let mut sign_zero_ext_sel = SignalValue::Uninitialized; - let mut sign_zero_ext_data = SignalValue::Uninitialized; - let mut imm_a_mux_data = SignalValue::Uninitialized; - let mut data_mem_size = SignalValue::Uninitialized; - let mut data_se = SignalValue::Uninitialized; - let mut data_mem_ctrl = SignalValue::from(MemCtrl::None as u32); - let mut big_imm = SignalValue::Uninitialized; - let mut pc_imm_sel = SignalValue::Uninitialized; - let mut branch_imm = SignalValue::Uninitialized; - let mut branch_logic_ctl = SignalValue::Uninitialized; + let zimm = (instruction & (0b11111 << 15)) >> 15; + //outputs + //let mut imm_sig = SignalValue::Uninitialized; + let branch_imm_sig = SignalValue::Data(sign_zero_extend(true, 13, branch_imm)); + let lui_auipc_imm_sig = SignalValue::Data(imm_big); + let shamt_sig = SignalValue::Data(shamt); + let mut imm_sig = SignalValue::Data(imm); + let store_offset_sig = SignalValue::Data(imm_store); + let zimm_sig = SignalValue::Data(zimm); + let jal_imm_sig = SignalValue::Data(sign_zero_extend(true, 21, imm_big_shuffled)); + let mut imm_sel_sig = SignalValue::Uninitialized; + let mut wb_mux_sel = SignalValue::Uninitialized; + let mut alu_a_mux_sel = SignalValue::Uninitialized; + let mut alu_b_mux_sel = SignalValue::Uninitialized; + let mut rd = SignalValue::Uninitialized; + let mut rs1 = SignalValue::Uninitialized; + let mut rs2 = SignalValue::Uninitialized; + let mut wb_write_enable = SignalValue::from(0); //this must be 0 + let mut alu_op = SignalValue::Uninitialized; + let mut sub_arith = SignalValue::Uninitialized; + let mut dmem_width = SignalValue::Uninitialized; + let mut dmem_sign_extend = SignalValue::Uninitialized; + let mut dmem_write_enable = SignalValue::from(MemCtrl::None as u32); + // ?? + let pc_imm_sel = SignalValue::Uninitialized; + let mut branch_instr = SignalValue::Uninitialized; let mut branch_logic_enable = SignalValue::from(0); //this must be 0 - let mut jalr_imm = SignalValue::Uninitialized; + let mut csr_ctl = SignalValue::Uninitialized; + let mut csr_data_mux = SignalValue::Uninitialized; + let mut csr_addr = SignalValue::Uninitialized; + let mut mret = SignalValue::Uninitialized; match opcode { 0b0110011 => { //OP - alu_operand_a_sel = SignalValue::from(0); //rs1 - alu_operand_b_sel = SignalValue::from(0); //rs2 - //rs1 [19:15] rs2 [24:20] rd [11:7] - regfile_rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); - regfile_rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); - regfile_rs2 = SignalValue::from((instruction & (0b11111 << 20)) >> 20); - regfile_we = SignalValue::from(1); //enable write - wb_mux = SignalValue::from(0); //ALU source + alu_a_mux_sel = SignalValue::from(4); //rs1 + alu_b_mux_sel = SignalValue::from(0); //rs2 + //rs1 [19:15] rs2 [24:20] rd [11:7] + rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); + rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + rs2 = SignalValue::from((instruction & (0b11111 << 20)) >> 20); + wb_write_enable = SignalValue::from(1); //enable write + wb_mux_sel = SignalValue::from(0); //ALU source trace!("opcode=OP"); match funct3 { 0b000 => { // add/sub match funct7 { 0b0000000 => { - alu_operator = SignalValue::from(1); + alu_op = SignalValue::from(1); } //add 0b0100000 => { - alu_operator = SignalValue::from(2); + alu_op = SignalValue::from(2); } //sub _ => panic!("Invalid funct7 {:b}", funct7), } @@ -119,7 +250,7 @@ impl Component for Decoder { match funct7 { // sll 0b0000000 => { - alu_operator = SignalValue::from(3); + alu_op = SignalValue::from(3); } //sll _ => panic!("Invalid funct7 {:b}", funct7), } @@ -128,7 +259,7 @@ impl Component for Decoder { match funct7 { // slt 0b0000000 => { - alu_operator = SignalValue::from(10); + alu_op = SignalValue::from(10); } //slt _ => panic!("Invalid funct7 {:b}", funct7), } @@ -137,7 +268,7 @@ impl Component for Decoder { match funct7 { // sltu 0b0000000 => { - alu_operator = SignalValue::from(9); + alu_op = SignalValue::from(9); } //sltu _ => panic!("Invalid funct7 {:b}", funct7), } @@ -146,7 +277,7 @@ impl Component for Decoder { match funct7 { // xor 0b0000000 => { - alu_operator = SignalValue::from(6); + alu_op = SignalValue::from(6); } //xor _ => panic!("Invalid funct7 {:b}", funct7), } @@ -155,10 +286,10 @@ impl Component for Decoder { match funct7 { // srl 0b0000000 => { - alu_operator = SignalValue::from(4); + alu_op = SignalValue::from(4); } //srl 0b0100000 => { - alu_operator = SignalValue::from(5); + alu_op = SignalValue::from(5); } //sra _ => panic!("Invalid funct7 {:b}", funct7), } @@ -167,7 +298,7 @@ impl Component for Decoder { match funct7 { // or 0b0000000 => { - alu_operator = SignalValue::from(7); + alu_op = SignalValue::from(7); } //or _ => panic!("Invalid funct7 {:b}", funct7), } @@ -176,7 +307,7 @@ impl Component for Decoder { //and match funct7 { 0b0000000 => { - alu_operator = SignalValue::from(8); + alu_op = SignalValue::from(8); } //and _ => panic!("Invalid funct7 {:b}", funct7), } @@ -188,68 +319,66 @@ impl Component for Decoder { } 0b0010011 => { //OP_IMM - alu_operand_a_sel = SignalValue::from(0); //rs1 - alu_operand_b_sel = SignalValue::from(1); //imm - regfile_rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); - regfile_rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); - regfile_we = SignalValue::from(1); //enable write - wb_mux = SignalValue::from(0); //ALU source + alu_a_mux_sel = SignalValue::from(4); //rs1 + alu_b_mux_sel = SignalValue::from(2); //imm + rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); + rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + wb_write_enable = SignalValue::from(1); //enable write + wb_mux_sel = SignalValue::from(0); //ALU source + imm_sig = SignalValue::from(sign_zero_extend(true, 12, imm)); + imm_sel_sig = ImmSel::Imm.into(); trace!("opcode=OP_IMM"); match funct3 { 0b000 => { //ADDI - alu_operator = SignalValue::from(1); - sign_zero_ext_sel = SignalValue::from(0); - sign_zero_ext_data = SignalValue::from(imm); + alu_op = SignalValue::from(1); + sub_arith = SignalValue::from(0); } 0b010 => { //SLTI - alu_operator = SignalValue::from(10); - sign_zero_ext_sel = SignalValue::from(0); - sign_zero_ext_data = SignalValue::from(imm); + alu_op = SignalValue::from(10); + sub_arith = SignalValue::from(0); } 0b011 => { //SLTIU - alu_operator = SignalValue::from(9); - sign_zero_ext_sel = SignalValue::from(1); - sign_zero_ext_data = SignalValue::from(imm); + alu_op = SignalValue::from(9); + sub_arith = SignalValue::from(1); + imm_sig = SignalValue::from(sign_zero_extend(false, 12, imm)); } 0b100 => { //XORI - alu_operator = SignalValue::from(6); - sign_zero_ext_sel = SignalValue::from(1); - sign_zero_ext_data = SignalValue::from(imm); + alu_op = SignalValue::from(6); + sub_arith = SignalValue::from(0); } 0b110 => { //ORI - alu_operator = SignalValue::from(7); - sign_zero_ext_sel = SignalValue::from(1); - sign_zero_ext_data = SignalValue::from(imm); + alu_op = SignalValue::from(7); + sub_arith = SignalValue::from(0); } 0b111 => { //ANDI - alu_operator = SignalValue::from(8); - sign_zero_ext_sel = SignalValue::from(1); - sign_zero_ext_data = SignalValue::from(imm); + alu_op = SignalValue::from(8); + sub_arith = SignalValue::from(0); } 0b001 => { //SLLI - alu_operator = SignalValue::from(3); - sign_zero_ext_sel = SignalValue::from(1); - sign_zero_ext_data = SignalValue::from(shamt); + alu_op = SignalValue::from(3); + sub_arith = SignalValue::from(1); + //imm_sel_sig = ImmSel::Shamt.into(); + alu_b_mux_sel = SignalValue::from(3); //shamt } 0b101 => { //SRLI SRAI match funct7 { 0b0000000 => { - alu_operator = SignalValue::from(4); - sign_zero_ext_sel = SignalValue::from(1); - sign_zero_ext_data = SignalValue::from(shamt); + alu_op = SignalValue::from(4); + sub_arith = SignalValue::from(1); + alu_b_mux_sel = SignalValue::from(3); //shamt } //SRLI 0b0100000 => { - alu_operator = SignalValue::from(5); - sign_zero_ext_sel = SignalValue::from(1); - sign_zero_ext_data = SignalValue::from(shamt); + alu_op = SignalValue::from(5); + sub_arith = SignalValue::from(1); + alu_b_mux_sel = SignalValue::from(3); //shamt } //SRAI _ => panic!("Invalid funct7! {:b}", funct7), } @@ -262,123 +391,124 @@ impl Component for Decoder { 0b0110111 => { //LUI trace!("opcode=LUI"); - alu_operand_a_sel = SignalValue::from(1); //big-imm - alu_operand_b_sel = SignalValue::from(1); //imm - regfile_rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); + alu_a_mux_sel = SignalValue::from(3); //0 + alu_b_mux_sel = SignalValue::from(4); //lui imm + rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); //regfile_rs1 = 0; //x0 dont care - regfile_we = SignalValue::from(1); //enable write - wb_mux = SignalValue::from(0); //ALU source - alu_operator = SignalValue::from(1); //ADD - sign_zero_ext_data = SignalValue::from(0); //add 0 - sign_zero_ext_sel = SignalValue::from(1); //zero-extend - imm_a_mux_data = SignalValue::from(imm_big); + wb_write_enable = SignalValue::from(1); //enable write + wb_mux_sel = SignalValue::from(0); //ALU source + alu_op = SignalValue::from(1); //ADD + + //THIS NEEDS FIX + //sign_zero_ext_data = SignalValue::from(0); //add 0 + sub_arith = SignalValue::from(1); //zero-extend + //imm_sig = SignalValue::from(imm_big); + imm_sel_sig = ImmSel::LuiAuipc.into(); } 0b0010111 => { //AUIPC trace!("opcode=AUIPC"); - alu_operand_a_sel = SignalValue::from(1); //big-imm - alu_operand_b_sel = SignalValue::from(3); //PC - regfile_rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); + alu_a_mux_sel = SignalValue::from(0); //auipc imm + alu_b_mux_sel = SignalValue::from(5); //PC + rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); //regfile_rs1 = SignalValue::from(0); //x0 dont care - regfile_we = SignalValue::from(1); //enable write - wb_mux = SignalValue::from(0); //ALU source - alu_operator = SignalValue::from(1); //ADD - //sign_zero_ext_data = SignalValue::from(0); //don't care - //sign_zero_ext_sel = SignalValue::from(1); //don't care - imm_a_mux_data = SignalValue::from(imm_big); + wb_write_enable = SignalValue::from(1); //enable write + wb_mux_sel = SignalValue::from(0); //ALU source + alu_op = SignalValue::from(1); //ADD + //sign_zero_ext_data = SignalValue::from(0); //don't care + //sign_zero_ext_sel = SignalValue::from(1); //don't care + imm_sel_sig = ImmSel::LuiAuipc.into(); + //imm_sig = SignalValue::from(imm_big); } 0b1101111 => { //JAL trace!("opcode=JAL"); - alu_operand_a_sel = SignalValue::from(2); //0 - alu_operand_b_sel = SignalValue::from(2); //PC - regfile_rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); + alu_a_mux_sel = SignalValue::from(1); //jal imm + alu_b_mux_sel = SignalValue::from(5); //PC + sub_arith = SignalValue::from(0); //sign extend + rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); //regfile_rs1 = SignalValue::from(0); //dont care - regfile_we = SignalValue::from(1); //enable write - wb_mux = SignalValue::from(0); //ALU source - alu_operator = SignalValue::from(1); //ADD - //sign_zero_ext_data = 0; //don't care - //sign_zero_ext_sel = 1; //don't care - big_imm = SignalValue::from(imm_big_shuffled); - pc_imm_sel = SignalValue::from(0); - branch_logic_ctl = SignalValue::from(0b010); //jal + wb_write_enable = SignalValue::from(1); //enable write + wb_mux_sel = SignalValue::from(3); //PC_p4 + alu_op = SignalValue::from(1); //ADD + //sign_zero_ext_data = 0; //don't care + //sign_zero_ext_sel = 1; //don't care + //imm_sig = SignalValue::from(sign_zero_extend(true, 21, imm_big_shuffled)); + //pc_imm_sel = SignalValue::from(0); + imm_sel_sig = ImmSel::JalImm.into(); + branch_instr = SignalValue::from(0b010); //jal branch_logic_enable = SignalValue::from(0b1); } 0b1100111 => { //JALR trace!("opcode=JALR"); - alu_operand_a_sel = SignalValue::from(2); //0 - alu_operand_b_sel = SignalValue::from(2); //PC - regfile_rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); - regfile_rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); - regfile_we = SignalValue::from(1); //enable write - wb_mux = SignalValue::from(0); //ALU source - alu_operator = SignalValue::from(1); //ADD - //sign_zero_ext_data = 0; //don't care - //sign_zero_ext_sel = 1; //don't care - //big_imm = imm_big_shuffled; //don't care - //pc_imm_sel = 0; //don't care - branch_logic_ctl = SignalValue::from(0b011); //jalr + alu_a_mux_sel = SignalValue::from(4); //rs1 + alu_b_mux_sel = SignalValue::from(2); //imm + sub_arith = SignalValue::from(0); //sign extend + rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); + rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + wb_write_enable = SignalValue::from(1); //enable write + wb_mux_sel = SignalValue::from(3); //PC_p4 + alu_op = SignalValue::from(1); //ADD + //sign_zero_ext_data = 0; //don't care + //sign_zero_ext_sel = 1; //don't care + //big_imm = imm_big_shuffled; //don't care + //pc_imm_sel = 0; //don't care + branch_instr = SignalValue::from(0b011); //jalr branch_logic_enable = SignalValue::from(0b1); - jalr_imm = SignalValue::from(imm); + imm_sig = SignalValue::from(sign_zero_extend(true, 12, imm)); + //imm_sig = SignalValue::from(sign_zero_extend(true, 12, imm)); } 0b1100011 => { //BRANCH trace!("opcode=BRANCH"); - regfile_rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); - regfile_rs2 = SignalValue::from((instruction & (0b11111 << 20)) >> 20); - //pc_imm_sel = 1; - //branch_imm = imm; - //regfile_rd = 0; //don't care - //regfile_we = 0; //no link - //wb_mux = 0; //don't care - //alu_operator = 0; //don't care - //sign_zero_ext_data = 0; //don't care - //big_imm = 0; //don't care - pc_imm_sel = SignalValue::from(1); //branch imm - branch_logic_ctl = SignalValue::from(funct3); //use funct3 + rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + rs2 = SignalValue::from((instruction & (0b11111 << 20)) >> 20); + alu_a_mux_sel = SignalValue::from(2); //branch imm + alu_b_mux_sel = SignalValue::from(5); //PC + alu_op = SignalValue::from(1); //add + sub_arith = SignalValue::from(0); //sign extend + branch_instr = SignalValue::from(funct3); //use funct3 branch_logic_enable = SignalValue::from(0b1); //enable branch logic - branch_imm = (((instruction & (0b1 << 31)) >> 19) - | ((instruction & (0b111111 << 25)) >> 20) - | ((instruction & (0b1111 << 8)) >> 7) - | ((instruction & (0b1 << 7)) << 4)) - .into(); + imm_sel_sig = ImmSel::Imm.into(); + //imm_sig = sign_zero_extend(true, 13, imm_int).into(); } 0b0000011 => { //LOAD trace!("opcode=LOAD"); - alu_operand_a_sel = SignalValue::from(0); //rs1 - alu_operand_b_sel = SignalValue::from(1); //imm - regfile_rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); - regfile_rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); - regfile_we = SignalValue::from(1); - wb_mux = SignalValue::from(1); //data memory - alu_operator = SignalValue::from(1); //ADD - sign_zero_ext_data = SignalValue::from(imm); //immediate - sign_zero_ext_sel = SignalValue::from(0); //sign extend + alu_a_mux_sel = SignalValue::from(4); //rs1 + alu_b_mux_sel = SignalValue::from(2); //imm + rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); + rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + wb_write_enable = SignalValue::from(1); + wb_mux_sel = SignalValue::from(1); //data memory + alu_op = SignalValue::from(1); //ADD + imm_sel_sig = ImmSel::Imm.into(); + imm_sig = SignalValue::from(sign_zero_extend(true, 13, imm)); //immediate + sub_arith = SignalValue::from(0); //sign extend - data_mem_ctrl = SignalValue::from(MemCtrl::Read as u32); + dmem_write_enable = SignalValue::from(MemCtrl::Read as u32); match funct3 { 0b000 => { - data_mem_size = SignalValue::from(1); - data_se = SignalValue::from(1) + dmem_width = SignalValue::from(1); + dmem_sign_extend = SignalValue::from(1) } //lb 0b001 => { - data_mem_size = SignalValue::from(2); - data_se = SignalValue::from(1) + dmem_width = SignalValue::from(2); + dmem_sign_extend = SignalValue::from(1) } //lh 0b010 => { - data_mem_size = SignalValue::from(4); - data_se = SignalValue::from(1) + dmem_width = SignalValue::from(4); + dmem_sign_extend = SignalValue::from(1) } //lw 0b100 => { - data_mem_size = SignalValue::from(1); - data_se = SignalValue::from(0) + dmem_width = SignalValue::from(1); + dmem_sign_extend = SignalValue::from(0) } //lbu 0b101 => { - data_mem_size = SignalValue::from(2); - data_se = SignalValue::from(0) + dmem_width = SignalValue::from(2); + dmem_sign_extend = SignalValue::from(0) } //lhu _ => { panic!("Unsupported funct3 {:b}", funct3) @@ -388,58 +518,135 @@ impl Component for Decoder { 0b0100011 => { //STORE trace!("opcode=STORE"); - alu_operand_a_sel = SignalValue::from(0); //rs1 - alu_operand_b_sel = SignalValue::from(1); //imm - regfile_rd = SignalValue::Uninitialized; - regfile_rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); - regfile_rs2 = SignalValue::from((instruction & (0b11111 << 20)) >> 20); - regfile_we = SignalValue::from(0); + alu_a_mux_sel = SignalValue::from(4); //rs1 + alu_b_mux_sel = SignalValue::from(1); // store imm + rd = SignalValue::Uninitialized; + rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + rs2 = SignalValue::from((instruction & (0b11111 << 20)) >> 20); + wb_write_enable = SignalValue::from(0); //wb_mux = 0; //don't care - alu_operator = SignalValue::from(1); //ADD - sign_zero_ext_data = SignalValue::from(imm_store); //immediate store type - sign_zero_ext_sel = SignalValue::from(0); //sign extend + alu_op = SignalValue::from(1); //ADD + imm_sel_sig = ImmSel::StoreImm.into(); + //imm_sig = SignalValue::from(imm_store); //immediate store type + sub_arith = SignalValue::from(0); //sign extend - data_mem_ctrl = SignalValue::from(MemCtrl::Write as u32); + dmem_write_enable = SignalValue::from(MemCtrl::Write as u32); match funct3 { //size 0b000 => { - data_mem_size = SignalValue::from(1); + dmem_width = SignalValue::from(1); } 0b001 => { - data_mem_size = SignalValue::from(2); + dmem_width = SignalValue::from(2); } 0b010 => { - data_mem_size = SignalValue::from(4); + dmem_width = SignalValue::from(4); } _ => panic!("Unsupported funct3 {:b}", funct3), } } - 0b0 => {} + 0b1110011 => { + //SYSTEM + csr_addr = SignalValue::from(imm); //imm + wb_write_enable = SignalValue::from(1); //write enable + wb_mux_sel = SignalValue::from(2); //csr data out + rd = SignalValue::from((instruction & (0b11111 << 7)) >> 7); + if instruction == 807403635 + //mret, basically magic number + { + mret = SignalValue::from(1); + } else { + match funct3 { + 0b001 => { + //CSRRW + csr_ctl = SignalValue::from(1); //write + csr_data_mux = SignalValue::from(0); //register + rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + //rs1 + } + 0b010 => { + //CSRRS + csr_ctl = SignalValue::from(2); //set + csr_data_mux = SignalValue::from(0); //register + rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + //rs1 + } + 0b011 => { + //CSRRC + csr_ctl = SignalValue::from(3); //clear + csr_data_mux = SignalValue::from(0); //register + rs1 = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + //rs1 + } + 0b101 => { + //CSRRWI + csr_ctl = SignalValue::from(1); //write + csr_data_mux = SignalValue::from(1); //immediate + imm_sel_sig = ImmSel::Zimm.into(); + //imm_sig = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + //zimm + } + 0b110 => { + //CSRRSI + csr_ctl = SignalValue::from(2); //set + csr_data_mux = SignalValue::from(1); //immediate + imm_sel_sig = ImmSel::Zimm.into(); + + // imm_sig = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + //zimm + } + 0b111 => { + //CSRRCI + csr_ctl = SignalValue::from(3); //clear + csr_data_mux = SignalValue::from(1); //immediate + imm_sel_sig = ImmSel::Zimm.into(); + + //imm_sig = SignalValue::from((instruction & (0b11111 << 15)) >> 15); + //zimm + } + _ => panic!("Unsupported funct3 {:b}", funct3), + } + } + } _ => { - panic!("Invalid opcode! {:b}", opcode); + if !(opcode == 0 && simulator.cycle == 0) { + panic!("Invalid opcode! {:b}", opcode) + } } }; - simulator.set_out_value(&self.id, "wb_mux", wb_mux); - simulator.set_out_value(&self.id, "alu_operand_a_sel", alu_operand_a_sel); - simulator.set_out_value(&self.id, "alu_operand_b_sel", alu_operand_b_sel); - simulator.set_out_value(&self.id, "regfile_rs1", regfile_rs1); - simulator.set_out_value(&self.id, "regfile_rs2", regfile_rs2); - simulator.set_out_value(&self.id, "regfile_rd", regfile_rd); - simulator.set_out_value(&self.id, "regfile_we", regfile_we); - simulator.set_out_value(&self.id, "alu_operator", alu_operator); - simulator.set_out_value(&self.id, "sign_zero_ext_sel", sign_zero_ext_sel); - simulator.set_out_value(&self.id, "sign_zero_ext_data", sign_zero_ext_data); - simulator.set_out_value(&self.id, "imm_a_mux_data", imm_a_mux_data); - simulator.set_out_value(&self.id, "data_mem_size", data_mem_size); - simulator.set_out_value(&self.id, "data_se", data_se); - simulator.set_out_value(&self.id, "data_mem_ctrl", data_mem_ctrl); - simulator.set_out_value(&self.id, "big_imm", big_imm); - simulator.set_out_value(&self.id, "pc_imm_sel", pc_imm_sel); - simulator.set_out_value(&self.id, "branch_imm", branch_imm); - simulator.set_out_value(&self.id, "branch_logic_ctl", branch_logic_ctl); - simulator.set_out_value(&self.id, "branch_logic_enable", branch_logic_enable); - simulator.set_out_value(&self.id, "jalr_imm", jalr_imm); + simulator.set_out_value(&self.id, DECODER_WB_MUX_SEL_ID, wb_mux_sel); + simulator.set_out_value(&self.id, DECODER_ALU_A_MUX_SEL_ID, alu_a_mux_sel); + simulator.set_out_value(&self.id, DECODER_ALU_B_MUX_SEL_ID, alu_b_mux_sel); + simulator.set_out_value(&self.id, DECODER_RS1_ID, rs1); + simulator.set_out_value(&self.id, DECODER_RS2_ID, rs2); + simulator.set_out_value(&self.id, DECODER_RD_ID, rd); + simulator.set_out_value(&self.id, DECODER_WB_WRITE_ENABLE_ID, wb_write_enable); + simulator.set_out_value(&self.id, DECODER_ALU_OP_ID, alu_op); + simulator.set_out_value(&self.id, DECODER_SIGN_ZERO_EXT_SEL_ID, sub_arith); + simulator.set_out_value(&self.id, DECODER_IMM_ID, imm_sig); + //simulator.set_out_value(&self.id, DECODER_IMM_A_MUX_DATA_ID, imm_a_mux_data); + simulator.set_out_value(&self.id, DECODER_DATA_MEM_SIZE_ID, dmem_width); + simulator.set_out_value(&self.id, DECODER_DATA_SE_ID, dmem_sign_extend); + simulator.set_out_value(&self.id, DECODER_DATA_MEM_CTRL_ID, dmem_write_enable); + //simulator.set_out_value(&self.id, DECODER_BIG_IMM_ID, big_imm); + simulator.set_out_value(&self.id, DECODER_PC_IMM_SEL_ID, pc_imm_sel); + //simulator.set_out_value(&self.id, DECODER_BRANCH_IMM_ID, branch_imm); + simulator.set_out_value(&self.id, DECODER_BRANCH_OP, branch_instr); + simulator.set_out_value(&self.id, DECODER_BRANCH_INSTR, branch_logic_enable); + //simulator.set_out_value(&self.id, DECODER_JALR_IMM_ID, jalr_imm); + simulator.set_out_value(&self.id, DECODER_CSR_CTL_ID, csr_ctl); + simulator.set_out_value(&self.id, DECODER_CSR_DATA_MUX_ID, csr_data_mux); + // simulator.set_out_value(&self.id, DECODER_CSR_DATA_ID, csr_data); + simulator.set_out_value(&self.id, DECODER_CSR_ADDR_ID, csr_addr); + simulator.set_out_value(&self.id, DECODER_MRET_ID, mret); + simulator.set_out_value(&self.id, DECODER_LUI_AUIPC_IMM_ID, lui_auipc_imm_sig); + simulator.set_out_value(&self.id, DECODER_SHAMT_ID, shamt_sig); + simulator.set_out_value(&self.id, DECODER_STORE_OFFSET_IMM_ID, store_offset_sig); + simulator.set_out_value(&self.id, DECODER_ZIMM_ID, zimm_sig); + simulator.set_out_value(&self.id, DECODER_JAL_IMM_ID, jal_imm_sig); + simulator.set_out_value(&self.id, DECODER_IMM_SEL_ID, imm_sel_sig); + simulator.set_out_value(&self.id, DECODER_BRANCH_IMM_ID, branch_imm_sig); Ok(()) } } @@ -459,376 +666,128 @@ mod test { store: vec![ Rc::new(ProbeOut::new("instruction")), Rc::new(Decoder { + width: 0.0, + height: 0.0, id: "decoder".to_string(), pos: (0.0, 0.0), instruction: Input::new("instruction", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); // outputs - let wb_mux = &Input::new("decoder", "wb_mux"); - let alu_operand_a_sel = &Input::new("decoder", "alu_operand_a_sel"); - let alu_operand_b_sel = &Input::new("decoder", "alu_operand_b_sel"); - let regfile_rs1 = &Input::new("decoder", "regfile_rs1"); - let regfile_rs2 = &Input::new("decoder", "regfile_rs2"); - let regfile_rd = &Input::new("decoder", "regfile_rd"); - let regfile_we = &Input::new("decoder", "regfile_we"); - let alu_operator = &Input::new("decoder", "alu_operator"); - let sign_zero_ext_sel = &Input::new("decoder", "sign_zero_ext_sel"); - let sign_zero_ext_data = &Input::new("decoder", "sign_zero_ext_data"); - let imm_a_mux_data = &Input::new("decoder", "imm_a_mux_data"); - let data_mem_size = &Input::new("decoder", "data_mem_size"); - let data_se = &Input::new("decoder", "data_se"); - let data_mem_ctrl = &Input::new("decoder", "data_mem_ctrl"); - let big_imm = &Input::new("decoder", "big_imm"); - let pc_imm_sel = &Input::new("decoder", "pc_imm_sel"); - let branch_imm = &Input::new("decoder", "branch_imm"); - let branch_logic_ctl = &Input::new("decoder", "branch_logic_ctl"); - let branch_logic_enable = &Input::new("decoder", "branch_logic_enable"); - let jalr_imm = &Input::new("decoder", "jalr_imm"); + let wb_mux = &Input::new("decoder", DECODER_WB_MUX_SEL_ID); + let alu_operand_a_sel = &Input::new("decoder", DECODER_ALU_A_MUX_SEL_ID); + let alu_operand_b_sel = &Input::new("decoder", DECODER_ALU_B_MUX_SEL_ID); + let regfile_rs1 = &Input::new("decoder", DECODER_RS1_ID); + let regfile_rs2 = &Input::new("decoder", DECODER_RS2_ID); + let regfile_rd = &Input::new("decoder", DECODER_RD_ID); + let regfile_we = &Input::new("decoder", DECODER_WB_WRITE_ENABLE_ID); + let alu_operator = &Input::new("decoder", DECODER_ALU_OP_ID); + let data_mem_ctrl = &Input::new("decoder", DECODER_DATA_MEM_CTRL_ID); + let branch_logic_enable = &Input::new("decoder", DECODER_BRANCH_INSTR); simulator.set_out_value("instruction", "out", 0x003100b3); //add x1, x2, x3 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 0.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 3.into()); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x40410133); //sub x2, x2, x4 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 0.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 4.into()); assert_eq!(simulator.get_input_value(regfile_rd), 2.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 2.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x004121b3); //slt x3, x2, x4 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 0.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 4.into()); assert_eq!(simulator.get_input_value(regfile_rd), 3.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 10.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x004131b3); //sltu x3, x2, x4 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 0.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 4.into()); assert_eq!(simulator.get_input_value(regfile_rd), 3.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 9.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x001151b3); //srl x3, x2, x1 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 0.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 1.into()); assert_eq!(simulator.get_input_value(regfile_rd), 3.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 4.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x401151b3); //sra x3, x2, x1 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 0.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 1.into()); assert_eq!(simulator.get_input_value(regfile_rd), 3.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 5.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x001111b3); //sll x3, x2, x1 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 0.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 1.into()); @@ -836,218 +795,58 @@ mod test { assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 3.into()); assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_ctrl), - (MemCtrl::None as u32).into() - ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized + simulator.get_input_value(data_mem_ctrl), + (MemCtrl::None as u32).into() ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x0020c1b3); //xor x3, x1, x2 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 0.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 1.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 2.into()); assert_eq!(simulator.get_input_value(regfile_rd), 3.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 6.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x0020f1b3); //and x3, x1, x2) simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 0.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 1.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 2.into()); assert_eq!(simulator.get_input_value(regfile_rd), 3.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 8.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x0060e1b3); //or x3, x1, x6 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 0.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 1.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 6.into()); assert_eq!(simulator.get_input_value(regfile_rd), 3.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 7.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); } #[test] fn test_op_imm() { @@ -1055,518 +854,178 @@ mod test { store: vec![ Rc::new(ProbeOut::new("instruction")), Rc::new(Decoder { + width: 0.0, + height: 0.0, id: "decoder".to_string(), pos: (0.0, 0.0), instruction: Input::new("instruction", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); // outputs - let wb_mux = &Input::new("decoder", "wb_mux"); - let alu_operand_a_sel = &Input::new("decoder", "alu_operand_a_sel"); - let alu_operand_b_sel = &Input::new("decoder", "alu_operand_b_sel"); - let regfile_rs1 = &Input::new("decoder", "regfile_rs1"); - let regfile_rs2 = &Input::new("decoder", "regfile_rs2"); - let regfile_rd = &Input::new("decoder", "regfile_rd"); - let regfile_we = &Input::new("decoder", "regfile_we"); - let alu_operator = &Input::new("decoder", "alu_operator"); - let sign_zero_ext_sel = &Input::new("decoder", "sign_zero_ext_sel"); - let sign_zero_ext_data = &Input::new("decoder", "sign_zero_ext_data"); - let imm_a_mux_data = &Input::new("decoder", "imm_a_mux_data"); - let data_mem_size = &Input::new("decoder", "data_mem_size"); - let data_se = &Input::new("decoder", "data_se"); - let data_mem_ctrl = &Input::new("decoder", "data_mem_ctrl"); - let big_imm = &Input::new("decoder", "big_imm"); - let pc_imm_sel = &Input::new("decoder", "pc_imm_sel"); - let branch_imm = &Input::new("decoder", "branch_imm"); - let branch_logic_ctl = &Input::new("decoder", "branch_logic_ctl"); - let branch_logic_enable = &Input::new("decoder", "branch_logic_enable"); - let jalr_imm = &Input::new("decoder", "jalr_imm"); + let wb_mux = &Input::new("decoder", DECODER_WB_MUX_SEL_ID); + let alu_operand_a_sel = &Input::new("decoder", DECODER_ALU_A_MUX_SEL_ID); + let alu_operand_b_sel = &Input::new("decoder", DECODER_ALU_B_MUX_SEL_ID); + let regfile_rs1 = &Input::new("decoder", DECODER_RS1_ID); + let regfile_rd = &Input::new("decoder", DECODER_RD_ID); + let regfile_we = &Input::new("decoder", DECODER_WB_WRITE_ENABLE_ID); + let alu_operator = &Input::new("decoder", DECODER_ALU_OP_ID); + let sign_zero_ext_sel = &Input::new("decoder", DECODER_SIGN_ZERO_EXT_SEL_ID); + let sign_zero_ext_data = &Input::new("decoder", DECODER_IMM_ID); + let data_mem_ctrl = &Input::new("decoder", DECODER_DATA_MEM_CTRL_ID); + let branch_logic_enable = &Input::new("decoder", DECODER_BRANCH_INSTR); simulator.set_out_value("instruction", "out", 0x00310093); //addi x1, x2, 3 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 0.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_data), 3.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0xffd0a093); //slti x1, x1, -3 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 1.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 10.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 0.into()); assert_eq!( simulator.get_input_value(sign_zero_ext_data), - ((-3i32 as u32) & 0b111111111111).into() - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized + (-3i32 as u32).into() ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0xffd0b093); //sltiu x1, x1, -3 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 1.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 9.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 1.into()); assert_eq!( simulator.get_input_value(sign_zero_ext_data), - ((-3i32 as u32) & 0b111111111111).into() - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized + ((-3i32 & 0b111111111111) as u32).into() ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x00324093); //xori x1, x4, 3 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 4.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 6.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 1.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_data), 3.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x00326093); //ori x1, x4, 3 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 4.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 7.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 1.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_data), 3.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x00327093); //andi x1, x4, 3 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 4.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 8.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 1.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_data), 3.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x00c19093); //slli x1, x3, 12 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 3.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 3.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 3.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 1.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_data), 12.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x0011d093); //srli x1, x3, 1 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 3.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 3.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 4.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 1.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_data), 1.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x4020d093); //srai x1, x1, 2 simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 3.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 1.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 5.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 1.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_data), 2.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); } #[test] fn test_lui_auipc_store_load() { @@ -1574,41 +1033,38 @@ mod test { store: vec![ Rc::new(ProbeOut::new("instruction")), Rc::new(Decoder { + width: 0.0, + height: 0.0, id: "decoder".to_string(), pos: (0.0, 0.0), instruction: Input::new("instruction", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); // outputs - let wb_mux = &Input::new("decoder", "wb_mux"); - let alu_operand_a_sel = &Input::new("decoder", "alu_operand_a_sel"); - let alu_operand_b_sel = &Input::new("decoder", "alu_operand_b_sel"); - let regfile_rs1 = &Input::new("decoder", "regfile_rs1"); - let regfile_rs2 = &Input::new("decoder", "regfile_rs2"); - let regfile_rd = &Input::new("decoder", "regfile_rd"); - let regfile_we = &Input::new("decoder", "regfile_we"); - let alu_operator = &Input::new("decoder", "alu_operator"); - let sign_zero_ext_sel = &Input::new("decoder", "sign_zero_ext_sel"); - let sign_zero_ext_data = &Input::new("decoder", "sign_zero_ext_data"); - let imm_a_mux_data = &Input::new("decoder", "imm_a_mux_data"); - let data_mem_size = &Input::new("decoder", "data_mem_size"); - let data_se = &Input::new("decoder", "data_se"); - let data_mem_ctrl = &Input::new("decoder", "data_mem_ctrl"); - let big_imm = &Input::new("decoder", "big_imm"); - let pc_imm_sel = &Input::new("decoder", "pc_imm_sel"); - let branch_imm = &Input::new("decoder", "branch_imm"); - let branch_logic_ctl = &Input::new("decoder", "branch_logic_ctl"); - let branch_logic_enable = &Input::new("decoder", "branch_logic_enable"); - let jalr_imm = &Input::new("decoder", "jalr_imm"); + let wb_mux = &Input::new("decoder", DECODER_WB_MUX_SEL_ID); + let alu_operand_a_sel = &Input::new("decoder", DECODER_ALU_A_MUX_SEL_ID); + let alu_operand_b_sel = &Input::new("decoder", DECODER_ALU_B_MUX_SEL_ID); + let regfile_rs1 = &Input::new("decoder", DECODER_RS1_ID); + let regfile_rs2 = &Input::new("decoder", DECODER_RS2_ID); + let regfile_rd = &Input::new("decoder", DECODER_RD_ID); + let regfile_we = &Input::new("decoder", DECODER_WB_WRITE_ENABLE_ID); + let alu_operator = &Input::new("decoder", DECODER_ALU_OP_ID); + let sign_zero_ext_sel = &Input::new("decoder", DECODER_SIGN_ZERO_EXT_SEL_ID); + let sign_zero_ext_data = &Input::new("decoder", DECODER_IMM_ID); + let imm_a_mux_data = &Input::new("decoder", DECODER_LUI_AUIPC_IMM_ID); + let data_mem_size = &Input::new("decoder", DECODER_DATA_MEM_SIZE_ID); + let data_se = &Input::new("decoder", DECODER_DATA_SE_ID); + let data_mem_ctrl = &Input::new("decoder", DECODER_DATA_MEM_CTRL_ID); + let branch_logic_enable = &Input::new("decoder", DECODER_BRANCH_INSTR); simulator.set_out_value("instruction", "out", 0xfffff0b7); //lui x1, 0xFFFFF simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 1.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 3.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 4.into()); assert_eq!( simulator.get_input_value(regfile_rs1), SignalValue::Uninitialized @@ -1620,495 +1076,160 @@ mod test { assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 1.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_data), 0.into()); assert_eq!(simulator.get_input_value(imm_a_mux_data), 0xFFFFF000.into()); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); - simulator.set_out_value("instruction", "out", 0xfffff097); //auipc x1, 0xFFFFF simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 1.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 3.into()); - assert_eq!( - simulator.get_input_value(regfile_rs1), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 5.into()); assert_eq!(simulator.get_input_value(regfile_rd), 1.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(imm_a_mux_data), 0xFFFFF000.into()); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); + assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); + + simulator.set_out_value("instruction", "out", 0x0082a223); //sw x8, 4(x5) + simulator.clock(); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(regfile_rs1), 5.into()); + assert_eq!(simulator.get_input_value(regfile_rs2), 8.into()); + assert_eq!(simulator.get_input_value(regfile_we), 0.into()); + assert_eq!(simulator.get_input_value(alu_operator), 1.into()); + assert_eq!(simulator.get_input_value(data_mem_size), 4.into()); assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); - - simulator.set_out_value("instruction", "out", 0x0082a223); //sw x8, 4(x5) - simulator.clock(); - assert_eq!( - simulator.get_input_value(wb_mux), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); - assert_eq!(simulator.get_input_value(regfile_rs1), 5.into()); - assert_eq!(simulator.get_input_value(regfile_rs2), 8.into()); - assert_eq!( - simulator.get_input_value(regfile_rd), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(regfile_we), 0.into()); - assert_eq!(simulator.get_input_value(alu_operator), 1.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 0.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_data), 4.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(data_mem_size), 4.into()); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_ctrl), - (MemCtrl::Write as u32).into() - ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized + simulator.get_input_value(data_mem_ctrl), + (MemCtrl::Write as u32).into() ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x00829223); //sh x8, 4(x5) simulator.clock(); - assert_eq!( - simulator.get_input_value(wb_mux), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 5.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 8.into()); - assert_eq!( - simulator.get_input_value(regfile_rd), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_we), 0.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 0.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_data), 4.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(data_mem_size), 2.into()); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::Write as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); - simulator.set_out_value("instruction", "out", 0x00828223); //sb x8, 4(x5) simulator.clock(); - assert_eq!( - simulator.get_input_value(wb_mux), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 5.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 8.into()); - assert_eq!( - simulator.get_input_value(regfile_rd), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_we), 0.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 0.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_data), 4.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(data_mem_size), 1.into()); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::Write as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x0042a403); //lw x8, 4(x5) simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 1.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 5.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 8.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 0.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_data), 4.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(data_mem_size), 4.into()); assert_eq!(simulator.get_input_value(data_se), 1.into()); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::Read as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x00429403); //lh x8, 4(x5) simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 1.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 5.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 8.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 0.into()); - assert_eq!(simulator.get_input_value(sign_zero_ext_data), 4.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(data_mem_size), 2.into()); assert_eq!(simulator.get_input_value(data_se), 1.into()); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::Read as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); - simulator.set_out_value("instruction", "out", 0x00428403); //lb x8, 4(x5) simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 1.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 5.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 8.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 0.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_data), 4.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(data_mem_size), 1.into()); assert_eq!(simulator.get_input_value(data_se), 1.into()); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::Read as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x0042d403); //lhu x8, 4(x5) simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 1.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 5.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 8.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 0.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_data), 4.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(data_mem_size), 2.into()); assert_eq!(simulator.get_input_value(data_se), 0.into()); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::Read as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x0042c403); //lbu x8, 4(x5) simulator.clock(); assert_eq!(simulator.get_input_value(wb_mux), 1.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 5.into()); - assert_eq!( - simulator.get_input_value(regfile_rs2), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rd), 8.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_sel), 0.into()); assert_eq!(simulator.get_input_value(sign_zero_ext_data), 4.into()); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(data_mem_size), 1.into()); assert_eq!(simulator.get_input_value(data_se), 0.into()); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::Read as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_logic_ctl), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_enable), 0.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); } #[test] @@ -2117,41 +1238,40 @@ mod test { store: vec![ Rc::new(ProbeOut::new("instruction")), Rc::new(Decoder { + width: 0.0, + height: 0.0, id: "decoder".to_string(), pos: (0.0, 0.0), instruction: Input::new("instruction", "out"), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); // outputs - let wb_mux = &Input::new("decoder", "wb_mux"); - let alu_operand_a_sel = &Input::new("decoder", "alu_operand_a_sel"); - let alu_operand_b_sel = &Input::new("decoder", "alu_operand_b_sel"); - let regfile_rs1 = &Input::new("decoder", "regfile_rs1"); - let regfile_rs2 = &Input::new("decoder", "regfile_rs2"); - let regfile_rd = &Input::new("decoder", "regfile_rd"); - let regfile_we = &Input::new("decoder", "regfile_we"); - let alu_operator = &Input::new("decoder", "alu_operator"); - let sign_zero_ext_sel = &Input::new("decoder", "sign_zero_ext_sel"); - let sign_zero_ext_data = &Input::new("decoder", "sign_zero_ext_data"); - let imm_a_mux_data = &Input::new("decoder", "imm_a_mux_data"); - let data_mem_size = &Input::new("decoder", "data_mem_size"); - let data_se = &Input::new("decoder", "data_se"); - let data_mem_ctrl = &Input::new("decoder", "data_mem_ctrl"); - let big_imm = &Input::new("decoder", "big_imm"); - let pc_imm_sel = &Input::new("decoder", "pc_imm_sel"); - let branch_imm = &Input::new("decoder", "branch_imm"); - let branch_logic_ctl = &Input::new("decoder", "branch_logic_ctl"); - let branch_logic_enable = &Input::new("decoder", "branch_logic_enable"); - let jalr_imm = &Input::new("decoder", "jalr_imm"); + let wb_mux = &Input::new("decoder", DECODER_WB_MUX_SEL_ID); + let alu_operand_a_sel = &Input::new("decoder", DECODER_ALU_A_MUX_SEL_ID); + let alu_operand_b_sel = &Input::new("decoder", DECODER_ALU_B_MUX_SEL_ID); + let regfile_rs1 = &Input::new("decoder", DECODER_RS1_ID); + let regfile_rs2 = &Input::new("decoder", DECODER_RS2_ID); + let regfile_rd = &Input::new("decoder", DECODER_RD_ID); + let regfile_we = &Input::new("decoder", DECODER_WB_WRITE_ENABLE_ID); + let alu_operator = &Input::new("decoder", DECODER_ALU_OP_ID); + let sign_zero_ext_data = &Input::new("decoder", DECODER_IMM_ID); + let data_mem_size = &Input::new("decoder", DECODER_DATA_MEM_SIZE_ID); + let data_se = &Input::new("decoder", DECODER_DATA_SE_ID); + let data_mem_ctrl = &Input::new("decoder", DECODER_DATA_MEM_CTRL_ID); + let big_imm = &Input::new("decoder", DECODER_JAL_IMM_ID); + let branch_imm = &Input::new("decoder", DECODER_BRANCH_IMM_ID); + let branch_logic_ctl = &Input::new("decoder", DECODER_BRANCH_OP); + let branch_logic_enable = &Input::new("decoder", DECODER_BRANCH_INSTR); + let jalr_imm = &Input::new("decoder", DECODER_IMM_ID); simulator.set_out_value("instruction", "out", 0x0080016f); //jal x2, 8 simulator.clock(); - assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 2.into()); - assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); + assert_eq!(simulator.get_input_value(wb_mux), 3.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 1.into()); + assert_eq!(simulator.get_input_value(alu_operand_b_sel), 5.into()); assert_eq!( simulator.get_input_value(regfile_rs1), SignalValue::Uninitialized @@ -2163,18 +1283,7 @@ mod test { assert_eq!(simulator.get_input_value(regfile_rd), 2.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); + assert_eq!(simulator.get_input_value(sign_zero_ext_data), 8.into()); assert_eq!( simulator.get_input_value(data_mem_size), SignalValue::Uninitialized @@ -2188,22 +1297,13 @@ mod test { (MemCtrl::None as u32).into() ); assert_eq!(simulator.get_input_value(big_imm), 8.into()); - assert_eq!(simulator.get_input_value(pc_imm_sel), 0.into()); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_ctl), 2.into()); assert_eq!(simulator.get_input_value(branch_logic_enable), 1.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x00410167); //jalr x2, x2, 4 simulator.clock(); - assert_eq!(simulator.get_input_value(wb_mux), 0.into()); - assert_eq!(simulator.get_input_value(alu_operand_a_sel), 2.into()); + assert_eq!(simulator.get_input_value(wb_mux), 3.into()); + assert_eq!(simulator.get_input_value(alu_operand_a_sel), 4.into()); assert_eq!(simulator.get_input_value(alu_operand_b_sel), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); assert_eq!( @@ -2213,435 +1313,93 @@ mod test { assert_eq!(simulator.get_input_value(regfile_rd), 2.into()); assert_eq!(simulator.get_input_value(regfile_we), 1.into()); assert_eq!(simulator.get_input_value(alu_operator), 1.into()); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(pc_imm_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(branch_imm), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(branch_logic_ctl), 3.into()); assert_eq!(simulator.get_input_value(branch_logic_enable), 1.into()); assert_eq!(simulator.get_input_value(jalr_imm), 4.into()); simulator.set_out_value("instruction", "out", 0xfe209ee3); //bne x1, x2, -4 simulator.clock(); - assert_eq!( - simulator.get_input_value(wb_mux), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_a_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_b_sel), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rs1), 1.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 2.into()); - assert_eq!( - simulator.get_input_value(regfile_rd), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(regfile_we), 0.into()); - assert_eq!( - simulator.get_input_value(alu_operator), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(pc_imm_sel), 1.into()); - assert_eq!( - simulator.get_input_value(branch_imm), - ((-4i32 & 0b1111111111111) as u32).into() - ); + assert_eq!(simulator.get_input_value(branch_imm), (-4i32 as u32).into()); assert_eq!(simulator.get_input_value(branch_logic_ctl), 1.into()); assert_eq!(simulator.get_input_value(branch_logic_enable), 1.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0x00208463); //beq, x1, x2, 8 simulator.clock(); - assert_eq!( - simulator.get_input_value(wb_mux), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_a_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_b_sel), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rs1), 1.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 2.into()); - assert_eq!( - simulator.get_input_value(regfile_rd), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_we), 0.into()); - assert_eq!( - simulator.get_input_value(alu_operator), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(pc_imm_sel), 1.into()); assert_eq!( simulator.get_input_value(branch_imm), - ((8i32 & 0b1111111111111) as u32).into() + ((8i32) as u32).into() ); assert_eq!(simulator.get_input_value(branch_logic_ctl), 0.into()); assert_eq!(simulator.get_input_value(branch_logic_enable), 1.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0xfe20cee3); //blt x1, x2, -4 simulator.clock(); - assert_eq!( - simulator.get_input_value(wb_mux), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_a_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_b_sel), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rs1), 1.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 2.into()); - assert_eq!( - simulator.get_input_value(regfile_rd), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_we), 0.into()); - assert_eq!( - simulator.get_input_value(alu_operator), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(pc_imm_sel), 1.into()); - assert_eq!( - simulator.get_input_value(branch_imm), - ((-4i32 & 0b1111111111111) as u32).into() - ); + assert_eq!(simulator.get_input_value(branch_imm), (-4i32 as u32).into()); assert_eq!(simulator.get_input_value(branch_logic_ctl), 0b100.into()); assert_eq!(simulator.get_input_value(branch_logic_enable), 1.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0xfe116ee3); //bltu, x2, x1, -4 simulator.clock(); - assert_eq!( - simulator.get_input_value(wb_mux), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_a_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_b_sel), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 1.into()); - assert_eq!( - simulator.get_input_value(regfile_rd), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_we), 0.into()); - assert_eq!( - simulator.get_input_value(alu_operator), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(pc_imm_sel), 1.into()); - assert_eq!( - simulator.get_input_value(branch_imm), - ((-4i32 & 0b1111111111111) as u32).into() - ); + assert_eq!(simulator.get_input_value(branch_imm), (-4i32 as u32).into()); assert_eq!(simulator.get_input_value(branch_logic_ctl), 0b110.into()); assert_eq!(simulator.get_input_value(branch_logic_enable), 1.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0xfe115ee3); //bge x2, x1, -4 simulator.clock(); - assert_eq!( - simulator.get_input_value(wb_mux), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_a_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_b_sel), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rs1), 2.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 1.into()); - assert_eq!( - simulator.get_input_value(regfile_rd), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_we), 0.into()); - assert_eq!( - simulator.get_input_value(alu_operator), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(pc_imm_sel), 1.into()); - assert_eq!( - simulator.get_input_value(branch_imm), - ((-4i32 & 0b1111111111111) as u32).into() - ); + assert_eq!(simulator.get_input_value(branch_imm), (-4i32 as u32).into()); assert_eq!(simulator.get_input_value(branch_logic_ctl), 0b101.into()); assert_eq!(simulator.get_input_value(branch_logic_enable), 1.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); simulator.set_out_value("instruction", "out", 0xfe20fee3); //bgeu x1, x2, -4 simulator.clock(); - assert_eq!( - simulator.get_input_value(wb_mux), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_a_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(alu_operand_b_sel), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_rs1), 1.into()); assert_eq!(simulator.get_input_value(regfile_rs2), 2.into()); - assert_eq!( - simulator.get_input_value(regfile_rd), - SignalValue::Uninitialized - ); assert_eq!(simulator.get_input_value(regfile_we), 0.into()); - assert_eq!( - simulator.get_input_value(alu_operator), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_sel), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(sign_zero_ext_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(imm_a_mux_data), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_mem_size), - SignalValue::Uninitialized - ); - assert_eq!( - simulator.get_input_value(data_se), - SignalValue::Uninitialized - ); assert_eq!( simulator.get_input_value(data_mem_ctrl), (MemCtrl::None as u32).into() ); - assert_eq!( - simulator.get_input_value(big_imm), - SignalValue::Uninitialized - ); - assert_eq!(simulator.get_input_value(pc_imm_sel), 1.into()); - assert_eq!( - simulator.get_input_value(branch_imm), - ((-4i32 & 0b1111111111111) as u32).into() - ); + assert_eq!(simulator.get_input_value(branch_imm), (-4i32 as u32).into()); assert_eq!(simulator.get_input_value(branch_logic_ctl), 0b111.into()); assert_eq!(simulator.get_input_value(branch_logic_enable), 1.into()); - assert_eq!( - simulator.get_input_value(jalr_imm), - SignalValue::Uninitialized - ); } } diff --git a/riscv/src/components/gpio.rs b/riscv/src/components/gpio.rs new file mode 100644 index 00000000..967c2595 --- /dev/null +++ b/riscv/src/components/gpio.rs @@ -0,0 +1,490 @@ +use crate::components::mem::{MemCtrl, Memory}; +use log::trace; +use serde::{Deserialize, Serialize}; +use std::cell::RefCell; +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::{ + common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Simulator}, + signal::{SignalUnsigned, SignalValue}, +}; + +use std::collections::HashMap; +pub const GPIO_CSR_BASE: u32 = 0x0; +pub const GPIO_MMIO_BASE: u32 = 0x6000_0000; +pub const GPIO_DATA_I_ID: &str = "data_i"; +pub const GPIO_SIZE_I_ID: &str = "size_i"; +pub const GPIO_WE_I_ID: &str = "we_i"; +pub const GPIO_ADDR_I_ID: &str = "addr_i"; +pub const GPIO_SE_I_ID: &str = "se_i"; +pub const GPIO_CSR_D_ID: &str = "csr_d"; +pub const GPIO_CSR_A_ID: &str = "csr_a"; +pub const GPIO_CSR_CTL_ID: &str = "csr_ctl"; + +pub const GPIO_DATA_O_ID: &str = "data_o"; +pub const GPIO_PIN_O_ID: &str = "pin_o"; +//pub const GPIO_DATA_I_ID: &str = ""; +//pub const GPIO_SIZE_I_ID: &str = "out"; +pub const PIN_AMOUNT: i8 = 8; + +pub const GPIO_HEIGHT: f32 = 50.0; +pub const GPIO_WIDTH: f32 = 250.0; + +#[derive(Serialize, Deserialize)] +pub struct GPIO { + pub height: f32, + pub width: f32, + pub id: String, + pub pos: (f32, f32), + + // internal state + #[serde(skip)] + pub memory: Memory, + #[serde(skip)] + pub pins: Pins, + #[serde(skip)] + pub csrstore: GPIOCsrStore, + + pub data_i: Input, + pub size_i: Input, + pub we_i: Input, + pub addr_i: Input, + pub se_i: Input, + pub csr_d: Input, + pub csr_a: Input, + pub csr_ctl: Input, +} +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +pub struct Pin { + pub enabled: bool, + pub interrupts: bool, + pub is_input: bool, + pub state: bool, + pub id: u8, +} +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Pins(pub Rc>>); + +impl Default for Pins { + fn default() -> Pins { + let mut v = vec![]; + for i in 0..PIN_AMOUNT { + v.push(Pin { + enabled: false, + interrupts: false, + is_input: false, + state: false, + id: i as u8, + }); + } + Self::new(v) + } +} +impl Pins { + pub fn new(v: Vec) -> Self { + Pins(Rc::new(RefCell::new(v))) + } +} + +pub struct GPIOCsrStore(Rc>>); + +impl Default for GPIOCsrStore { + fn default() -> GPIOCsrStore { + let mut h = HashMap::new(); + for i in ((0 + GPIO_CSR_BASE) as usize)..=((6 + GPIO_CSR_BASE) as usize) { + h.insert(i, 0); + } + GPIOCsrStore(Rc::new(RefCell::new(h))) + } +} + +#[typetag::serde()] +impl Component for GPIO { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn to_(&self) { + println!("GPIO"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy = Input::new("dummy", "out"); + Box::new(Rc::new(GPIO { + height: GPIO_HEIGHT, + width: GPIO_WIDTH, + id: id.to_string(), + pos: (pos.0, pos.1), + csr_d: dummy.clone(), + csr_a: dummy.clone(), + csr_ctl: dummy.clone(), + data_i: dummy.clone(), + size_i: dummy.clone(), + we_i: dummy.clone(), + addr_i: dummy.clone(), + se_i: dummy.clone(), + csrstore: GPIOCsrStore::default(), + pins: Pins::default(), + memory: Memory::default(), + })) + } + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id.as_str() == GPIO_DATA_I_ID { + self.data_i = new_input; + } else if target_port_id.as_str() == GPIO_ADDR_I_ID { + self.addr_i = new_input; + } else if target_port_id.as_str() == GPIO_SIZE_I_ID { + self.size_i = new_input; + } else if target_port_id.as_str() == GPIO_WE_I_ID { + self.we_i = new_input; + } else if target_port_id.as_str() == GPIO_SE_I_ID { + self.se_i = new_input; + } else if target_port_id.as_str() == GPIO_CSR_D_ID { + self.csr_d = new_input; + } else if target_port_id.as_str() == GPIO_CSR_A_ID { + self.csr_a = new_input; + } else if target_port_id.as_str() == GPIO_CSR_CTL_ID { + self.csr_ctl = new_input; + } + } + fn get_id_ports(&self) -> (String, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: GPIO_DATA_I_ID.to_string(), + input: self.data_i.clone(), + }, + &InputPort { + port_id: GPIO_ADDR_I_ID.to_string(), + input: self.addr_i.clone(), + }, + &InputPort { + port_id: GPIO_SIZE_I_ID.to_string(), + input: self.size_i.clone(), + }, + &InputPort { + port_id: GPIO_WE_I_ID.to_string(), + input: self.we_i.clone(), + }, + &InputPort { + port_id: GPIO_SE_I_ID.to_string(), + input: self.se_i.clone(), + }, + ], + OutputType::Combinatorial, + vec![ + GPIO_DATA_O_ID, + "pin_o0", + "pin_o1", + "pin_o2", + "pin_o3", + "pin_o4", + "pin_o5", + "pin_o6", + "pin_o7", + ], + ), + ) + } + #[allow(non_snake_case)] + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + let data = simulator.get_input_value(&self.data_i); + let addr = simulator.get_input_value(&self.addr_i); + let size = simulator.get_input_value(&self.size_i); + let sign = simulator.get_input_value(&self.se_i); + let csr_data = simulator.get_input_value(&self.csr_d); + let csr_addr = simulator.get_input_value(&self.csr_a); + let csr_ctl = simulator.get_input_value(&self.csr_ctl); + match csr_ctl { + SignalValue::Data(ctl) => { + let csr_addr: u32 = csr_addr.try_into().unwrap(); + let csr_data: u32 = csr_data.try_into().unwrap_or(0); // could be a read still + let mut csrstore = self.csrstore.0.borrow_mut(); + let _ = self.csr_op(&mut csrstore, ctl, csr_data, csr_addr); + trace!( + "CSR TOUCH addr: {:x}", + (csr_addr - GPIO_CSR_BASE) * 4 + GPIO_MMIO_BASE + ); + trace!("CSR addr: {:x}", csr_addr); + self.handle_gpio_write((csr_addr - GPIO_CSR_BASE) * 4 + GPIO_MMIO_BASE); + } + _ => {} + } + match simulator.get_input_value(&self.we_i) { + SignalValue::Data(ctrl) => { + let addr: u32 = addr.try_into().unwrap_or(0); + if (0x6000_0000..=0x6000_0018).contains(&addr) { + let ctrl = MemCtrl::try_from(ctrl as u8).unwrap(); + match ctrl { + MemCtrl::Read => { + let size: u32 = size.try_into().unwrap(); + let sign: u32 = sign.try_into().unwrap(); + let value = + self.memory + .read(addr as usize, size as usize, sign != 0, false); + simulator.set_out_value(&self.id, "data_o", value); + } + MemCtrl::Write => { + let size: u32 = size.try_into().unwrap(); + //let data: u32 = size.try_into().unwrap(); + self.memory.write(addr as usize, size as usize, false, data); + self.handle_gpio_write(addr); + } + _ => { + simulator.set_out_value(&self.id, "data_o", SignalValue::Unknown); + trace!("nothing for GPIO") + } + } + } + } + _ => { + trace!("ctrl uninit"); + } + }; + for pin in &*self.pins.0.borrow() { + let mut name = GPIO_PIN_O_ID.to_string(); + name.push_str(&format!("{}", pin.id)); + if pin.state { + simulator.set_out_value(&self.id, &name, SignalValue::Data(1)); + } else { + simulator.set_out_value(&self.id, &name, SignalValue::Data(0)); + } + } + Ok(()) + } +} + +impl GPIO { + fn handle_gpio_write(&self, _addr: u32) { + let mut pins = self.pins.0.borrow_mut(); + /*let rel_addr = addr - 0x6000_0000; + let touched_indices = [ + (rel_addr - rel_addr % 4) / 4, + (rel_addr + 4 - (rel_addr % 4)) / 4, + ]; + + trace!("touched indices {:?}", touched_indices);*/ + // enable + /* if touched_indices.contains(&0) { + let mut data: u32 = self + .memory + .read(0x6000_0000, 4, false, false) + .try_into() + .unwrap(); + for i in 0..PIN_AMOUNT { + let mut pin = *pins.get(i as usize).unwrap(); + if data & 0b1 == 1 { + pin.enabled = true; + trace!("PIN {} ENABLED", i); + } else { + pin.enabled = false; + trace!("PIN {} DISABLED", i); + } + let _ = std::mem::replace(&mut pins[i as usize], pin); + + data = data >> 1; + } + }*/ + /*// set input/output + if touched_indices.contains(&1) { + let mut data: u32 = self + .memory + .read(0x6000_0004, 4, false, false) + .try_into() + .unwrap(); + for i in 0..PIN_AMOUNT { + let mut pin = *pins.get(i as usize).unwrap(); + if data & 0b1 == 1 { + pin.is_input = true; + trace!("PIN {} IS INPUT", i); + } else { + pin.is_input = false; + trace!("PIN {} IS OUTPUT", i); + } + let _ = std::mem::replace(&mut pins[i as usize], pin); + data = data >> 1; + } + }*/ + /* // enable interrupts + if touched_indices.contains(&2) {}*/ + // set + /* if touched_indices.contains(&3) { + let mut data: u32 = self + .memory + .read(0x6000_000C, 4, false, false) + .try_into() + .unwrap(); + let mut state: u32 = self + .memory + .read(0x6000_0018, 4, false, false) + .try_into() + .unwrap(); + for i in 0..PIN_AMOUNT { + let mut pin = *pins.get(i as usize).unwrap(); + if data & 0b1 == 1 { + if pin.enabled && !pin.is_input { + pin.state = true; + trace!("PIN {} SET HIGH", i); + state |= 1 << i; + } + } + data = data >> 1; + self.memory.write(0x6000_000C, 4, false, 0.into()); + let _ = std::mem::replace(&mut pins[i as usize], pin); + } + self.memory.write(0x6000_0018, 4, false, state.into()); + } + // clear + if touched_indices.contains(&4) { + let mut data: u32 = self + .memory + .read(0x6000_0010, 4, false, false) + .try_into() + .unwrap(); + let mut state: u32 = self + .memory + .read(0x6000_0018, 4, false, false) + .try_into() + .unwrap(); + for i in 0..PIN_AMOUNT { + let mut pin = *pins.get(i as usize).unwrap(); + if data & 0b1 == 1 { + if !pin.is_input { + pin.state = false; + trace!("PIN {} CLEAR", i); + state &= !(1 << i); + } + } + data = data >> 1; + self.memory.write(0x6000_0010, 4, false, 0.into()); + let _ = std::mem::replace(&mut pins[i as usize], pin); + } + trace!("CLEAR STATE: {}", state); + self.memory.write(0x6000_0018, 4, false, state.into()); + } + // toggle + if touched_indices.contains(&5) { + let mut data: u32 = self + .memory + .read(0x6000_0014, 4, false, false) + .try_into() + .unwrap(); + let mut state: u32 = self + .memory + .read(0x6000_0018, 4, false, false) + .try_into() + .unwrap(); + for i in 0..PIN_AMOUNT { + let mut pin = *pins.get(i as usize).unwrap(); + if data & 0b1 == 1 { + if pin.enabled && !pin.is_input { + pin.state = !pin.state; + trace!("PIN {} TOGGLE", i); + if pin.state { + state |= 1 << i; + } else { + state &= !(1 << i); + } + } + } + data = data >> 1; + self.memory.write(0x6000_0014, 4, false, 0.into()); + let _ = std::mem::replace(&mut pins[i as usize], pin); + } + self.memory.write(0x6000_0018, 4, false, state.into()); + }*/ + let mut data: u32 = self + .memory + .read(0x6000_0000, 4, false, false) + .try_into() + .unwrap(); + for i in 0..PIN_AMOUNT { + let mut pin = *pins.get(i as usize).unwrap(); + if data & 0b1 == 1 { + pin.state = true; + trace!("PIN {} state high", i); + } else { + pin.state = false; + trace!("PIN {} state low", i); + } + data = data >> 1; + let _ = std::mem::replace(&mut pins[i as usize], pin); + } + } + fn csr_op( + &self, + csrstore: &mut HashMap, + csr_ctl: SignalUnsigned, + csr_data: SignalUnsigned, + csr_addr: SignalUnsigned, + ) -> SignalValue { + let mut val = SignalValue::Unknown; + let csr_data = csr_data.clone(); + match csr_ctl { + 0 => {} + //write + 1 => { + if csrstore.contains_key(&(csr_addr as usize)) { + val = self.memory.read( + ((csr_addr - GPIO_CSR_BASE) * 4 + GPIO_MMIO_BASE) as usize, + 4, + false, + false, + ); + csrstore.insert(csr_addr as usize, csr_data as usize); + self.memory.write( + ((csr_addr - GPIO_CSR_BASE) * 4 + GPIO_MMIO_BASE) as usize, + 4, + false, + SignalValue::Data(csr_data), + ); + } + } + //set + 2 => { + if csrstore.contains_key(&(csr_addr as usize)) { + trace!("csr set"); + val = self.memory.read( + ((csr_addr - GPIO_CSR_BASE) * 4 + GPIO_MMIO_BASE) as usize, + 4, + false, + false, + ); + let val_i: usize = val.try_into().unwrap(); + csrstore.insert(csr_addr as usize, (csr_data as usize) | val_i); + self.memory.write( + ((csr_addr - GPIO_CSR_BASE) * 4 + GPIO_MMIO_BASE) as usize, + 4, + false, + SignalValue::Data((csr_data) | val_i as u32), + ); + } + } + //clear + 3 => { + if csrstore.contains_key(&(csr_addr as usize)) { + trace!("csr clear"); + val = self.memory.read( + ((csr_addr - GPIO_CSR_BASE) * 4 + GPIO_MMIO_BASE) as usize, + 4, + false, + false, + ); + let val_i: usize = val.try_into().unwrap(); + csrstore.insert(csr_addr as usize, val_i & !(csr_data as usize)); + self.memory.write( + ((csr_addr - GPIO_CSR_BASE) * 4 + GPIO_MMIO_BASE) as usize, + 4, + false, + SignalValue::Data((val_i as u32) & !csr_data), + ); + } + } + _ => {} + } + val + } +} diff --git a/riscv/src/components/instr_mem.rs b/riscv/src/components/instr_mem.rs index eb5d74a6..55937fcd 100644 --- a/riscv/src/components/instr_mem.rs +++ b/riscv/src/components/instr_mem.rs @@ -1,52 +1,122 @@ use asm_riscv::{self}; -use std::{collections::BTreeMap, panic}; +use std::{ + cell::RefCell, + collections::{BTreeMap, HashMap, HashSet}, + ops::Range, + rc::Rc, +}; use log::trace; +use riscv_asm_strings; use serde::{Deserialize, Serialize}; -use syncrim::common::{Component, Condition, Input, OutputType, Ports, Simulator}; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Simulator}; +pub const INSTR_MEM_PC_ID: &str = "pc"; -#[derive(Serialize, Deserialize)] +pub const INSTR_MEM_INSTRUCTION_ID: &str = "instruction"; + +pub const INSTR_MEM_HEIGHT: f32 = 100.0; +pub const INSTR_MEM_WIDTH: f32 = 100.0; + +#[derive(Serialize, Deserialize, Clone)] pub struct InstrMem { + pub width: f32, + pub height: f32, pub id: String, pub pos: (f32, f32), + #[serde(skip)] pub bytes: BTreeMap, pub pc: Input, + pub range: Range, + #[serde(skip)] + pub breakpoints: Rc>>, + #[serde(skip)] + pub symbols: HashMap, + pub le: bool, } #[typetag::serde()] impl Component for InstrMem { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn to_(&self) { - println!("InstrMem"); + //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 { + width: INSTR_MEM_WIDTH, + height: INSTR_MEM_HEIGHT, + id: id.to_string(), + pos: (pos.0, pos.1), + bytes: BTreeMap::new(), + pc: dummy_input, + range: Range { + start: 0, + end: 0x1000, + }, + breakpoints: Rc::new(RefCell::new(HashSet::new())), + symbols: HashMap::new(), + le: true, + })) + } + 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 { - inputs: vec![self.pc.clone()], - out_type: OutputType::Combinatorial, - outputs: vec!["instruction".into()], - }, + 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(); - - let instr = (*self.bytes.get(&((pc) as usize)).unwrap() as u32) << 24 - | (*self.bytes.get(&((pc + 1) as usize)).unwrap() as u32) << 16 - | (*self.bytes.get(&((pc + 2) as usize)).unwrap() as u32) << 8 - | (*self.bytes.get(&((pc + 3) as usize)).unwrap() as u32); + let instr = if !self.le { + (*self.bytes.get(&((pc) as usize)).unwrap() as u32) << 24 + | (*self.bytes.get(&((pc + 1) as usize)).unwrap() as u32) << 16 + | (*self.bytes.get(&((pc + 2) as usize)).unwrap() as u32) << 8 + | (*self.bytes.get(&((pc + 3) as usize)).unwrap() as u32) + } else { + (*self.bytes.get(&((pc) as usize)).unwrap() as u32) + | (*self.bytes.get(&((pc + 1) as usize)).unwrap() as u32) << 8 + | (*self.bytes.get(&((pc + 2) as usize)).unwrap() as u32) << 16 + | (*self.bytes.get(&((pc + 3) as usize)).unwrap() as u32) << 24 + }; //the asm_riscv crate incorrectly panics when trying from instead of //returning Err, catch it and handle instead - let instruction_fmt = - panic::catch_unwind(|| format!("{:?}", asm_riscv::I::try_from(instr))) - .unwrap_or_else(|_| format!("Unknown instruction")); + let instruction_fmt = { + format!( + "{:?}", + match asm_riscv::I::try_from(instr) { + Ok(i) => riscv_asm_strings::StringifyUpperHex::to_string(&i), + Err(_) => "Unknown instruction".to_string(), + } + ) + }; trace!("instruction: {}", instruction_fmt); trace!("pc:0x{:08x}", pc); // set output simulator.set_out_value(&self.id, "instruction", instr); - Ok(()) + if !self.breakpoints.borrow().contains(&(pc as usize)) { + Ok(()) + } else { + Err(Condition::Halt(format!("Breakpoint at {}", pc))) + } } } mod test { @@ -72,15 +142,24 @@ mod test { store: vec![ Rc::new(ProbeOut::new("pc")), Rc::new(InstrMem { + width: 0.0, + height: 0.0, id: "imem".to_string(), pos: (0.0, 0.0), pc: Input::new("pc", "out"), bytes: instr_mem, + range: Range { + start: 0, + end: 0x1000, + }, + breakpoints: Rc::new(RefCell::new(HashSet::new())), + symbols: HashMap::new(), + le: false, }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs diff --git a/riscv/src/components/led.rs b/riscv/src/components/led.rs new file mode 100644 index 00000000..dff49b0f --- /dev/null +++ b/riscv/src/components/led.rs @@ -0,0 +1,64 @@ +use serde::{Deserialize, Serialize}; +#[cfg(feature = "gui-egui")] +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Simulator}; + +pub const LED_I_ID: &str = "input"; +pub const LED_HEIGHT: f32 = 20.0; +pub const LED_WIDTH: f32 = 20.0; + +#[derive(Serialize, Deserialize)] +pub struct LED { + pub height: f32, + pub width: f32, + pub id: String, + pub pos: (f32, f32), + + pub input: Input, +} + +#[typetag::serde()] +impl Component for LED { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn to_(&self) { + println!("LED"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy = Input::new("dummy", "out"); + Box::new(Rc::new(LED { + height: LED_HEIGHT, + width: LED_WIDTH, + id: id.to_string(), + pos: (pos.0, pos.1), + input: dummy.clone(), + })) + } + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id.as_str() == LED_I_ID { + self.input = new_input; + } + } + fn get_id_ports(&self) -> (String, Ports) { + ( + self.id.clone(), + Ports::new( + vec![&InputPort { + port_id: LED_I_ID.to_string(), + input: self.input.clone(), + }], + OutputType::Combinatorial, + vec![], + ), + ) + } + #[allow(non_snake_case)] + fn clock(&self, _simulator: &mut Simulator) -> Result<(), Condition> { + Ok(()) + } +} diff --git a/riscv/src/components/lsb_zero.rs b/riscv/src/components/lsb_zero.rs index cfd63c67..04c04b9c 100644 --- a/riscv/src/components/lsb_zero.rs +++ b/riscv/src/components/lsb_zero.rs @@ -1,11 +1,23 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "gui-egui")] +use std::rc::Rc; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; use syncrim::{ - common::{Component, Condition, Input, OutputType, Ports, Simulator}, + common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Simulator}, signal::SignalValue, }; +pub const LSB_ZERO_DATA_I_ID: &str = "data_i"; +pub const LSB_ZERO_OUT_ID: &str = "out"; + +pub const LSB_ZERO_HEIGHT: f32 = 10.0; +pub const LSB_ZERO_WIDTH: f32 = 10.0; + #[derive(Serialize, Deserialize)] pub struct LSBZero { + pub height: f32, + pub width: f32, pub id: String, pub pos: (f32, f32), @@ -14,17 +26,40 @@ pub struct LSBZero { #[typetag::serde()] impl Component for LSBZero { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn to_(&self) { println!("LSBZero"); } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy = Input::new("dummy", "out"); + Box::new(Rc::new(LSBZero { + height: LSB_ZERO_HEIGHT, + width: LSB_ZERO_WIDTH, + id: id.to_string(), + pos: (pos.0, pos.1), + data_i: dummy.clone(), + })) + } + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id.as_str() == LSB_ZERO_DATA_I_ID { + self.data_i = new_input; + } + } fn get_id_ports(&self) -> (String, Ports) { ( self.id.clone(), - Ports { - inputs: vec![self.data_i.clone()], - out_type: OutputType::Combinatorial, - outputs: vec!["out".into()], - }, + Ports::new( + vec![&InputPort { + port_id: LSB_ZERO_DATA_I_ID.to_string(), + input: self.data_i.clone(), + }], + OutputType::Combinatorial, + vec![LSB_ZERO_OUT_ID], + ), ) } #[allow(non_snake_case)] @@ -56,6 +91,8 @@ mod test { store: vec![ Rc::new(ProbeOut::new("input")), Rc::new(LSBZero { + height: 0.0, + width: 0.0, id: "lzero".to_string(), pos: (0.0, 0.0), data_i: Input::new("input", "out"), @@ -63,7 +100,7 @@ mod test { ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs diff --git a/riscv/src/components/mem.rs b/riscv/src/components/mem.rs new file mode 100644 index 00000000..c8a5d207 --- /dev/null +++ b/riscv/src/components/mem.rs @@ -0,0 +1,818 @@ +use log::*; +use num_enum::IntoPrimitive; +use num_enum::TryFromPrimitive; +use serde::{Deserialize, Serialize}; +use std::ops::Deref; +use std::ops::Range; +use std::{cell::RefCell, collections::BTreeMap, convert::TryFrom, rc::Rc}; +use syncrim::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalSigned, SignalUnsigned, + SignalValue, Simulator, +}; +//#[feature("gui-egui")] +//use egui_extras::TableBuilder; +pub const RV_MEM_DATA_I_ID: &str = "data_i"; +pub const RV_MEM_ADDR_ID: &str = "addr"; +pub const RV_MEM_CTRL_ID: &str = "ctrl"; +pub const RV_MEM_SEXT_ID: &str = "sext"; +pub const RV_MEM_SIZE_ID: &str = "size"; +pub const RV_INTERRUPT_ID: &str = "interrupt"; +pub const RV_MEM_DATA_O_ID: &str = "data_o"; +#[derive(Serialize, Deserialize, Clone)] +pub struct RVMem { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) width: f32, + pub(crate) height: f32, + + // configuration + pub big_endian: bool, + + // ports + pub(crate) data: Input, + pub(crate) addr: Input, + pub(crate) ctrl: Input, + pub(crate) sext: Input, + pub(crate) size: Input, + pub(crate) interrupt: Input, + // pub(crate) mem_int_addr: Input, + + // memory + #[serde(skip)] + pub memory: Memory, + pub(crate) range: Range, + // later history... tbd + // + history: RefCell>, + #[serde(skip)] + pub init_state: BTreeMap, +} +#[derive(Serialize, Deserialize, Clone)] +struct MemOp { + pub data: Option, + pub addr: usize, + pub size: u8, +} + +impl RVMem { + #[allow(clippy::too_many_arguments)] + pub fn new( + id: &str, + pos: (f32, f32), + width: f32, + height: f32, + big_endian: bool, + data: Input, + addr: Input, + ctrl: Input, + sext: Input, + size: Input, + interrupt: Input, + memory: BTreeMap, + range: Range, + ) -> Self { + RVMem { + id: id.to_string(), + pos, + width, + height, + big_endian, + data, + addr, + ctrl, + sext, + size, + interrupt, + memory: Memory::new(memory.clone()), + range, + history: RefCell::new(vec![]), + init_state: memory.clone(), + } + } + + #[allow(clippy::too_many_arguments)] + pub fn rc_new( + id: &str, + pos: (f32, f32), + width: f32, + height: f32, + big_endian: bool, + data: Input, + addr: Input, + ctrl: Input, + sext: Input, + size: Input, + interrupt: Input, + range: Range, + ) -> Rc { + let mut mem = BTreeMap::new(); + //fill the defined memory range with zeroes + for i in range.clone() { + mem.insert(i as usize, 0u8); + } + Rc::new(RVMem::new( + id, pos, width, height, big_endian, data, addr, ctrl, sext, size, interrupt, mem, range, + )) + } + + #[allow(clippy::too_many_arguments)] + pub fn rc_new_from_bytes( + id: &str, + pos: (f32, f32), + width: f32, + height: f32, + big_endian: bool, + data: Input, + addr: Input, + ctrl: Input, + sext: Input, + size: Input, + interrupt: Input, + memory: BTreeMap, + range: Range, + ) -> Rc { + Rc::new(RVMem::new( + id, pos, width, height, big_endian, data, addr, ctrl, sext, size, interrupt, memory, + range, + )) + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Memory(pub Rc>>); + +impl Default for Memory { + fn default() -> Self { + Self::new(BTreeMap::new()) + } +} + +impl Memory { + pub fn new(data: BTreeMap) -> Self { + Memory(Rc::new(RefCell::new(data))) + } + + fn align(&self, addr: usize, size: usize) -> SignalValue { + ((addr % size != 0) as SignalUnsigned).into() + } + + pub fn read(&self, addr: usize, size: usize, sign: bool, big_endian: bool) -> SignalValue { + let data: Vec = (0..size) + .map(|i| *self.0.borrow().get(&(addr + i)).unwrap_or(&0)) + .collect(); + + let data = data.as_slice(); + + //trace!("{:x?}", data); + + match size { + 1 => { + if sign { + data[0] as i8 as SignalSigned as SignalUnsigned + } else { + data[0] as SignalUnsigned + } + } + 2 => { + if sign { + if 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); + let i_32 = i_16 as i32; + trace!("i_32 {:x?}", i_32); + i_32 as SignalUnsigned + } else { + trace!("read signed half word le"); + let i_16 = i16::from_le_bytes(data.try_into().unwrap()); + trace!("i_16 {:x?}", i_16); + let i_32 = i_16 as i32; + trace!("i_32 {:x?}", i_32); + i_32 as SignalUnsigned + } + } else if 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); + let u_32 = u_16 as u32; + trace!("u_32 {:x?}", u_32); + u_32 as SignalUnsigned + } else { + trace!("read unsigned half word le"); + let u_16 = u16::from_le_bytes(data.try_into().unwrap()); + trace!("u_16 {:x?}", u_16); + let u_32 = u_16 as u32; + trace!("u_32 {:x?}", u_32); + u_32 as SignalUnsigned + } + } + 4 => { + if sign { + if big_endian { + i32::from_be_bytes(data.try_into().unwrap()) as SignalUnsigned + } else { + i32::from_le_bytes(data.try_into().unwrap()) as SignalUnsigned + } + } else if big_endian { + u32::from_be_bytes(data.try_into().unwrap()) as SignalUnsigned + } else { + u32::from_le_bytes(data.try_into().unwrap()) as SignalUnsigned + } + } + _ => panic!("illegal sized memory operation"), + } + .into() + } + + pub fn write(&self, addr: usize, size: usize, big_endian: bool, data: SignalValue) { + let data: SignalUnsigned = data.try_into().unwrap(); + match size { + 1 => { + trace!("write byte"); + self.0.borrow_mut().insert(addr, data as u8); + } + 2 => { + if big_endian { + trace!("write half word be"); + (data as u16) + .to_be_bytes() + .iter() + .enumerate() + .for_each(|(i, bytes)| { + self.0.borrow_mut().insert(addr + i, *bytes); + }) + } else { + trace!("write half word le"); + (data as u16) + .to_le_bytes() + .iter() + .enumerate() + .for_each(|(i, bytes)| { + self.0.borrow_mut().insert(addr + i, *bytes); + }) + } + } + + 4 => { + if big_endian { + trace!("write word be"); + data.to_be_bytes() + .iter() + .enumerate() + .for_each(|(i, bytes)| { + self.0.borrow_mut().insert(addr + i, *bytes); + }) + } else { + trace!("write word le"); + data.to_le_bytes() + .iter() + .enumerate() + .for_each(|(i, bytes)| { + self.0.borrow_mut().insert(addr + i, *bytes); + }) + } + } + _ => { + panic!("illegal sized memory operation, size = {}", size) + } + }; + } +} + +#[derive(Copy, Clone, Debug, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] // Unfortunately Rust does not allow Signal here, we need to cast manually +pub enum MemCtrl { + None, + Read, + Write, + ReadIsr, +} + +// impl From for MemCtrl { +// fn from(value:SignalValue) -> Self { +// CliError::ParseError(error) +// } +// } + +#[typetag::serde()] +impl Component for RVMem { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn to_(&self) { + trace!("Mem"); + } + + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: RV_MEM_DATA_I_ID.to_string(), + input: self.data.clone(), + }, + &InputPort { + port_id: RV_MEM_ADDR_ID.to_string(), + input: self.addr.clone(), + }, + &InputPort { + port_id: RV_MEM_CTRL_ID.to_string(), + input: self.ctrl.clone(), + }, + &InputPort { + port_id: RV_MEM_SEXT_ID.to_string(), + input: self.sext.clone(), + }, + &InputPort { + port_id: RV_MEM_SIZE_ID.to_string(), + input: self.size.clone(), + }, + &InputPort { + port_id: RV_INTERRUPT_ID.to_string(), + input: self.interrupt.clone(), + }, + ], + OutputType::Combinatorial, + vec!["data_o", "err", "mmio_mux_ctl"], + ), + ) + } + + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + let mut history_entry = MemOp { + data: None, + addr: 0, + size: 0, + }; + let data = simulator.get_input_value(&self.data); + let addr = simulator.get_input_value(&self.addr); + let size = simulator.get_input_value(&self.size); + let sign = simulator.get_input_value(&self.sext); + let interrupt = simulator.get_input_value(&self.interrupt); + // let mem_int_addr = simulator.get_input_value(&self.mem_int_addr); + + // match mem_int_addr { + // SignalValue::Data(addr) => { + // let value = self.memory.read(addr as usize, 4, false, self.big_endian); + // simulator.set_out_value(&self.id, "isr_addr", value); + // } + // _ => simulator.set_out_value(&self.id, "isr_addr", SignalValue::Unknown), + // } + + match simulator.get_input_value(&self.ctrl) { + SignalValue::Data(ctrl) => { + let interrupt_occurred = SignalValue::Data(true as u32) == interrupt; + + let ctrl: MemCtrl = if interrupt_occurred { + MemCtrl::ReadIsr + } else { + MemCtrl::try_from(ctrl as u8).unwrap() + }; + + match ctrl { + MemCtrl::ReadIsr => { + let addr: u32 = addr.try_into().unwrap(); + + //if not in mmio range + + trace!("read isr {:?}", addr); + let value = self.memory.read(addr as usize, 4, false, self.big_endian); + simulator.set_out_value(&self.id, "data_o", value); + let error = self.memory.align(addr as usize, 4); + trace!("align {:?}", error); + simulator.set_out_value(&self.id, "err", error); // align + simulator.set_out_value(&self.id, "mmio_mux_ctl", 0); + } + MemCtrl::Read => { + let addr: u32 = addr.try_into().unwrap(); + if !(0x1000..=0x500F).contains(&addr) { + //if not in mmio range + let size: u32 = size.try_into().unwrap(); + let sign: u32 = sign.try_into().unwrap(); + trace!("read addr {:?} size {:?}", addr, size); + let value = self.memory.read( + addr as usize, + size as usize, + sign != 0, + self.big_endian, + ); + simulator.set_out_value(&self.id, "data_o", value); + let error = self.memory.align(addr as usize, size as usize); + trace!("align {:?}", error); + simulator.set_out_value(&self.id, "err", error); // align + simulator.set_out_value(&self.id, "mmio_mux_ctl", 0); + } else { + simulator.set_out_value(&self.id, "mmio_mux_ctl", 1); + } + } + MemCtrl::Write => { + let addr: u32 = addr.try_into().unwrap(); + if !(0x1000..=0x500F).contains(&addr) { + //if not in mmio range + let size: u32 = size.try_into().unwrap(); + history_entry = MemOp { + data: match self.memory.read( + addr as usize, + size as usize, + false, + self.big_endian, + ) { + SignalValue::Data(d) => Some(d as usize), + _ => None, + }, + addr: addr as usize, + size: size as u8, + }; + trace!("write addr {:?} size {:?}", addr, size); + self.memory + .write(addr as usize, size as usize, self.big_endian, data); + let value = self.memory.align(addr as usize, size as usize); + trace!("align {:?}", value); + simulator.set_out_value(&self.id, "err", value); // align + } + } + MemCtrl::None => { + trace!("no read/write"); + } + } + } + _ => { + simulator.set_out_value(&self.id, "data_o", SignalValue::Unknown); + simulator.set_out_value(&self.id, "err", SignalValue::Unknown); // align + } + } + + // for (idx, i) in self.memory.0.borrow().iter().enumerate() { + // if i.0 % 4 == 0 && idx < 40 { + // //only print 40 bytes so the trace isn't busy + // trace!( + // "0x{:08x} : 0x{:02x}{:02x}{:02x}{:02x}", + // i.0, + // self.memory.0.borrow().get(i.0).unwrap_or(&0u8), + // self.memory.0.borrow().get(&(i.0 + 1)).unwrap_or(&0u8), + // self.memory.0.borrow().get(&(i.0 + 2)).unwrap_or(&0u8), + // self.memory.0.borrow().get(&(i.0 + 3)).unwrap_or(&0u8), + // ) + // } + // } + self.history.borrow_mut().push(history_entry); + Ok(()) + } + + fn un_clock(&self) { + let entry = self.history.borrow_mut().pop().unwrap(); + if let Some(d) = entry.data { + self.memory.write( + entry.addr, + entry.size.into(), + self.big_endian, + SignalValue::Data(d as u32), + ) + } //self.memory.write(, size, big_endian, data) + } + + fn reset(&self) { + //let mut mem = self.memory.0.borrow_mut(); + //let b = self.init_state.0.clone(); + self.memory.0.replace(self.init_state.clone()); + //mem = self.init_state.0.borrow_mut(); + //self.memory.0.swap(&*self.init_state.0.clone()); + self.history.swap(&RefCell::new(vec![])); + } +} + +impl Deref for Memory { + type Target = RefCell>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::rc::Rc; + use syncrim::common::ComponentStore; + use syncrim::components::ProbeOut; + + #[test] + fn test_mem_be() { + let cs = ComponentStore { + store: vec![ + Rc::new(ProbeOut::new("data")), + Rc::new(ProbeOut::new("addr")), + Rc::new(ProbeOut::new("ctrl")), + Rc::new(ProbeOut::new("size")), + Rc::new(ProbeOut::new("sign")), + Rc::new(ProbeOut::new("interrupt")), + Rc::new(RVMem { + id: "mem".into(), + pos: (0.0, 0.0), + width: 0.0, + height: 0.0, + + // configuration + big_endian: true, // i.e., big endian + + // ports + data: Input::new("data", "out"), + addr: Input::new("addr", "out"), + ctrl: Input::new("ctrl", "out"), + size: Input::new("size", "out"), + sext: Input::new("sign", "out"), + //interrupt: Input::new("sign", "out"), + interrupt: Input::new("interrupt", "out"), + + // memory + memory: Memory(Rc::new(RefCell::new(BTreeMap::new()))), + range: Range { + start: 0u32, + end: 1u32, + }, + history: RefCell::new(vec![]), + init_state: BTreeMap::new(), + }), + ], + }; + + let mut simulator = Simulator::new(cs).unwrap(); + + assert_eq!(simulator.cycle, 1); + + // outputs + let out = &Input::new("mem", "data_o"); + let err = &Input::new("mem", "err"); + + // reset + assert_eq!(simulator.get_input_value(out), 0.into()); + assert_eq!( + simulator.get_input_value(err), + (false as SignalUnsigned).into() + ); + + println!(""); + + simulator.set_out_value("data", "out", 0xf0); + simulator.set_out_value("addr", "out", 4); + simulator.set_out_value("ctrl", "out", MemCtrl::Write as SignalUnsigned); + simulator.set_out_value("size", "out", 1); + simulator.set_out_value("interrupt", "out", false as u32); + 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), 0.into()); + assert_eq!( + simulator.get_input_value(err), + (false as SignalUnsigned).into() + ); + + println!(""); + + simulator.set_out_value("ctrl", "out", MemCtrl::Read as SignalUnsigned); + simulator.set_out_value("size", "out", 1); + + simulator.clock(); + + assert_eq!(simulator.cycle, 3); + assert_eq!(simulator.get_input_value(out), 0xf0.into()); + assert_eq!( + simulator.get_input_value(err), + (false as SignalUnsigned).into() + ); + + println!(""); + simulator.set_out_value("size", "out", 1); + simulator.set_out_value("sign", "out", true); + + simulator.clock(); + assert_eq!(simulator.cycle, 4); + assert_eq!(simulator.get_input_value(out), 0xffff_fff0.into()); + assert_eq!( + simulator.get_input_value(err), + (false as SignalUnsigned).into() + ); + + println!(""); + simulator.set_out_value("size", "out", 2); + simulator.set_out_value("sign", "out", true as SignalUnsigned); + + simulator.clock(); + assert_eq!(simulator.cycle, 5); + assert_eq!(simulator.get_input_value(out), 0xffff_f000.into()); + assert_eq!( + simulator.get_input_value(err), + (false as SignalUnsigned).into() + ); + + println!(""); + simulator.set_out_value("size", "out", 4); + simulator.set_out_value("sign", "out", true); + + simulator.clock(); + assert_eq!(simulator.cycle, 6); + assert_eq!(simulator.get_input_value(out), 0xf000_0000.into()); + assert_eq!(simulator.get_input_value(err), false.into()); + + println!(""); + simulator.set_out_value("addr", "out", 5); + + simulator.clock(); + assert_eq!(simulator.cycle, 7); + assert_eq!(simulator.get_input_value(err), true.into()); + + println!(""); + simulator.set_out_value("addr", "out", 6); + + simulator.clock(); + assert_eq!(simulator.cycle, 8); + assert_eq!(simulator.get_input_value(err), true.into()); + + println!(""); + simulator.set_out_value("addr", "out", 7); + + simulator.clock(); + assert_eq!(simulator.cycle, 9); + assert_eq!(simulator.get_input_value(err), true.into()); + + println!(""); + simulator.set_out_value("addr", "out", 8); + + simulator.clock(); + assert_eq!(simulator.cycle, 10); + assert_eq!(simulator.get_input_value(err), false.into()); + + println!(""); + simulator.set_out_value("addr", "out", 9); + simulator.set_out_value("size", "out", 2); + simulator.clock(); + assert_eq!(simulator.cycle, 11); + assert_eq!(simulator.get_input_value(err), true.into()); + + println!(""); + simulator.set_out_value("addr", "out", 10); + + simulator.clock(); + assert_eq!(simulator.cycle, 12); + assert_eq!(simulator.get_input_value(err), false.into()); + + println!(""); + simulator.set_out_value("addr", "out", 10); + simulator.set_out_value("data", "out", 0x1234); + simulator.set_out_value("ctrl", "out", MemCtrl::Write as SignalUnsigned); + simulator.clock(); + assert_eq!(simulator.cycle, 13); + assert_eq!(simulator.get_input_value(err), false.into()); + + println!(""); + simulator.set_out_value("ctrl", "out", MemCtrl::Read as SignalUnsigned); + simulator.set_out_value("size", "out", 1); + simulator.clock(); + assert_eq!(simulator.cycle, 14); + assert_eq!(simulator.get_input_value(out), 0x12.into()); + + println!(""); + simulator.set_out_value("addr", "out", 11); + simulator.clock(); + assert_eq!(simulator.cycle, 15); + assert_eq!(simulator.get_input_value(out), 0x34.into()); + + println!("test done") + } + + #[test] + fn test_mem_le() { + let cs = ComponentStore { + store: vec![ + Rc::new(ProbeOut::new("data")), + Rc::new(ProbeOut::new("addr")), + Rc::new(ProbeOut::new("ctrl")), + Rc::new(ProbeOut::new("size")), + Rc::new(ProbeOut::new("sign")), + Rc::new(ProbeOut::new("interrupt")), + Rc::new(RVMem { + id: "mem".into(), + pos: (0.0, 0.0), + width: 0.0, + height: 0.0, + + // configuration + big_endian: false, // i.e., little endian + + // ports + data: Input::new("data", "out"), + addr: Input::new("addr", "out"), + ctrl: Input::new("ctrl", "out"), + size: Input::new("size", "out"), + sext: Input::new("sign", "out"), + interrupt: Input::new("interrupt", "out"), + + // memory + memory: Memory(Rc::new(RefCell::new(BTreeMap::new()))), + // later history... tbd + range: Range { + start: 0u32, + end: 1u32, + }, + history: RefCell::new(vec![]), + init_state: BTreeMap::new(), + }), + ], + }; + + let mut simulator = Simulator::new(cs).unwrap(); + + assert_eq!(simulator.cycle, 1); + + // outputs + let out = &Input::new("mem", "data_o"); + let err = &Input::new("mem", "err"); + + // reset + assert_eq!(simulator.get_input_value(out), 0.into()); + assert_eq!(simulator.get_input_value(err), false.into()); + + // println!(""); + + simulator.set_out_value("data", "out", 0xf0); + simulator.set_out_value("addr", "out", 4); + simulator.set_out_value("ctrl", "out", MemCtrl::Write as SignalUnsigned); + simulator.set_out_value("size", "out", 1); // byte + + 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), 0.into()); + assert_eq!(simulator.get_input_value(err), false.into()); + + println!(""); + + simulator.set_out_value("ctrl", "out", MemCtrl::Read as SignalUnsigned); + simulator.set_out_value("size", "out", 1); + + simulator.clock(); + + assert_eq!(simulator.cycle, 3); + assert_eq!(simulator.get_input_value(out), 0xf0.into()); + assert_eq!(simulator.get_input_value(err), false.into()); + + println!(""); + simulator.set_out_value("size", "out", 1); + simulator.set_out_value("sign", "out", true); + + simulator.clock(); + assert_eq!(simulator.cycle, 4); + assert_eq!(simulator.get_input_value(out), 0xffff_fff0.into()); + assert_eq!(simulator.get_input_value(err), false.into()); + + println!(""); + simulator.set_out_value("size", "out", 2); + simulator.set_out_value("sign", "out", true); + + simulator.clock(); + assert_eq!(simulator.cycle, 5); + assert_eq!(simulator.get_input_value(out), 0x0000_00f0.into()); + assert_eq!(simulator.get_input_value(err), false.into()); + + println!(""); + simulator.set_out_value("size", "out", 4); + simulator.set_out_value("sign", "out", true); + simulator.clock(); + assert_eq!(simulator.cycle, 6); + assert_eq!(simulator.get_input_value(out), 0x0000_00f0.into()); + assert_eq!(simulator.get_input_value(err), false.into()); + + println!(""); + simulator.set_out_value("addr", "out", 10); // b + simulator.set_out_value("data", "out", 0x1234); + simulator.set_out_value("ctrl", "out", MemCtrl::Write as SignalUnsigned); + simulator.set_out_value("size", "out", 2); + + simulator.clock(); + assert_eq!(simulator.cycle, 7); + assert_eq!(simulator.get_input_value(err), false.into()); + + println!(""); + simulator.set_out_value("ctrl", "out", MemCtrl::Read as SignalUnsigned); + simulator.set_out_value("size", "out", 1); + simulator.clock(); + assert_eq!(simulator.cycle, 8); + assert_eq!(simulator.get_input_value(out), 0x34.into()); + + println!(""); + simulator.set_out_value("addr", "out", 11); + simulator.clock(); + assert_eq!(simulator.cycle, 9); + assert_eq!(simulator.get_input_value(out), 0x12.into()); + } +} diff --git a/riscv/src/components/mod.rs b/riscv/src/components/mod.rs index 0fd8ed27..5c4eb715 100644 --- a/riscv/src/components/mod.rs +++ b/riscv/src/components/mod.rs @@ -1,17 +1,29 @@ mod alu; mod branch_logic; +mod clic; mod csr; mod decoder; +mod gpio; mod instr_mem; +mod led; mod lsb_zero; +mod mem; +mod probe_label; mod reg_file; mod sign_zero_ext; +mod wb_ctl; pub use alu::*; pub use branch_logic::*; +pub use clic::*; pub use csr::*; pub use decoder::*; +pub use gpio::*; pub use instr_mem::*; +pub use led::*; pub use lsb_zero::*; +pub use mem::*; +pub use probe_label::*; pub use reg_file::*; pub use sign_zero_ext::*; +pub use wb_ctl::*; diff --git a/riscv/src/components/probe_label.rs b/riscv/src/components/probe_label.rs new file mode 100644 index 00000000..abe2bfb6 --- /dev/null +++ b/riscv/src/components/probe_label.rs @@ -0,0 +1,73 @@ +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, Id, Input, InputPort, OutputType, Ports}; + +pub const PROBE_IN_ID: &str = "in"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct ProbeLabel { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) input: Input, +} + +#[typetag::serde] +impl Component for ProbeLabel { + fn to_(&self) { + trace!("ProbeLabel"); + } + + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(ProbeLabel { + id: id.to_string(), + pos: (pos.0, pos.1), + input: dummy_input.clone(), + })) + } + + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + // Probes take one input + vec![&InputPort { + port_id: PROBE_IN_ID.to_string(), + input: self.input.clone(), + }], + OutputType::Combinatorial, + // No output value + vec![], + ), + ) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id.as_str() == PROBE_IN_ID { + self.input = new_input + } + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl ProbeLabel { + pub fn new(id: &str, pos: (f32, f32), input: Input) -> Self { + ProbeLabel { + id: id.to_string(), + pos, + input, + } + } + + pub fn rc_new(id: &str, pos: (f32, f32), input: Input) -> Rc { + Rc::new(ProbeLabel::new(id, pos, input)) + } +} diff --git a/riscv/src/components/reg_file.rs b/riscv/src/components/reg_file.rs index 413daae2..233f1ad3 100644 --- a/riscv/src/components/reg_file.rs +++ b/riscv/src/components/reg_file.rs @@ -3,12 +3,15 @@ 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, OutputType, Ports, SignalUnsigned, Simulator}; +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalUnsigned, Simulator, +}; use syncrim::signal::SignalValue; - #[allow(non_camel_case_types)] #[rustfmt::skip] -#[derive(Copy, Clone, Debug, TryFromPrimitive)] +#[derive(Copy, Clone, Debug, TryFromPrimitive, PartialEq)] #[repr(u8)] pub enum Reg { zero = 0, // Hard-wired zero @@ -37,15 +40,80 @@ pub enum Reg { s7 = 23, // Saved registers s8 = 24, // Saved registers s9 = 25, // Saved registers - s10 = 26, // Saved registers - s11 = 27, // Saved registers + s10 = 26, // Saved registers + s11 = 27, // Saved registers t3 = 28, // Temporaries t4 = 29, // Temporaries t5 = 30, // Temporaries t6 = 31, // Temporaries } -#[derive(Serialize, Deserialize)] +#[rustfmt::skip] +impl std::convert::TryFrom for Reg { + type Error = (); + + fn try_from(value: u32) -> Result { + Ok(match value { + 0 => Reg::zero, // Hard-wired zero + 1 => Reg::ra, // Return address + 2 => Reg::sp, // Stack pointer + 3 => Reg::gp, // Global pointer + 4 => Reg::tp, // Thread pointer + 5 => Reg::t0, // Temporaries + 6 => Reg::t1, // Temporaries + 7 => Reg::t2, // Temporaries + 8 => Reg::s0, // Saved register/frame pointer + 9 => Reg::s1, // Saved register + 10 =>Reg::a0, // Function arguments/return values + 11 =>Reg::a1, // Function arguments/return values + 12 =>Reg::a2, // Function arguments + 13 =>Reg::a3, // Function arguments + 14 =>Reg::a4, // Function arguments + 15 =>Reg::a5, // Function arguments + 16 =>Reg::a6, // Function arguments + 17 =>Reg::a7, // Function arguments + 18 =>Reg::s2, // Saved registers + 19 =>Reg::s3, // Saved registers + 20 =>Reg::s4, // Saved registers + 21 =>Reg::s5, // Saved registers + 22 =>Reg::s6, // Saved registers + 23 =>Reg::s7, // Saved registers + 24 =>Reg::s8, // Saved registers + 25 =>Reg::s9, // Saved registers + 26 =>Reg::s10, // Saved registers + 27 =>Reg::s11, // Saved registers + 28 =>Reg::t3, // Temporaries + 29 =>Reg::t4, // Temporaries + 30 =>Reg::t5, // Temporaries + 31 =>Reg::t6, // Temporaries + _ => Err(())?, + }) + } +} + +//const REG_ZERO: u32 = Reg::zero as u32; +const REG_RA: u32 = Reg::ra as u32; + +pub const REG_FILE_MAX_DEPTH: usize = 8; + +pub const REG_FILE_STACK_DEPTH_ID: &str = "stack_depth"; +//pub const REG_FILE_CLIC_MEPC_ID: &str = "clic_mepc"; +pub const REG_FILE_CLIC_RA_WE_ID: &str = "clic_ra_we"; + +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"; +pub const REG_FILE_RA_OUT: &str = "ra"; + +pub const REG_FILE_WIDTH: f32 = 250.0; +pub const REG_FILE_HEIGHT: f32 = 500.0; + +#[derive(Serialize, Deserialize, Clone)] pub struct RegFile { pub id: String, pub pos: (f32, f32), @@ -53,6 +121,10 @@ pub struct RegFile { pub height: f32, // ports + pub stack_depth: Input, + //pub clic_mepc: Input, + pub clic_ra_we: Input, + pub read_addr1: Input, pub read_addr2: Input, pub write_data: Input, @@ -60,16 +132,25 @@ pub struct RegFile { pub write_enable: Input, // data + #[serde(skip)] pub registers: RegStore, pub history: RegHistory, + // this is purely for the graphical view + // should be removed eventually with the gui + // implementing tabs or something over the different + // register sets + #[serde(skip)] + pub stack_depth_state: RefCell, } #[derive(Serialize, Deserialize, Clone)] pub struct RegOp { + stack_depth: u8, read_addr1: u8, read_addr2: u8, write_addr2: Option<(u8, u32)>, old_data: Option, + old_ra: Option, } // TODO: Perhaps we want registers to be of Signal type (containing potentially Signal::Unknown) @@ -88,11 +169,13 @@ impl Default for RegHistory { } } +type RegStack = [[u32; 32]; REG_FILE_MAX_DEPTH]; + #[derive(Serialize, Deserialize, Clone)] -pub struct RegStore(pub Rc>); +pub struct RegStore(pub Rc>); impl RegStore { - pub fn new(regs: Rc>) -> Self { + pub fn new(regs: Rc>) -> Self { RegStore(regs) } @@ -111,12 +194,12 @@ impl RegStore { impl Default for RegStore { fn default() -> Self { - Self::new(Rc::new(RefCell::new([0; 32]))) + Self::new(Rc::new(RefCell::new([[0; 32]; REG_FILE_MAX_DEPTH]))) } } impl Deref for RegStore { - type Target = RefCell<[u32; 32]>; + type Target = RefCell<[[u32; 32]; REG_FILE_MAX_DEPTH]>; fn deref(&self) -> &Self::Target { &self.0 @@ -124,61 +207,278 @@ impl Deref for RegStore { } impl RegFile { - fn read_reg(&self, simulator: &Simulator, input: &Input) -> SignalValue { - match simulator.get_input_value(input) { + pub fn read_reg(&self, simulator: &Simulator, input: impl Into) -> SignalValue { + let input = input.into(); + let stack_depth: SignalUnsigned = simulator + .get_input_value(&self.stack_depth) + .try_into() + .unwrap(); + let stack_depth = if stack_depth as i32 >= 0 { + stack_depth + } else { + 0 + }; + match input { SignalValue::Data(read_addr) => { - if read_addr > 0 { - trace!("read_addr {}", read_addr); - SignalValue::from(self.registers.borrow()[read_addr as usize]) - } else { - trace!("read_addr {}", read_addr); - SignalValue::from(0) + trace!("read_addr {}", read_addr); + let read_reg: Reg = read_addr.try_into().unwrap(); + let read_addr = read_reg as usize; + match read_reg { + Reg::zero => { + // reg zero always reads 0 + SignalValue::from(0) + } + Reg::sp => { + // reg sp shared among all stacks, we use stack_depth 0 for that + SignalValue::from(self.registers.borrow()[0][read_addr]) + } + _ => { + // all other registers + SignalValue::from(self.registers.borrow()[stack_depth as usize][read_addr]) + } } } _ => SignalValue::Unknown, } } + + fn write_reg(&self, simulator: &Simulator, input: impl Into, data: SignalValue) { + let input: SignalValue = input.into(); + let stack_depth: SignalUnsigned = simulator + .get_input_value(&self.stack_depth) + .try_into() + .unwrap(); + let stack_depth = if stack_depth as i32 >= 0 { + stack_depth + } else { + 0 + }; + + match input { + SignalValue::Data(write_addr) => { + trace!("write_addr {}", write_addr); + let write_reg: Reg = write_addr.try_into().unwrap(); + match write_reg { + Reg::zero => { + // reg zero always reads 0 + // do nothing on write + } + Reg::sp => { + // reg sp shared among all stacks, we use stack_depth 0 for that + self.registers.borrow_mut()[0][write_addr as usize] = + data.try_into().unwrap(); + } + _ => { + // all other registers + self.registers.borrow_mut()[stack_depth as usize][write_addr as usize] = + data.try_into().unwrap(); + } + } + } + _ => { + panic!("Unknown write address") + } + } + } + + pub fn dummy() -> RegFile { + let dummy = Input::new("id", "field"); + RegFile { + id: "dummy_reg_file".into(), + pos: (0.0, 0.0), + width: REG_FILE_WIDTH, + height: REG_FILE_HEIGHT, + stack_depth: dummy.clone(), + //clic_mepc: dummy.clone(), + clic_ra_we: dummy.clone(), + read_addr1: dummy.clone(), + read_addr2: dummy.clone(), + write_data: dummy.clone(), + write_addr: dummy.clone(), + write_enable: dummy.clone(), + registers: RegStore::new(Rc::new(RefCell::new([[0; 32]; REG_FILE_MAX_DEPTH]))), + history: RegHistory::new(), + stack_depth_state: 0.into(), + } + } } #[typetag::serde()] impl Component for RegFile { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn to_(&self) { trace!("RegFile"); } + fn reset(&self) { + self.registers + .borrow_mut() + .swap_with_slice(&mut [[0; 32]; REG_FILE_MAX_DEPTH]); + self.history.0.swap(&RefCell::new(vec![])); + } + fn get_id_ports(&self) -> (String, Ports) { ( self.id.clone(), Ports { - inputs: vec![self.read_addr1.clone(), self.read_addr2.clone()], + inputs: vec![ + InputPort { + port_id: REG_FILE_STACK_DEPTH_ID.to_string(), + input: self.stack_depth.clone(), + }, + // InputPort { + // port_id: REG_FILE_CLIC_MEPC_ID.to_string(), + // input: self.stack_depth.clone(), + // + //}, + InputPort { + port_id: REG_FILE_CLIC_RA_WE_ID.to_string(), + input: self.stack_depth.clone(), + }, + 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()], + outputs: vec![ + REG_FILE_REG_A_OUT.into(), + REG_FILE_REG_B_OUT.into(), + REG_FILE_RA_OUT.into(), + ], }, ) } - + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(RegFile { + width: REG_FILE_WIDTH, + height: REG_FILE_HEIGHT, + id: id.to_string(), + pos: (pos.0, pos.1), + registers: RegStore::new(Rc::new(RefCell::new([[0; 32]; REG_FILE_MAX_DEPTH]))), + history: RegHistory::new(), + stack_depth: dummy_input.clone(), + //clic_mepc: dummy_input.clone(), + clic_ra_we: dummy_input.clone(), + read_addr1: dummy_input.clone(), + read_addr2: dummy_input.clone(), + write_data: dummy_input.clone(), + write_addr: dummy_input.clone(), + write_enable: dummy_input.clone(), + stack_depth_state: 0.into(), + })) + } + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + REG_FILE_STACK_DEPTH_ID => self.stack_depth = new_input, + REG_FILE_READ_ADDR1_ID => self.read_addr1 = new_input, + REG_FILE_READ_ADDR2_ID => self.read_addr2 = new_input, + REG_FILE_WRITE_DATA_ID => self.write_data = new_input, + REG_FILE_WRITE_ADDR_ID => self.write_addr = new_input, + REG_FILE_WRITE_ENABLE_ID => self.write_enable = new_input, + _ => (), + } + } fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + let mut regop = RegOp { + stack_depth: 0, + read_addr1: 0, + read_addr2: 0, + write_addr2: None, + old_data: None, + old_ra: None, + }; + let stack_depth: SignalUnsigned = simulator + .get_input_value(&self.stack_depth) + .try_into() + .unwrap(); + + // special handling if clic_ra_we + // THIS IS 2 WHEN + let clic_ra_we = + simulator.get_input_value(&self.clic_ra_we) == (true as SignalUnsigned).into(); + + if clic_ra_we && (stack_depth as i32 >= 0) { + // println!( + // "update ra register {:?}", + // simulator.get_input_value(&self.clic_mepc) + // ); + let old_ra = self.read_reg(simulator, REG_RA); + regop.old_ra = Some(old_ra.try_into().unwrap()); + // self.write_reg( + // simulator, + // REG_RA, + // simulator.get_input_value(&self.clic_mepc), + // ); + // write magic number to RA + self.write_reg(simulator, REG_RA, SignalValue::Data(0xFFFF_FFFF)); + } + + let stack_depth = stack_depth as usize; + *self.stack_depth_state.borrow_mut() = stack_depth as u32; + let read_addr1 = simulator.get_input_value(&self.read_addr1); + let read_addr2 = simulator.get_input_value(&self.read_addr2); + //*depth_state = stack_depth; + if simulator.get_input_value(&self.write_enable) == (true as SignalUnsigned).into() { let data = simulator.get_input_value(&self.write_data); trace!("write 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(); + let write_addr = simulator.get_input_value(&self.write_addr); + + regop.write_addr2 = Some(( + TryInto::::try_into(write_addr).unwrap() as u8, + self.read_reg(simulator, write_addr).try_into().unwrap(), // read old value + )); + + self.write_reg(&simulator, write_addr, data); } + self.history.0.borrow_mut().push(regop); // read after write - let reg_value_a = self.read_reg(simulator, &self.read_addr1); + let reg_value_a = self.read_reg(simulator, read_addr1); trace!("reg_value_a {:?}", reg_value_a); - simulator.set_out_value(&self.id, "reg_a", 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); + let reg_value_b = self.read_reg(simulator, read_addr2); trace!("reg_value_b {:?}", reg_value_b); - simulator.set_out_value(&self.id, "reg_b", reg_value_b); + simulator.set_out_value(&self.id, REG_FILE_REG_B_OUT, reg_value_b); + + let reg_value_ra = self.read_reg(simulator, REG_RA); + trace!("reg_value ra {:?}", reg_value_ra); + simulator.set_out_value(&self.id, REG_FILE_RA_OUT, reg_value_ra); + Ok(()) } + + fn un_clock(&self) { + //println!("unclock"); + let regop = self.history.0.borrow_mut().pop().unwrap(); + let mut regstore = self.registers.borrow_mut(); + if let Some(w) = regop.write_addr2 { + regstore[regop.stack_depth as usize][w.0 as usize] = w.1 + } + } } #[cfg(test)] @@ -195,6 +495,9 @@ mod test { fn test_reg_file() { let cs = ComponentStore { store: vec![ + Rc::new(ProbeOut::new("stack_depth")), + //Rc::new(ProbeOut::new("clic_mepc")), + Rc::new(ProbeOut::new("clic_ra_we")), Rc::new(ProbeOut::new("read_reg_1")), Rc::new(ProbeOut::new("read_reg_2")), Rc::new(ProbeOut::new("write_data")), @@ -208,6 +511,10 @@ mod test { height: 150.0, // ports + stack_depth: Input::new("stack_depth", "out"), + //clic_mepc: Input::new("clic_mepc", "out"), + clic_ra_we: Input::new("clic_ra_we", "out"), + read_addr1: Input::new("read_reg_1", "out"), read_addr2: Input::new("read_reg_2", "out"), write_data: Input::new("write_data", "out"), @@ -217,11 +524,13 @@ mod test { // data registers: RegStore::default(), history: RegHistory::new(), + + stack_depth_state: 0.into(), }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); @@ -234,12 +543,15 @@ mod test { assert_eq!(simulator.get_input_value(out_reg_2), 0.into()); println!(""); + simulator.set_out_value("stack_depth", "out", 0); 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); + // simulator.set_out_value("clic_write", "out", 1); // fatal test + // test write and read to reg # 1 in same cycle println!("sim_state {:?}", simulator.sim_state); println!(""); @@ -262,5 +574,40 @@ mod test { 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()); + + println!(""); + simulator.set_out_value("stack_depth", "out", 1); + simulator.set_out_value("read_reg_1", "out", 31); + simulator.set_out_value("read_reg_2", "out", 1); + simulator.set_out_value("write_data", "out", 1234); + simulator.set_out_value("write_addr", "out", 31); + simulator.set_out_value("write_enable", "out", true as SignalUnsigned); + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 4); + assert_eq!(simulator.get_input_value(out_reg_1), 1234.into()); + assert_eq!(simulator.get_input_value(out_reg_2), 0.into()); + + println!(""); + simulator.un_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()); + + println!(""); + simulator.un_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()); + + println!(""); + simulator.un_clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 1); + assert_eq!(simulator.get_input_value(out_reg_1), 0.into()); + assert_eq!(simulator.get_input_value(out_reg_2), 0.into()) } } diff --git a/riscv/src/components/sign_zero_ext.rs b/riscv/src/components/sign_zero_ext.rs index 35f5592a..921d35a0 100644 --- a/riscv/src/components/sign_zero_ext.rs +++ b/riscv/src/components/sign_zero_ext.rs @@ -1,9 +1,24 @@ use log::trace; use serde::{Deserialize, Serialize}; -use syncrim::common::{Component, Condition, Input, OutputType, Ports, SignalValue, Simulator}; +#[cfg(feature = "gui-egui")] +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 SIGN_ZERO_EXT_DATA_I_ID: &str = "data_i"; +pub const SIGN_ZERO_EXT_SEL_I_ID: &str = "sel_i"; + +pub const SIGN_ZERO_EXT_OUT_ID: &str = "out"; + +pub const SIGN_ZERO_EXT_HEIGHT: f32 = 30.0; +pub const SIGN_ZERO_EXT_WIDTH: f32 = 60.0; #[derive(Serialize, Deserialize)] pub struct SZExt { + pub height: f32, + pub width: f32, pub id: String, pub pos: (f32, f32), @@ -13,17 +28,49 @@ pub struct SZExt { #[typetag::serde()] impl Component for SZExt { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn to_(&self) { println!("s_z_ext"); } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy = Input::new("dummy", "out"); + Box::new(Rc::new(SZExt { + height: SIGN_ZERO_EXT_HEIGHT, + width: SIGN_ZERO_EXT_WIDTH, + id: id.to_string(), + pos: (pos.0, pos.1), + data_i: dummy.clone(), + sel_i: dummy.clone(), + })) + } + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + SIGN_ZERO_EXT_DATA_I_ID => self.data_i = new_input, + SIGN_ZERO_EXT_SEL_I_ID => self.sel_i = new_input, + _ => (), + } + } fn get_id_ports(&self) -> (String, Ports) { ( self.id.clone(), - Ports { - inputs: vec![self.data_i.clone(), self.sel_i.clone()], - out_type: OutputType::Combinatorial, - outputs: vec!["out".into()], - }, + Ports::new( + vec![ + &InputPort { + port_id: SIGN_ZERO_EXT_DATA_I_ID.to_string(), + input: self.data_i.clone(), + }, + &InputPort { + port_id: SIGN_ZERO_EXT_SEL_I_ID.to_string(), + input: self.sel_i.clone(), + }, + ], + OutputType::Combinatorial, + vec![SIGN_ZERO_EXT_OUT_ID], + ), ) } #[allow(non_snake_case)] @@ -33,7 +80,10 @@ impl Component for SZExt { match simulator.get_input_value(&self.data_i) { //if there is data, sel should be defined, otherwise panic is good. SignalValue::Data(mut data) => { - let sel: u32 = simulator.get_input_value(&self.sel_i).try_into().unwrap(); + let sel: u32 = simulator + .get_input_value(&self.sel_i) + .try_into() + .unwrap_or(0); //println!("SZEDATA:{:x}", data); match sel { 0 => { @@ -75,6 +125,8 @@ mod test { Rc::new(ProbeOut::new("input")), Rc::new(ProbeOut::new("sel")), Rc::new(SZExt { + height: 0.0, + width: 0.0, id: "szext".to_string(), pos: (0.0, 0.0), data_i: Input::new("input", "out"), @@ -83,7 +135,7 @@ mod test { ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); let szext = &Input::new("szext", "out"); let val = 0b100000000000; diff --git a/riscv/src/components/wb_ctl.rs b/riscv/src/components/wb_ctl.rs new file mode 100644 index 00000000..c252171c --- /dev/null +++ b/riscv/src/components/wb_ctl.rs @@ -0,0 +1,130 @@ +use serde::{Deserialize, Serialize}; +#[cfg(feature = "gui-egui")] +use std::rc::Rc; + +#[cfg(feature = "gui-egui")] +use syncrim::common::EguiComponent; +use syncrim::common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Simulator}; +pub const WB_CTL_INTR_IN_ID: &str = "clic_i"; +pub const WB_CTL_DEC_IN_ID: &str = "dec_i"; + +pub const WB_CTL_WE_OUT_ID: &str = "we_o"; +pub const WB_CTL_MUX_CTL_O_ID: &str = "mux_sel"; + +pub const WB_CTL_HEIGHT: f32 = 20.0; +pub const WB_CTL_WIDTH: f32 = 20.0; + +#[derive(Serialize, Deserialize)] +pub struct WBCtl { + pub height: f32, + pub width: f32, + pub id: String, + pub pos: (f32, f32), + + pub clic_i: Input, + pub dec_i: Input, +} + +#[typetag::serde()] +impl Component for WBCtl { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn to_(&self) { + println!("WBCtl"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy = Input::new("dummy", "out"); + Box::new(Rc::new(WBCtl { + height: WB_CTL_HEIGHT, + width: WB_CTL_WIDTH, + id: id.to_string(), + pos: (pos.0, pos.1), + clic_i: dummy.clone(), + dec_i: dummy.clone(), + })) + } + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id.as_str() == WB_CTL_DEC_IN_ID { + self.dec_i = new_input; + } else if target_port_id.as_str() == WB_CTL_INTR_IN_ID { + self.clic_i = new_input; + } + } + fn get_id_ports(&self) -> (String, Ports) { + ( + self.id.clone(), + Ports::new( + vec![ + &InputPort { + port_id: WB_CTL_INTR_IN_ID.to_string(), + input: self.clic_i.clone(), + }, + &InputPort { + port_id: WB_CTL_DEC_IN_ID.to_string(), + input: self.dec_i.clone(), + }, + ], + OutputType::Combinatorial, + vec![WB_CTL_WE_OUT_ID, WB_CTL_MUX_CTL_O_ID], + ), + ) + } + #[allow(non_snake_case)] + fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { + let dec_we: u32 = simulator + .get_input_value(&self.dec_i) + .try_into() + .unwrap_or(0); + let clic_we: u32 = simulator + .get_input_value(&self.clic_i) + .try_into() + .unwrap_or(0); + //assert_ne!(dec_we, clic_we); + let mux_ctl = if dec_we != 0 { 0 } else { 1 }; + let we = if dec_we == 1 || clic_we == 1 { 1 } else { 0 }; + simulator.set_out_value(&self.id, WB_CTL_MUX_CTL_O_ID, mux_ctl); + simulator.set_out_value(&self.id, WB_CTL_WE_OUT_ID, we); + Ok(()) + } +} + +mod test { + #![allow(unused_imports)] + use super::*; + + use crate::components::LSBZero; + use std::rc::Rc; + use syncrim::{ + common::{ComponentStore, Input, Simulator}, + components::ProbeOut, + }; + #[test] + fn lsb_zero_test() { + let cs = ComponentStore { + store: vec![ + Rc::new(ProbeOut::new("input")), + Rc::new(LSBZero { + height: 0.0, + width: 0.0, + id: "lzero".to_string(), + pos: (0.0, 0.0), + data_i: Input::new("input", "out"), + }), + ], + }; + + let mut simulator = Simulator::new(cs).unwrap(); + assert_eq!(simulator.cycle, 1); + + // outputs + let lout = &Input::new("lzero", "out"); + for i in 0..100 { + simulator.set_out_value("input", "out", i); + simulator.clock(); + assert_eq!(simulator.get_input_value(lout), (i & (!0b1)).into()); + } + } +} diff --git a/riscv/src/gui_egui/components/alu.rs b/riscv/src/gui_egui/components/alu.rs new file mode 100644 index 00000000..36108498 --- /dev/null +++ b/riscv/src/gui_egui/components/alu.rs @@ -0,0 +1,173 @@ +use crate::components::ALU; +use egui::{Color32, Pos2, Rect, Response, Shape, Stroke, Ui, Vec2}; +use syncrim::common::{EguiComponent, 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; + +#[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::RED, + }, + )); + let rect = Rect { + min: oh((-20f32, -40f32), s, o), + max: oh((20f32, 40f32), s, o), + }; + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("ALU"); + }); + 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.operator_i, + crate::components::ALU_OPERATOR_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.operand_a_i, + crate::components::ALU_OPERAND_A_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.operand_b_i, + crate::components::ALU_OPERAND_B_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::ALU_OPERAND_A_I_ID.to_string(), + Pos2::new(-20f32, -20f32) + own_pos, + ), + ( + crate::components::ALU_OPERAND_B_I_ID.to_string(), + Pos2::new(-20f32, 20f32) + own_pos, + ), + ( + crate::components::ALU_RESULT_O_ID.to_string(), + Pos2::new(20f32, 0f32) + own_pos, + ), + ( + crate::components::ALU_OPERATOR_I_ID.to_string(), + Pos2::new(0f32, -40f32) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + 40f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/branch_logic.rs b/riscv/src/gui_egui/components/branch_logic.rs new file mode 100644 index 00000000..091253fe --- /dev/null +++ b/riscv/src/gui_egui/components/branch_logic.rs @@ -0,0 +1,187 @@ +use crate::components::BranchLogic; +use egui::FontId; +use egui::{Color32, Pos2, Rect, Response, Rounding, Shape, Stroke, Ui, Vec2}; +use syncrim::common::{EguiComponent, 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; +#[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> { + // 21x41 + // middle: 11x 21y (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 + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + ui.painter().text( + o.to_pos2(), + egui::Align2::CENTER_CENTER, + "BLU", + FontId::monospace(14.0), + Color32::BLACK, + ); + + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("BranchLogicUnit"); + }); + 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 = BranchLogic::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.rs1, + crate::components::BRANCH_LOGIC_RS1_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.rs2, + crate::components::BRANCH_LOGIC_RS2_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.ctrl, + crate::components::BRANCH_LOGIC_CTRL_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.enable, + crate::components::BRANCH_LOGIC_ENABLE_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::BRANCH_LOGIC_RS1_ID.to_string(), + Pos2::new( + -self.width / 2f32, + -self.height / 2f32 + self.height / 10f32, + ) + own_pos, + ), + ( + crate::components::BRANCH_LOGIC_RS2_ID.to_string(), + Pos2::new(-self.width / 2f32, self.height / 2f32 - self.height / 10f32) + own_pos, + ), + ( + crate::components::BRANCH_LOGIC_CTRL_ID.to_string(), + Pos2::new(self.width / 4f32, -self.height / 2f32) + own_pos, + ), + ( + crate::components::BRANCH_LOGIC_ENABLE_ID.to_string(), + Pos2::new(-self.width / 4f32, -self.height / 2f32) + own_pos, + ), + ( + crate::components::BRANCH_LOGIC_OUT_ID.to_string(), + Pos2::new(0.0, self.height / 2f32) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 4f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/clic.rs b/riscv/src/gui_egui/components/clic.rs new file mode 100644 index 00000000..231605fd --- /dev/null +++ b/riscv/src/gui_egui/components/clic.rs @@ -0,0 +1,229 @@ +use crate::components::CLIC; +use egui::FontId; +use egui::{Color32, Pos2, Rect, Response, Rounding, Shape, Stroke, Ui, Vec2}; +use syncrim::common::{EguiComponent, 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; + +#[typetag::serde] +impl EguiComponent for CLIC { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 21x41 + // middle: 11x 21y (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 + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + ui.painter().text( + o.to_pos2(), + egui::Align2::CENTER_CENTER, + "N-CLIC", + FontId::monospace(14.0), + Color32::BLACK, + ); + + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("CLIC"); + }); + 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 = CLIC::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.csr_addr, + crate::components::CLIC_CSR_ADDR_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.csr_ctl, + crate::components::CLIC_CSR_CTL_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.csr_data, + crate::components::CLIC_CSR_DATA_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.data, + crate::components::CLIC_DATA_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.addr, + crate::components::CLIC_ADDR_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.data_we, + crate::components::CLIC_DATA_WE_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.mret, + crate::components::CLIC_MRET_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.data_size, + crate::components::CLIC_DATA_SIZE_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.pc, + crate::components::CLIC_PC_ID.to_string(), + id_ports, + self.id.clone(), + ); + + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::CLIC_CSR_DATA_OUT_ID.to_string(), + Pos2::new( + self.width / 10f32 * 4f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::CLIC_MMIO_DATA_OUT_ID.to_string(), + Pos2::new( + self.width / 10f32 * 3f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::CLIC_DATA_ID.to_string(), + Pos2::new( + self.width / 10f32 * 1f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::CLIC_CSR_DATA_ID.to_string(), + Pos2::new( + self.width / 10f32 * 2f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 4f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/decoder.rs b/riscv/src/gui_egui/components/decoder.rs new file mode 100644 index 00000000..1215a22b --- /dev/null +++ b/riscv/src/gui_egui/components/decoder.rs @@ -0,0 +1,189 @@ +use crate::components::Decoder; +use egui::{Color32, Pos2, Rect, Response, Rounding, Shape, Stroke, Ui, Vec2}; +use syncrim::common::{EguiComponent, 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; + +#[typetag::serde] +impl EguiComponent for Decoder { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 21x41 + // middle: 11x 21y (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 + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("Decoder"); + }); + 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 = Decoder::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.instruction, + crate::components::DECODER_INSTRUCTION_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::DECODER_SIGN_ZERO_EXT_SEL_ID.to_string(), + Pos2::new( + self.width / 2f32, + -self.height / 2f32 + 9f32 * self.height / 20f32, + ) + own_pos, + ), + ( + crate::components::DECODER_DATA_MEM_SIZE_ID.to_string(), + Pos2::new( + self.width / 2f32, + -self.height / 2f32 + 12f32 * self.height / 20f32, + ) + own_pos, + ), + ( + crate::components::DECODER_DATA_SE_ID.to_string(), + Pos2::new( + self.width / 2f32, + -self.height / 2f32 + 13f32 * self.height / 20f32, + ) + own_pos, + ), + ( + crate::components::DECODER_DATA_MEM_CTRL_ID.to_string(), + Pos2::new( + self.width / 2f32, + -self.height / 2f32 + 14f32 * self.height / 20f32, + ) + own_pos, + ), + ( + crate::components::DECODER_PC_IMM_SEL_ID.to_string(), + Pos2::new( + self.width / 2f32, + -self.height / 2f32 + 15f32 * self.height / 20f32, + ) + own_pos, + ), + ( + crate::components::DECODER_BRANCH_OP.to_string(), + Pos2::new( + self.width / 2f32, + -self.height / 2f32 + 18f32 * self.height / 20f32, + ) + own_pos, + ), + ( + crate::components::DECODER_BRANCH_INSTR.to_string(), + Pos2::new( + self.width / 2f32, + -self.height / 2f32 + 19f32 * self.height / 20f32, + ) + own_pos, + ), + ( + crate::components::DECODER_INSTRUCTION_ID.to_string(), + Pos2::new(0.0, self.height / 2f32) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 4f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/gpio.rs b/riscv/src/gui_egui/components/gpio.rs new file mode 100644 index 00000000..6bc6264d --- /dev/null +++ b/riscv/src/gui_egui/components/gpio.rs @@ -0,0 +1,288 @@ +use crate::components::GPIO; +use egui::{Color32, Pos2, Rect, Response, Rounding, Shape, Stroke, Ui, Vec2}; +use egui_extras::{Column, TableBuilder}; +use syncrim::common::{EguiComponent, 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; + +#[typetag::serde] +impl EguiComponent for GPIO { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 21x41 + // middle: 11x 21y (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 + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("GPIO"); + }); + match editor_mode { + EditorMode::Simulator => { + ui.allocate_ui_at_rect(rect, |ui| { + ui.set_clip_rect(rect); + ui.push_id(1337, |ui| { + TableBuilder::new(ui) + .column(Column::initial(30.0)) + .column(Column::initial(15.0)) + .column(Column::initial(15.0)) + .column(Column::initial(15.0)) + .column(Column::initial(15.0)) + .column(Column::initial(15.0)) + .column(Column::initial(15.0)) + .column(Column::initial(15.0)) + .column(Column::initial(15.0)) + .header(10.0, |mut header| { + header.col(|ui| { + ui.heading("Pin"); + }); + header.col(|ui| { + ui.heading("7"); + }); + header.col(|ui| { + ui.heading("6"); + }); + header.col(|ui| { + ui.heading("5"); + }); + header.col(|ui| { + ui.heading("4"); + }); + header.col(|ui| { + ui.heading("3"); + }); + + header.col(|ui| { + ui.heading("2"); + }); + + header.col(|ui| { + ui.heading("1"); + }); + + header.col(|ui| { + ui.heading("0"); + }); + }) + .body(|mut body| { + body.row(15.0, |mut row| { + row.col(|ui| { + ui.label(format!("State")); + }); + row.col(|ui| { + ui.label(format!( + "{}", + self.pins.0.borrow().get(7).unwrap().state as u32 + )); + }); + row.col(|ui| { + ui.label(format!( + "{}", + self.pins.0.borrow().get(6).unwrap().state as u32 + )); + }); + row.col(|ui| { + ui.label(format!( + "{}", + self.pins.0.borrow().get(5).unwrap().state as u32 + )); + }); + row.col(|ui| { + ui.label(format!( + "{}", + self.pins.0.borrow().get(4).unwrap().state as u32 + )); + }); + row.col(|ui| { + ui.label(format!( + "{}", + self.pins.0.borrow().get(3).unwrap().state as u32 + )); + }); + row.col(|ui| { + ui.label(format!( + "{}", + self.pins.0.borrow().get(2).unwrap().state as u32 + )); + }); + row.col(|ui| { + ui.label(format!( + "{}", + self.pins.0.borrow().get(1).unwrap().state as u32 + )); + }); + row.col(|ui| { + ui.label(format!( + "{}", + self.pins.0.borrow().get(0).unwrap().state as u32 + )); + }); + }) + }); + }); + }); + } + _ => 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 = GPIO::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.data_i, + crate::components::GPIO_DATA_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.addr_i, + crate::components::GPIO_ADDR_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.we_i, + crate::components::GPIO_WE_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.size_i, + crate::components::GPIO_SIZE_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.se_i, + crate::components::GPIO_SE_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::GPIO_DATA_I_ID.to_string(), + Pos2::new(-self.width / 5f32, -self.height / 2f32) + own_pos, + ), + ( + crate::components::GPIO_ADDR_I_ID.to_string(), + Pos2::new(self.width / 5f32, -self.height / 2f32) + own_pos, + ), + ( + crate::components::GPIO_SIZE_I_ID.to_string(), + Pos2::new(2f32 * (self.width / 5f32), -self.height / 2f32) + own_pos, + ), + ( + crate::components::GPIO_WE_I_ID.to_string(), + Pos2::new(2f32 * (-self.width / 5f32), -self.height / 2f32) + own_pos, + ), + ( + crate::components::GPIO_SE_I_ID.to_string(), + Pos2::new(0.0, -self.height / 2f32) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 4f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/instr_mem.rs b/riscv/src/gui_egui/components/instr_mem.rs index 65d74270..14989232 100644 --- a/riscv/src/gui_egui/components/instr_mem.rs +++ b/riscv/src/gui_egui/components/instr_mem.rs @@ -1,5 +1,290 @@ use crate::components::InstrMem; -use syncrim::common::EguiComponent; +use egui::FontId; +use egui::{ + Color32, Context, Label, Pos2, Rect, Response, RichText, Rounding, Sense, Shape, Stroke, Ui, + Vec2, Window, +}; +use egui_extras::{Column, TableBuilder}; +use log::trace; +use riscv_asm_strings; +use syncrim::common::{EguiComponent, 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; +impl InstrMem { + fn side_panel(&self, ctx: &Context, simulator: Option<&mut Simulator>) { + Window::new("Instruction Memory") + .resizable(true) + .show(ctx, |ui| { + TableBuilder::new(ui) + .striped(true) + .column(Column::initial(75.0).at_least(75.0)) + .column(Column::initial(10.0).resizable(false)) + .column(Column::initial(75.0).at_least(75.0)) + .column(Column::initial(75.0).at_least(50.0)) + .column(Column::initial(150.0).at_least(85.0).resizable(true)) + .column(Column::initial(5.0).at_least(5.0).resizable(false)) + .header(30.0, |mut header| { + header.col(|ui| { + ui.heading("Label"); + }); + header.col(|_ui| {}); + header.col(|ui| { + ui.heading("Address"); + }); + header.col(|ui| { + ui.heading("HEX"); + }); + header.col(|ui| { + ui.heading("Instruction"); + }); + }) + .body(|body| { + body.rows( + 15.0, + (self.range.end - self.range.start) / 4, + |index, mut row| { + let address = index * 4 + self.range.start; + let pc: u32 = { + if simulator.as_ref().is_some() { + simulator + .as_ref() + .unwrap() + .get_input_value(&self.pc) + .try_into() + .unwrap_or(0) + } else { + 0 + } + }; + let (bg_color, fg_color) = { + if pc as usize == address { + (Color32::DARK_GRAY, Color32::WHITE) + } else { + (Color32::TRANSPARENT, Color32::LIGHT_GRAY) + } + }; + let breakpoint_color = { + if self.breakpoints.borrow_mut().contains(&address) { + Color32::RED + } else { + Color32::TRANSPARENT + } + }; + row.col(|ui| match &self.symbols.get(&address) { + Some(s) => { + ui.add(Label::new(format!("{}:", s)).truncate(true)); + } + None => {} + }); + //breakpoint + row.col(|ui| { + ui.label(RichText::new("•").color(breakpoint_color)); + }); + //address + row.col(|ui| { + ui.add(Label::new(format!("0x{:08x}", address)).truncate(true)); + }); + let mut bytes = [0u8; 4]; + if !self.le { + bytes[3] = *self.bytes.get(&address).unwrap(); + bytes[2] = *self.bytes.get(&(address + 1)).unwrap(); + bytes[1] = *self.bytes.get(&(address + 2)).unwrap(); + bytes[0] = *self.bytes.get(&(address + 3)).unwrap(); + } else { + bytes[0] = *self.bytes.get(&address).unwrap(); + bytes[1] = *self.bytes.get(&(address + 1)).unwrap(); + bytes[2] = *self.bytes.get(&(address + 2)).unwrap(); + bytes[3] = *self.bytes.get(&(address + 3)).unwrap(); + } + let instr = ((bytes[3] as u32) << 24) + | ((bytes[2] as u32) << 16) + | ((bytes[1] as u32) << 8) + | (bytes[0] as u32); + + let instr_fmt = match asm_riscv::I::try_from(instr) { + Ok(i) => riscv_asm_strings::StringifyUpperHex::to_string(&i), + Err(_) => "Unknown instruction".to_string(), + }; + //hex instr + row.col(|ui| { + ui.add(Label::new(format!("0x{:08X}", instr)).truncate(true)); + }); + row.col(|ui| { + if ui + .add( + Label::new( + RichText::new(instr_fmt) + .color(fg_color) + .background_color(bg_color), + ) + .truncate(true) + .sense(Sense::click()), + ) + .clicked() + { + trace!("clicked"); + if !self.breakpoints.borrow_mut().remove(&address) { + self.breakpoints.borrow_mut().insert(address); + } + }; + }); + row.col(|_| {}); + }, + ); + }); + }); + } +} #[typetag::serde] -impl EguiComponent for InstrMem {} +impl EguiComponent for InstrMem { + fn render( + &self, + ui: &mut Ui, + _ctx: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 21x41 + // middle: 11x 21y (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; + //self.side_panel(ui.ctx(), simulator); + // The shape + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + ui.painter().text( + o.to_pos2(), + egui::Align2::CENTER_CENTER, + "InstrMem", + FontId::monospace(14.0), + Color32::BLACK, + ); + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("InstrMem"); + }); + match editor_mode { + EditorMode::Simulator => { + self.side_panel(ui.ctx(), 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 = InstrMem::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.pc, + crate::components::INSTR_MEM_PC_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::INSTR_MEM_PC_ID.to_string(), + Pos2::new( + self.width / 10f32 * 1f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::INSTR_MEM_INSTRUCTION_ID.to_string(), + Pos2::new( + -self.width / 10f32 * 2f32 + self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 4f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/led.rs b/riscv/src/gui_egui/components/led.rs new file mode 100644 index 00000000..1e20ba7e --- /dev/null +++ b/riscv/src/gui_egui/components/led.rs @@ -0,0 +1,156 @@ +use crate::components::LED; +use egui::epaint::RectShape; +use egui::{Color32, Pos2, Rect, Response, Rounding, Shape, Stroke, Ui, Vec2}; +use syncrim::common::{EguiComponent, 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::SignalValue; + +#[typetag::serde] +impl EguiComponent for LED { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 21x41 + // middle: 11x 21y (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; + let bg_color = match simulator { + Some(s) => { + let input = s.get_input_value(&self.input); + match input { + SignalValue::Data(d) => { + if d == 1 { + Color32::RED + } else { + Color32::GRAY + } + } + _ => Color32::GRAY, + } + } + None => Color32::GRAY, + }; + // The shape + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::Rect(RectShape::new( + rect, + Rounding::ZERO, + bg_color, + Stroke { + width: scale, + color: Color32::BLACK, + }, + ))); + + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("LED"); + }); + 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 = LED::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.input, + crate::components::LED_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![( + crate::components::LED_I_ID.to_string(), + Pos2::new(-self.width / 2f32, 0.0) + own_pos, + )] + } + + fn top_padding(&self) -> f32 { + self.height / 4f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/lsb_zero.rs b/riscv/src/gui_egui/components/lsb_zero.rs new file mode 100644 index 00000000..6f43e57a --- /dev/null +++ b/riscv/src/gui_egui/components/lsb_zero.rs @@ -0,0 +1,144 @@ +use crate::components::LSBZero; +use egui::{Color32, Pos2, Rect, Response, Rounding, Shape, Stroke, Ui, Vec2}; +use syncrim::common::{EguiComponent, 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; + +#[typetag::serde] +impl EguiComponent for LSBZero { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 21x41 + // middle: 11x 21y (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 + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("LSBZero"); + }); + 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 = LSBZero::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.data_i, + crate::components::LSB_ZERO_DATA_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::LSB_ZERO_DATA_I_ID.to_string(), + Pos2::new(-self.width / 2f32, 0.0) + own_pos, + ), + ( + crate::components::LSB_ZERO_OUT_ID.to_string(), + Pos2::new(self.width / 2f32, 0.0) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 4f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/mod.rs b/riscv/src/gui_egui/components/mod.rs index a063d2de..4ce4b68d 100644 --- a/riscv/src/gui_egui/components/mod.rs +++ b/riscv/src/gui_egui/components/mod.rs @@ -1,2 +1,13 @@ +pub mod alu; +pub mod branch_logic; +pub mod clic; +pub mod decoder; +pub mod gpio; pub mod instr_mem; +pub mod led; +pub mod lsb_zero; +pub mod probe_label; pub mod reg_file; +pub mod rv_mem; +pub mod sign_zero_ext; +pub mod wb_ctl; diff --git a/riscv/src/gui_egui/components/probe_label.rs b/riscv/src/gui_egui/components/probe_label.rs new file mode 100644 index 00000000..57ce4a63 --- /dev/null +++ b/riscv/src/gui_egui/components/probe_label.rs @@ -0,0 +1,168 @@ +use crate::components::ProbeLabel; +use egui::{Align2, Area, Color32, Order, Pos2, Rect, Response, RichText, Ui, Vec2}; +use syncrim::common::{EguiComponent, Ports, SignalValue, 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; + +#[typetag::serde] +impl EguiComponent for ProbeLabel { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + let offset_old = offset; + let mut offset = offset; + offset.x += self.pos.0 * scale; + offset.y += self.pos.1 * scale; + let input = self.input.clone(); + let value = match simulator { + Some(s) => s.get_input_value(&input), + None => SignalValue::Uninitialized, + }; + let area = Area::new(self.id.to_string()) + .order(Order::Middle) + .current_pos(offset.to_pos2()) + .movable(false) + .enabled(true) + .interactable(false) + .pivot(Align2::CENTER_CENTER) + .show(ui.ctx(), |ui| { + ui.set_clip_rect(clip_rect); + let text = if self.input.field != "out" { + if let SignalValue::Data(v) = value { + format!("{}:\n{:#010x}", self.input.field, v) + } else { + format!("{}: \n{:?}", self.input.field, value) + } + } else { + if let SignalValue::Data(v) = value { + format!("{} {}:\n{:#010x}", self.input.id, self.input.field, v) + } else { + format!("{} {}: \n{:?}", self.input.id, self.input.field, value) + } + }; + + match editor_mode { + EditorMode::Simulator => ui.label( + RichText::new(text.clone()) + .size(scale * 12f32) + .background_color(Color32::LIGHT_BLUE), + ), + _ => ui.label(RichText::new(text.clone()).size(scale * 12f32).underline()), + } + .on_hover_text(text); + }); + + let r = rect_with_hover( + area.response.rect, + clip_rect, + editor_mode, + ui, + self.id.clone(), + |ui| { + ui.label(format!("Id: {}", self.id.clone())); + if let SignalValue::Data(v) = value { + ui.label(format!("{:#010x}", v)); + } else { + ui.label(format!("{:?}", value)); + } + }, + ); + 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 = ProbeLabel::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.input, + crate::components::PROBE_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![( + crate::components::PROBE_IN_ID.to_string(), + Pos2::new(0f32, 0f32) + own_pos, + )] + } + + fn top_padding(&self) -> f32 { + // todo: make this accurate? + 10f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/reg_file.rs b/riscv/src/gui_egui/components/reg_file.rs index 65486a76..208eb3e1 100644 --- a/riscv/src/gui_egui/components/reg_file.rs +++ b/riscv/src/gui_egui/components/reg_file.rs @@ -1,5 +1,333 @@ -use crate::components::RegFile; -use syncrim::common::EguiComponent; +use crate::components::{Reg, RegFile, RegStore}; +use egui::{ + Color32, Context, Label, Pos2, Rect, Response, RichText, Rounding, Shape, Stroke, Ui, Vec2, + Window, +}; +use egui_extras::{Column, TableBuilder}; +use syncrim::common::{EguiComponent, 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; + +impl RegFile { + fn side_panel(&self, ctx: &Context, _simulator: Option<&mut Simulator>) { + Window::new("Register File").show(ctx, |ui| { + TableBuilder::new(ui) + .column(Column::initial(40.0)) + .column(Column::initial(85.0)) + .header(30.0, |mut header| { + header.col(|ui| { + ui.heading("Reg"); + }); + header.col(|ui| { + ui.heading("Contents"); + }); + }) + .body(|mut body| { + for reg in RegStore::full_range() { + body.row(15.0, |mut row| { + row.col(|ui| { + ui.label(format!("{:?}", Reg::try_from(reg).unwrap())); + }); + row.col(|ui| { + let stack_depth = *self.stack_depth_state.borrow() as i32; + let stack_depth = if stack_depth >= 0 { + stack_depth as usize + } else { + 0_usize + }; + if Reg::try_from(reg).unwrap() == Reg::sp { + ui.label(format!( + "0x{:08X}", + self.registers.0.borrow()[0][reg as usize] //self.registers.0.borrow()[*(self.stack_depth_state.borrow()) as usize].get(reg as usize).unwrap() + )); + } else { + ui.label(format!( + "0x{:08X}", + self.registers.0.borrow()[stack_depth][reg as usize] //self.registers.0.borrow()[*(self.stack_depth_state.borrow()) as usize].get(reg as usize).unwrap() + )); + } + }); + }); + } + }); + }); + } +} #[typetag::serde] -impl EguiComponent for RegFile {} +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> { + // 21x41 + // middle: 11x 21y (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 + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("RegFile"); + }); + match editor_mode { + EditorMode::Simulator => { + ui.allocate_ui_at_rect(rect, |ui| { + ui.set_clip_rect(rect); + TableBuilder::new(ui) + .striped(true) + .column(Column::exact(50.0 * scale)) + .column(Column::exact(100.0 * scale)) + .header(20.0 * scale, |mut header| { + header.col(|ui| { + ui.label(RichText::new("Reg").size(20.0 * scale)); + }); + header.col(|ui| { + ui.label( + RichText::new(format!( + "Level {}", + *self.stack_depth_state.borrow() as i32 + )) + .size(20.0 * scale), + ); + }); + }) + .body(|body| { + body.rows( + 15.0 * scale, + RegStore::lo_range().end as usize + - RegStore::lo_range().start as usize, + |index, mut row| { + row.col(|ui| { + ui.add(Label::new( + RichText::new(format!( + "{:?}:", + Reg::try_from(index as u8).unwrap() + )) + .size(15.0 * scale), + )); + }); + row.col(|ui| { + let stack_depth = *self.stack_depth_state.borrow() as i32; + let stack_depth = if stack_depth >= 0 { + stack_depth as usize + } else { + 0_usize + }; + if Reg::try_from(index as u8).unwrap() == Reg::sp { + ui.add(Label::new( + RichText::new(format!( + "0x{:08x}", + self.registers.0.borrow()[0][index] + )) + .size(15.0 * scale), + )); + } else { + ui.add(Label::new( + RichText::new(format!( + "0x{:08x}", + self.registers.0.borrow()[stack_depth][index] + )) + .size(15.0 * scale), + )); + } + }); + }, + ); + }); + }); + self.side_panel(ui.ctx(), 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 = RegFile::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.stack_depth, + crate::components::REG_FILE_STACK_DEPTH_ID.to_string(), + id_ports, + self.id.clone(), + ); + + // clicked_dropdown |= input_selector( + // ui, + // &mut self.clic_mepc, + // crate::components::REG_FILE_CLIC_MEPC_ID.to_string(), + // id_ports, + // self.id.clone(), + // ); + + clicked_dropdown |= input_selector( + ui, + &mut self.clic_ra_we, + crate::components::REG_FILE_CLIC_RA_WE_ID.to_string(), + id_ports, + self.id.clone(), + ); + + clicked_dropdown |= input_selector( + ui, + &mut self.read_addr1, + crate::components::REG_FILE_READ_ADDR1_ID.to_string(), + id_ports, + self.id.clone(), + ); + + clicked_dropdown |= input_selector( + ui, + &mut self.read_addr2, + crate::components::REG_FILE_READ_ADDR2_ID.to_string(), + id_ports, + self.id.clone(), + ); + + clicked_dropdown |= input_selector( + ui, + &mut self.write_data, + crate::components::REG_FILE_WRITE_DATA_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.write_addr, + crate::components::REG_FILE_WRITE_ADDR_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.write_enable, + crate::components::REG_FILE_WRITE_ENABLE_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::REG_FILE_READ_ADDR1_ID.to_string(), + Pos2::new(-self.width / 2f32, -self.height / 5f32) + own_pos, + ), + ( + crate::components::REG_FILE_READ_ADDR2_ID.to_string(), + Pos2::new(-self.width / 2f32, self.height / 5f32) + own_pos, + ), + ( + crate::components::REG_FILE_WRITE_ADDR_ID.to_string(), + Pos2::new(-self.width / 2f32, 0f32) + own_pos, + ), + ( + crate::components::REG_FILE_WRITE_DATA_ID.to_string(), + Pos2::new(self.width / 4f32, -self.height / 2f32) + own_pos, + ), + ( + crate::components::REG_FILE_WRITE_ENABLE_ID.to_string(), + Pos2::new(-self.width / 4f32, -self.height / 2f32) + own_pos, + ), + ( + crate::components::REG_FILE_REG_A_OUT.to_string(), + Pos2::new(self.width / 2f32, -self.height / 5f32) + own_pos, + ), + ( + crate::components::REG_FILE_REG_B_OUT.to_string(), + Pos2::new(self.width / 2f32, self.height / 5f32) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 4f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/rv_mem.rs b/riscv/src/gui_egui/components/rv_mem.rs new file mode 100644 index 00000000..e70e1226 --- /dev/null +++ b/riscv/src/gui_egui/components/rv_mem.rs @@ -0,0 +1,292 @@ +use crate::components::RVMem; +use egui::FontId; +use egui::{ + Color32, Context, Label, Pos2, Rect, Response, Rounding, Shape, Slider, Stroke, Ui, Vec2, + Window, +}; +use egui_extras::{Column, TableBuilder}; +use syncrim::common::{EguiComponent, 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; +impl RVMem { + fn side_panel(&self, ctx: &Context, _simulator: Option<&mut Simulator>) { + Window::new("Data Memory").show(ctx, |ui| { + TableBuilder::new(ui) + .striped(true) + .column(Column::initial(75.0)) + .column(Column::initial(75.0)) + .column(Column::initial(50.0)) + .header(30.0, |mut header| { + header.col(|ui| { + ui.heading("Address"); + }); + header.col(|ui| { + ui.heading("HEX"); + }); + header.col(|ui| { + ui.heading("ASCII"); + }); + }) + .body(|body| { + body.rows( + 15.0, + ((self.range.end - self.range.start) / 4) as usize, + |index, mut row| { + //println!("{}", index); + let address = self.range.start as usize + index * 4; + let memory = self.memory.0.borrow().clone(); + row.col(|ui| { + ui.label(format!("0x{:08x}", address)); + }); + let mut bytes = [0u8; 4]; + if self.big_endian { + bytes[0] = *(memory).get(&address).unwrap(); + bytes[1] = *(memory).get(&(address + 1)).unwrap(); + bytes[2] = *(memory).get(&(address + 2)).unwrap(); + bytes[3] = *(memory).get(&(address + 3)).unwrap(); + } else { + bytes[3] = *(memory).get(&address).unwrap(); + bytes[2] = *(memory).get(&(address + 1)).unwrap(); + bytes[1] = *(memory).get(&(address + 2)).unwrap(); + bytes[0] = *(memory).get(&(address + 3)).unwrap(); + } + let word = format!( + "0x{:02x}{:02x}{:02x}{:02x}", + bytes[0], bytes[1], bytes[2], bytes[3] + ); + let mut ascii = "".to_string(); + for b in bytes { + if b > 0x1f && b < 0x7f { + ascii += &format!("{}", b as char); + } else { + ascii += " "; + } + } + row.col(|ui| { + ui.add(Label::new(word).truncate(true)); + }); + row.col(|ui| { + ui.add(Label::new(ascii).truncate(true)); + }); + }, + ); + }); + }); + } +} + +#[typetag::serde] +impl EguiComponent for RVMem { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 201x101 + // middle: 101x 51y (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 + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + ui.painter().text( + o.to_pos2(), + egui::Align2::CENTER_CENTER, + "Data Mem", + FontId::monospace(14.0), + Color32::BLACK, + ); + + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("Mem"); + }); + match editor_mode { + EditorMode::Simulator => { + self.side_panel(ui.ctx(), 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 = RVMem::render( + self, + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ) + .unwrap(); + let resp = &r_vec[0]; + context.size_rect = resp.rect; + 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); + ui.horizontal(|ui| { + ui.add(Slider::new(&mut self.width, 0f32..=400f32).text("width")); + ui.add(Slider::new(&mut self.height, 0f32..=400f32).text("height")); + }); + clicked_dropdown |= input_selector( + ui, + &mut self.data, + crate::components::RV_MEM_DATA_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.addr, + crate::components::RV_MEM_ADDR_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.ctrl, + crate::components::RV_MEM_CTRL_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.sext, + crate::components::RV_MEM_SEXT_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.size, + crate::components::RV_MEM_SIZE_ID.to_string(), + id_ports, + self.id.clone(), + ); + // todo: something about memory? + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::RV_MEM_DATA_I_ID.to_string(), + Pos2::new( + self.width / 10f32 * 1f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::RV_MEM_ADDR_ID.to_string(), + Pos2::new( + self.width / 10f32 * 2f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::RV_MEM_CTRL_ID.to_string(), + Pos2::new( + self.width / 10f32 * 3f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::RV_MEM_SEXT_ID.to_string(), + Pos2::new( + self.width / 10f32 * 4f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::RV_MEM_SIZE_ID.to_string(), + Pos2::new( + -self.width / 10f32 * 3f32 + self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::RV_MEM_DATA_O_ID.to_string(), + Pos2::new( + -self.width / 10f32 * 2f32 + self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 2f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/sign_zero_ext.rs b/riscv/src/gui_egui/components/sign_zero_ext.rs new file mode 100644 index 00000000..7a30ab10 --- /dev/null +++ b/riscv/src/gui_egui/components/sign_zero_ext.rs @@ -0,0 +1,192 @@ +use crate::components::SZExt; +use egui::{Color32, Pos2, Rect, Response, Rounding, Shape, Stroke, Ui, Vec2}; +use syncrim::common::{EguiComponent, Input, Ports, SignalUnsigned, 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; + +#[typetag::serde] +impl EguiComponent for SZExt { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 21x41 + // middle: 11x 21y (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 + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + + 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.sel_i).try_into(); + match r { + Ok(data) => { + if data == 0 { + "Sign Extend" + } else { + "Zero Extend" + } + } + _ => "Undefined", + } + }); + ui.label({ + let r: Result = + s.get_input_value(&self.data_i).try_into(); + match r { + Ok(data) => format!("In {:#x}", data), + + _ => "Undefined".to_string(), + } + }); + ui.label({ + let r: Result = s + .get_input_value(&Input { + id: self.id.clone(), + field: "out".to_string(), + }) + .try_into(); + match r { + Ok(data) => format!("Out {:#x}", data), + _ => "Undefined".to_string(), + } + }); + } + }); + 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 = SZExt::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.data_i, + crate::components::SIGN_ZERO_EXT_DATA_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.sel_i, + crate::components::SIGN_ZERO_EXT_SEL_I_ID.to_string(), + id_ports, + self.id.clone(), + ); + + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::SIGN_ZERO_EXT_DATA_I_ID.to_string(), + Pos2::new(-self.width / 2f32, 0.0) + own_pos, + ), + ( + crate::components::SIGN_ZERO_EXT_SEL_I_ID.to_string(), + Pos2::new(0.0, -self.height / 2f32) + own_pos, + ), + ( + crate::components::SIGN_ZERO_EXT_OUT_ID.to_string(), + Pos2::new(self.width / 2f32, 0.0) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 4f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_egui/components/wb_ctl.rs b/riscv/src/gui_egui/components/wb_ctl.rs new file mode 100644 index 00000000..0415a9b6 --- /dev/null +++ b/riscv/src/gui_egui/components/wb_ctl.rs @@ -0,0 +1,159 @@ +use crate::components::WBCtl; +use egui::{Color32, Pos2, Rect, Response, Rounding, Shape, Stroke, Ui, Vec2}; +use syncrim::common::{EguiComponent, 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; + +#[typetag::serde] +impl EguiComponent for WBCtl { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 21x41 + // middle: 11x 21y (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 + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::RED, + }, + )); + + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("WB_CTL"); + }); + 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 = WBCtl::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.dec_i, + crate::components::WB_CTL_DEC_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.clic_i, + crate::components::WB_CTL_INTR_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(syncrim::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::WB_CTL_INTR_IN_ID.to_string(), + Pos2::new(-self.width / 2f32, -self.height / 2f32) + own_pos, + ), + ( + crate::components::WB_CTL_DEC_IN_ID.to_string(), + Pos2::new(-self.width / 2f32, self.height / 2f32) + own_pos, + ), + ( + crate::components::WB_CTL_WE_OUT_ID.to_string(), + Pos2::new(self.width / 2f32, -self.height / 2f32) + own_pos, + ), + ( + crate::components::WB_CTL_MUX_CTL_O_ID.to_string(), + Pos2::new(self.width / 2f32, self.height / 2f32) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 4f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/riscv/src/gui_vizia/components/alu.rs b/riscv/src/gui_vizia/components/alu.rs index fff5b845..239f4e21 100644 --- a/riscv/src/gui_vizia/components/alu.rs +++ b/riscv/src/gui_vizia/components/alu.rs @@ -1,7 +1,7 @@ use crate::components::ALU; use log::trace; use syncrim::{ - gui_vizia::{tooltip::new_component_tooltip, ViziaComponent, V}, + gui_vizia::{ViziaComponent, V}, vizia::{ prelude::*, vg::{Color, Paint, Path}, @@ -13,20 +13,19 @@ impl ViziaComponent for ALU { // create view fn view<'a>(&self, cx: &'a mut Context) -> Handle<'a, V> { V::new(cx, self, move |cx| { - trace!("---- Create ALU View"); + trace!("---- Create Add View"); - View::build(ALUView {}, cx, |cx| { + View::build(ALUView {}, cx, move |cx| { Label::new(cx, "ALU") - .left(Percentage(20.0)) - .top(Percentage(45.0)); + .left(Percentage(25.0)) + .top(Pixels(40.0 - 10.0)) + .hoverable(false); }) }) - .position_type(PositionType::SelfDirected) - .left(Pixels(self.pos.0 - 50.0)) - .top(Pixels(self.pos.1 - 100.0)) - .width(Pixels(100.0)) - .height(Pixels(200.0)) - .tooltip(|cx| new_component_tooltip(cx, self)) + .left(Pixels(self.pos.0 - 20.0)) + .top(Pixels(self.pos.1 - 40.0)) + .width(Pixels(40.0)) + .height(Pixels(80.0)) } } @@ -39,18 +38,37 @@ impl View for ALUView { fn draw(&self, cx: &mut DrawContext<'_>, canvas: &mut Canvas) { let bounds = cx.bounds(); - // println!("InstMem draw {:?}", bounds); + //trace!("Add draw {:?}", bounds); let mut path = Path::new(); - let mut paint = Paint::color(Color::rgbf(0.0, 1.0, 1.0)); + let mut paint = Paint::color(Color::rgbf(1.0, 0.0, 0.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); + let height = bounds.height(); + let width = bounds.width(); + let top = bounds.top(); + let left = bounds.left(); + let right = bounds.right(); + let bottom = bounds.bottom(); - canvas.fill_path(&path, &paint); + // top left + path.move_to(left + 0.5, top + 0.5); + + // top right corner + path.line_to(left + width * 0.5 + 0.5, top + 0.5); + path.line_to(right + 0.5, top + height * 0.25 + 0.5); + + // bottom right corner + path.line_to(right + 0.5, bottom - height * 0.25 + 0.5); + path.line_to(left + width * 0.5 + 0.5, bottom + 0.5); + path.line_to(left + 0.5, bottom + 0.5); + + // left outtake + path.line_to(left + 0.5, bottom - 0.25 * height + 0.5); + path.line_to(left + width * 0.25 + 0.5, top + 0.5 * height + 0.5); + path.line_to(left + 0.5, top + 0.25 * height + 0.5); + path.line_to(left + 0.5, top + 0.5); + + canvas.stroke_path(&path, &paint); } } diff --git a/riscv/src/gui_vizia/components/branch_logic.rs b/riscv/src/gui_vizia/components/branch_logic.rs index abf864e9..bc334b2c 100644 --- a/riscv/src/gui_vizia/components/branch_logic.rs +++ b/riscv/src/gui_vizia/components/branch_logic.rs @@ -16,16 +16,16 @@ impl ViziaComponent for BranchLogic { trace!("---- Create BranchLogic View"); View::build(BranchLogicView {}, cx, |cx| { - Label::new(cx, "BranchLogic") + Label::new(cx, "Branch\nLogic") .left(Percentage(0.0)) .top(Percentage(0.0)); }) }) .position_type(PositionType::SelfDirected) - .left(Pixels(self.pos.0 - 50.0)) - .top(Pixels(self.pos.1 - 100.0)) - .width(Pixels(50.0)) - .height(Pixels(20.0)) + .left(Pixels(self.pos.0 - self.width / 2f32)) + .top(Pixels(self.pos.1 - self.height / 2f32)) + .width(Pixels(self.width)) + .height(Pixels(self.height)) .tooltip(|cx| new_component_tooltip(cx, self)) } } diff --git a/riscv/src/gui_vizia/components/clic.rs b/riscv/src/gui_vizia/components/clic.rs new file mode 100644 index 00000000..0e910885 --- /dev/null +++ b/riscv/src/gui_vizia/components/clic.rs @@ -0,0 +1,56 @@ +use crate::components::CLIC; +use log::trace; +use syncrim::{ + gui_vizia::{tooltip::new_component_tooltip, ViziaComponent, V}, + vizia::{ + prelude::*, + vg::{Color, Paint, Path}, + }, +}; + +#[typetag::serde] +impl ViziaComponent for CLIC { + // create view + fn view<'a>(&self, cx: &'a mut Context) -> Handle<'a, V> { + V::new(cx, self, move |cx| { + trace!("---- Create CLIC View"); + + View::build(CLICView {}, cx, |cx| { + Label::new(cx, "CLIC") + .left(Percentage(20.0)) + .top(Percentage(45.0)); + }) + }) + .position_type(PositionType::SelfDirected) + .left(Pixels(self.pos.0 - 50.0)) + .top(Pixels(self.pos.1 - 100.0)) + .width(Pixels(100.0)) + .height(Pixels(200.0)) + .tooltip(|cx| new_component_tooltip(cx, self)) + } +} + +pub struct CLICView {} + +impl View for CLICView { + fn element(&self) -> Option<&'static str> { + Some("CLIC") + } + + fn draw(&self, cx: &mut DrawContext<'_>, canvas: &mut Canvas) { + let bounds = cx.bounds(); + // println!("InstMem draw {:?}", bounds); + + let mut path = Path::new(); + let mut paint = Paint::color(Color::rgbf(1.0, 0.9, 0.9)); + 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/riscv/src/gui_vizia/components/decoder.rs b/riscv/src/gui_vizia/components/decoder.rs index 6ea717fb..98bcc854 100644 --- a/riscv/src/gui_vizia/components/decoder.rs +++ b/riscv/src/gui_vizia/components/decoder.rs @@ -21,10 +21,10 @@ impl ViziaComponent for Decoder { }) }) .position_type(PositionType::SelfDirected) - .left(Pixels(self.pos.0 - 50.0)) - .top(Pixels(self.pos.1 - 100.0)) - .width(Pixels(100.0)) - .height(Pixels(200.0)) + .left(Pixels(self.pos.0 - self.width / 2f32)) + .top(Pixels(self.pos.1 - self.height / 2f32)) + .width(Pixels(self.width)) + .height(Pixels(self.height)) .tooltip(|cx| new_component_tooltip(cx, self)) } } diff --git a/riscv/src/gui_vizia/components/instr_mem.rs b/riscv/src/gui_vizia/components/instr_mem.rs index 1223f4bc..e0fc4e82 100644 --- a/riscv/src/gui_vizia/components/instr_mem.rs +++ b/riscv/src/gui_vizia/components/instr_mem.rs @@ -1,55 +1,194 @@ +use std::{ + cell::RefCell, + collections::{BTreeMap, HashMap, HashSet}, + ops::Range, + rc::Rc, +}; + use crate::components::InstrMem; use log::trace; use syncrim::{ - gui_vizia::{tooltip::new_component_tooltip, ViziaComponent, V}, - vizia::{ - prelude::*, - vg::{Color, Paint, Path}, - }, + common::{Input, Simulator}, + gui_vizia::{GuiData, ViziaComponent, V}, + vizia::{prelude::*, style::Color}, }; +use riscv_asm_strings::*; + #[typetag::serde] impl ViziaComponent for InstrMem { // create view fn view<'a>(&self, cx: &'a mut Context) -> Handle<'a, V> { - V::new(cx, self, move |cx| { - trace!("---- Create InsrMem View"); - View::build(InstMem {}, cx, |cx| { - Label::new(cx, "Inst Mem") - .left(Percentage(35.0)) - .top(Percentage(35.0)); - }) + V::new(cx, self, |cx| { + trace!("---- Create InstMem View "); + Label::new(cx, "Instruction Memory") + .left(Pixels(10.0)) + .top(Pixels(10.0)) + .hoverable(false) }) - .position_type(PositionType::SelfDirected) - .left(Pixels(self.pos.0 - 50.0)) - .top(Pixels(self.pos.1 - 100.0)) - .width(Pixels(200.0)) - .height(Pixels(100.0)) - .tooltip(|cx| new_component_tooltip(cx, self)) + .left(Pixels(self.pos.0 - self.width / 2.0)) + .top(Pixels(self.pos.1 - self.height / 2.0)) + .width(Pixels(self.width)) + .height(Pixels(self.height)) + .background_color(Color::lightgrey()) } -} + fn left_view(&self, cx: &mut Context) { + trace!("---- Create Left Instr View View"); + let data_slice = { + let mut data_slice = vec![]; + trace!("range {:x?}", self.range); + for idx in (self.range.start..self.range.end).step_by(4) { + trace!("idx {:x?}", idx); + let instr = if self.le { + (*self.bytes.get(&idx).unwrap() as u32) + | (*self.bytes.get(&(idx + 1)).unwrap() as u32) << 8 + | (*self.bytes.get(&(idx + 2)).unwrap() as u32) << 16 + | (*self.bytes.get(&(idx + 3)).unwrap() as u32) << 24 + } else { + (*self.bytes.get(&idx).unwrap() as u32) << 24 + | (*self.bytes.get(&(idx + 1)).unwrap() as u32) << 16 + | (*self.bytes.get(&(idx + 2)).unwrap() as u32) << 8 + | (*self.bytes.get(&(idx + 3)).unwrap() as u32) + }; + data_slice.push( + format!( + "0x{:08x}: {:08x} ", + self.range.start + idx, + instr, + ) + &stringify_instruction(instr), + ); + } + data_slice + }; + println!("{:?}", self.symbols); + let view = View::build( + InstrMemView { + data: self.bytes.clone(), + start: self.range.start, + data_slice, + //we may init to 0 range, once view opens this will be updated. + slice_range: Range { start: 0, end: 0 }, + breakpoints: self.breakpoints.clone(), + pc_input: self.pc.clone(), + pc: 0, + symbols: self.symbols.clone(), + }, + cx, + |cx| { + Label::new(cx, "Instruction Memory") + .left(Pixels(10.0)) + .top(Pixels(10.0)); -pub struct InstMem {} - -impl View for InstMem { - fn element(&self) -> Option<&'static str> { - Some("InstMem") + VirtualList::new(cx, InstrMemView::data_slice, 20.0, |cx, idx, item| { + HStack::new(cx, |cx| { + if InstrMemView::symbols + .map(move |map| map.contains_key(&(idx * 4))) + .get(cx) + { + Label::new( + cx, + &InstrMemView::symbols + .map(move |map| { + format!("{}:", map.get(&(idx * 4)).unwrap().clone()) + }) + .get(cx), + ); + }; + Label::new(cx, "◉") + .on_mouse_up(move |cx, btn| { + if btn == MouseButton::Right { + cx.emit(DataEvent::Breakpoint(idx)) + } + }) + .color(InstrMemView::breakpoints.map(move |breakpoints| { + if breakpoints.borrow().contains(&(idx * 4)) { + Color::rgba(255, 0, 0, 255) + //red + } else { + Color::rgba(255, 255, 255, 0) + //transluscent + } + })) + .position_type(PositionType::SelfDirected) + .left(Percentage(17.5)); + Label::new(cx, item) + .on_mouse_up(move |cx, btn| { + if btn == MouseButton::Right { + cx.emit(DataEvent::Breakpoint(idx)) + } + }) + .position_type(PositionType::SelfDirected) + .left(Percentage(20.0)); + }) + .background_color(InstrMemView::pc.map(move |pc| { + if *pc as usize == idx * 4 { + Color::yellow() + } else { + Color::white() + } + })) + .child_left(Pixels(10.0)) + }); + }, + ) + .entity(); + Binding::new( + cx, + GuiData::simulator.then(Simulator::cycle), + move |cx, _| cx.emit_to(view, DataEvent::UpdateClock), + ); } +} - fn draw(&self, cx: &mut DrawContext<'_>, canvas: &mut Canvas) { - let bounds = cx.bounds(); - // println!("InstMem draw {:?}", bounds); +#[derive(Lens, Clone)] +pub struct InstrMemView { + data: BTreeMap, + start: usize, + data_slice: Vec, + slice_range: Range, + breakpoints: Rc>>, + pc_input: Input, + pc: u32, + symbols: HashMap, +} - 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)); +pub enum DataEvent { + UpdateClock, + Breakpoint(usize), +} - 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); +impl View for InstrMemView { + fn element(&self) -> Option<&'static str> { + Some("InstrMemView") + } + fn event(&mut self, cx: &mut EventContext, event: &mut Event) { + event.map(|event, _| match event { + DataEvent::UpdateClock => { + self.pc = GuiData::simulator + .get(cx) + .get_input_value(&self.pc_input) + .try_into() + .unwrap(); + } + DataEvent::Breakpoint(idx) => { + if self.breakpoints.borrow().contains(&(idx * 4)) { + trace!("Breakpoint removed"); + self.breakpoints.borrow_mut().remove(&(idx * 4)); + } else { + trace!("New breakpoint!"); + self.breakpoints.borrow_mut().insert(idx * 4); + } + } + }) + } +} - canvas.fill_path(&path, &paint); +fn stringify_instruction(instr: u32) -> String { + match asm_riscv::I::try_from(instr) { + Ok(i) => i.to_string(), + Err(err) => { + println!("{:?}", err); + "Unknown instruction".to_string() + } } } diff --git a/riscv/src/gui_vizia/components/lsb_zero.rs b/riscv/src/gui_vizia/components/lsb_zero.rs index 721f5a37..31f6983a 100644 --- a/riscv/src/gui_vizia/components/lsb_zero.rs +++ b/riscv/src/gui_vizia/components/lsb_zero.rs @@ -15,16 +15,16 @@ impl ViziaComponent for LSBZero { V::new(cx, self, move |cx| { trace!("---- Create LSBZero View"); View::build(LSBZeroView {}, cx, |cx| { - Label::new(cx, "LSBZero") + Label::new(cx, "") .left(Percentage(0.0)) .top(Percentage(0.0)); }) }) .position_type(PositionType::SelfDirected) - .left(Pixels(self.pos.0 - 50.0)) - .top(Pixels(self.pos.1 - 100.0)) - .width(Pixels(50.0)) - .height(Pixels(20.0)) + .left(Pixels(self.pos.0 - self.width / 2f32)) + .top(Pixels(self.pos.1 - self.height / 2f32)) + .width(Pixels(self.width)) + .height(Pixels(self.height)) .tooltip(|cx| new_component_tooltip(cx, self)) } } diff --git a/riscv/src/gui_vizia/components/mem.rs b/riscv/src/gui_vizia/components/mem.rs new file mode 100644 index 00000000..792ea401 --- /dev/null +++ b/riscv/src/gui_vizia/components/mem.rs @@ -0,0 +1,173 @@ +use std::ops::Range; + +use crate::components::{Memory, RVMem}; +use log::*; +use syncrim::vizia::prelude::*; +use syncrim::{ + common::Simulator, + //components::{Mem, Memory}, + gui_vizia::{GuiData, ViziaComponent, V}, +}; + +#[typetag::serde] +impl ViziaComponent for RVMem { + // create view + fn view<'a>(&self, cx: &'a mut Context) -> Handle<'a, V> { + V::new(cx, self, |cx| { + trace!("---- Create Mem View "); + Label::new(cx, "DataMemory") + .left(Pixels(10.0)) + .top(Pixels(10.0)) + .hoverable(false) + }) + .left(Pixels(self.pos.0 - self.width / 2.0)) + .top(Pixels(self.pos.1 - self.height / 2.0)) + .width(Pixels(self.width)) + .height(Pixels(self.height)) + .background_color(Color::lightgrey()) + } + + fn left_view(&self, cx: &mut Context) { + trace!("---- Create Left Mem View"); + //We initialize data_slice with the initial state of the memory. + //from now on, data_slice only gets updated over + //the relevant (visible) data interval, and when needed (so only on clock) + //so as to not trigger unnecessary redraws. + let data_slice = { + let mut data_slice = vec![]; + let mem = self.memory.clone(); + trace!("range {:x?}", self.range); + for idx in (self.range.start as usize..self.range.end as usize).step_by(4) { + trace!("idx {:x?}", idx); + + data_slice.push(format!( + "0x{:08x}: {:02x}{:02x}{:02x}{:02x}", + self.range.start as usize + idx * 4, + mem.0.borrow().get(&idx).copied().unwrap_or(0u8), + mem.0.borrow().get(&(idx + 1)).copied().unwrap_or(0u8), + mem.0.borrow().get(&(idx + 2)).copied().unwrap_or(0u8), + mem.0.borrow().get(&(idx + 3)).copied().unwrap_or(0u8), + )); + } + data_slice + }; + let view = View::build( + DataMemView { + data: self.memory.clone(), + start: self.range.start as usize, + data_slice, + //we may init to 0 range, once view opens this will be updated. + slice_range: Range { start: 0, end: 0 }, + }, + cx, + |cx| { + Label::new(cx, "Data Memory") + .left(Pixels(10.0)) + .top(Pixels(10.0)); + + VirtualList::new(cx, DataMemView::data_slice, 20.0, |cx, _, item| { + HStack::new(cx, |cx| { + Label::new(cx, item); + }) + .child_left(Pixels(10.0)) + }) + .on_change(|cx, range| { + cx.emit(DataEvent::UpdateScroll(range)); + }); + }, + ) + .entity(); + Binding::new( + cx, + GuiData::simulator.then(Simulator::cycle), + move |cx, _| cx.emit_to(view, DataEvent::UpdateClock), + ); + } +} + +#[derive(Lens, Clone)] +pub struct DataMemView { + data: Memory, + start: usize, + data_slice: Vec, + slice_range: Range, +} + +pub enum DataEvent { + UpdateClock, + UpdateScroll(Range), + UpdateView(Range), +} + +impl View for DataMemView { + fn element(&self) -> Option<&'static str> { + Some("MemView") + } + fn event(&mut self, cx: &mut EventContext, event: &mut Event) { + event.map(|event, _| match event { + DataEvent::UpdateView(range) => { + for idx in range.clone() { + if let Some(data_fmt) = self.data_slice.get_mut(idx) { + *data_fmt = format!( + "0x{:08x}: 0x{:02x}{:02x}{:02x}{:02x}", + idx * 4 + self.start, + self.data + .0 + .borrow() + .get(&(self.start + idx * 4)) + .copied() + .unwrap_or(0u8), + self.data + .0 + .borrow() + .get(&(self.start + idx * 4 + 1)) + .copied() + .unwrap_or(0u8), + self.data + .0 + .borrow() + .get(&(self.start + idx * 4 + 2)) + .copied() + .unwrap_or(0u8), + self.data + .0 + .borrow() + .get(&(self.start + idx * 4 + 3)) + .copied() + .unwrap_or(0u8), + ); + } else { + // Why do we end up here, seems wrong + panic!("Internal error, lookup should always succeed.") + } + } + } + DataEvent::UpdateClock => cx.emit(DataEvent::UpdateView(self.slice_range.clone())), //update the entire view on clock. + DataEvent::UpdateScroll(new_range) => { + //calculate the "delta" between the view before and after scroll, update that. + let old_range = self.slice_range.clone(); + self.slice_range = new_range.clone(); + let dirty_range_start = if new_range.start < old_range.start { + new_range.start + } else if new_range.start < old_range.end { + old_range.end + } else { + new_range.start + }; + let dirty_range_end = if new_range.end < old_range.start { + new_range.end + } else if new_range.end < old_range.end { + old_range.start + } else { + new_range.end + }; + let dirty_range = Range { + start: dirty_range_start, + end: dirty_range_end, + }; + + cx.emit(DataEvent::UpdateView(dirty_range)) + } + }) + } +} diff --git a/riscv/src/gui_vizia/components/mod.rs b/riscv/src/gui_vizia/components/mod.rs index f2314f5e..e8f160ed 100644 --- a/riscv/src/gui_vizia/components/mod.rs +++ b/riscv/src/gui_vizia/components/mod.rs @@ -1,7 +1,9 @@ pub mod alu; pub mod branch_logic; +pub mod clic; pub mod decoder; pub mod instr_mem; pub mod lsb_zero; +pub mod mem; pub mod reg_file; pub mod sign_zero_ext; diff --git a/riscv/src/gui_vizia/components/reg_file.rs b/riscv/src/gui_vizia/components/reg_file.rs index be04759c..70737ef3 100644 --- a/riscv/src/gui_vizia/components/reg_file.rs +++ b/riscv/src/gui_vizia/components/reg_file.rs @@ -16,8 +16,8 @@ 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()); + let item = RegFileView::registers + .map(move |reg| format!("0x{:08x}", reg.borrow().get(i as usize).copied().unwrap())); HStack::new(cx, |cx| { Label::new(cx, &format!("{:?}", Reg::try_from(i).unwrap())) @@ -112,7 +112,7 @@ impl ViziaComponent for RegFile { .background_color(Color::lightgrey()) .border_width(Pixels(1.0)) .border_color(Color::black()) - .width(Pixels(200.0)) + .width(Pixels(250.0)) .height(Pixels(500.0)) } } diff --git a/riscv/src/gui_vizia/components/sign_zero_ext.rs b/riscv/src/gui_vizia/components/sign_zero_ext.rs index 3fe53b23..f549f0b2 100644 --- a/riscv/src/gui_vizia/components/sign_zero_ext.rs +++ b/riscv/src/gui_vizia/components/sign_zero_ext.rs @@ -21,10 +21,10 @@ impl ViziaComponent for SZExt { }) }) .position_type(PositionType::SelfDirected) - .left(Pixels(self.pos.0 - 50.0)) - .top(Pixels(self.pos.1 - 100.0)) - .width(Pixels(50.0)) - .height(Pixels(20.0)) + .left(Pixels(self.pos.0 - self.width / 2f32)) + .top(Pixels(self.pos.1 - self.height / 2f32)) + .width(Pixels(self.width)) + .height(Pixels(self.height)) .tooltip(|cx| new_component_tooltip(cx, self)) } } diff --git a/riscv/src/main.rs b/riscv/src/main.rs index 78cd2b8a..030d00ba 100644 --- a/riscv/src/main.rs +++ b/riscv/src/main.rs @@ -8,5 +8,5 @@ fn main() { let _cs = ComponentStore::load_file(&path); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&_cs, &path); + syncrim::gui_vizia::gui(_cs, &path); } diff --git a/src/common.rs b/src/common.rs index ae3776c6..03f556b2 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,7 +1,11 @@ use petgraph::Graph; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::{collections::HashMap, rc::Rc}; +#[cfg(feature = "gui-egui")] +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions, SnapPriority}; + #[cfg(feature = "gui-vizia")] use vizia::prelude::*; @@ -17,7 +21,7 @@ type Components = Vec>; type Components = Vec>; #[cfg(feature = "gui-egui")] -type Components = Vec>; +pub type Components = Vec>; #[cfg_attr(feature = "gui-vizia", derive(Lens))] #[derive(Clone)] @@ -63,13 +67,23 @@ pub trait Component { /// returns the (id, Ports) of the component fn get_id_ports(&self) -> (Id, Ports); + fn set_id_port(&mut self, _target_port_id: Id, _new_input: Input) { + todo!("Set set_id_port for this Component"); + } + #[cfg(feature = "gui-egui")] + fn dummy(&self, _id: &str, _pos: (f32, f32)) -> Box> { + todo!("implement dummy component factory for this component") + } /// evaluate component based on current internal state fn clock(&self, _simulator: &mut Simulator) -> Result<(), Condition> { Ok(()) } - /// update component internal state fn un_clock(&self) {} + /// reset component internal state to initial value + fn reset(&self) {} + /// any + fn as_any(&self) -> &dyn Any; } #[derive(Clone, Debug, Eq, PartialEq)] @@ -80,30 +94,81 @@ pub enum Condition { Halt(String), } +#[cfg(feature = "gui-egui")] +use crate::gui_egui::gui::EguiExtra; + // Specific functionality for EGui frontend #[cfg(feature = "gui-egui")] #[typetag::serde(tag = "type")] pub trait EguiComponent: Component { + #[allow(clippy::too_many_arguments)] fn render( &self, _ui: &mut egui::Ui, - _simulator: Simulator, - _start: egui::Vec2, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + _offset: egui::Vec2, + _scale: f32, + _clip_rect: egui::Rect, + _editor_mode: EditorMode, + ) -> Option> { + None + } + + #[allow(clippy::too_many_arguments)] + 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 { + EditorRenderReturn { + delete: false, + resp: None, + } + } + + fn top_padding(&self) -> f32 { + todo!("Create top_padding for this EguiComponent"); + } + + /// Get ports location relative to self, (inputs, outputs) + fn ports_location(&self) -> Vec<(Id, egui::Pos2)> { + todo!("Create ports_location for this EguiComponent"); + } + + fn snap_priority(&self) -> SnapPriority { + SnapPriority::Default + } + + fn set_pos(&mut self, _pos: (f32, f32)) { + todo!("Create set_pos for this EguiComponent"); + } + + fn get_pos(&self) -> (f32, f32) { + todo!("Create get_pos for this EguiComponent"); + } + + fn set_id_tmp(&self, context: &mut EguiExtra) { + context.id_tmp = self.get_id_ports().0.clone(); } } #[derive(Debug, Clone)] pub struct Ports { - pub inputs: Vec, + pub inputs: Vec, pub out_type: OutputType, pub outputs: Vec, } impl Ports { - pub fn new(inputs: Vec<&Input>, out_type: OutputType, outputs: Vec<&str>) -> Self { + pub fn new(inputs: Vec<&InputPort>, out_type: OutputType, outputs: Vec<&str>) -> Self { Ports { inputs: inputs.into_iter().cloned().collect(), out_type, @@ -127,6 +192,21 @@ impl Input { } } +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct InputPort { + pub port_id: Id, + pub input: Input, +} + +impl InputPort { + pub fn new(id_self: &str, id: &str, field: &str) -> Self { + InputPort { + port_id: id_self.into(), + input: Input::new(id, field), + } + } +} + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub enum OutputType { // Will be evaluated as a combinatorial function from inputs to outputs diff --git a/src/components/add.rs b/src/components/add.rs index 7c4e9c94..005b7f65 100644 --- a/src/components/add.rs +++ b/src/components/add.rs @@ -1,12 +1,20 @@ +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; use crate::common::{ - Component, Condition, Id, Input, OutputType, Ports, SignalSigned, SignalUnsigned, SignalValue, - Simulator, + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalSigned, SignalUnsigned, + SignalValue, Simulator, }; use log::*; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::rc::Rc; +pub const ADD_A_IN_ID: &str = "a_in"; +pub const ADD_B_IN_ID: &str = "b_in"; -#[derive(Serialize, Deserialize)] +pub const ADD_OUT_ID: &str = "out"; +pub const ADD_OVERFLOW_ID: &str = "overflow"; + +#[derive(Serialize, Deserialize, Clone)] pub struct Add { pub(crate) id: Id, pub(crate) pos: (f32, f32), @@ -19,14 +27,32 @@ impl Component for Add { fn to_(&self) { trace!("Add"); } - + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(Add { + 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![&self.a_in, &self.b_in], + vec![ + &InputPort { + port_id: ADD_A_IN_ID.to_string(), + input: self.a_in.clone(), + }, + &InputPort { + port_id: ADD_B_IN_ID.to_string(), + input: self.b_in.clone(), + }, + ], OutputType::Combinatorial, - vec!["out", "overflow"], + vec![ADD_OUT_ID, ADD_OVERFLOW_ID], ), ) } @@ -67,6 +93,18 @@ impl Component for Add { simulator.set_out_value(&self.id, "overflow", overflow); res } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + ADD_A_IN_ID => self.a_in = new_input, + ADD_B_IN_ID => self.b_in = new_input, + _ => (), + } + } + + fn as_any(&self) -> &dyn Any { + self + } } impl Add { @@ -108,7 +146,7 @@ mod test { }), ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); diff --git a/src/components/constant.rs b/src/components/constant.rs index a85b344c..98e5713b 100644 --- a/src/components/constant.rs +++ b/src/components/constant.rs @@ -1,8 +1,12 @@ +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; use crate::common::{Component, Condition, Id, OutputType, Ports, Signal, Simulator}; use log::*; use serde::{Deserialize, Serialize}; use std::{convert::Into, rc::Rc}; -#[derive(Serialize, Deserialize)] +pub const CONSTANT_OUT_ID: &str = "out"; +use std::any::Any; +#[derive(Serialize, Deserialize, Clone)] pub struct Constant { pub(crate) id: Id, pub(crate) pos: (f32, f32), @@ -14,7 +18,14 @@ impl Component for Constant { fn to_(&self) { trace!("constant {:?}", self.value); } - + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + Box::new(Rc::new(Constant { + id: id.to_string(), + pos: (pos.0, pos.1), + value: 0.into(), + })) + } fn get_id_ports(&self) -> (Id, Ports) { ( self.id.clone(), @@ -22,15 +33,19 @@ impl Component for Constant { // Constants do not take any inputs vec![], OutputType::Combinatorial, - vec!["out"], + vec![CONSTANT_OUT_ID], ), ) } fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { - simulator.set_out_value(&self.id, "out", self.value.get_value()); + simulator.set_out_value(&self.id, CONSTANT_OUT_ID, self.value.get_value()); Ok(()) } + + fn as_any(&self) -> &dyn Any { + self + } } impl Constant { diff --git a/src/components/cross.rs b/src/components/cross.rs new file mode 100644 index 00000000..5758474b --- /dev/null +++ b/src/components/cross.rs @@ -0,0 +1,73 @@ +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; +use crate::common::{Component, Id, Input, InputPort, OutputType, Ports}; +use log::*; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; + +pub const CROSS_IN_ID: &str = "in"; + +#[derive(Serialize, Deserialize, Clone)] +pub struct Cross { + pub(crate) id: Id, + pub(crate) pos: (f32, f32), + pub(crate) input: Input, +} + +#[typetag::serde] +impl Component for Cross { + fn to_(&self) { + trace!("Cross"); + } + + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(Cross { + id: id.to_string(), + pos: (pos.0, pos.1), + input: dummy_input.clone(), + })) + } + + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new( + // Probes take one input + vec![&InputPort { + port_id: CROSS_IN_ID.to_string(), + input: self.input.clone(), + }], + OutputType::Combinatorial, + // No output value + vec![], + ), + ) + } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id.as_str() == CROSS_IN_ID { + self.input = new_input + } + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl Cross { + pub fn new(id: &str, pos: (f32, f32), input: Input) -> Self { + Cross { + id: id.to_string(), + pos, + input, + } + } + + pub fn rc_new(id: &str, pos: (f32, f32), input: Input) -> Rc { + Rc::new(Cross::new(id, pos, input)) + } +} diff --git a/src/components/mem.rs b/src/components/mem.rs index 96419d36..a3f7e617 100644 --- a/src/components/mem.rs +++ b/src/components/mem.rs @@ -1,16 +1,28 @@ +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; use crate::common::{ - Component, Condition, Id, Input, OutputType, Ports, SignalSigned, SignalUnsigned, SignalValue, - Simulator, + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalSigned, SignalUnsigned, + SignalValue, Simulator, }; use log::*; use num_enum::IntoPrimitive; use num_enum::TryFromPrimitive; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::ops::Deref; use std::ops::Range; use std::{cell::RefCell, collections::BTreeMap, convert::TryFrom, rc::Rc}; -#[derive(Serialize, Deserialize)] +pub const MEM_DATA_ID: &str = "data"; +pub const MEM_ADDR_ID: &str = "addr"; +pub const MEM_CTRL_ID: &str = "ctrl"; +pub const MEM_SEXT_ID: &str = "sext"; +pub const MEM_SIZE_ID: &str = "size"; + +pub const MEM_DATA_OUT_ID: &str = "data_o"; +pub const MEM_ERR_OUT_ID: &str = "err"; + +#[derive(Serialize, Deserialize, Clone)] pub struct Mem { pub(crate) id: Id, pub(crate) pos: (f32, f32), @@ -270,13 +282,55 @@ impl Component for Mem { fn to_(&self) { trace!("Mem"); } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(Mem { + id: id.to_string(), + pos: (pos.0, pos.1), + width: 100.0, + height: 50.0, + big_endian: true, + data: dummy_input.clone(), + addr: dummy_input.clone(), + ctrl: dummy_input.clone(), + size: dummy_input.clone(), + sext: dummy_input.clone(), + range: Range { + start: 0, + end: 0x20, + }, + memory: Memory::new(BTreeMap::new()), + })) + } fn get_id_ports(&self) -> (Id, Ports) { ( self.id.clone(), Ports::new( - vec![&self.data, &self.addr, &self.ctrl, &self.sext, &self.size], + vec![ + &InputPort { + port_id: MEM_DATA_ID.to_string(), + input: self.data.clone(), + }, + &InputPort { + port_id: MEM_ADDR_ID.to_string(), + input: self.addr.clone(), + }, + &InputPort { + port_id: MEM_CTRL_ID.to_string(), + input: self.ctrl.clone(), + }, + &InputPort { + port_id: MEM_SEXT_ID.to_string(), + input: self.sext.clone(), + }, + &InputPort { + port_id: MEM_SIZE_ID.to_string(), + input: self.size.clone(), + }, + ], OutputType::Combinatorial, - vec!["data", "err"], + vec![MEM_DATA_OUT_ID, MEM_ERR_OUT_ID], ), ) } @@ -302,7 +356,7 @@ impl Component for Mem { sign != 0, self.big_endian, ); - simulator.set_out_value(&self.id, "data", value); + simulator.set_out_value(&self.id, "data_o", value); let value = self.memory.align(addr as usize, size as usize); trace!("align {:?}", value); simulator.set_out_value(&self.id, "err", value); // align @@ -323,7 +377,7 @@ impl Component for Mem { } } _ => { - simulator.set_out_value(&self.id, "data", SignalValue::Unknown); + simulator.set_out_value(&self.id, "data_o", SignalValue::Unknown); simulator.set_out_value(&self.id, "err", SignalValue::Unknown); // align } } @@ -344,6 +398,21 @@ impl Component for Mem { Ok(()) } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + match target_port_id.as_str() { + MEM_DATA_ID => self.data = new_input, + MEM_ADDR_ID => self.addr = new_input, + MEM_CTRL_ID => self.ctrl = new_input, + MEM_SEXT_ID => self.sext = new_input, + MEM_SIZE_ID => self.size = new_input, + _ => (), + } + } + + fn as_any(&self) -> &dyn Any { + self + } } impl Deref for Memory { @@ -365,7 +434,7 @@ mod test { fn test_mem_be() { let cs = ComponentStore { store: vec![ - Rc::new(ProbeOut::new("data")), + Rc::new(ProbeOut::new("data_o")), Rc::new(ProbeOut::new("addr")), Rc::new(ProbeOut::new("ctrl")), Rc::new(ProbeOut::new("size")), @@ -380,7 +449,7 @@ mod test { big_endian: true, // i.e., big endian // ports - data: Input::new("data", "out"), + data: Input::new("data_o", "out"), addr: Input::new("addr", "out"), ctrl: Input::new("ctrl", "out"), size: Input::new("size", "out"), @@ -396,12 +465,12 @@ mod test { ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs - let out = &Input::new("mem", "data"); + let out = &Input::new("mem", "data_o"); let err = &Input::new("mem", "err"); // reset @@ -413,7 +482,7 @@ mod test { println!(""); - simulator.set_out_value("data", "out", 0xf0); + simulator.set_out_value("data_o", "out", 0xf0); simulator.set_out_value("addr", "out", 4); simulator.set_out_value("ctrl", "out", MemCtrl::Write as SignalUnsigned); simulator.set_out_value("size", "out", 1); @@ -521,7 +590,7 @@ mod test { println!(""); simulator.set_out_value("addr", "out", 10); - simulator.set_out_value("data", "out", 0x1234); + simulator.set_out_value("data_o", "out", 0x1234); simulator.set_out_value("ctrl", "out", MemCtrl::Write as SignalUnsigned); simulator.clock(); assert_eq!(simulator.cycle, 13); @@ -547,7 +616,7 @@ mod test { fn test_mem_le() { let cs = ComponentStore { store: vec![ - Rc::new(ProbeOut::new("data")), + Rc::new(ProbeOut::new("data_o")), Rc::new(ProbeOut::new("addr")), Rc::new(ProbeOut::new("ctrl")), Rc::new(ProbeOut::new("size")), @@ -562,7 +631,7 @@ mod test { big_endian: false, // i.e., little endian // ports - data: Input::new("data", "out"), + data: Input::new("data_o", "out"), addr: Input::new("addr", "out"), ctrl: Input::new("ctrl", "out"), size: Input::new("size", "out"), @@ -579,12 +648,12 @@ mod test { ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); // outputs - let out = &Input::new("mem", "data"); + let out = &Input::new("mem", "data_o"); let err = &Input::new("mem", "err"); // reset @@ -593,7 +662,7 @@ mod test { // println!(""); - simulator.set_out_value("data", "out", 0xf0); + simulator.set_out_value("data_o", "out", 0xf0); simulator.set_out_value("addr", "out", 4); simulator.set_out_value("ctrl", "out", MemCtrl::Write as SignalUnsigned); simulator.set_out_value("size", "out", 1); // byte @@ -647,7 +716,7 @@ mod test { println!(""); simulator.set_out_value("addr", "out", 10); // b - simulator.set_out_value("data", "out", 0x1234); + simulator.set_out_value("data_o", "out", 0x1234); simulator.set_out_value("ctrl", "out", MemCtrl::Write as SignalUnsigned); simulator.set_out_value("size", "out", 2); diff --git a/src/components/mod.rs b/src/components/mod.rs index 6ea2061e..2121b0d2 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,5 +1,6 @@ mod add; mod constant; +mod cross; mod mem; mod mux; mod probe; @@ -13,6 +14,7 @@ mod wire; pub use add::*; pub use constant::*; +pub use cross::*; pub use mem::*; pub use mux::*; pub use probe::*; diff --git a/src/components/mux.rs b/src/components/mux.rs index 0a9ef47d..1883ebbc 100644 --- a/src/components/mux.rs +++ b/src/components/mux.rs @@ -1,11 +1,19 @@ +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; use crate::common::{ - Component, Condition, Id, Input, OutputType, Ports, SignalUnsigned, SignalValue, Simulator, + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalUnsigned, SignalValue, + Simulator, }; use log::*; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::rc::Rc; -#[derive(Serialize, Deserialize)] +pub const MUX_SELECT_ID: &str = "select"; +pub const MUX_TEMPLATE_ID: &str = "in"; +pub const MUX_OUT_ID: &str = "out"; + +#[derive(Serialize, Deserialize, Clone)] pub struct Mux { pub(crate) id: Id, pub(crate) pos: (f32, f32), @@ -18,17 +26,35 @@ impl Component for Mux { fn to_(&self) { trace!("mux"); } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(Mux { + id: id.to_string(), + pos: (pos.0, pos.1), + select: dummy_input.clone(), + m_in: vec![dummy_input.clone(), dummy_input.clone()], + })) + } fn get_id_ports(&self) -> (Id, Ports) { - let mut inputs = vec![self.select.clone()]; - let mut m = self.m_in.clone(); - inputs.append(&mut m); + let mut inputs: Vec = Vec::with_capacity(self.m_in.len() + 1); + inputs.push(InputPort { + port_id: MUX_SELECT_ID.to_string(), + input: self.select.clone(), + }); + for (i, item) in self.m_in.iter().enumerate() { + inputs.push(InputPort { + port_id: format!("{}{}", MUX_TEMPLATE_ID, i), + input: item.clone(), + }); + } ( self.id.clone(), Ports { inputs, out_type: OutputType::Combinatorial, - outputs: vec!["out".into()], + outputs: vec![MUX_OUT_ID.to_string()], }, ) } @@ -37,7 +63,7 @@ impl Component for Mux { fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { // get input value let select: SignalValue = simulator.get_input_value(&self.select); - + trace!("-----------{}------------", self.id); let (value, res) = if let Ok(select) = TryInto::::try_into(select) { let select = select as usize; trace!("select {}", select); @@ -55,11 +81,29 @@ impl Component for Mux { Err(Condition::Warning("select unknown".to_string())), ) }; - + trace!("-----------------value:{:?}, end---------------", value); // set output simulator.set_out_value(&self.id, "out", value); res } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + let target_port_id = target_port_id.as_str(); + if target_port_id == MUX_SELECT_ID { + self.select = new_input; + return; + } + for i in 0..=self.m_in.len() - 1 { + if target_port_id == format!("{}{}", MUX_TEMPLATE_ID, i) { + self.m_in[i] = new_input; + return; + } + } + } + + fn as_any(&self) -> &dyn Any { + self + } } impl Mux { diff --git a/src/components/probe.rs b/src/components/probe.rs index a966c1bf..9f8c2a82 100644 --- a/src/components/probe.rs +++ b/src/components/probe.rs @@ -1,8 +1,14 @@ -use crate::common::{Component, Id, Input, OutputType, Ports}; +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; +use crate::common::{Component, Id, Input, InputPort, OutputType, Ports}; use log::*; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::rc::Rc; -#[derive(Serialize, Deserialize)] + +pub const PROBE_IN_ID: &str = "in"; + +#[derive(Serialize, Deserialize, Clone)] pub struct Probe { pub(crate) id: Id, pub(crate) pos: (f32, f32), @@ -14,18 +20,42 @@ impl Component for Probe { fn to_(&self) { trace!("Probe"); } + + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(Probe { + id: id.to_string(), + pos: (pos.0, pos.1), + input: dummy_input.clone(), + })) + } + fn get_id_ports(&self) -> (Id, Ports) { ( self.id.clone(), Ports::new( // Probes take one input - vec![&self.input], + vec![&InputPort { + port_id: PROBE_IN_ID.to_string(), + input: self.input.clone(), + }], OutputType::Combinatorial, // No output value vec![], ), ) } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id.as_str() == PROBE_IN_ID { + self.input = new_input + } + } + + fn as_any(&self) -> &dyn Any { + self + } } impl Probe { diff --git a/src/components/probe_assert.rs b/src/components/probe_assert.rs index 925ebfd4..4330b84c 100644 --- a/src/components/probe_assert.rs +++ b/src/components/probe_assert.rs @@ -1,12 +1,15 @@ use crate::{ - common::{Component, Condition, Id, Input, OutputType, Ports, Signal, Simulator}, + common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Signal, Simulator}, signal::SignalValue, }; use log::*; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::rc::Rc; -#[derive(Serialize, Deserialize)] +pub const PROBE_ASSERT_IN_ID: &str = "in"; + +#[derive(Serialize, Deserialize, Clone)] pub struct ProbeAssert { pub(crate) id: Id, pub(crate) pos: (f32, f32), @@ -23,7 +26,14 @@ impl Component for ProbeAssert { fn get_id_ports(&self) -> (Id, Ports) { ( self.id.clone(), - Ports::new(vec![&self.input], OutputType::Combinatorial, vec![]), + Ports::new( + vec![&InputPort { + port_id: PROBE_ASSERT_IN_ID.to_string(), + input: self.input.clone(), + }], + OutputType::Combinatorial, + vec![], + ), ) } @@ -49,6 +59,10 @@ impl Component for ProbeAssert { } // notice we don't implement `un_clock` since the state is already kept in history + + fn as_any(&self) -> &dyn Any { + self + } } impl ProbeAssert { @@ -93,7 +107,7 @@ mod test { ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); // output let out = &Input::new("stim", "out"); @@ -167,7 +181,7 @@ mod test { ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); // output let out = &Input::new("stim", "out"); diff --git a/src/components/probe_edit.rs b/src/components/probe_edit.rs index 9c0ddb5e..21077fed 100644 --- a/src/components/probe_edit.rs +++ b/src/components/probe_edit.rs @@ -1,11 +1,16 @@ +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; use crate::common::{Component, Condition, Id, OutputType, Ports, Signal, Simulator}; use log::*; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::{ rc::Rc, sync::{Arc, RwLock}, }; +pub const PROBE_EDIT_OUT_ID: &str = "out"; + #[derive(Serialize, Deserialize, Clone)] pub struct ProbeEdit { pub(crate) id: Id, @@ -24,7 +29,10 @@ impl Component for ProbeEdit { fn to_(&self) { trace!("ProbeEdit"); } - + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + Box::new(Rc::new(ProbeEdit::new(id, (pos.0, pos.1)))) + } fn get_id_ports(&self) -> (Id, Ports) { ( self.id.clone(), @@ -33,7 +41,7 @@ impl Component for ProbeEdit { vec![], OutputType::Combinatorial, // Single output value - vec!["out"], + vec![PROBE_EDIT_OUT_ID], ), ) } @@ -63,6 +71,10 @@ impl Component for ProbeEdit { edit_history.push(prev.clone()); // push as current edit_history.push(prev); // push as next (to be edited) } + + fn as_any(&self) -> &dyn Any { + self + } } impl ProbeEdit { diff --git a/src/components/probe_out.rs b/src/components/probe_out.rs index a2010b21..a55fea27 100644 --- a/src/components/probe_out.rs +++ b/src/components/probe_out.rs @@ -1,8 +1,9 @@ use crate::common::{Component, Id, OutputType, Ports}; use log::*; use serde::{Deserialize, Serialize}; +use std::any::Any; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct ProbeOut { pub(crate) id: Id, } @@ -12,6 +13,7 @@ impl Component for ProbeOut { fn to_(&self) { trace!("ProbeOut"); } + fn get_id_ports(&self) -> (Id, Ports) { ( self.id.clone(), @@ -24,6 +26,10 @@ impl Component for ProbeOut { ), ) } + + fn as_any(&self) -> &dyn Any { + self + } } impl ProbeOut { diff --git a/src/components/probe_stim.rs b/src/components/probe_stim.rs index 57dc3bc0..fe8791a6 100644 --- a/src/components/probe_stim.rs +++ b/src/components/probe_stim.rs @@ -1,9 +1,10 @@ use crate::common::{Component, Condition, Id, OutputType, Ports, Signal, SignalValue, Simulator}; use log::*; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::rc::Rc; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct ProbeStim { pub(crate) id: Id, pub(crate) pos: (f32, f32), @@ -46,6 +47,9 @@ impl Component for ProbeStim { } // notice we don't implement `un_clock` since the state is already kept in history + fn as_any(&self) -> &dyn Any { + self + } } impl ProbeStim { @@ -73,7 +77,7 @@ mod test { store: vec![ProbeStim::rc_new("stim", (0.0, 0.0), vec![0, 1, 2, 3])], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); // output let out = &Input::new("stim", "out"); diff --git a/src/components/register.rs b/src/components/register.rs index 9701c94c..cba6e29a 100644 --- a/src/components/register.rs +++ b/src/components/register.rs @@ -1,9 +1,16 @@ -use crate::common::{Component, Condition, Id, Input, OutputType, Ports, Simulator}; +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; +use crate::common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Simulator}; use log::*; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::rc::Rc; -#[derive(Serialize, Deserialize)] +pub const REGISTER_R_IN_ID: &str = "r_in"; + +pub const REGISTER_OUT_ID: &str = "out"; + +#[derive(Serialize, Deserialize, Clone)] pub struct Register { pub(crate) id: Id, pub(crate) pos: (f32, f32), @@ -15,14 +22,26 @@ impl Component for Register { fn to_(&self) { trace!("register"); } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(Register { + id: id.to_string(), + pos: (pos.0, pos.1), + r_in: dummy_input.clone(), + })) + } fn get_id_ports(&self) -> (Id, Ports) { ( self.id.clone(), Ports::new( // Vector of inputs - vec![&self.r_in], + vec![&InputPort { + port_id: REGISTER_R_IN_ID.to_string(), + input: self.r_in.clone(), + }], OutputType::Sequential, - vec!["out"], + vec![REGISTER_OUT_ID], ), ) } @@ -36,6 +55,16 @@ impl Component for Register { trace!("eval: register id {} in {:?}", self.id, value); Ok(()) } + + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id == REGISTER_R_IN_ID { + self.r_in = new_input; + } + } + + fn as_any(&self) -> &dyn Any { + self + } } impl Register { diff --git a/src/components/sext.rs b/src/components/sext.rs index cbbed4b4..8f36e12c 100644 --- a/src/components/sext.rs +++ b/src/components/sext.rs @@ -1,14 +1,20 @@ // use std::fmt::Alignment; -use crate::{ - common::{ - Component, Condition, Id, Input, OutputType, Ports, SignalSigned, SignalUnsigned, Simulator, - }, - signal::SignalValue, +#[cfg(feature = "gui-egui")] +use crate::common::EguiComponent; +use crate::common::{ + Component, Condition, Id, Input, InputPort, OutputType, Ports, SignalSigned, SignalUnsigned, + SignalValue, Simulator, }; use log::*; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::rc::Rc; -#[derive(Serialize, Deserialize)] + +pub const SEXT_IN_ID: &str = "sext_in"; + +pub const SEXT_OUT_ID: &str = "out"; + +#[derive(Serialize, Deserialize, Clone)] pub struct Sext { pub(crate) id: Id, pub(crate) pos: (f32, f32), @@ -22,13 +28,37 @@ impl Component for Sext { fn to_(&self) { trace!("Sign Extension"); } + #[cfg(feature = "gui-egui")] + fn dummy(&self, id: &str, pos: (f32, f32)) -> Box> { + let dummy_input = Input::new("dummy", "out"); + Box::new(Rc::new(Sext { + id: id.to_string(), + pos: (pos.0, pos.1), + sext_in: dummy_input.clone(), + in_size: 16, + out_size: 24, + })) + } fn get_id_ports(&self) -> (Id, Ports) { ( self.id.clone(), - Ports::new(vec![&self.sext_in], OutputType::Combinatorial, vec!["out"]), + Ports::new( + vec![&InputPort { + port_id: SEXT_IN_ID.to_string(), + input: self.sext_in.clone(), + }], + OutputType::Combinatorial, + vec![SEXT_OUT_ID], + ), ) } + fn set_id_port(&mut self, target_port_id: Id, new_input: Input) { + if target_port_id.as_str() == SEXT_IN_ID { + self.sext_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> { @@ -61,6 +91,10 @@ impl Component for Sext { } Ok(()) } + + fn as_any(&self) -> &dyn Any { + self + } } impl Sext { @@ -84,7 +118,6 @@ impl Sext { Rc::new(Sext::new(id, pos, sext_in, in_size, out_size)) } } - #[cfg(test)] mod test { use super::*; @@ -112,7 +145,7 @@ mod test { ], }; - let mut simulator = Simulator::new(&cs); + let mut simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); diff --git a/src/components/wire.rs b/src/components/wire.rs index 5a3210da..e0057807 100644 --- a/src/components/wire.rs +++ b/src/components/wire.rs @@ -1,8 +1,12 @@ -use crate::common::{Component, Id, Input, OutputType, Ports}; +use crate::common::{Component, Id, Input, InputPort, OutputType, Ports}; use log::*; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::rc::Rc; -#[derive(Serialize, Deserialize)] + +pub const WIRE_INPUT_ID: &str = "in"; + +#[derive(Serialize, Deserialize, Clone)] pub struct Wire { pub(crate) id: Id, pub(crate) pos: Vec<(f32, f32)>, @@ -14,18 +18,26 @@ impl Component for Wire { fn to_(&self) { trace!("Wire"); } + fn get_id_ports(&self) -> (Id, Ports) { ( self.id.clone(), Ports::new( // Wires take one input - vec![&self.input], + vec![&InputPort { + port_id: WIRE_INPUT_ID.to_string(), + input: self.input.clone(), + }], OutputType::Combinatorial, // No output value vec![], ), ) } + + fn as_any(&self) -> &dyn Any { + self + } } impl Wire { diff --git a/src/fern.rs b/src/fern.rs index 9704d145..eedf3f78 100644 --- a/src/fern.rs +++ b/src/fern.rs @@ -30,6 +30,11 @@ pub fn fern_setup() { .level_for("cosmic_text::font::fallback", LevelFilter::Warn) .level_for("async_io::driver", LevelFilter::Warn); + #[cfg(feature = "gui-egui")] + let f = f + .level_for("eframe::native::run", LevelFilter::Info) + .level_for("async_io::driver", LevelFilter::Warn); + f // Output to stdout, files, and other Dispatch configurations .chain(std::io::stdout()) diff --git a/src/gui_egui/component_ui.rs b/src/gui_egui/component_ui.rs new file mode 100644 index 00000000..7e199d00 --- /dev/null +++ b/src/gui_egui/component_ui.rs @@ -0,0 +1,243 @@ +use crate::common::{Input, Ports}; +use crate::gui_egui::editor::{EditorMode, GridOptions}; +use crate::gui_egui::editor_wire_mode::get_grid_snap; +use crate::gui_egui::helper::{ + editor_mode_to_sense, offset_helper, out_of_bounds, unique_component_name, +}; +use egui::{ + containers, Color32, ComboBox, Context, DragValue, Frame, Key, KeyboardShortcut, Margin, + Modifiers, PointerButton, Pos2, Rect, Response, Rounding, Shape, Stroke, Ui, Vec2, Window, +}; +use epaint::{CircleShape, Shadow}; + +pub fn rect_with_hover

( + rect: Rect, + clip_rect: Rect, + editor_mode: EditorMode, + ui: &mut Ui, + id: String, + f: P, +) -> Response +where + P: Fn(&mut Ui), +{ + let rect = out_of_bounds(rect, clip_rect); + let r = ui.allocate_rect(rect, editor_mode_to_sense(editor_mode)); + + if r.hovered() && !r.dragged() { + containers::popup::show_tooltip_for(ui.ctx(), egui::Id::new(id), &rect, |ui| { + f(ui); + }); + } + r +} + +pub fn properties_window

( + ui: &mut Ui, + id: String, + resp: &Response, + properties_window: &mut bool, + mut f: P, +) where + P: FnMut(&mut Ui) -> bool, +{ + let mut clicked_dropdown = false; + if *properties_window { + let resp = Window::new(format!("Properties: {}", id)) + .frame(Frame { + inner_margin: Margin::same(10f32), + outer_margin: Margin::same(0f32), + rounding: Rounding::same(10f32), + shadow: Shadow::small_dark(), + fill: ui.visuals().panel_fill, + stroke: ui.visuals().window_stroke, + }) + .default_pos(Pos2 { + x: (resp.rect.min.x + resp.rect.max.x) / 2f32, + y: (resp.rect.min.y + resp.rect.max.y) / 2f32, + }) + .show(ui.ctx(), |ui| { + clicked_dropdown = f(ui); + }); + if !clicked_dropdown && resp.unwrap().response.clicked_elsewhere() { + *properties_window = false; + } + } + if resp.clicked_by(PointerButton::Secondary) { + // Open properties window + *properties_window = true; + } +} + +pub fn pos_drag_value(ui: &mut Ui, pos: &mut (f32, f32)) { + ui.horizontal(|ui| { + ui.label("pos x"); + ui.add(DragValue::new(&mut pos.0)); + ui.label("pos y"); + ui.add(DragValue::new(&mut pos.1)); + }); +} + +pub fn input_selector_removeable( + ui: &mut Ui, + input: &mut Input, + port_name: crate::common::Id, + id_ports: &[(crate::common::Id, Ports)], + own_id: crate::common::Id, + removable: bool, +) -> (bool, bool) { + let mut port_id = input.id.clone(); + let mut port_field = input.field.clone(); + let label_port_id = format!("{}.id", port_name.clone()); + let text_port_id = port_id.to_string(); + let label_port_field = format!("{}.field", port_name.clone()); + let text_port_field = port_field.to_string(); + let mut should_be_removed = false; + ui.horizontal(|ui| { + ComboBox::from_label(label_port_id) + .selected_text(text_port_id) + .show_ui(ui, |ui| { + for c in id_ports { + let id = c.0.clone(); + if id == own_id { + continue; + } + ui.selectable_value(&mut port_id, id.clone(), id); + } + }); + ComboBox::from_label(label_port_field) + .selected_text(text_port_field) + .show_ui(ui, |ui| { + for c in id_ports { + let id = c.0.clone(); + if id != port_id { + continue; + } + let fields = c.1.outputs.clone(); + for field in fields { + ui.selectable_value(&mut port_field, field.clone(), field); + } + } + }); + if removable && ui.button("🗙").clicked() { + should_be_removed = true; + } + }); + let clicked_dropdown = input.id != port_id || input.field != port_field; + + input.id = port_id; + input.field = port_field; + (clicked_dropdown, should_be_removed) +} + +pub fn input_selector( + ui: &mut Ui, + input: &mut Input, + port_name: crate::common::Id, + id_ports: &[(crate::common::Id, Ports)], + own_id: crate::common::Id, +) -> bool { + input_selector_removeable(ui, input, port_name, id_ports, own_id, false).0 +} + +pub fn input_change_id( + ui: &mut Ui, + id_tmp: &mut String, + id: &mut String, + id_ports: &[(crate::common::Id, Ports)], +) { + ui.horizontal(|ui| { + let id_label = ui.label("Id: "); + let r = ui + .text_edit_singleline(&mut *id_tmp) + .labelled_by(id_label.id); + if (r.lost_focus() || r.clicked_elsewhere()) && *id_tmp != *id { + *id = unique_component_name(id_ports, (*id_tmp).as_str()); + } + }); +} + +pub fn visualize_ports( + ui: &mut Ui, + ports: Vec<(crate::common::Id, Pos2)>, + offset: Vec2, + scale: f32, + clip_rect: Rect, +) { + for (id, pos) in ports { + let pos = offset_helper((pos.x, pos.y), scale, offset); + let scalev2 = Vec2 { + x: scale * 2f32, + y: scale * 2f32, + }; + let circle = Shape::Circle(CircleShape { + center: pos, + radius: scale * 3f32, + fill: Color32::TRANSPARENT, + stroke: Stroke { + width: 1f32 * scale, + color: Color32::BLUE, + }, + }); + ui.painter().add(circle); + let rect = Rect { + min: pos - scalev2, + max: pos + scalev2, + }; + rect_with_hover(rect, clip_rect, EditorMode::Wire, ui, id.clone(), |ui| { + ui.label(format!("Port id: {}", id)); + }); + } +} + +/// Returns if the dragged object should be deleted +pub fn drag_logic( + ctx: &Context, + resp: &Response, + pos: &mut (f32, f32), + tmp_pos: &mut Pos2, + scale: f32, + offset: Vec2, + grid: &GridOptions, +) -> bool { + let mut delete = false; + if resp.dragged_by(PointerButton::Primary) { + let mod_none = Modifiers { + alt: false, + ctrl: false, + shift: false, + mac_cmd: false, + command: false, + }; + + if ctx.input_mut(|i| { + i.consume_shortcut(&KeyboardShortcut { + modifiers: mod_none, + key: Key::Delete, + }) + }) || ctx.input_mut(|i| { + i.consume_shortcut(&KeyboardShortcut { + modifiers: mod_none, + key: Key::X, + }) + }) { + delete = true; + } + let delta = resp.drag_delta() / scale; + *tmp_pos += delta; + if grid.enable && grid.snap_enable { + match get_grid_snap(grid.snap_distance, *tmp_pos, grid.size) { + Some(p) => *pos = (p.x, p.y), + None => *pos = (pos.0 + delta.x, pos.1 + delta.y), + } + } else { + *pos = (pos.0 + delta.x, pos.1 + delta.y); + } + } + if resp.drag_released_by(PointerButton::Primary) + && resp.interact_pointer_pos().unwrap().x < offset.x + { + delete = true; + } + delete +} diff --git a/src/gui_egui/components/add.rs b/src/gui_egui/components/add.rs index a398cda0..4f2adf8c 100644 --- a/src/gui_egui/components/add.rs +++ b/src/gui_egui/components/add.rs @@ -1,31 +1,39 @@ +use crate::gui_egui::component_ui::{ + drag_logic, input_change_id, input_selector, pos_drag_value, properties_window, + rect_with_hover, visualize_ports, +}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; use crate::gui_egui::helper::offset_helper; use crate::{ - common::{EguiComponent, Simulator}, + common::{EguiComponent, Ports, Simulator}, components::Add, }; +use egui::{Color32, Pos2, Rect, Response, Shape, Stroke, Ui, Vec2}; #[typetag::serde] impl EguiComponent for Add { fn render( &self, - ui: &mut egui::Ui, - _simulator: Simulator, - offset: egui::Vec2, + ui: &mut Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + offset: Vec2, scale: f32, - _clip_rect: egui::Rect, - ) { + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { // 41x81 // middle: 21x 41y (0 0) - let oh: fn((f32, f32), f32, egui::Vec2) -> egui::Pos2 = offset_helper; + 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; - //trace!("---- Create Add View"); // The shape - // 40x30 - ui.painter().add(egui::Shape::closed_line( + ui.painter().add(Shape::closed_line( vec![ oh((-20f32, -40f32), s, o), oh((0f32, -40f32), s, o), @@ -37,25 +45,139 @@ impl EguiComponent for Add { oh((-10f32, 0f32), s, o), oh((-20f32, -20f32), s, o), ], - egui::Stroke { + Stroke { width: scale, - color: egui::Color32::RED, + color: Color32::RED, }, )); // plus sign - ui.painter().add(egui::Shape::line_segment( + ui.painter().add(Shape::line_segment( [oh((0f32, 0f32), s, o), oh((10f32, 0f32), s, o)], - egui::Stroke { + Stroke { width: scale, - color: egui::Color32::BLACK, + color: Color32::BLACK, }, )); - ui.painter().add(egui::Shape::line_segment( + ui.painter().add(Shape::line_segment( [oh((5f32, -5f32), s, o), oh((5f32, 5f32), s, o)], - egui::Stroke { + Stroke { width: scale, - color: egui::Color32::BLACK, + color: Color32::BLACK, }, )); + let rect = Rect { + min: oh((-20f32, -40f32), s, o), + max: oh((20f32, 40f32), s, o), + }; + 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: &[(crate::common::Id, Ports)], + grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let r_vec = Add::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::ADD_A_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.b_in, + crate::components::ADD_B_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::ADD_A_IN_ID.to_string(), + Pos2::new(-20f32, -20f32) + own_pos, + ), + ( + crate::components::ADD_B_IN_ID.to_string(), + Pos2::new(-20f32, 20f32) + own_pos, + ), + ( + crate::components::ADD_OUT_ID.to_string(), + Pos2::new(20f32, 0f32) + own_pos, + ), + ( + crate::components::ADD_OVERFLOW_ID.to_string(), + Pos2::new(0f32, -40f32) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + 40f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos } } diff --git a/src/gui_egui/components/constant.rs b/src/gui_egui/components/constant.rs index b645ecd9..88a70186 100644 --- a/src/gui_egui/components/constant.rs +++ b/src/gui_egui/components/constant.rs @@ -1,40 +1,152 @@ -use crate::common::{EguiComponent, SignalUnsigned, Simulator}; +use crate::common::{EguiComponent, Ports, SignalUnsigned, SignalValue, Simulator}; use crate::components::Constant; -use egui::{Align2, Area, Color32, Order, Rect, RichText}; +use crate::gui_egui::component_ui::{ + drag_logic, input_change_id, pos_drag_value, properties_window, rect_with_hover, + visualize_ports, +}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use egui::{Align2, Area, Color32, DragValue, Order, Pos2, Rect, Response, RichText, Ui, Vec2}; #[typetag::serde] impl EguiComponent for Constant { fn render( &self, - ui: &mut egui::Ui, - _simulator: Simulator, - offset: egui::Vec2, + ui: &mut Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + offset: Vec2, scale: f32, clip_rect: Rect, - ) { + editor_mode: EditorMode, + ) -> Option> { + let offset_old = offset; let mut offset = offset; offset.x += self.pos.0 * scale; offset.y += self.pos.1 * scale; - Area::new(self.id.to_string()) + let area = Area::new(self.id.to_string()) .order(Order::Middle) .current_pos(offset.to_pos2()) .movable(false) .enabled(true) + .interactable(false) .pivot(Align2::CENTER_CENTER) .show(ui.ctx(), |ui| { ui.set_clip_rect(clip_rect); - ui.label( - RichText::new(format!("{:?}", self.value)) - .size(scale * 12f32) - .background_color(Color32::LIGHT_GREEN), - ) + match editor_mode { + EditorMode::Simulator => ui.label( + RichText::new(format!("{}", self.value)) + .size(scale * 12f32) + .background_color(Color32::LIGHT_GREEN), + ), + _ => ui.label( + RichText::new(format!("{}", self.value)) + .size(scale * 12f32) + .underline(), + ), + } .on_hover_text({ let r: Result = self.value.try_into(); match r { - Ok(data) => format!("{:#x}", data), - _ => format!("{:?}", self.value), + Ok(data) => format!("{}", data), + _ => format!("{}", self.value), } - }) + }); }); + let r = rect_with_hover( + area.response.rect, + clip_rect, + editor_mode, + ui, + self.id.clone(), + |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label(format!("{:?}", self.value)); + }, + ); + 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: &[(crate::common::Id, Ports)], + grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let r_vec = Constant::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| { + input_change_id(ui, &mut context.id_tmp, &mut self.id, id_ports); + pos_drag_value(ui, &mut self.pos); + let mut x = match self.value.get_value() { + SignalValue::Data(s) => s, + _ => 0, + }; + ui.add(DragValue::new(&mut x)); + self.value.set_value(SignalValue::Data(x)); + false + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![( + crate::components::CONSTANT_OUT_ID.to_string(), + Pos2::new(0f32, 0f32) + own_pos, + )] + } + + fn top_padding(&self) -> f32 { + // todo: make this accurate? + 10f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos } } diff --git a/src/gui_egui/components/cross.rs b/src/gui_egui/components/cross.rs new file mode 100644 index 00000000..b6046089 --- /dev/null +++ b/src/gui_egui/components/cross.rs @@ -0,0 +1,149 @@ +use crate::common::{EguiComponent, Ports, SignalValue, Simulator}; +use crate::components::Cross; +use crate::gui_egui::component_ui::{ + drag_logic, input_change_id, input_selector, pos_drag_value, properties_window, + rect_with_hover, visualize_ports, +}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::helper::offset_helper; +use egui::{Color32, Pos2, Rect, Response, Shape, Stroke, Ui, Vec2}; +use epaint::{RectShape, Rounding}; + +#[typetag::serde] +impl EguiComponent for Cross { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + let input = self.input.clone(); + let value = match simulator { + Some(s) => s.get_input_value(&input), + None => SignalValue::Uninitialized, + }; + + 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 + + let rect = Rect { + min: oh((-5.0, -5.0), s, o), + max: oh((5.0, 5.0), s, o), + }; + + ui.painter().add(Shape::Rect(RectShape::new( + rect, + Rounding::ZERO, + Color32::DARK_BLUE, + Stroke { + width: scale, + color: Color32::RED, + }, + ))); + + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + match value { + SignalValue::Data(data) => ui.label(format!("{:#x?}", data)), + _ => ui.label(format!("{:?}", value)), + }; + }); + 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: &[(crate::common::Id, Ports)], + grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let r_vec = Cross::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.input, + crate::components::CROSS_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![( + crate::components::PROBE_IN_ID.to_string(), + Pos2::new(0f32, 0f32) + own_pos, + )] + } + + fn top_padding(&self) -> f32 { + // todo: make this accurate? + 10f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/src/gui_egui/components/mem.rs b/src/gui_egui/components/mem.rs index 24704d24..2d30ba60 100644 --- a/src/gui_egui/components/mem.rs +++ b/src/gui_egui/components/mem.rs @@ -1,5 +1,212 @@ -use crate::common::EguiComponent; +use crate::common::{EguiComponent, Ports, Simulator}; use crate::components::Mem; +use crate::gui_egui::component_ui::{ + drag_logic, input_change_id, input_selector, pos_drag_value, properties_window, + rect_with_hover, visualize_ports, +}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::helper::offset_helper; +use egui::{Color32, Pos2, Rect, Response, Rounding, Shape, Slider, Stroke, Ui, Vec2}; #[typetag::serde] -impl EguiComponent for Mem {} +impl EguiComponent for Mem { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 201x101 + // middle: 101x 51y (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 + let rect = Rect { + min: oh((-self.width / 2f32, -self.height / 2f32), s, o), + max: oh((self.width / 2f32, self.height / 2f32), s, o), + }; + ui.painter().add(Shape::rect_stroke( + rect, + Rounding::ZERO, + Stroke { + width: scale, + color: Color32::BLACK, + }, + )); + + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("Mem"); + }); + 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: &[(crate::common::Id, Ports)], + grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let r_vec = Mem::render( + self, + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ) + .unwrap(); + let resp = &r_vec[0]; + context.size_rect = resp.rect; + 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); + ui.horizontal(|ui| { + ui.add(Slider::new(&mut self.width, 0f32..=400f32).text("width")); + ui.add(Slider::new(&mut self.height, 0f32..=400f32).text("height")); + }); + clicked_dropdown |= input_selector( + ui, + &mut self.data, + crate::components::MEM_DATA_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.addr, + crate::components::MEM_ADDR_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.ctrl, + crate::components::MEM_CTRL_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.sext, + crate::components::MEM_SEXT_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown |= input_selector( + ui, + &mut self.size, + crate::components::MEM_SIZE_ID.to_string(), + id_ports, + self.id.clone(), + ); + // todo: something about memory? + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::MEM_DATA_ID.to_string(), + Pos2::new( + self.width / 10f32 * 1f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::MEM_ADDR_ID.to_string(), + Pos2::new( + self.width / 10f32 * 2f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::MEM_CTRL_ID.to_string(), + Pos2::new( + self.width / 10f32 * 3f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::MEM_SEXT_ID.to_string(), + Pos2::new( + self.width / 10f32 * 4f32 - self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::MEM_SIZE_ID.to_string(), + Pos2::new( + -self.width / 10f32 * 3f32 + self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ( + crate::components::MEM_DATA_OUT_ID.to_string(), + Pos2::new( + -self.width / 10f32 * 2f32 + self.width / 2f32, + -self.height / 2f32, + ) + own_pos, + ), + ] + } + + fn top_padding(&self) -> f32 { + self.height / 2f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} diff --git a/src/gui_egui/components/mod.rs b/src/gui_egui/components/mod.rs index 11a74478..428041e6 100644 --- a/src/gui_egui/components/mod.rs +++ b/src/gui_egui/components/mod.rs @@ -1,5 +1,6 @@ mod add; mod constant; +mod cross; mod mem; mod mux; mod probe; diff --git a/src/gui_egui/components/mux.rs b/src/gui_egui/components/mux.rs index fc7919c3..d2a747f6 100644 --- a/src/gui_egui/components/mux.rs +++ b/src/gui_egui/components/mux.rs @@ -1,20 +1,30 @@ -use crate::common::{EguiComponent, SignalUnsigned, Simulator}; +use crate::common::{EguiComponent, Input, Ports, SignalUnsigned, Simulator}; use crate::components::Mux; +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, +}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; use crate::gui_egui::helper::offset_helper; +use egui::{Color32, Pos2, Rect, Response, Shape, Stroke, Ui, Vec2}; #[typetag::serde] impl EguiComponent for Mux { fn render( &self, - ui: &mut egui::Ui, - simulator: Simulator, - offset: egui::Vec2, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, scale: f32, - _clip_rect: egui::Rect, - ) { + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { // 41x(20*ports + 11) // middle: 21x ((20*ports + 10)/2+1)y (0 0) - let oh: fn((f32, f32), f32, egui::Vec2) -> egui::Pos2 = offset_helper; + 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; @@ -23,37 +33,169 @@ impl EguiComponent for Mux { let pa = self.m_in.len() as f32; // selector, here we can treat Signal better (see Vizia counterpart) - let select: SignalUnsigned = simulator.get_input_value(&self.select).try_into().unwrap(); + let select: SignalUnsigned = match simulator { + Some(s) => s.get_input_value(&self.select).try_into().unwrap_or(0), + None => 0, + }; // The shape - ui.painter().add(egui::Shape::closed_line( + ui.painter().add(Shape::closed_line( vec![ oh((-20f32, pa * (-10f32) - 10f32), s, o), oh((0f32, pa * (-10f32) - 10f32), s, o), - oh((20f32, pa * (-10f32) + 10f32), s, o), - oh((20f32, 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), ], - egui::Stroke { + Stroke { width: scale, - color: egui::Color32::BLACK, + color: Color32::BLACK, }, )); + // select line - ui.painter().add(egui::Shape::line_segment( + ui.painter().add(Shape::line_segment( [ oh( (-20f32, ((select as f32) * 20f32) - pa * 10f32 + 10f32), s, o, ), - oh((20f32, 0f32), s, o), + oh((10f32, 0f32), s, o), ], - egui::Stroke { + Stroke { width: scale, - color: egui::Color32::RED, + color: Color32::RED, }, )); + + let rect = Rect { + min: oh((-20f32, pa * (-10f32) - 10f32), s, o), + max: oh((10f32, pa * 10f32 + 10f32), s, o), + }; + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label("Mux"); + }); + 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: &[(crate::common::Id, Ports)], + grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let r_vec = Mux::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.select, + crate::components::MUX_SELECT_ID.to_string(), + id_ports, + self.id.clone(), + ); + let mut i = 0; + //for i in 0..=self.m_in.len() - 1 { + self.m_in.retain_mut(|inp| { + let (clicked, delete) = input_selector_removeable( + ui, + inp, + format!("{}{}", crate::components::MUX_TEMPLATE_ID, i), + id_ports, + self.id.clone(), + i != 0, + ); + i += 1; + clicked_dropdown |= clicked; + !delete + }); + if ui.button("+ Add new input").clicked() { + self.m_in.push(Input { + id: "id".to_string(), + field: "field".to_string(), + }); + } + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + 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, + )]; + 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, + )); + } + v.push(( + crate::components::MUX_OUT_ID.to_string(), + Pos2::new(10f32, 0f32) + own_pos, + )); + v + } + + fn top_padding(&self) -> f32 { + self.m_in.len() as f32 * 10f32 + 5f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos } } diff --git a/src/gui_egui/components/probe.rs b/src/gui_egui/components/probe.rs index b6ef199f..848171ad 100644 --- a/src/gui_egui/components/probe.rs +++ b/src/gui_egui/components/probe.rs @@ -1,42 +1,159 @@ -use crate::common::{EguiComponent, SignalUnsigned, Simulator}; +use crate::common::{EguiComponent, Ports, SignalValue, Simulator}; use crate::components::Probe; -use egui::{Align2, Area, Color32, Order, Rect, RichText}; +use crate::gui_egui::component_ui::{ + drag_logic, input_change_id, input_selector, pos_drag_value, properties_window, + rect_with_hover, visualize_ports, +}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use egui::{Align2, Area, Color32, Order, Pos2, Rect, Response, RichText, Ui, Vec2}; #[typetag::serde] impl EguiComponent for Probe { fn render( &self, - ui: &mut egui::Ui, - simulator: Simulator, - offset: egui::Vec2, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, scale: f32, clip_rect: Rect, - ) { + editor_mode: EditorMode, + ) -> Option> { + let offset_old = offset; let mut offset = offset; offset.x += self.pos.0 * scale; offset.y += self.pos.1 * scale; let input = self.input.clone(); - let value = simulator.get_input_value(&input); - Area::new(self.id.to_string()) + let value = match simulator { + Some(s) => s.get_input_value(&input), + None => SignalValue::Uninitialized, + }; + let area = Area::new(self.id.to_string()) .order(Order::Middle) .current_pos(offset.to_pos2()) .movable(false) .enabled(true) + .interactable(false) .pivot(Align2::CENTER_CENTER) .show(ui.ctx(), |ui| { ui.set_clip_rect(clip_rect); - ui.label( - RichText::new(format!("{:?}", value)) - .size(scale * 12f32) - .background_color(Color32::LIGHT_BLUE), - ) - .on_hover_text({ - let r: Result = value.try_into(); - match r { - Ok(data) => format!("{:#x}", data), - _ => format!("{:?}", value), - } - }); + let text = if let SignalValue::Data(v) = value { + format!("{:#010x}", v) + } else { + format!("{:?}", value) + }; + match editor_mode { + EditorMode::Simulator => ui.label( + RichText::new(text.clone()) + .size(scale * 12f32) + .background_color(Color32::LIGHT_BLUE), + ), + _ => ui.label(RichText::new(text.clone()).size(scale * 12f32).underline()), + } + .on_hover_text(text); }); + + let r = rect_with_hover( + area.response.rect, + clip_rect, + editor_mode, + ui, + self.id.clone(), + |ui| { + ui.label(format!("Id: {}", self.id.clone())); + if let SignalValue::Data(v) = value { + ui.label(format!("{:#010x}", v)); + } else { + ui.label(format!("{:?}", value)); + } + }, + ); + 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: &[(crate::common::Id, Ports)], + grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let r_vec = Probe::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.input, + crate::components::PROBE_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![( + crate::components::PROBE_IN_ID.to_string(), + Pos2::new(0f32, 0f32) + own_pos, + )] + } + + fn top_padding(&self) -> f32 { + // todo: make this accurate? + 10f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos } } diff --git a/src/gui_egui/components/probe_edit.rs b/src/gui_egui/components/probe_edit.rs index 9e7eb49c..cadead9b 100644 --- a/src/gui_egui/components/probe_edit.rs +++ b/src/gui_egui/components/probe_edit.rs @@ -1,5 +1,184 @@ -use crate::common::EguiComponent; -use crate::components::ProbeEdit; +use crate::common::{EguiComponent, Ports, SignalSigned, SignalUnsigned, SignalValue, Simulator}; +use crate::components::{ProbeEdit, TextSignal}; +use crate::gui_egui::component_ui::{ + drag_logic, input_change_id, pos_drag_value, properties_window, rect_with_hover, + visualize_ports, +}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use egui::{Align2, Area, DragValue, Order, Pos2, Rect, Response, Ui, Vec2}; #[typetag::serde] -impl EguiComponent for ProbeEdit {} +impl EguiComponent for ProbeEdit { + fn render( + &self, + ui: &mut Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + let offset_old = offset; + let mut offset = offset; + offset.x += self.pos.0 * scale; + offset.y += self.pos.1 * scale; + let interact = matches!(editor_mode, EditorMode::Simulator); + let area = Area::new(self.id.to_string()) + .order(Order::Middle) + .current_pos(offset.to_pos2()) + .movable(false) + .enabled(true) + .interactable(interact) + .pivot(Align2::CENTER_CENTER) + .show(ui.ctx(), |ui| { + ui.set_clip_rect(clip_rect); + let hst = self.edit_history.clone(); + let x = hst.read().unwrap().last().unwrap().text.clone(); + let signal = parse_signal(x.as_str()); + let r = match signal { + SignalValue::Data(d) => { + let mut val = d; + // todo: Somehow make this scale... + let r = ui.add(DragValue::new(&mut val)); + *self.edit_history.write().unwrap().last_mut().unwrap() = TextSignal { + text: format!("{}", val), + signal: d.into(), + }; + r + } + SignalValue::Uninitialized => ui.label("Uninitialized"), + SignalValue::DontCare => ui.label("DontCare"), + SignalValue::Unknown => ui.label("Unknown"), + }; + r.on_hover_text(format!( + "{:?}", + parse_signal( + self.edit_history + .read() + .unwrap() + .last() + .unwrap() + .text + .as_str() + ) + )); + }); + + let r = rect_with_hover( + area.response.rect, + clip_rect, + editor_mode, + ui, + self.id.clone(), + |ui| { + ui.label(format!("Id: {}", self.id.clone())); + ui.label( + self.edit_history + .read() + .unwrap() + .last() + .unwrap() + .text + .to_string(), + ); + }, + ); + 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: &[(crate::common::Id, Ports)], + grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let r_vec = ProbeEdit::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 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 + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![( + crate::components::PROBE_EDIT_OUT_ID.to_string(), + Pos2::new(0f32, 0f32) + own_pos, + )] + } + + fn top_padding(&self) -> f32 { + // todo: make this accurate? + 10f32 + } + + fn set_pos(&mut self, pos: (f32, f32)) { + self.pos = pos; + } + + fn get_pos(&self) -> (f32, f32) { + self.pos + } +} + +fn parse_signal(text: &str) -> SignalValue { + let text = text.trim(); + + 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() + } else { + SignalValue::Unknown + } + } else { + SignalValue::Unknown + } +} diff --git a/src/gui_egui/components/probe_out.rs b/src/gui_egui/components/probe_out.rs index 5144e774..24f324f7 100644 --- a/src/gui_egui/components/probe_out.rs +++ b/src/gui_egui/components/probe_out.rs @@ -1,5 +1,21 @@ -use crate::common::EguiComponent; +use crate::common::{EguiComponent, Simulator}; use crate::components::ProbeOut; +use crate::gui_egui::editor::EditorMode; +use crate::gui_egui::gui::EguiExtra; +use egui::Rect; #[typetag::serde] -impl EguiComponent for ProbeOut {} +impl EguiComponent for ProbeOut { + fn render( + &self, + _ui: &mut egui::Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + _offset: egui::Vec2, + _scale: f32, + _clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + todo!(); + } +} diff --git a/src/gui_egui/components/probe_stim.rs b/src/gui_egui/components/probe_stim.rs index b28797fa..53fcc07c 100644 --- a/src/gui_egui/components/probe_stim.rs +++ b/src/gui_egui/components/probe_stim.rs @@ -1,5 +1,21 @@ -use crate::common::EguiComponent; +use crate::common::{EguiComponent, Simulator}; use crate::components::ProbeStim; +use crate::gui_egui::editor::EditorMode; +use crate::gui_egui::gui::EguiExtra; +use egui::Rect; #[typetag::serde] -impl EguiComponent for ProbeStim {} +impl EguiComponent for ProbeStim { + fn render( + &self, + _ui: &mut egui::Ui, + _context: &mut EguiExtra, + _simulator: Option<&mut Simulator>, + _offset: egui::Vec2, + _scale: f32, + _clip_rect: Rect, + _editor_mode: EditorMode, + ) -> Option> { + todo!(); + } +} diff --git a/src/gui_egui/components/register.rs b/src/gui_egui/components/register.rs index 3429fdc0..2fd862ba 100644 --- a/src/gui_egui/components/register.rs +++ b/src/gui_egui/components/register.rs @@ -1,20 +1,30 @@ -use crate::common::{EguiComponent, Simulator}; +use crate::common::{EguiComponent, Input, Ports, SignalUnsigned, Simulator}; use crate::components::Register; +use crate::gui_egui::component_ui::{ + drag_logic, input_change_id, input_selector, pos_drag_value, properties_window, + rect_with_hover, visualize_ports, +}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; use crate::gui_egui::helper::offset_helper; +use egui::{Color32, Pos2, Rect, Response, Shape, Stroke, Ui, Vec2}; #[typetag::serde] impl EguiComponent for Register { fn render( &self, - ui: &mut egui::Ui, - _simulator: Simulator, - offset: egui::Vec2, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, scale: f32, - _clip_rect: egui::Rect, - ) { + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { // 21x41 // middle: 11x 21y (0 0) - let oh: fn((f32, f32), f32, egui::Vec2) -> egui::Pos2 = offset_helper; + 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; @@ -22,7 +32,7 @@ impl EguiComponent for Register { let o = offset; // The shape - ui.painter().add(egui::Shape::line( + ui.painter().add(Shape::line( vec![ oh((-10f32, -20f32), s, o), oh((10f32, -20f32), s, o), @@ -32,10 +42,131 @@ impl EguiComponent for Register { oh((10f32, 20f32), s, o), oh((10f32, -20f32), s, o), ], - egui::Stroke { + Stroke { width: scale, - color: egui::Color32::BLACK, + color: Color32::BLACK, }, )); + let rect = Rect { + min: oh((-10f32, -20f32), s, o), + max: oh((10f32, 20f32), s, o), + }; + 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.r_in).try_into(); + match r { + Ok(data) => format!("In {:#x}", data), + _ => format!("In {:?}", r), + } + }); + ui.label({ + let r: Result = s + .get_input_value(&Input { + id: self.id.clone(), + field: "out".to_string(), + }) + .try_into(); + match r { + Ok(data) => format!("Out {:#x}", data), + _ => format!("Out {:?}", r), + } + }); + } + }); + + 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: &[(crate::common::Id, Ports)], + grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let r_vec = Register::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.r_in, + crate::components::REGISTER_R_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + clicked_dropdown + }, + ); + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::REGISTER_R_IN_ID.to_string(), + Pos2::new(-10f32, 0f32) + own_pos, + ), + ( + crate::components::REGISTER_OUT_ID.to_string(), + Pos2::new(10f32, 0f32) + 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/src/gui_egui/components/sext.rs b/src/gui_egui/components/sext.rs index d17006ca..05ce6bee 100644 --- a/src/gui_egui/components/sext.rs +++ b/src/gui_egui/components/sext.rs @@ -1,16 +1,161 @@ -use crate::common::{EguiComponent, Simulator}; +use crate::common::{EguiComponent, Ports, SignalUnsigned, Simulator}; use crate::components::Sext; +use crate::gui_egui::component_ui::{ + drag_logic, input_change_id, input_selector, pos_drag_value, properties_window, + rect_with_hover, visualize_ports, +}; +use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions}; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::helper::offset_helper; +use egui::{Color32, Pos2, Rect, Response, Shape, Slider, Stroke, Ui, Vec2}; #[typetag::serde] impl EguiComponent for Sext { fn render( &self, - _ui: &mut egui::Ui, - _simulator: Simulator, - _offset: egui::Vec2, - _scale: f32, - _clip_rect: egui::Rect, - ) { - todo!("implement sext"); + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + // 81x41 + // middle: 41x 21y (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((-40f32, 0f32), s, o), + oh((40f32, -20f32), s, o), + oh((40f32, 20f32), s, o), + oh((-40f32, 20f32), s, o), + ], + Stroke { + width: scale, + color: Color32::RED, + }, + )); + + let rect = Rect { + min: oh((-40f32, -20f32), s, o), + max: oh((40f32, 20f32), s, o), + }; + let r = rect_with_hover(rect, clip_rect, editor_mode, ui, self.id.clone(), |ui| { + ui.label(format!("Id: {}", self.id.clone())); + // todo: is this actually correct? + if let Some(s) = &simulator { + ui.label({ + let r: Result = + s.get_input_value(&self.sext_in).try_into(); + match r { + Ok(data) => format!("{:#x}", data), + _ => format!("{:?}", r), + } + }); + ui.label("Sign Extend"); + } + }); + 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: &[(crate::common::Id, Ports)], + grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let r_vec = Sext::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.sext_in, + crate::components::SEXT_IN_ID.to_string(), + id_ports, + self.id.clone(), + ); + ui.horizontal(|ui| { + ui.add(Slider::new(&mut self.in_size, 0..=32).text("in_size")); + ui.add(Slider::new(&mut self.out_size, self.in_size..=32).text("out_size")); + }); + clicked_dropdown + }, + ); + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let own_pos = Vec2::new(self.pos.0, self.pos.1); + vec![ + ( + crate::components::SEXT_IN_ID.to_string(), + Pos2::new(-40f32, 0f32) + own_pos, + ), + ( + crate::components::SEXT_OUT_ID.to_string(), + Pos2::new(40f32, 0f32) + 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/src/gui_egui/components/wire.rs b/src/gui_egui/components/wire.rs index 7da2c7b7..73d28a38 100644 --- a/src/gui_egui/components/wire.rs +++ b/src/gui_egui/components/wire.rs @@ -1,18 +1,31 @@ -use crate::common::{EguiComponent, Simulator}; +use crate::common::{EguiComponent, Ports, SignalUnsigned, 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::editor::{EditorMode, EditorRenderReturn, GridOptions, SnapPriority}; +use crate::gui_egui::gui::EguiExtra; use crate::gui_egui::helper::offset_helper; +use egui::{ + Color32, DragValue, Frame, Key, KeyboardShortcut, Margin, Modifiers, PointerButton, Pos2, Rect, + Response, Rounding, Shape, Stroke, Ui, Vec2, Window, +}; +use epaint::Shadow; #[typetag::serde] impl EguiComponent for Wire { fn render( &self, - ui: &mut egui::Ui, - _simulator: Simulator, - offset: egui::Vec2, + ui: &mut Ui, + _context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, scale: f32, - _clip_rect: egui::Rect, - ) { - let oh: fn((f32, f32), f32, egui::Vec2) -> egui::Pos2 = offset_helper; + clip_rect: Rect, + editor_mode: EditorMode, + ) -> Option> { + let oh: fn((f32, f32), f32, Vec2) -> Pos2 = offset_helper; + let offset_old = offset; let s = scale; let o = offset; let mut line_vec = vec![]; @@ -20,12 +33,198 @@ impl EguiComponent for Wire { line_vec.push(oh(pos, s, o)); } - ui.painter().add(egui::Shape::line( - line_vec, - egui::Stroke { + ui.painter().add(Shape::line( + line_vec.clone(), + Stroke { width: scale, - color: egui::Color32::BLACK, + color: 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 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), + } + }); + } + }); + r_vec.push(r); + } + + match editor_mode { + EditorMode::Simulator => (), + _ => visualize_ports(ui, self.ports_location(), offset_old, scale, clip_rect), + } + + Some(r_vec) + } + + fn render_editor( + &mut self, + ui: &mut Ui, + context: &mut EguiExtra, + simulator: Option<&mut Simulator>, + offset: Vec2, + scale: f32, + clip_rect: Rect, + id_ports: &[(crate::common::Id, Ports)], + _grid: &GridOptions, + editor_mode: EditorMode, + ) -> EditorRenderReturn { + let mut delete = false; + let r_vec = Wire::render( + self, + ui, + context, + simulator, + offset, + scale, + clip_rect, + editor_mode, + ) + .unwrap(); + + let mut properties_window_open = false; + for (i, resp) in r_vec.iter().enumerate() { + if resp.dragged_by(PointerButton::Primary) { + if ui.ctx().input_mut(|i| { + i.consume_shortcut(&KeyboardShortcut { + modifiers: Modifiers { + alt: false, + ctrl: false, + shift: false, + mac_cmd: false, + command: false, + }, + key: Key::Delete, + }) + }) || ui.ctx().input_mut(|i| { + i.consume_shortcut(&KeyboardShortcut { + modifiers: Modifiers { + alt: false, + ctrl: false, + shift: false, + mac_cmd: false, + command: false, + }, + key: Key::X, + }) + }) { + delete = true; + } + let delta = resp.drag_delta() / scale; + self.pos[i] = (self.pos[i].0 + delta.x, self.pos[i].1 + delta.y); + self.pos[i + 1] = (self.pos[i + 1].0 + delta.x, self.pos[i + 1].1 + delta.y); + } + if resp.drag_released_by(PointerButton::Primary) + && resp.interact_pointer_pos().unwrap().x < offset.x + { + delete = true; + } + properties_window_open |= resp.clicked_by(PointerButton::Secondary); + } + let mut clicked_dropdown = false; + if properties_window_open || context.properties_window { + let resp = Window::new(format!("Properties: {}", self.id)) + .frame(Frame { + inner_margin: Margin::same(10f32), + outer_margin: Margin::same(0f32), + rounding: Rounding::same(10f32), + shadow: Shadow::small_dark(), + fill: ui.visuals().panel_fill, + stroke: ui.visuals().window_stroke, + }) + .default_pos(Pos2 { + x: self.pos[0].0, + y: self.pos[0].1, + }) + .show(ui.ctx(), |ui| { + input_change_id(ui, &mut context.id_tmp, &mut self.id, id_ports); + clicked_dropdown |= input_selector( + ui, + &mut self.input, + crate::components::WIRE_INPUT_ID.to_string(), + id_ports, + self.id.clone(), + ); + + let mut i = 0; + 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.label("pos y"); + ui.add(DragValue::new(&mut seg_pos.1)); + + if first_item { + first_item = false; + } else if ui.button("🗙").clicked() { + delete = true; + } + }); + i += 1; + !delete + }); + + if ui.button("+ Add new segment").clicked() { + self.pos.push(*self.pos.last().unwrap()); + } + }); + if !context.properties_window { + context.properties_window = true; + } else if !clicked_dropdown && resp.unwrap().response.clicked_elsewhere() { + context.properties_window = false; + } + } + + EditorRenderReturn { + delete, + resp: Some(r_vec), + } + } + + fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> { + let mut vec: Vec<(crate::common::Id, Pos2)> = vec![]; + for (i, pos) in self.pos.iter().enumerate() { + vec.push(( + format!("{}-{}", crate::components::WIRE_INPUT_ID, i), + Pos2 { x: pos.0, y: pos.1 }, + )); + } + vec + } + + fn snap_priority(&self) -> SnapPriority { + SnapPriority::Wire + } + + fn get_pos(&self) -> (f32, f32) { + (0f32, 0f32) } } diff --git a/src/gui_egui/editor.rs b/src/gui_egui/editor.rs new file mode 100644 index 00000000..ecee55f8 --- /dev/null +++ b/src/gui_egui/editor.rs @@ -0,0 +1,420 @@ +use crate::common::{Components, EguiComponent, Id, Input}; +use crate::components::*; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::{ + editor_wire_mode::WireMode, + gui::Gui, + helper::{id_ports_of_all_components_non_wires, offset_helper}, + keymap, + library::InputMode, + menu::Menu, +}; +use eframe::{egui, Frame}; +use egui::{Color32, Context, LayerId, PointerButton, Pos2, Rect, Shape, Vec2}; +use std::{ + collections::{BTreeMap, HashMap}, + ops::Range, + path::Path, + rc::Rc, +}; + +pub struct Editor { + pub components: Components, + pub scale: f32, + pub pan: Vec2, + pub offset: Vec2, + pub offset_and_pan: Vec2, + pub clip_rect: Rect, + pub side_panel_width: f32, + pub ui_change: bool, + pub snap_distance: f32, + pub grid: GridOptions, + pub library: Components, + pub dummy_input: Input, + pub editor_mode: EditorMode, + pub wm: WireMode, + pub im: InputMode, + pub contexts: HashMap, +} + +#[derive(Clone)] +pub struct CloseToComponent { + pub comp: Rc, + pub pos: Pos2, + pub dist: f32, + pub port_id: Id, + pub potential_actual_input: Option, +} + +#[derive(Debug, Clone, Copy)] +pub enum EditorMode { + Simulator, + Default, + Wire, + Input, +} + +#[cfg(feature = "gui-egui")] +pub struct EditorRenderReturn { + pub delete: bool, + pub resp: Option>, +} + +// Specific structs for egui +#[cfg(feature = "gui-egui")] +pub enum SnapPriority { + Default, + Wire, +} + +#[derive(Clone)] +pub struct GridOptions { + pub enable: bool, + pub size: f32, + pub opacity: f32, + pub snap_enable: bool, + pub snap_distance: f32, +} +#[derive(Clone)] +pub struct Library(pub Components); +impl Default for Library { + fn default() -> Library { + let dummy_input = Input::new("id", "field"); + let library: Components = vec![ + Rc::new(Add { + id: "add".to_string(), + pos: (0.0, 0.0), + a_in: dummy_input.clone(), + b_in: dummy_input.clone(), + }), + Rc::new(Constant { + id: "c".to_string(), + pos: (0.0, 0.0), + value: 0.into(), + }), + Rc::new(Cross { + id: "p".to_string(), + pos: (0.0, 0.0), + input: dummy_input.clone(), + }), + Rc::new(ProbeEdit::new("pe", (0.0, 0.0))), + Rc::new(Sext { + id: "sext".to_string(), + pos: (0.0, 0.0), + sext_in: dummy_input.clone(), + in_size: 16, + out_size: 24, + }), + Rc::new(Mem { + id: "mem".to_string(), + pos: (0.0, 0.0), + width: 100.0, + height: 50.0, + big_endian: true, + data: dummy_input.clone(), + addr: dummy_input.clone(), + ctrl: dummy_input.clone(), + size: dummy_input.clone(), + sext: dummy_input.clone(), + range: Range { + start: 0, + end: 0x20, + }, + memory: Memory::new(BTreeMap::new()), + }), + Rc::new(Mux { + id: "mux".to_string(), + pos: (0.0, 0.0), + select: dummy_input.clone(), + m_in: vec![dummy_input.clone(), dummy_input.clone()], + }), + Rc::new(Register { + id: "reg".to_string(), + pos: (0.0, 0.0), + r_in: dummy_input.clone(), + }), + ]; + Library(library) + } +} +impl Editor { + pub fn gui(components: Components, _path: &Path, library: &Library) -> Self { + let dummy_input = Input::new("id", "field"); + let library: Components = library.clone().0; + let library_contexts = crate::gui_egui::gui::create_contexts(&library); + let mut e = Editor { + components, + scale: 1f32, + pan: Vec2::new(0f32, 0f32), + offset: Vec2::new(0f32, 0f32), + offset_and_pan: Vec2::new(0f32, 0f32), + clip_rect: Rect { + min: Pos2 { x: 0f32, y: 0f32 }, + max: Pos2 { + x: 1000f32, + y: 1000f32, + }, + }, + side_panel_width: 550f32, + ui_change: true, + snap_distance: 10f32, + grid: GridOptions { + enable: true, + size: 20f32, + opacity: 0.5f32, + snap_enable: true, + snap_distance: 20f32, + }, + library, + dummy_input, + editor_mode: EditorMode::Default, + wm: WireMode { + mode_ended: true, + last_pos: None, + input: None, + cursor_location: Pos2::ZERO, + start_comp_port: None, + temp_positions: vec![], + }, + im: InputMode { + comp: None, + cursor_location: Pos2::ZERO, + library_contexts, + }, + contexts: HashMap::new(), + }; + e.contexts = crate::gui_egui::gui::create_contexts(&e.components); + e + } + + pub fn update(ctx: &Context, _frame: &mut Frame, gui: &mut Gui) { + let frame = egui::Frame::none().fill(egui::Color32::WHITE); + + if Editor::gui_to_editor(gui).should_area_update(ctx) { + egui::TopBottomPanel::top("topBarEditor").show(ctx, |ui| { + Menu::new_editor(ui, gui); + }); + Editor::library(ctx, gui); + let top = + egui::containers::panel::PanelState::load(ctx, egui::Id::from("topBarEditor")) + .unwrap(); + let side = + egui::containers::panel::PanelState::load(ctx, egui::Id::from("leftLibrary")) + .unwrap(); + let e = Editor::gui_to_editor(gui); + e.offset = egui::Vec2 { + x: side.rect.max.x, + y: top.rect.max.y, + }; + e.offset_and_pan = e.pan + e.offset; + e.clip_rect = egui::Rect { + min: egui::Pos2 { + x: e.offset.to_pos2().x, + y: e.offset.to_pos2().y, + }, + max: egui::Pos2 { + x: f32::INFINITY, + y: f32::INFINITY, + }, + }; + egui::Context::request_repaint(ctx); + } else { + egui::TopBottomPanel::top("topBarEditor").show(ctx, |ui| { + Menu::new_editor(ui, gui); + }); + if gui.editor_use { + Editor::library(ctx, gui); + Editor::draw_area(ctx, gui, frame); + } + } + } + + fn should_area_update(&mut self, ctx: &egui::Context) -> bool { + if self.ui_change { + self.ui_change = false; + true + } else { + (egui::containers::panel::PanelState::load(ctx, egui::Id::from("topBarEditor")) + .unwrap() + .rect + .max + .y + - self.offset.y) + .abs() + > 0.1 + || (egui::containers::panel::PanelState::load(ctx, egui::Id::from("leftLibrary")) + .unwrap() + .rect + .max + .x + - self.offset.x) + .abs() + > 0.1 + } + } + + // Clicking library items will create a clone of them and insert them into the component store + fn library(ctx: &Context, gui: &mut Gui) { + egui::SidePanel::left("leftLibrary") + .default_width(gui.editor.as_mut().unwrap().side_panel_width) + .frame(egui::Frame::side_top_panel(&(*ctx.style()).clone()).fill(Color32::WHITE)) + .show(ctx, |ui| { + egui::ScrollArea::vertical().show(ui, |ui| { + ui.horizontal(|ui| { + let e = Editor::gui_to_editor(gui); + crate::gui_egui::library::show_library(e, ui); + }); + }); + }); + // + } + + fn draw_area(ctx: &Context, gui: &mut Gui, frame: egui::Frame) { + let mut layer_id: Option = None; + let central_panel = egui::CentralPanel::default().frame(frame).show(ctx, |ui| { + ui.set_clip_rect(Editor::gui_to_editor(gui).clip_rect); + + // draw a marker to show 0,0 + { + let e = Editor::gui_to_editor(gui); + ui.painter().add(Shape::line( + vec![ + offset_helper((30f32, 0f32), e.scale, e.offset_and_pan), + offset_helper((0f32, 0f32), e.scale, e.offset_and_pan), + offset_helper((0f32, 30f32), e.scale, e.offset_and_pan), + ], + egui::Stroke { + width: e.scale, + color: Color32::BLACK, + }, + )); + layer_id = Some(ui.layer_id()); + } + + // draw grid + if Editor::gui_to_editor(gui).grid.enable { + let e = Editor::gui_to_editor(gui); + let screen_rect = ui.ctx().screen_rect(); + let grid_scale = e.grid.size * e.scale; + let start = -(e.pan / e.grid.size / e.scale).floor(); + + let end = + (Vec2::new(screen_rect.width(), screen_rect.height()) / e.scale / e.grid.size) + .ceil() + + start; + + for y in (start.y as i32)..(end.y as i32) { + ui.painter().hline( + 0f32..=screen_rect.width(), + y as f32 * grid_scale + e.offset_and_pan.y, + egui::Stroke { + width: e.scale * 0.5f32, + color: egui::Color32::BLACK.gamma_multiply(e.grid.opacity), + }, + ); + } + for x in (start.x as i32)..(end.x as i32) { + ui.painter().vline( + x as f32 * grid_scale + e.offset_and_pan.x, + 0f32..=screen_rect.height(), + egui::Stroke { + width: e.scale * 0.5f32, + color: egui::Color32::BLACK.gamma_multiply(e.grid.opacity), + }, + ); + } + } + + let e = Editor::gui_to_editor(gui); + let id_ports = id_ports_of_all_components_non_wires(&e.components); + // The reason we do this is because some of the input modes requires references to + // components, but that makes us unable to get the mutable reference to it + // (We can only get a mutable reference if only ONE reference to it exists) + match e.editor_mode { + EditorMode::Wire | EditorMode::Input => { + for c in &e.components { + let old_key = c.as_ref().get_id_ports().0; + //println!("{}, {:?}", old_key, e.contexts); + match e.contexts.remove(&old_key) { + Some(mut context) => { + c.render( + ui, + &mut context, + None, + e.offset + e.pan, + e.scale, + e.clip_rect, + e.editor_mode, + ); + e.contexts.insert(context.id_tmp.clone(), context); + } + _ => { + println!("could remove old key") + } + } + } + } + _ => 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( + ui, + &mut context, + None, + e.offset_and_pan, + e.scale, + e.clip_rect, + &id_ports, + &e.grid, + e.editor_mode, + ); + // only reinsert if it's not getting deleted + if !render_return.delete { + e.contexts.insert(c.get_id_ports().0, context.clone()); + } + !render_return.delete + }), + } + }); + let e = Editor::gui_to_editor(gui); + + let cpr = central_panel.response.interact(egui::Sense::drag()); + if cpr.dragged_by(PointerButton::Middle) { + e.pan += cpr.drag_delta(); + e.offset_and_pan = e.pan + e.offset; + } + match e.editor_mode { + EditorMode::Wire => crate::gui_egui::editor_wire_mode::wire_mode(ctx, e, cpr, layer_id), + EditorMode::Input => crate::gui_egui::library::input_mode(ctx, e, cpr, layer_id), + EditorMode::Default | EditorMode::Simulator => { + ctx.output_mut(|o| o.cursor_icon = egui::CursorIcon::Default) + } + } + if central_panel.response.hovered() { + ctx.input_mut(|i| { + if i.scroll_delta.y > 0f32 { + keymap::view_zoom_in_fn(gui); + } else if i.scroll_delta.y < 0f32 { + keymap::view_zoom_out_fn(gui); + } + }); + } + } + + fn gui_to_editor(gui: &mut Gui) -> &mut Editor { + gui.editor.as_mut().unwrap() + } +} + +pub fn get_component(components: &Components, comp: CloseToComponent) -> Option { + for (i, c) in components.iter().enumerate() { + // doing std::ptr::eq doesn't work and this works so I'm going to keep it + // even if clippy errors on it + #[allow(clippy::vtable_address_comparisons)] + if Rc::ptr_eq(c, &comp.comp) { + drop(comp); + return Some(i); + } + } + None +} diff --git a/src/gui_egui/editor_wire_mode.rs b/src/gui_egui/editor_wire_mode.rs new file mode 100644 index 00000000..b30872ad --- /dev/null +++ b/src/gui_egui/editor_wire_mode.rs @@ -0,0 +1,416 @@ +use crate::common::{ComponentStore, Components, Id, Input}; +use crate::components::Wire; +use crate::gui_egui::editor::{ + get_component, CloseToComponent, Editor, EditorMode, GridOptions, SnapPriority, +}; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::helper::{ + id_ports_of_all_components, offset_helper, offset_helper_pos2, offset_reverse_helper, + offset_reverse_helper_pos2, unique_component_name, +}; +use egui::{ + Color32, Context, CursorIcon, LayerId, PointerButton, Pos2, Rect, Response, Shape, Stroke, Vec2, +}; +use std::{path::PathBuf, rc::Rc}; + +pub struct WireMode { + pub mode_ended: bool, + pub last_pos: Option, + pub input: Option, + pub cursor_location: Pos2, + pub start_comp_port: Option, + pub temp_positions: Vec<(f32, f32)>, +} + +pub fn drag_started(ctx: &Context, e: &mut Editor, _cpr: Response) { + ctx.input_mut(|i| { + let origin = i.pointer.press_origin().unwrap(); + e.wm.cursor_location = origin; + + let offset_cursor_scale = offset_reverse_helper_pos2(origin, e.scale, e.offset_and_pan); + let (closest, closest_wire) = + clicked_close_to_input_output(offset_cursor_scale, &e.components); + + match get_closest_component_non_wire_prio(closest, closest_wire, e.snap_distance) { + Some(closest_uw) => { + if e.wm.temp_positions.is_empty() { + // New Component + e.wm.mode_ended = false; + let new_pos = closest_uw.pos; + e.wm.start_comp_port = Some(closest_uw); + let new_pos = offset_helper((new_pos.x, new_pos.y), e.scale, e.offset_and_pan); + e.wm.temp_positions.push((new_pos.x, new_pos.y)); + } else { + // Continue existing component + last_click(e, closest_uw); + } + } + None => { + if !e.wm.temp_positions.is_empty() { + let mut wires = if e.grid.enable && e.grid.snap_enable { + match get_grid_snap(e.grid.snap_distance, offset_cursor_scale, e.grid.size) + { + Some(g) => { + let new_loc = offset_helper_pos2(g, e.scale, e.offset_and_pan); + wire_split_into_two_vec( + *e.wm.temp_positions.last().unwrap(), + (new_loc.x, new_loc.y), + ) + } + None => wire_split_into_two_vec( + *e.wm.temp_positions.last().unwrap(), + (origin.x, origin.y), + ), + } + } else { + wire_split_into_two_vec( + *e.wm.temp_positions.last().unwrap(), + (origin.x, origin.y), + ) + }; + e.wm.temp_positions.append(&mut wires) + } + } + } + }); +} + +pub fn last_click(e: &mut Editor, closest_uw: CloseToComponent) { + let in_c = e.wm.start_comp_port.take().unwrap(); + let out_c = closest_uw; + let (field_name, input_and_bool) = get_outputs_from_port(&in_c, &out_c); + match input_and_bool { + Some((i, is_input_in_comp_start)) => { + let id_ports = id_ports_of_all_components(&e.components); + let id = unique_component_name(&id_ports, "w"); + let id = id.as_str(); + let mut pos_v: Vec<(f32, f32)> = vec![]; + + for pos in &e.wm.temp_positions { + let pos_2 = offset_reverse_helper(*pos, e.scale, e.offset_and_pan); + pos_v.push((pos_2.x, pos_2.y)); + } + + let last_pos = *e.wm.temp_positions.last().unwrap(); + let last_pos = offset_reverse_helper(last_pos, e.scale, e.offset_and_pan); + let mut v = + wire_split_into_two_vec((last_pos.x, last_pos.y), (out_c.pos.x, out_c.pos.y)); + pos_v.append(&mut v); + + // Now actually set the input of the wired component + #[allow(clippy::vtable_address_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.contexts.insert( + id.to_string(), + EguiExtra { + properties_window: false, + size_rect: Rect::NAN, + id_tmp: id.to_string(), + pos_tmp: Pos2::ZERO, + }, + ); + + if let Some(c) = get_component(&e.components, comp) { + println!("setting id_port"); + + Rc::get_mut(&mut e.components[c]) + .unwrap() + .set_id_port(field_name, i); + } + } else { + println!("You cannot connect a wire to itself"); + } + } + None => { + println!("Seems like you don't exactly have one input at the start or end of the wire"); + } + } + let path = PathBuf::from("autosave.json"); + ComponentStore { + store: e.components.clone(), + } + .save_file(&path); + reset_wire_mode(&mut e.wm); +} + +pub fn wire_mode(ctx: &Context, e: &mut Editor, cpr: Response, layer_id: Option) { + ctx.output_mut(|o| o.cursor_icon = CursorIcon::Crosshair); + if e.wm.mode_ended && cpr.drag_started_by(PointerButton::Secondary) { + e.editor_mode = EditorMode::Default; + reset_wire_mode(&mut e.wm); + return; + } + + if cpr.drag_started_by(PointerButton::Primary) { + drag_started(ctx, e, cpr); + } else { + if cpr.drag_started_by(PointerButton::Secondary) { + // abort wire mode + reset_wire_mode(&mut e.wm); + } + + if !e.wm.mode_ended { + ctx.input_mut(|i| { + e.wm.cursor_location += i.pointer.delta(); + }); + let offset_cursor_scale = + offset_reverse_helper_pos2(e.wm.cursor_location, e.scale, e.offset_and_pan); + let (closest, closest_wire) = + clicked_close_to_input_output(offset_cursor_scale, &e.components); + + // Here we prioritize component ports over wires in snap distance + let wire_shown_location = get_location_of_port_wire_grid_inside_radius( + closest, + closest_wire, + e.snap_distance, + e.wm.cursor_location, + e.offset_and_pan, + e.scale, + &e.grid, + ); + + let v = wire_split_into_two_vec( + *e.wm.temp_positions.last().unwrap(), + (wire_shown_location.x, wire_shown_location.y), + ); + let mut draw_vec: Vec = vec![]; + for (posx, posy) in e.wm.temp_positions.clone().into_iter().chain(v.into_iter()) { + draw_vec.push(Pos2::new(posx, posy)) + } + + ctx.layer_painter(layer_id.unwrap()).add(Shape::line( + draw_vec, + Stroke { + width: e.scale * 1.5f32, + color: Color32::BLACK, + }, + )); + } else { + // todo: Marker that you are potientially close to connecting to something to start a wire + // here, like a circle or similar over the port + } + } +} + +/// This will only occasionally split the wire into two parts in case needed (e.g. drawing a straight +/// line) +pub fn wire_split_into_two_vec(prev: (f32, f32), current: (f32, f32)) -> Vec<(f32, f32)> { + let mut v = vec![]; + if f32::abs(current.0 - prev.0) < 0.1f32 && f32::abs(current.1 - prev.1) < 0.01f32 { + v.push(current); + } else { + let (wire1, wire2) = wire_split_into_two(prev, current); + v.push(wire1); + v.push(wire2); + } + v +} + +pub fn wire_split_into_two(prev: (f32, f32), current: (f32, f32)) -> ((f32, f32), (f32, f32)) { + if f32::abs(prev.0 - current.0) < f32::abs(prev.1 - current.1) { + ((current.0, prev.1), (current.0, current.1)) + } else { + ((prev.0, current.1), (current.0, current.1)) + } +} + +pub fn reset_wire_mode(wm: &mut WireMode) { + wm.mode_ended = true; + wm.last_pos = None; + wm.input = None; + wm.cursor_location = Pos2::ZERO; + wm.start_comp_port = None; + wm.temp_positions = vec![]; +} + +/// returns an input made from the output +pub fn get_outputs_from_port( + comp_start: &CloseToComponent, + comp_end: &CloseToComponent, +) -> (Id, Option<(Input, bool)>) { + let out_start: Option = match &comp_start.potential_actual_input { + Some(i) => Some(i.clone()), + None => match comp_start.potential_actual_input.clone() { + Some(s) => Some(s), + None => { + let mut o = None; + let (id, ports_start) = comp_start.comp.get_id_ports(); + for port_id in ports_start.outputs { + if port_id == *comp_start.port_id { + o = Some(Input { + id: id.clone(), + field: port_id, + }); + } + } + o + } + }, + }; + let out_end: Option = match &comp_end.potential_actual_input { + Some(i) => Some(i.clone()), + None => match comp_end.potential_actual_input.clone() { + Some(s) => Some(s), + None => { + let mut o = None; + let (id, ports_end) = comp_end.comp.get_id_ports(); + for port_id in ports_end.outputs { + if port_id == *comp_end.port_id { + o = Some(Input { + id: id.clone(), + field: port_id, + }); + } + } + o + } + }, + }; + match out_start { + Some(s) => match out_end { + Some(_) => (String::new(), None), + None => (comp_end.port_id.clone(), Some((s, true))), + }, + None => match out_end { + Some(e) => (comp_start.port_id.clone(), Some((e, false))), + None => (String::new(), None), + }, + } +} + +/// returns (Component, Wire) +pub fn clicked_close_to_input_output( + clicked_pos: Pos2, + components: &Components, +) -> (Option, Option) { + let mut closest: Option = None; + let mut closest_wire: Option = None; + for comp in components { + let ports = comp.ports_location(); + let prio = comp.snap_priority(); + let clos: &mut Option = match prio { + SnapPriority::Wire => &mut closest_wire, + _ => &mut closest, + }; + for (port_id, pos) in ports { + let dist = clicked_pos.distance(pos); + let potential_actual_input = match comp.snap_priority() { + SnapPriority::Wire => Some(comp.get_id_ports().1.inputs[0].input.clone()), + _ => None, + }; + match clos.as_ref() { + Some(c) => { + if dist < c.dist { + *clos = Some(CloseToComponent { + comp: comp.clone(), + pos, + dist, + port_id, + potential_actual_input, + }) + } + } + None => { + *clos = Some(CloseToComponent { + comp: comp.clone(), + pos, + dist, + port_id, + potential_actual_input, + }) + } + }; + } + } + + (closest, closest_wire) +} + +pub fn get_closest_component_non_wire_prio( + port: Option, + wire: Option, + distance: f32, +) -> Option { + fn is_wire_in_range(wire: Option, distance: f32) -> Option { + match wire { + Some(w) => { + if w.dist <= distance { + Some(w) + } else { + None + } + } + None => None, + } + } + + match port { + Some(p) => { + if p.dist <= distance { + Some(p) + } else { + is_wire_in_range(wire, distance) + } + } + None => None, + } +} + +#[allow(clippy::too_many_arguments)] +pub fn get_location_of_port_wire_grid_inside_radius( + port: Option, + wire: Option, + distance: f32, + cursor_location: Pos2, + offset: Vec2, + scale: f32, + grid: &GridOptions, +) -> Pos2 { + match get_closest_component_non_wire_prio(port, wire, distance) { + Some(c) => offset_helper_pos2(c.pos, scale, offset), + None => { + if grid.enable && grid.snap_enable { + match get_grid_snap( + grid.snap_distance, + offset_reverse_helper_pos2(cursor_location, scale, offset), + grid.size, + ) { + Some(s) => offset_helper_pos2(s, scale, offset), + None => cursor_location, + } + } else { + cursor_location + } + } + } +} + +pub fn get_grid_snap(distance: f32, clicked_pos: Pos2, grid_size: f32) -> Option { + let clicked_pos_v = clicked_pos.to_vec2(); + let top_left = (clicked_pos_v / grid_size).floor() * grid_size; + let top_right = Pos2::new(top_left.x + grid_size, top_left.y); + let bottom_left = Pos2::new(top_left.x, top_left.y + grid_size); + let bottom_right = Pos2::new(top_left.x + grid_size, top_left.y + grid_size); + let top_left = Pos2::new(top_left.x, top_left.y); + let mut closest: Option<(Pos2, f32)> = None; + for pos in &[top_left, top_right, bottom_left, bottom_right] { + let d = pos.distance(clicked_pos); + if d <= distance { + match closest { + Some(s) => { + if s.1 > d { + closest = Some((*pos, d)) + } + } + None => closest = Some((*pos, d)), + } + } + } + closest.map(|s| s.0) +} diff --git a/src/gui_egui/gui.rs b/src/gui_egui/gui.rs index 0d5e95e6..f3131150 100644 --- a/src/gui_egui/gui.rs +++ b/src/gui_egui/gui.rs @@ -1,118 +1,162 @@ -use crate::common::{ComponentStore, Simulator}; -use crate::gui_egui::{keymap, keymap::Shortcuts, menu::Menu}; -use eframe::egui; +use crate::common::{ComponentStore, Components, Simulator}; +use crate::gui_egui::editor::EditorMode; +use crate::gui_egui::{ + editor::{Editor, Library}, + keymap, + keymap::Shortcuts, + menu::Menu, +}; +use eframe::{egui, Frame}; +use egui::{ + containers, CentralPanel, Color32, Context, PointerButton, Pos2, Rect, Sense, TopBottomPanel, + Vec2, +}; +use std::collections::HashMap; use std::path::PathBuf; pub struct Gui { - pub simulator: Simulator, + pub simulator: Option, pub path: PathBuf, // History, acts like a stack - pub history: Vec>, pub scale: f32, // When the ui elements change size pub ui_change: bool, - pub offset: egui::Vec2, - pub pan: egui::Vec2, - pub clip_rect: egui::Rect, + pub offset: Vec2, + pub pan: Vec2, + pub clip_rect: Rect, pub shortcuts: Shortcuts, pub pause: bool, + pub editor: Option, + pub editor_use: bool, + pub contexts: HashMap, + pub library: Library, } -pub fn gui(cs: &ComponentStore, path: &PathBuf) -> Result<(), eframe::Error> { - let simulator = Simulator::new(cs); +#[derive(Clone, Debug)] +pub struct EguiExtra { + pub properties_window: bool, + pub size_rect: Rect, + pub id_tmp: String, + pub pos_tmp: Pos2, +} + +pub fn gui(cs: ComponentStore, path: &PathBuf, library: Library) -> Result<(), eframe::Error> { + let contexts = create_contexts(&cs.store); + let simulator = Simulator::new(cs).unwrap(); let options = eframe::NativeOptions::default(); let path = path.to_owned(); simulator.save_dot(&path); + let gui = Gui { path, - simulator, - history: vec![], + simulator: Some(simulator), scale: 1.0f32, ui_change: true, - offset: egui::Vec2 { x: 0f32, y: 0f32 }, - pan: egui::Vec2 { x: 0f32, y: 0f32 }, - clip_rect: egui::Rect::NOTHING, + offset: Vec2 { x: 0f32, y: 0f32 }, + pan: Vec2 { x: 0f32, y: 0f32 }, + clip_rect: Rect::NOTHING, shortcuts: Shortcuts::new(), pause: true, + editor: None, + editor_use: false, + contexts, + library, }; + eframe::run_native("SyncRim", options, Box::new(|_cc| Box::new(gui))) } impl eframe::App for Gui { - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + fn update(&mut self, ctx: &Context, frame: &mut eframe::Frame) { self.shortcuts.inputs(ctx, self); - let frame = egui::Frame::none().fill(egui::Color32::WHITE); + if self.editor_use { + crate::gui_egui::editor::Editor::update(ctx, frame, self); + return; + } + let frame = egui::Frame::none().fill(Color32::WHITE); + //let frame = egui::Frame::canvas(&(*ctx.style()).clone()); // For getting the correct offset for our drawing we need to get the top bar // and side panel of the ui once before we draw if self.should_area_update(ctx) { + // todo: Implement proper light and dark mode? + // for testing light and dark mode + //ctx.set_visuals(Visuals::dark()); + //ctx.set_visuals(Visuals::light()); self.top_bar(ctx); - self.side_panel(ctx); - let top = - egui::containers::panel::PanelState::load(ctx, egui::Id::from("topBar")).unwrap(); - let side = - egui::containers::panel::PanelState::load(ctx, egui::Id::from("leftGui")).unwrap(); - self.offset = egui::Vec2 { - x: side.rect.max.x, + //self.side_panel(ctx); + let top = containers::panel::PanelState::load(ctx, egui::Id::from("topBar")).unwrap(); + // let side = containers::panel::PanelState::load(ctx, egui::Id::from("leftGui")).unwrap(); + self.offset = Vec2 { + x: 0.0, y: top.rect.max.y, }; - self.clip_rect = egui::Rect { + self.clip_rect = Rect { min: self.offset.to_pos2(), - max: egui::Pos2 { + max: Pos2 { x: f32::INFINITY, y: f32::INFINITY, }, }; - egui::Context::request_repaint(ctx); + Context::request_repaint(ctx); } else { self.top_bar(ctx); - self.side_panel(ctx); - self.draw_area(ctx, frame); + if self.simulator.is_some() { + // self.side_panel(ctx); + self.draw_area(ctx, frame); + if self.simulator.as_ref().unwrap().running { + self.simulator.as_mut().unwrap().clock(); + ctx.request_repaint(); + } + } + } + } + fn post_rendering(&mut self, _window_size_px: [u32; 2], _frame: &Frame) { + if !self.pause { + match &mut self.simulator { + Some(s) => { + if s.running { + s.run(); + } + } + None => {} + } } } } impl Gui { - fn should_area_update(&mut self, ctx: &egui::Context) -> bool { + fn should_area_update(&mut self, _ctx: &Context) -> bool { if self.ui_change { self.ui_change = false; true } else { - (egui::containers::panel::PanelState::load(ctx, egui::Id::from("topBar")) - .unwrap() - .rect - .max - .y - - self.offset.y) - .abs() - > 0.1 - || (egui::containers::panel::PanelState::load(ctx, egui::Id::from("leftGui")) - .unwrap() - .rect - .max - .x - - self.offset.x) - .abs() - > 0.1 + false } } - fn draw_area(&mut self, ctx: &egui::Context, frame: egui::Frame) { - let central_panel = egui::CentralPanel::default().frame(frame).show(ctx, |ui| { + fn draw_area(&mut self, ctx: &Context, frame: egui::Frame) { + let central_panel = CentralPanel::default().frame(frame).show(ctx, |ui| { + let sim = self.simulator.as_mut().unwrap(); ui.set_clip_rect(self.clip_rect); // Don't draw over the rest of the ui - for c in &self.simulator.ordered_components { + for c in &sim.ordered_components.clone() { + let old_key = c.as_ref().get_id_ports().0; + let mut context = self.contexts.remove(&old_key).unwrap(); c.render( ui, - self.simulator.clone(), + &mut context, + Some(sim), self.offset + self.pan, self.scale, self.clip_rect, + EditorMode::Simulator, ); + self.contexts.insert(context.id_tmp.clone(), context); } }); - let cpr = central_panel.response.interact(egui::Sense::drag()); - if cpr.dragged_by(egui::PointerButton::Middle) { + let cpr = central_panel.response.interact(Sense::drag()); + if cpr.dragged_by(PointerButton::Middle) { self.pan += cpr.drag_delta(); } if central_panel.response.hovered() { @@ -124,20 +168,31 @@ impl Gui { } }); } + if self.simulator.as_ref().unwrap().running { + self.simulator.as_mut().unwrap().clock(); + ctx.request_repaint(); + } } - fn side_panel(&mut self, ctx: &egui::Context) { - egui::SidePanel::left("leftGui").show(ctx, |ui| { - egui::ScrollArea::vertical().show(ui, |ui| { - ui.horizontal(|ui| { - ui.label("0x00000004\n0x00000008\n".repeat(100)); - ui.label("100000\n20000\n".repeat(100)); - }); - }); - }); + fn top_bar(&mut self, ctx: &Context) { + TopBottomPanel::top("topBar").show(ctx, |ui| Menu::new(ui, self)); } +} - fn top_bar(&mut self, ctx: &egui::Context) { - egui::TopBottomPanel::top("topBar").show(ctx, |ui| Menu::new(ui, self)); +pub fn create_contexts(components: &Components) -> HashMap { + let mut contexts = HashMap::new(); + for c in &components.clone() { + let id = c.get_id_ports().0; + let pos = c.get_pos(); + contexts.insert( + id.clone(), + EguiExtra { + properties_window: false, + size_rect: Rect::NAN, + id_tmp: id, + pos_tmp: Pos2::new(pos.0, pos.1), + }, + ); } + contexts } diff --git a/src/gui_egui/helper.rs b/src/gui_egui/helper.rs index f3045963..4ccb5520 100644 --- a/src/gui_egui/helper.rs +++ b/src/gui_egui/helper.rs @@ -1,6 +1,108 @@ -pub fn offset_helper(xy: (f32, f32), scale: f32, offset: egui::Vec2) -> egui::Pos2 { +use crate::common::{Components, Ports}; +use crate::gui_egui::editor::{EditorMode, SnapPriority}; +use egui::{Pos2, Rect, Sense, Vec2}; + +pub fn offset_reverse_helper_pos2(xy: Pos2, scale: f32, offset: Vec2) -> Pos2 { + egui::Pos2 { + x: (xy.x - offset.x) / scale, + y: (xy.y - offset.y) / scale, + } +} + +pub fn offset_reverse_helper(xy: (f32, f32), scale: f32, offset: Vec2) -> Pos2 { + egui::Pos2 { + x: (xy.0 - offset.x) / scale, + y: (xy.1 - offset.y) / scale, + } +} + +pub fn offset_helper_pos2(xy: Pos2, scale: f32, offset: Vec2) -> Pos2 { + egui::Pos2 { + x: xy.x * scale, + y: xy.y * scale, + } + offset +} + +pub fn offset_helper(xy: (f32, f32), scale: f32, offset: Vec2) -> Pos2 { egui::Pos2 { x: xy.0 * scale, y: xy.1 * scale, } + offset } + +pub fn out_of_bounds(request: Rect, clip_rect: Rect) -> Rect { + let mut rect = Rect::NOTHING; + if request.max.x < clip_rect.min.x + || request.max.y < clip_rect.min.y + || request.min.x > clip_rect.max.x + || request.min.y > clip_rect.max.y + { + return rect; + } + rect = request; + if request.max.x > clip_rect.max.x { + rect.max.x = clip_rect.max.x; + } + if request.max.y > clip_rect.max.y { + rect.max.y = clip_rect.max.y; + } + if request.min.x < clip_rect.min.x { + rect.min.x = clip_rect.min.x; + } + if request.min.y < clip_rect.min.y { + rect.min.y = clip_rect.min.y; + } + rect +} + +pub fn unique_component_name(id_ports: &[(crate::common::Id, Ports)], id: &str) -> String { + let mut new_id: String = id.into(); + let mut contains_id = true; + while contains_id { + contains_id = false; + for c in id_ports { + let id = c.0.clone(); + if id == new_id { + contains_id = true; + // todo: make this fancier + new_id.push('1'); + break; + } + } + } + new_id +} + +pub fn id_ports_of_all_components(cs: &Components) -> Vec<(crate::common::Id, Ports)> { + let mut v = vec![]; + for c in cs.iter() { + v.push(c.get_id_ports()); + } + v +} + +pub fn id_ports_of_all_components_non_wires(cs: &Components) -> Vec<(crate::common::Id, Ports)> { + let mut v = vec![]; + for c in cs.iter() { + match c.snap_priority() { + SnapPriority::Wire => (), + _ => v.push(c.get_id_ports()), + } + } + v +} + +pub fn editor_mode_to_sense(editor_mode: EditorMode) -> Sense { + match editor_mode { + EditorMode::Wire => Sense { + click: false, + drag: false, + focusable: false, + }, + _ => Sense { + click: true, + drag: true, + focusable: true, + }, + } +} diff --git a/src/gui_egui/keymap.rs b/src/gui_egui/keymap.rs index 24956bac..6ca0cf39 100644 --- a/src/gui_egui/keymap.rs +++ b/src/gui_egui/keymap.rs @@ -1,4 +1,12 @@ +use crate::common::{ComponentStore, Simulator}; +use crate::gui_egui::editor::{Editor, EditorMode}; +use crate::gui_egui::editor_wire_mode::reset_wire_mode; +use crate::gui_egui::gui::create_contexts; +use crate::gui_egui::library::reset_input_mode; +use crate::gui_egui::Gui; use egui::{Key, KeyboardShortcut, Modifiers}; +use rfd::FileDialog; +use std::path::PathBuf; #[derive(Copy, Clone)] pub struct Shortcuts { @@ -6,6 +14,7 @@ pub struct Shortcuts { pub file_open: KeyboardShortcut, pub file_save: KeyboardShortcut, pub file_save_as: KeyboardShortcut, + pub file_editor_toggle: KeyboardShortcut, pub file_preferences: KeyboardShortcut, pub file_quit: KeyboardShortcut, pub edit_cut: KeyboardShortcut, @@ -13,12 +22,16 @@ pub struct Shortcuts { pub edit_paste: KeyboardShortcut, pub view_zoom_in: KeyboardShortcut, pub view_zoom_out: KeyboardShortcut, + pub view_grid_toggle: KeyboardShortcut, + pub view_grid_snap_toggle: KeyboardShortcut, pub control_play_toggle: KeyboardShortcut, pub control_play: KeyboardShortcut, pub control_pause: KeyboardShortcut, pub control_reset: KeyboardShortcut, pub control_step_forward: KeyboardShortcut, pub control_step_back: KeyboardShortcut, + pub editor_wire_mode: KeyboardShortcut, + pub editor_escape: KeyboardShortcut, } impl Default for Shortcuts { @@ -74,6 +87,10 @@ impl Shortcuts { }, key: Key::S, }, + file_editor_toggle: KeyboardShortcut { + modifiers: ctrl, + key: Key::E, + }, file_preferences: KeyboardShortcut { modifiers: ctrl, key: Key::P, @@ -102,6 +119,20 @@ impl Shortcuts { modifiers: ctrl, key: Key::Minus, }, + view_grid_toggle: KeyboardShortcut { + modifiers: ctrl, + key: Key::G, + }, + view_grid_snap_toggle: KeyboardShortcut { + modifiers: Modifiers { + alt: false, + ctrl: true, + shift: true, + mac_cmd: false, + command: false, + }, + key: Key::G, + }, control_play: KeyboardShortcut { modifiers: none, key: Key::F6, @@ -132,10 +163,18 @@ impl Shortcuts { modifiers: shift, key: Key::F10, }, + editor_wire_mode: KeyboardShortcut { + modifiers: none, + key: Key::W, + }, + editor_escape: KeyboardShortcut { + modifiers: none, + key: Key::Escape, + }, } } - pub fn inputs(self, ctx: &egui::Context, gui: &mut crate::gui_egui::gui::Gui) { + pub fn inputs(self, ctx: &egui::Context, gui: &mut Gui) { if ctx.input_mut(|i| i.consume_shortcut(&self.file_new)) { file_new_fn(gui); } @@ -148,6 +187,9 @@ impl Shortcuts { if ctx.input_mut(|i| i.consume_shortcut(&self.file_save_as)) { file_save_as_fn(gui); } + if ctx.input_mut(|i| i.consume_shortcut(&self.file_editor_toggle)) { + file_editor_toggle_fn(gui); + } if ctx.input_mut(|i| i.consume_shortcut(&self.file_preferences)) { file_preferences_fn(gui); } @@ -169,72 +211,232 @@ impl Shortcuts { if ctx.input_mut(|i| i.consume_shortcut(&self.view_zoom_out)) { view_zoom_out_fn(gui); } + if ctx.input_mut(|i| i.consume_shortcut(&self.view_grid_toggle)) { + view_grid_toggle_fn(gui); + } + if ctx.input_mut(|i| i.consume_shortcut(&self.view_grid_snap_toggle)) { + view_grid_snap_toggle_fn(gui); + } if ctx.input_mut(|i| i.consume_shortcut(&self.control_play_toggle)) { - control_play_toggle(gui); + control_play_toggle_fn(gui); } if ctx.input_mut(|i| i.consume_shortcut(&self.control_play)) { - control_play(gui); + control_play_fn(gui); } if ctx.input_mut(|i| i.consume_shortcut(&self.control_pause)) { - control_pause(gui); + control_pause_fn(gui); } if ctx.input_mut(|i| i.consume_shortcut(&self.control_reset)) { - control_reset(gui); + control_reset_fn(gui); } if ctx.input_mut(|i| i.consume_shortcut(&self.control_step_forward)) { - control_step_forward(gui); + control_step_forward_fn(gui); } if ctx.input_mut(|i| i.consume_shortcut(&self.control_step_back)) { - control_step_back(gui); + control_step_back_fn(gui); + } + if ctx.input_mut(|i| i.consume_shortcut(&self.editor_wire_mode)) { + editor_wire_mode_fn(gui); + } + if ctx.input_mut(|i| i.consume_shortcut(&self.editor_escape)) { + editor_escape_fn(gui); } } } -pub fn file_new_fn(_gui: &mut crate::gui_egui::gui::Gui) {} -pub fn file_open_fn(_gui: &mut crate::gui_egui::gui::Gui) {} -pub fn file_save_fn(_gui: &mut crate::gui_egui::gui::Gui) {} -pub fn file_save_as_fn(_gui: &mut crate::gui_egui::gui::Gui) {} -pub fn file_preferences_fn(_gui: &mut crate::gui_egui::gui::Gui) {} -pub fn file_quit_fn(_gui: &mut crate::gui_egui::gui::Gui) {} -pub fn edit_cut_fn(_gui: &mut crate::gui_egui::gui::Gui) {} -pub fn edit_copy_fn(_gui: &mut crate::gui_egui::gui::Gui) {} -pub fn edit_paste_fn(_gui: &mut crate::gui_egui::gui::Gui) {} -pub fn view_zoom_in_fn(gui: &mut crate::gui_egui::gui::Gui) { - match gui.scale { - x if (0.0f32..0.2f32).contains(&x) => gui.scale = 0.25f32, - x if (0.2f32..0.4f32).contains(&x) => gui.scale = 0.5f32, - x if (0.4f32..0.6f32).contains(&x) => gui.scale = 1f32, - x if (0.9f32..1.1f32).contains(&x) => gui.scale = 1.5f32, - x if (1.4f32..1.6f32).contains(&x) => gui.scale = 2f32, - _ => gui.scale = 2f32, - } -} -pub fn view_zoom_out_fn(gui: &mut crate::gui_egui::gui::Gui) { - match gui.scale { - x if (0.2f32..0.4f32).contains(&x) => gui.scale = 0.1f32, - x if (0.4f32..0.6f32).contains(&x) => gui.scale = 0.25f32, - x if (0.9f32..1.1f32).contains(&x) => gui.scale = 0.5f32, - x if (1.4f32..1.6f32).contains(&x) => gui.scale = 1f32, - x if (1.9f32..2.1f32).contains(&x) => gui.scale = 1.5f32, - _ => gui.scale = 0.1f32, - } -} -pub fn control_play_toggle(gui: &mut crate::gui_egui::gui::Gui) { - gui.pause = !gui.pause; -} -pub fn control_play(gui: &mut crate::gui_egui::gui::Gui) { - gui.pause = false; -} -pub fn control_pause(gui: &mut crate::gui_egui::gui::Gui) { - gui.pause = true; -} -pub fn control_reset(gui: &mut crate::gui_egui::gui::Gui) { - gui.simulator.reset(); - gui.pause = true; -} -pub fn control_step_forward(gui: &mut crate::gui_egui::gui::Gui) { - gui.simulator.clock(); -} -pub fn control_step_back(gui: &mut crate::gui_egui::gui::Gui) { - gui.simulator.un_clock(); +pub fn file_new_fn(_gui: &mut Gui) {} +pub fn file_open_fn(gui: &mut Gui) { + let files = FileDialog::new().add_filter("json", &["json"]).pick_file(); + if let Some(path_buf) = files { + gui.path = path_buf; + } + let cs = ComponentStore::load_file(&gui.path); + let contexts = create_contexts(&cs.store); + match gui.editor_use { + true => { + if let Some(e) = gui.editor.as_mut() { + // Clear all references + reset_wire_mode(&mut e.wm); + reset_input_mode(&mut e.im); + e.components = cs.store; + e.contexts = contexts; + } + } + false => { + let simulator = Simulator::new(cs); + gui.contexts = contexts; + match simulator { + Err(e) => { + println!("couldn't open file with simulator: {}", e); + } + Ok(s) => { + let _ = gui.simulator.take(); + gui.simulator = Some(s); + } + } + } + } +} +pub fn file_save_fn(gui: &mut Gui) { + match gui.editor_use { + true => { + if let Some(e) = gui.editor.as_mut() { + ComponentStore { + store: e.components.clone(), + } + .save_file(&gui.path) + } + } + false => ComponentStore { + store: gui.simulator.clone().unwrap().ordered_components, + } + .save_file(&PathBuf::from("file.json")), + } +} +pub fn file_save_as_fn(gui: &mut Gui) { + let files = FileDialog::new().add_filter("json", &["json"]).save_file(); + if let Some(path_buf) = files { + gui.path = path_buf; + file_save_fn(gui); + } +} +pub fn file_editor_toggle_fn(gui: &mut Gui) { + // Auto-save + file_save_fn(gui); + match gui.editor_use { + true => { + gui.editor_use = false; + if let Some(e) = gui.editor.as_mut() { + let components = e.components.clone(); + gui.contexts = create_contexts(&components); + let simulator = Simulator::new(ComponentStore { store: components }); + match simulator { + Err(e) => { + gui.editor_use = true; + println!("error: {}", e); + } + Ok(s) => gui.simulator = Some(s), + } + } + } + false => { + let editor_existed: bool = gui.editor.as_mut().is_some(); + + let simulator = gui.simulator.take().unwrap(); + let components = simulator.ordered_components; + + if !editor_existed { + gui.editor = Some(Editor::gui(components, &gui.path, &gui.library)); + } + + gui.editor_use = true; + } + } +} +pub fn file_preferences_fn(_gui: &mut Gui) {} +pub fn file_quit_fn(_gui: &mut Gui) {} +pub fn edit_cut_fn(_gui: &mut Gui) {} +pub fn edit_copy_fn(_gui: &mut Gui) {} +pub fn edit_paste_fn(_gui: &mut Gui) {} +pub fn view_zoom_in_fn(gui: &mut Gui) { + let scale: &mut f32 = match gui.editor_use { + true => &mut gui.editor.as_mut().unwrap().scale, + false => &mut gui.scale, + }; + match *scale { + x if (0.0f32..0.2f32).contains(&x) => *scale = 0.25f32, + x if (0.2f32..0.4f32).contains(&x) => *scale = 0.5f32, + x if (0.4f32..0.6f32).contains(&x) => *scale = 1f32, + x if (0.9f32..1.1f32).contains(&x) => *scale = 1.5f32, + x if (1.4f32..1.6f32).contains(&x) => *scale = 2f32, + _ => *scale = 2f32, + } +} +pub fn view_zoom_out_fn(gui: &mut Gui) { + let scale: &mut f32 = match gui.editor_use { + true => &mut gui.editor.as_mut().unwrap().scale, + false => &mut gui.scale, + }; + match *scale { + x if (0.2f32..0.4f32).contains(&x) => *scale = 0.1f32, + x if (0.4f32..0.6f32).contains(&x) => *scale = 0.25f32, + x if (0.9f32..1.1f32).contains(&x) => *scale = 0.5f32, + x if (1.4f32..1.6f32).contains(&x) => *scale = 1f32, + x if (1.9f32..2.1f32).contains(&x) => *scale = 1.5f32, + _ => *scale = 0.1f32, + } +} +pub fn view_grid_toggle_fn(gui: &mut Gui) { + if gui.editor_use { + let editor = gui.editor.as_mut().unwrap(); + editor.grid.enable = !editor.grid.enable; + } +} +pub fn view_grid_snap_toggle_fn(gui: &mut Gui) { + if gui.editor_use { + let editor = gui.editor.as_mut().unwrap(); + editor.grid.snap_enable = !editor.grid.snap_enable; + } +} +pub fn control_play_toggle_fn(gui: &mut Gui) { + if !gui.editor_use { + gui.pause = !gui.pause; + } +} +pub fn control_play_fn(gui: &mut Gui) { + if !gui.editor_use { + gui.pause = false; + gui.simulator.as_mut().unwrap().running = true; + //gui.simulator.as_mut().unwrap().run(); + //gui.pause = true; + } +} +//pub fn step(gui: &mut Gui) { +// if gui.simulator.as_ref().unwrap().running == true { +// gui.simulator.as_mut().unwrap().clock(); +// } +//} +pub fn control_pause_fn(gui: &mut Gui) { + if !gui.editor_use { + gui.pause = true; + gui.simulator.as_mut().unwrap().running = false; + } +} +pub fn control_reset_fn(gui: &mut Gui) { + if !gui.editor_use { + gui.simulator.as_mut().unwrap().reset(); + gui.pause = true; + } +} +pub fn control_step_forward_fn(gui: &mut Gui) { + if !gui.editor_use { + gui.simulator.as_mut().unwrap().clock(); + } +} +pub fn control_step_back_fn(gui: &mut Gui) { + if !gui.editor_use { + gui.simulator.as_mut().unwrap().un_clock(); + } +} +pub fn editor_wire_mode_fn(gui: &mut Gui) { + if gui.editor_use { + let editor = gui.editor.as_mut().unwrap(); + match editor.editor_mode { + EditorMode::Default | EditorMode::Input | EditorMode::Simulator => { + editor.editor_mode = EditorMode::Wire; + } + EditorMode::Wire => { + editor.editor_mode = EditorMode::Default; + } + } + reset_wire_mode(&mut editor.wm); + } +} +pub fn editor_escape_fn(gui: &mut Gui) { + if gui.editor_use { + let editor = gui.editor.as_mut().unwrap(); + editor.editor_mode = EditorMode::Default; + reset_wire_mode(&mut editor.wm); + reset_input_mode(&mut editor.im); + } } diff --git a/src/gui_egui/library.rs b/src/gui_egui/library.rs new file mode 100644 index 00000000..91982116 --- /dev/null +++ b/src/gui_egui/library.rs @@ -0,0 +1,161 @@ +use crate::common::{ComponentStore, EguiComponent}; +use crate::gui_egui::gui::EguiExtra; +use crate::gui_egui::{ + editor::{Editor, EditorMode}, + editor_wire_mode::get_grid_snap, + helper::{id_ports_of_all_components, offset_reverse_helper_pos2, unique_component_name}, +}; +use egui::{Context, CursorIcon, LayerId, PointerButton, Pos2, Rect, Response, Ui, Vec2}; +use std::{collections::HashMap, path::PathBuf, rc::Rc}; + +pub struct InputMode { + pub comp: Option>, + pub cursor_location: Pos2, + pub library_contexts: HashMap, +} + +pub fn input_mode(ctx: &Context, e: &mut Editor, cpr: Response, layer_id: Option) { + let layer_id = layer_id.unwrap(); + if cpr.drag_started_by(PointerButton::Secondary) { + e.editor_mode = EditorMode::Default; + reset_input_mode(&mut e.im); + return; + } + ctx.output_mut(|o| o.cursor_icon = CursorIcon::None); + ctx.input_mut(|i| { + e.im.cursor_location += i.pointer.delta(); + }); + let clip_rect = Rect { + min: Pos2 { + x: 0f32, + y: e.offset.y, + }, + max: Pos2 { + x: f32::INFINITY, + y: f32::INFINITY, + }, + }; + let mut ui = Ui::new( + ctx.to_owned(), + layer_id, + "input".into(), + clip_rect, + Rect::EVERYTHING, + ); + let pos = if e.grid.enable && e.grid.snap_enable { + match get_grid_snap( + e.grid.snap_distance, + offset_reverse_helper_pos2(e.im.cursor_location, e.scale, e.offset_and_pan), + e.grid.size, + ) { + Some(s) => Vec2::new(s.x, s.y) * e.scale + e.offset + e.pan * e.scale, + None => Vec2::new(e.im.cursor_location.x, e.im.cursor_location.y), + } + } else { + Vec2::new(e.im.cursor_location.x, e.im.cursor_location.y) + }; + e.im.comp.as_ref().unwrap().render( + &mut ui, + &mut EguiExtra { + properties_window: false, + size_rect: Rect::NAN, + id_tmp: String::new(), + pos_tmp: Pos2::ZERO, + }, + None, + pos, + e.scale, + clip_rect, + e.editor_mode, + ); + + if cpr.drag_started_by(PointerButton::Primary) { + add_comp_to_editor(e); + } +} + +pub fn reset_input_mode(im: &mut InputMode) { + im.comp = None; + im.cursor_location = Pos2::ZERO; +} + +pub fn show_library(e: &mut Editor, ui: &mut Ui) { + let mut padding = Vec2 { + x: e.offset.x / 2f32, + y: e.offset.y + 10f32, + }; + let clip_rect = Rect { + min: Pos2 { + x: 0f32, + y: e.offset.y, + }, + max: Pos2 { + x: e.offset.x, + y: f32::INFINITY, + }, + }; + for c in e.library.iter() { + let size = c.top_padding(); + padding.y += size; + let r_vec = c + .render( + ui, + &mut EguiExtra { + properties_window: false, + size_rect: Rect::NAN, + id_tmp: c.get_id_ports().0, + pos_tmp: Pos2::ZERO, + }, + None, + padding, + 0.5f32, + clip_rect, + e.editor_mode, + ) + .unwrap(); + let rect = r_vec[0].rect; + for resp in r_vec { + // Create new component + if resp.drag_started_by(PointerButton::Primary) { + e.editor_mode = EditorMode::Input; + e.im.comp = Some(c.clone()); + ui.input_mut(|i| { + let origin = i.pointer.press_origin().unwrap(); + e.im.cursor_location = origin; + }); + } else if resp.drag_started_by(PointerButton::Secondary) { + reset_input_mode(&mut e.im); + } + } + padding.y = rect.max.y + 10f32; + } +} + +// todo: This should really just copy the component that's in e.input_comp +pub fn add_comp_to_editor(e: &mut Editor) { + let mut pos = offset_reverse_helper_pos2(e.im.cursor_location, e.scale, e.offset_and_pan); + if e.grid.enable && e.grid.snap_enable { + if let Some(p) = get_grid_snap(e.grid.snap_distance, pos, e.grid.size) { + pos = p; + } + } + let id_ports = id_ports_of_all_components(&e.components); + let cloned = e.im.comp.clone().unwrap(); + let id = unique_component_name(&id_ports, cloned.get_id_ports().0.as_str()); + let instance = cloned.dummy(&id, (pos.x, pos.y)); + e.contexts.insert( + id.clone(), + EguiExtra { + properties_window: false, + size_rect: Rect::NAN, + id_tmp: id, + pos_tmp: pos, + }, + ); + e.components.push(*instance); + let path = PathBuf::from("autosave.json"); + ComponentStore { + store: e.components.clone(), + } + .save_file(&path); +} diff --git a/src/gui_egui/menu.rs b/src/gui_egui/menu.rs index 7f8f1945..6155e0ba 100644 --- a/src/gui_egui/menu.rs +++ b/src/gui_egui/menu.rs @@ -1,115 +1,209 @@ +use crate::gui_egui::{ + editor::{Editor, GridOptions}, + gui::Gui, + keymap, +}; +use egui::{menu, Button, DragValue, KeyboardShortcut, Response, Ui}; + pub(crate) struct Menu {} impl Menu { #[allow(clippy::new_ret_no_self)] - pub(crate) fn new(ui: &mut egui::Ui, gui: &mut crate::gui_egui::gui::Gui) { - fn btn(ui: &mut egui::Ui, name: &str, keys: egui::KeyboardShortcut) -> egui::Response { - ui.add(egui::Button::new(name).shortcut_text(ui.ctx().format_shortcut(&keys))) - } - - egui::menu::bar(ui, |ui| { - ui.menu_button("File", |ui| { - if btn(ui, "File", gui.shortcuts.file_new).clicked() { - // New here - } - if btn(ui, "Open", gui.shortcuts.file_open).clicked() { - // Open here - } - ui.menu_button("Open Recent", |_ui| { - // Recent here - //if ui.button("file1").clicked() { - // // Open file - //} - }); - ui.separator(); - if btn(ui, "Save", gui.shortcuts.file_save).clicked() { - // Save here - } - if btn(ui, "Save As", gui.shortcuts.file_save_as).clicked() { - // Save As here - } - ui.separator(); - if btn(ui, "Preferences", gui.shortcuts.file_preferences).clicked() { - // Preferences here - } - if btn(ui, "Quit", gui.shortcuts.file_quit).clicked() { - // Quit here - } - }); + pub(crate) fn new(ui: &mut Ui, gui: &mut Gui) { + menu::bar(ui, |ui| { + shared_buttons_file(gui, ui); + shared_buttons_edit(gui, ui); - ui.menu_button("Edit", |ui| { - if btn(ui, "Cut", gui.shortcuts.edit_cut).clicked() { - // Cut here - } - if btn(ui, "Copy", gui.shortcuts.edit_copy).clicked() { - // Copy here - } - if btn(ui, "Paste", gui.shortcuts.edit_paste).clicked() { - // Paste here - } - }); + let mut scale = gui.scale; + shared_buttons_view(gui, ui, &mut scale, |_| {}); + gui.scale = scale; - ui.menu_button("View", |ui| { - if btn(ui, "Zoom In", gui.shortcuts.view_zoom_in).clicked() { - crate::gui_egui::keymap::view_zoom_in_fn(gui); - } - if btn(ui, "Zoom Out", gui.shortcuts.view_zoom_out).clicked() { - crate::gui_egui::keymap::view_zoom_out_fn(gui); - } - ui.menu_button("Zoom Level", |ui| { - if ui.button("10%").clicked() { - // 10% zoom here here - gui.scale = 0.1f32; - } - if ui.button("25%").clicked() { - // 25% zoom here here - gui.scale = 0.25f32; - } - if ui.button("50%").clicked() { - // 50% zoom here here - gui.scale = 0.5f32; - } - if ui.button("100%").clicked() { - // 100% zoom here here - gui.scale = 1f32; - } - if ui.button("150%").clicked() { - // 150% zoom here here - gui.scale = 1.5f32; - } - if ui.button("200%").clicked() { - // 200% zoom here here - gui.scale = 2f32; - } - }); - }); - - ui.menu_button("Help", |ui| { - if ui.button("Show license").clicked() { - // Show license here - } - if ui.button("About").clicked() { - // About here - } - }); + shared_buttons_help(gui, ui); }); ui.horizontal(|ui| { if ui.button("⟲").clicked() { - crate::gui_egui::keymap::control_reset(gui); + keymap::control_reset_fn(gui); } if ui.button("⏮").clicked() { - crate::gui_egui::keymap::control_step_back(gui); + keymap::control_step_back_fn(gui); } if ui.button("⏭").clicked() { - crate::gui_egui::keymap::control_step_forward(gui); + keymap::control_step_forward_fn(gui); } if ui.button("▶").clicked() { - crate::gui_egui::keymap::control_play(gui); + keymap::control_play_fn(gui); } if ui.button("⏸").clicked() { - crate::gui_egui::keymap::control_pause(gui); + keymap::control_pause_fn(gui); + } + if let Some(s) = gui.simulator.as_ref() { + ui.label(format!("Cycle #{}", s.cycle)); } - ui.label(format!("Cycle #{}", gui.simulator.cycle)); }); } + + #[allow(clippy::new_ret_no_self)] + pub(crate) fn new_editor(ui: &mut Ui, gui: &mut Gui) { + fn editor(gui: &mut Gui) -> &mut Editor { + gui.editor.as_mut().unwrap() + } + + menu::bar(ui, |ui| { + shared_buttons_file(gui, ui); + shared_buttons_edit(gui, ui); + let mut scale = editor(gui).scale; + let mut grid_enable = editor(gui).grid.enable; + let mut grid_size = editor(gui).grid.size; + let mut grid_opacity = editor(gui).grid.opacity; + let mut grid_snap_enable = editor(gui).grid.snap_enable; + let mut grid_snap_distance = editor(gui).grid.snap_distance; + let view_grid_toggle = gui.shortcuts.view_grid_toggle; + let view_grid_snap_toggle = gui.shortcuts.view_grid_snap_toggle; + shared_buttons_view(gui, ui, &mut scale, |ui| { + ui.horizontal(|ui| { + ui.checkbox(&mut grid_enable, "Grid Enable"); + ui.label(ui.ctx().format_shortcut(&view_grid_toggle)); + }); + ui.horizontal(|ui| { + ui.label("Grid Size:"); + ui.add(DragValue::new(&mut grid_size).clamp_range(5f32..=10000f32)); + }); + ui.horizontal(|ui| { + ui.label("Grid Opacity:"); + ui.add( + DragValue::new(&mut grid_opacity) + .clamp_range(0f32..=1f32) + .speed(0.02f32), + ); + }); + ui.horizontal(|ui| { + ui.checkbox(&mut grid_snap_enable, "Grid Snapping Enable"); + ui.label(ui.ctx().format_shortcut(&view_grid_snap_toggle)); + }); + ui.horizontal(|ui| { + ui.label("Grid Snap Distance:"); + ui.add(DragValue::new(&mut grid_snap_distance).clamp_range(0f32..=100f32)); + }); + }); + editor(gui).scale = scale; + editor(gui).grid = GridOptions { + enable: grid_enable, + size: grid_size, + opacity: grid_opacity, + snap_enable: grid_snap_enable, + snap_distance: grid_snap_distance, + }; + shared_buttons_help(gui, ui); + }); + ui.horizontal(|ui| { + let wire_button = ui.button("➖").on_hover_text("Wire mode"); + if wire_button.clicked() { + keymap::editor_wire_mode_fn(gui); + } + }); + } +} + +fn btn(ui: &mut Ui, name: &str, keys: KeyboardShortcut) -> Response { + ui.add(Button::new(name).shortcut_text(ui.ctx().format_shortcut(&keys))) +} + +fn shared_buttons_file(gui: &mut Gui, ui: &mut Ui) { + ui.menu_button("File", |ui| { + if btn(ui, "New", gui.shortcuts.file_new).clicked() { + keymap::file_new_fn(gui); + } + if btn(ui, "Open", gui.shortcuts.file_open).clicked() { + keymap::file_open_fn(gui); + } + ui.menu_button("Open Recent", |_ui| { + // Recent here + //if ui.button("file1").clicked() { + // // Open file + //} + }); + ui.separator(); + if btn(ui, "Save", gui.shortcuts.file_save).clicked() { + keymap::file_save_fn(gui); + } + if btn(ui, "Save As", gui.shortcuts.file_save_as).clicked() { + keymap::file_save_as_fn(gui); + } + ui.separator(); + if btn(ui, "Editor", gui.shortcuts.file_editor_toggle).clicked() { + keymap::file_editor_toggle_fn(gui); + } + if btn(ui, "Preferences", gui.shortcuts.file_preferences).clicked() { + keymap::file_preferences_fn(gui); + } + if btn(ui, "Quit", gui.shortcuts.file_quit).clicked() { + keymap::file_quit_fn(gui); + } + }); +} + +fn shared_buttons_edit(gui: &mut Gui, ui: &mut Ui) { + ui.menu_button("Edit", |ui| { + if btn(ui, "Cut", gui.shortcuts.edit_cut).clicked() { + keymap::edit_cut_fn(gui); + } + if btn(ui, "Copy", gui.shortcuts.edit_copy).clicked() { + keymap::edit_copy_fn(gui); + } + if btn(ui, "Paste", gui.shortcuts.edit_paste).clicked() { + keymap::edit_paste_fn(gui); + } + }); +} + +fn shared_buttons_view

(gui: &mut Gui, ui: &mut Ui, scale: &mut f32, mut f: P) +where + P: FnMut(&mut Ui), +{ + ui.menu_button("View", |ui| { + if btn(ui, "Zoom In", gui.shortcuts.view_zoom_in).clicked() { + keymap::view_zoom_in_fn(gui); + } + if btn(ui, "Zoom Out", gui.shortcuts.view_zoom_out).clicked() { + keymap::view_zoom_out_fn(gui); + } + ui.menu_button("Zoom Level", |ui| { + if ui.button("10%").clicked() { + // 10% zoom here here + *scale = 0.1f32; + } + if ui.button("25%").clicked() { + // 25% zoom here here + *scale = 0.25f32; + } + if ui.button("50%").clicked() { + // 50% zoom here here + *scale = 0.5f32; + } + if ui.button("100%").clicked() { + // 100% zoom here here + *scale = 1f32; + } + if ui.button("150%").clicked() { + // 150% zoom here here + *scale = 1.5f32; + } + if ui.button("200%").clicked() { + // 200% zoom here here + *scale = 2f32; + } + }); + f(ui); + }); +} + +fn shared_buttons_help(_gui: &mut Gui, ui: &mut Ui) { + ui.menu_button("Help", |ui| { + if ui.button("Show license").clicked() { + // Show license here + } + if ui.button("About").clicked() { + // About here + } + }); } diff --git a/src/gui_egui/mod.rs b/src/gui_egui/mod.rs index 7660cea4..0d590501 100644 --- a/src/gui_egui/mod.rs +++ b/src/gui_egui/mod.rs @@ -1,6 +1,10 @@ -mod gui; -mod helper; +pub mod component_ui; +pub mod editor; +mod editor_wire_mode; +pub mod gui; +pub mod helper; mod keymap; +mod library; mod menu; #[cfg(feature = "components")] diff --git a/src/gui_vizia/components/cross.rs b/src/gui_vizia/components/cross.rs new file mode 100644 index 00000000..72a2e9da --- /dev/null +++ b/src/gui_vizia/components/cross.rs @@ -0,0 +1,38 @@ +use crate::{ + common::Simulator, + components::Cross, + gui_vizia::{GuiData, ViziaComponent, V}, +}; +use log::*; +use vizia::prelude::*; + +#[typetag::serde] +impl ViziaComponent for Cross { + // create view + fn view<'a>(&self, cx: &'a mut Context) -> Handle<'a, V> { + V::new(cx, self, |cx| { + trace!("---- Create Probe View"); + let input = self.input.clone(); + VStack::new(cx, |cx| { + Binding::new( + cx, + crate::gui_vizia::GuiData::simulator.then(Simulator::cycle), + move |cx, _| { + Label::new(cx, { + let simulator = GuiData::simulator.view(cx.data().unwrap()).unwrap(); + &format!("{}", simulator.get_input_signal(&input)) + }) + .hoverable(false); + }, + ) + }) + .size(Auto) + }) + .left(Pixels(self.pos.0 - 10.0)) + .top(Pixels(self.pos.1 - 10.0)) + // .width(Pixels(20.0)) // TODO, max width? + .width(Auto) + .height(Pixels(20.0)) + .background_color(Color::aqua()) + } +} diff --git a/src/gui_vizia/components/mem.rs b/src/gui_vizia/components/mem.rs index 1d443380..9b632d04 100644 --- a/src/gui_vizia/components/mem.rs +++ b/src/gui_vizia/components/mem.rs @@ -91,7 +91,7 @@ pub struct DataMemView { data_slice: Vec, slice_range: Range, } - +#[allow(clippy::enum_variant_names)] pub enum DataEvent { UpdateClock, UpdateScroll(Range), diff --git a/src/gui_vizia/components/mod.rs b/src/gui_vizia/components/mod.rs index 11a74478..428041e6 100644 --- a/src/gui_vizia/components/mod.rs +++ b/src/gui_vizia/components/mod.rs @@ -1,5 +1,6 @@ mod add; mod constant; +mod cross; mod mem; mod mux; mod probe; diff --git a/src/gui_vizia/components/mux.rs b/src/gui_vizia/components/mux.rs index 4821c6e6..f9e28cce 100644 --- a/src/gui_vizia/components/mux.rs +++ b/src/gui_vizia/components/mux.rs @@ -72,7 +72,8 @@ impl View for MuxView { canvas.stroke_path(&path, &paint); // selector - let simulator = GuiData::simulator.get(cx); + let simulator = GuiData::simulator.view(cx.data().unwrap()).unwrap(); + //let simulator = GuiData::simulator.get(cx); let select: Result = simulator.get_input_value(&self.select).try_into(); diff --git a/src/gui_vizia/components/probe.rs b/src/gui_vizia/components/probe.rs index 2e387d83..d382fe64 100644 --- a/src/gui_vizia/components/probe.rs +++ b/src/gui_vizia/components/probe.rs @@ -19,7 +19,8 @@ impl ViziaComponent for Probe { crate::gui_vizia::GuiData::simulator.then(Simulator::cycle), move |cx, _| { Label::new(cx, { - let simulator = GuiData::simulator.get(cx); + let simulator = GuiData::simulator.view(cx.data().unwrap()).unwrap(); + //let simulator = GuiData::simulator.get(cx); &format!("{}", simulator.get_input_signal(&input)) }) .hoverable(false); diff --git a/src/gui_vizia/components/probe_assert.rs b/src/gui_vizia/components/probe_assert.rs index 95b46096..16f47774 100644 --- a/src/gui_vizia/components/probe_assert.rs +++ b/src/gui_vizia/components/probe_assert.rs @@ -27,7 +27,8 @@ impl ViziaComponent for ProbeAssert { } else { (SignalValue::Unknown).into() }; - let simulator = GuiData::simulator.get(cx); + let simulator = GuiData::simulator.view(cx.data().unwrap()).unwrap(); + //let simulator = GuiData::simulator.get(cx); let signal = simulator.get_input_signal(&input); if signal == assert { Label::new(cx, &format!("{} == {}", signal, assert)) diff --git a/src/gui_vizia/components/probe_edit.rs b/src/gui_vizia/components/probe_edit.rs index 1eb67681..396ad5d4 100644 --- a/src/gui_vizia/components/probe_edit.rs +++ b/src/gui_vizia/components/probe_edit.rs @@ -27,12 +27,16 @@ impl ViziaComponent for ProbeEdit { trace!("bind: clock --- {}", cycle.get(cx)); let text = history_bind.read().unwrap().last().unwrap().text.clone(); trace!("last text: {:?}", text); - cx.emit(ProbeEditViewSetter::EditableText(text)); + ProbeEditView { + editable_text: text, + } + .build(cx); + //cx.emit(ProbeEditViewSetter::EditableText(text)); }, ) .on_submit(move |ex, text, enter| { trace!("submit: text {} enter {}", text, enter); - ex.emit(ProbeEditViewSetter::EditableText(text)); + //ex.emit(ProbeEditViewSetter::EditableText(text)); }) .on_edit(move |_ex, text| { trace!("edit: text {}", text); @@ -53,11 +57,13 @@ impl ViziaComponent for ProbeEdit { } } -#[derive(Lens, Setter, Model)] +#[derive(Lens)] pub struct ProbeEditView { editable_text: String, } +impl Model for ProbeEditView {} + fn parse_signal(text: &str) -> SignalValue { let text = text.trim(); diff --git a/src/gui_vizia/components/wire.rs b/src/gui_vizia/components/wire.rs index 2df5f508..7a189a54 100644 --- a/src/gui_vizia/components/wire.rs +++ b/src/gui_vizia/components/wire.rs @@ -4,13 +4,13 @@ use crate::{ gui_vizia::{popup::build_popup, tooltip::new_component_tooltip, ViziaComponent, V}, }; +use log::*; +use std::{cell::RefCell, rc::Rc}; use vizia::{ prelude::*, vg::{Paint, Path}, }; -use log::*; - #[typetag::serde] impl ViziaComponent for Wire { // create view @@ -20,9 +20,16 @@ impl ViziaComponent for Wire { let surround = 5.0; for (i, pos) in self.pos[1..].iter().enumerate() { - View::build(WireView { surround }, cx, |cx| { - build_popup(cx, self.get_id_ports()); - }) + View::build( + WireView { + surround, + drawn: Rc::new(RefCell::new(false)), + }, + cx, + |cx| { + build_popup(cx, self.get_id_ports()); + }, + ) .position_type(PositionType::SelfDirected) .left(Pixels(f32::min(pos.0, self.pos[i].0) - surround)) .top(Pixels(f32::min(pos.1, self.pos[i].1) - surround)) @@ -40,6 +47,7 @@ impl ViziaComponent for Wire { pub struct WireView { surround: f32, + drawn: Rc>, } impl View for WireView { @@ -48,8 +56,10 @@ impl View for WireView { } fn draw(&self, cx: &mut DrawContext<'_>, canvas: &mut Canvas) { + //let drawn = self.drawn.borrow().clone(); + //if !(drawn){ let bounds = cx.bounds(); - // trace!("Wire draw {:?}", bounds); + trace!("Wire draw {:?}", bounds); let mut path = Path::new(); let mut paint = Paint::color(vizia::vg::Color::rgbaf(0.0, 0.0, 0.1, 0.5)); @@ -64,7 +74,9 @@ impl View for WireView { bounds.left() + bounds.width() - surround + 0.5, bounds.top() + bounds.height() - surround + 0.5, ); - + self.drawn.replace(true); canvas.stroke_path(&path, &paint); + + //} } } diff --git a/src/gui_vizia/gui.rs b/src/gui_vizia/gui.rs index e8e9b245..d606765f 100644 --- a/src/gui_vizia/gui.rs +++ b/src/gui_vizia/gui.rs @@ -1,9 +1,6 @@ use crate::{ common::{ComponentStore, Simulator}, - gui_vizia::{ - grid::Grid, keymap::init_keymap, menu::Menu, tooltip::new_component_tooltip, - transport::Transport, - }, + gui_vizia::{grid::Grid, keymap::init_keymap, menu::Menu, transport::Transport}, }; use rfd::FileDialog; use std::collections::HashSet; @@ -114,17 +111,20 @@ impl GuiData { fn open(&mut self) { // Re-Open model trace!("open path {:?}", self.path); - let cs = Box::new(ComponentStore::load_file(&self.path)); - let simulator = Simulator::new(&cs); - - self.simulator = simulator; - - trace!("opened"); + let cs = ComponentStore::load_file(&self.path); + let simulator = Simulator::new(cs); + match simulator { + Ok(s) => { + self.simulator = s; + trace!("opened"); + } + Err(e) => trace!("File failed to open due to errors with simulator {}", e), + } } } -pub fn gui(cs: &ComponentStore, path: &PathBuf) { - let simulator = Simulator::new(cs); +pub fn gui(cs: ComponentStore, path: &PathBuf) { + let simulator = Simulator::new(cs).unwrap(); let path = path.to_owned(); simulator.save_dot(&path); @@ -152,7 +152,8 @@ pub fn gui(cs: &ComponentStore, path: &PathBuf) { let cycle = lens.get(cx); trace!("cycle changed {}", cycle); - let simulator = GuiData::simulator.get(cx); + //let simulator = GuiData::simulator.get(cx); + let simulator = GuiData::simulator.view(cx.data().unwrap()).unwrap(); if simulator.running { trace!("send clock event"); cx.emit(GuiEvent::Clock); @@ -194,27 +195,24 @@ pub fn gui(cs: &ComponentStore, path: &PathBuf) { VStack::new(cx, |cx| { // left pane bar HStack::new(cx, move |cx| { - Button::new( - cx, - move |cx| { - cx.emit(GuiEvent::ToggleExpandLeftPanel(i)) - }, - |cx| { - Label::new( - cx, - GuiData::expanded.map(move |expanded| { - if expanded.contains(&i) { - // expanded - icons::ICON_CHEVRON_DOWN - } else { - // folded - icons::ICON_CHEVRON_RIGHT - } - }), - ) - .class("icon") - }, - ) + Button::new(cx, |cx| { + Label::new( + cx, + if GuiData::expanded + .view(cx.data().unwrap()) + .unwrap() + .contains(&i) + { + // expanded + icons::ICON_CHEVRON_DOWN + } else { + // folded + icons::ICON_CHEVRON_RIGHT + }, + ) + .class("icon") + .on_press(|_cx| {}) + }) .left(Pixels(5.0)) .top(Stretch(1.0)) .bottom(Stretch(1.0)) @@ -229,11 +227,11 @@ pub fn gui(cs: &ComponentStore, path: &PathBuf) { .right(Stretch(1.0)) .size(Auto); - Button::new( - cx, - move |cx| cx.emit(GuiEvent::HideLeftPanel(i)), - |cx| Label::new(cx, icons::ICON_X).class("icon"), - ) + Button::new(cx, |cx| { + Label::new(cx, icons::ICON_X) + .class("icon") + .on_press(|_cx| {}) + }) .right(Pixels(1.0)) .top(Pixels(1.0)) .bottom(Pixels(1.0)); @@ -268,7 +266,8 @@ pub fn gui(cs: &ComponentStore, path: &PathBuf) { .border_width(Pixels(1.0)); }, ); - }); + }) + .width(Pixels(400.0)); // Mid panel ScrollView::new(cx, 0.0, 0.0, true, true, |cx| { @@ -284,8 +283,8 @@ pub fn gui(cs: &ComponentStore, path: &PathBuf) { for (i, c) in oc.into_iter().enumerate() { trace!("build view comp id {}", i); c.view(cx) - .tooltip(|cx| new_component_tooltip(cx, &*c)) - .hoverable(true) + //.tooltip(|cx| new_component_tooltip(cx, &*c)) + //.hoverable(true) .position_type(PositionType::SelfDirected) .on_mouse_down(move |ex, button| { trace!("on_mouse_down"); @@ -302,7 +301,7 @@ pub fn gui(cs: &ComponentStore, path: &PathBuf) { ex.emit(GuiEvent::ShowLeftPanel(i)) } _ => {} - } + }; }); } }) @@ -330,16 +329,14 @@ pub fn gui(cs: &ComponentStore, path: &PathBuf) { // .width(Pixels(140.0)); // About - Popup::new(cx, GuiData::show_about, true, |cx| { + Popup::new(cx, |cx| { Label::new(cx, "About").class("title"); Label::new(cx, "SyncRim 0.1.0"); Label::new(cx, "per.lindgren@ltu.se"); - Button::new( - cx, - |cx| cx.emit(GuiEvent::HideAbout), - |cx| Label::new(cx, "Ok"), - ) + Button::new(cx, |cx| { + Label::new(cx, "Ok").on_press(|cx| cx.emit(GuiEvent::HideAbout)) + }) .class("accent"); }) .on_blur(|cx| cx.emit(GuiEvent::HideAbout)) diff --git a/src/gui_vizia/popup.rs b/src/gui_vizia/popup.rs index 00032882..9cfdd4fc 100644 --- a/src/gui_vizia/popup.rs +++ b/src/gui_vizia/popup.rs @@ -8,16 +8,16 @@ use log::*; use crate::gui_vizia::popup::popup_data_derived_lenses::is_open; -pub fn build_popup(cx: &mut Context, id_ports: (Id, Ports)) -> Handle<'_, Popup>> { +pub fn build_popup(cx: &mut Context, id_ports: (Id, Ports)) -> Handle<'_, Popup> { trace!("build_popup"); PopupData::default().build(cx); - Popup::new(cx, PopupData::is_open, true, move |cx| { + Popup::new(cx, move |cx| { VStack::new(cx, |cx| { let (id, ports) = id_ports.clone(); Label::new(cx, &id); - for input in ports.inputs { + let input = input.input; HStack::new(cx, |cx| { Label::new(cx, &input.id); Binding::new( @@ -28,10 +28,12 @@ pub fn build_popup(cx: &mut Context, id_ports: (Id, Ports)) -> Handle<'_, Popup< cx, &format!( "{:?}", - GuiData::simulator.get(cx).get_input_value(&input) + GuiData::simulator + .view(cx.data().unwrap()) + .unwrap() + .get_input_value(&input) ), ); - //.class("tt_shortcut"); }, ) }) @@ -49,8 +51,11 @@ pub fn build_popup(cx: &mut Context, id_ports: (Id, Ports)) -> Handle<'_, Popup< cx, &format!( "{}", - GuiData::simulator.get(cx).get( - GuiData::simulator.get(cx).get_id_start_index(&id_clone) + GuiData::simulator.view(cx.data().unwrap()).unwrap().get( + GuiData::simulator + .view(cx.data().unwrap()) + .unwrap() + .get_id_start_index(&id_clone) + output ) ), diff --git a/src/gui_vizia/tooltip.rs b/src/gui_vizia/tooltip.rs index c2edf352..69596b87 100644 --- a/src/gui_vizia/tooltip.rs +++ b/src/gui_vizia/tooltip.rs @@ -4,53 +4,67 @@ use crate::{ }; use vizia::prelude::*; -pub fn new_component_tooltip(cx: &mut Context, component: &dyn ViziaComponent) { - VStack::new(cx, |cx| { - let (id, ports) = component.get_id_ports(); - Label::new(cx, &id); +pub fn new_component_tooltip<'a>( + cx: &'a mut Context, + component: &dyn ViziaComponent, +) -> Handle<'a, Tooltip> { + Tooltip::new(cx, |cx| { + VStack::new(cx, |cx| { + let (id, ports) = component.get_id_ports(); + Label::new(cx, &id); - for input in ports.inputs { - HStack::new(cx, |cx| { - Label::new(cx, &input.id); - Binding::new( - cx, - GuiData::simulator.then(Simulator::cycle), - move |cx, _| { - Label::new( - cx, - &format!("{:?}", GuiData::simulator.get(cx).get_input_value(&input)), - ) - .class("tt_shortcut"); - }, - ) - }) - .size(Auto); - } - for output in 0..ports.outputs.len() { - let id_clone = id.clone(); - HStack::new(cx, move |cx| { - Label::new(cx, &format!("out {}", output)); - Binding::new( - cx, - GuiData::simulator.then(Simulator::cycle), - move |cx, _| { - Label::new( - cx, - &format!( - "{:?}", - GuiData::simulator.get(cx).get( - GuiData::simulator.get(cx).get_id_start_index(&id_clone) - + output - ) - ), - ) - .class("tt_shortcut"); - }, - ); - // Label::new(cx, v).class("tt_shortcut"); - }) - .size(Auto); - } + for input_port in ports.inputs { + let input = input_port.input; + HStack::new(cx, |cx| { + Label::new(cx, &input.id); + Binding::new( + cx, + GuiData::simulator.then(Simulator::cycle), + move |cx, _| { + Label::new( + cx, + &format!( + "{:?}", + GuiData::simulator + .view(cx.data().unwrap()) + .unwrap() + .get_input_value(&input) + ), + ) + .class("tt_shortcut"); + }, + ) + }) + .size(Auto); + } + for output in 0..ports.outputs.len() { + let id_clone = id.clone(); + HStack::new(cx, move |cx| { + Label::new(cx, &format!("out {}", output)); + Binding::new( + cx, + GuiData::simulator.then(Simulator::cycle), + move |cx, _| { + Label::new( + cx, + &format!( + "{:?}", + GuiData::simulator.view(cx.data().unwrap()).unwrap().get( + GuiData::simulator + .view(cx.data().unwrap()) + .unwrap() + .get_id_start_index(&id_clone) + + output + ) + ), + ) + .class("tt_shortcut"); + }, + ); + }) + .size(Auto); + } + }) + .size(Auto); }) - .size(Auto); } diff --git a/src/gui_vizia/transport.rs b/src/gui_vizia/transport.rs index f9047bc3..bacfa2e2 100644 --- a/src/gui_vizia/transport.rs +++ b/src/gui_vizia/transport.rs @@ -12,49 +12,69 @@ impl Transport { // Reset Button::new( cx, - |ex| ex.emit(GuiEvent::Reset), - |cx| Label::new(cx, icons::ICON_PLAYER_SKIP_BACK).class("icon"), + //|ex| ex.emit(GuiEvent::Reset), + |cx| { + Label::new(cx, icons::ICON_PLAYER_SKIP_BACK) + .class("icon") + .on_press(move |cx| { + cx.emit(GuiEvent::Reset); + }) + }, ) .tooltip(|cx| { - HStack::new(cx, |cx| { - Label::new(cx, "Reset"); - Label::new(cx, " Shift + Ctrl + F5").class("tt_shortcut"); + Tooltip::new(cx, |cx| { + HStack::new(cx, |cx| { + Label::new(cx, "Reset"); + Label::new(cx, " Shift + Ctrl + F5").class("tt_shortcut"); + }) + .size(Auto); }) - .size(Auto); }); // UnClock (step back) Button::new( cx, - |ex| ex.emit(GuiEvent::UnClock), - |cx| Label::new(cx, icons::ICON_CHEVRON_LEFT).class("icon"), + //|ex| ex.emit(GuiEvent::UnClock), + |cx| { + Label::new(cx, icons::ICON_CHEVRON_LEFT) + .class("icon") + .on_press(|cx| cx.emit(GuiEvent::UnClock)) + }, ) .tooltip(|cx| { - HStack::new(cx, |cx| { - Label::new(cx, "UnClock"); - Label::new(cx, " Shift + F10").class("tt_shortcut"); + Tooltip::new(cx, |cx| { + HStack::new(cx, |cx| { + Label::new(cx, "UnClock"); + Label::new(cx, " Shift + F10").class("tt_shortcut"); + }) + .size(Auto); }) - .size(Auto); }); // Clock (step forward) Button::new( cx, - |ex| ex.emit(GuiEvent::Clock), - |cx| Label::new(cx, icons::ICON_CHEVRON_RIGHT).class("icon"), + //|ex| ex.emit(GuiEvent::Clock), + |cx| { + Label::new(cx, icons::ICON_CHEVRON_RIGHT) + .class("icon") + .on_press(|cx| cx.emit(GuiEvent::Clock)) + }, ) .tooltip(|cx| { - HStack::new(cx, |cx| { - Label::new(cx, "Clock"); - Label::new(cx, " F10").class("tt_shortcut"); + Tooltip::new(cx, |cx| { + HStack::new(cx, |cx| { + Label::new(cx, "Clock"); + Label::new(cx, " F10").class("tt_shortcut"); + }) + .size(Auto); }) - .size(Auto); }); // Play (continuous mode) Button::new( cx, - |ex| ex.emit(GuiEvent::Play), + //|ex| ex.emit(GuiEvent::Play), |cx| { Label::new( cx, @@ -66,21 +86,24 @@ impl Transport { } }), ) + .on_press(|cx| cx.emit(GuiEvent::Play)) .class("icon") }, ) .tooltip(|cx| { - HStack::new(cx, |cx| { - Label::new(cx, "Play"); - Label::new(cx, " F5 (Toggle)").class("tt_shortcut"); + Tooltip::new(cx, |cx| { + HStack::new(cx, |cx| { + Label::new(cx, "Play"); + Label::new(cx, " F5 (Toggle)").class("tt_shortcut"); + }) + .size(Auto); }) - .size(Auto); }); // Pause (step mode) Button::new( cx, - |ex| ex.emit(GuiEvent::Pause), + //|ex| ex.emit(GuiEvent::Pause), |cx| { Label::new( cx, @@ -92,15 +115,18 @@ impl Transport { } }), ) + .on_press(|cx| cx.emit(GuiEvent::Pause)) .class("icon") }, ) .tooltip(|cx| { - HStack::new(cx, |cx| { - Label::new(cx, "Pause"); - Label::new(cx, " F5 (Toggle)").class("tt_shortcut"); + Tooltip::new(cx, |cx| { + HStack::new(cx, |cx| { + Label::new(cx, "Pause"); + Label::new(cx, " F5 (Toggle)").class("tt_shortcut"); + }) + .size(Auto); }) - .size(Auto); }); }) .col_between(Pixels(5.0)) diff --git a/src/main.rs b/src/main.rs index 40b6d47d..5b17ae69 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +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)] + #[arg(short, long, default_value = "empty.json")] model: String, } fn main() { fern_setup(); let args = Args::parse(); - let _path = PathBuf::from(args.model); + let path = PathBuf::from(args.model); - let _cs = ComponentStore::load_file(&_path); + let cs = ComponentStore::load_file(&path); #[cfg(feature = "gui-egui")] - syncrim::gui_egui::gui(&_cs, &_path).ok(); + syncrim::gui_egui::gui(cs, &path, Library::default()).ok(); #[cfg(feature = "gui-vizia")] - syncrim::gui_vizia::gui(&_cs, &_path); + syncrim::gui_vizia::gui(cs, &path); + + #[cfg(not(any(feature = "gui-vizia", feature = "gui-egui")))] + syncrim::common::Simulator::new(cs).unwrap(); } diff --git a/src/signal.rs b/src/signal.rs index 6794ffac..8545240e 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -10,6 +10,7 @@ pub type Id = String; pub type SignalUnsigned = u32; pub type SignalSigned = i32; +pub type SignalBool = bool; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Copy, Clone)] pub struct Signal { @@ -44,6 +45,42 @@ pub enum SignalValue { Data(SignalUnsigned), // Maybe we should have something even more generic here } +impl TryFrom for bool { + type Error = String; + + fn try_from(signal: Signal) -> Result { + if let SignalValue::Data(data) = signal.data { + Ok(data == 1) + } else { + Err(format!("Could not convert {:?} into bool", signal)) + } + } +} + +impl TryFrom for bool { + type Error = String; + + fn try_from(data: SignalValue) -> Result { + if let SignalValue::Data(data) = data { + Ok(data == 1) + } else { + Err(format!("Could not convert {:?} into bool", data)) + } + } +} + +impl TryFrom for usize { + type Error = String; + + fn try_from(data: SignalValue) -> Result { + if let SignalValue::Data(data) = data { + Ok(data as usize) + } else { + Err(format!("Could not convert {:?} into usize", data)) + } + } +} + impl TryFrom for SignalUnsigned { type Error = String; diff --git a/src/simulator.rs b/src/simulator.rs index 9eba2afe..4eb87f6b 100644 --- a/src/simulator.rs +++ b/src/simulator.rs @@ -2,13 +2,12 @@ use crate::common::{ Component, ComponentStore, Condition, Id, Input, OutputType, Signal, SignalFmt, SignalValue, Simulator, }; +use log::*; use petgraph::{ algo::toposort, dot::{Config, Dot}, Graph, }; - -use log::*; use std::collections::HashMap; use std::{fs::File, io::prelude::*, path::PathBuf}; @@ -22,7 +21,10 @@ pub struct IdComponent(pub HashMap>); // A solution is to evaluate register updates separately from other components // ... but not currently implemented ... impl Simulator { - pub fn new(component_store: &ComponentStore) -> Self { + pub fn new(component_store: ComponentStore) -> Result { + for component in &component_store.store { + component.reset(); + } let mut lens_values = vec![]; let mut id_start_index = HashMap::new(); @@ -34,6 +36,7 @@ impl Simulator { trace!("-- allocate storage for lensed outputs"); for c in &component_store.store { + trace!("{:?}", c.get_id_ports().0); let (id, ports) = c.get_id_ports(); trace!("id {}, ports {:?}", id, ports); @@ -87,11 +90,18 @@ impl Simulator { trace!("to_id :{}, ports: {:?}", to_id, ports); if ports.out_type == OutputType::Combinatorial { + trace!("combinatorial, id:{}", to_component.get_id_ports().0); let to_node = id_node.get(to_id).unwrap(); let (_, ports) = c.get_id_ports(); for in_port in &ports.inputs { - let from_id = &in_port.id; - let from_node = id_node.get(from_id).unwrap(); + let from_id = &in_port.input.id; + let from_node = id_node.get(from_id); + if from_node.is_none() { + println!("to id: {} from port {} is not connected", to_id, from_id); + return Err("A port left unconnected"); + } + let from_node = from_node.unwrap(); + graph.add_edge(*from_node, *to_node, ()); trace!( "add_edge {}:{:?} -> {}:{:?}", @@ -108,7 +118,7 @@ impl Simulator { let top = toposort(&graph, None).expect("Topological sort failed, your model contains loops."); trace!("--- topologically ordered graph \n{:?}", top); - + //two passes, first add all sequential roots let mut ordered_components = vec![]; //two passes ensure the sorted list of nodes always starts with ALL of the roots //first push the sequential components, eg. graph roots @@ -119,7 +129,7 @@ impl Simulator { ordered_components.push(c); } } - //now push all of the other components + //then the rest... for node in &top { #[allow(suspicious_double_ref_op)] let c = (**node_comp.get(node).unwrap()).clone(); @@ -152,9 +162,8 @@ impl Simulator { }; trace!("sim_state {:?}", simulator.sim_state); - simulator.clock(); - simulator + Ok(simulator) } /// get input by index @@ -164,7 +173,11 @@ impl Simulator { /// get input signal pub fn get_input_signal(&self, input: &Input) -> Signal { - let nr_out = *self.id_nr_outputs.get(&input.id).unwrap(); + #[allow(unreachable_code)] + let nr_out = *self + .id_nr_outputs + .get(&input.id) + .unwrap_or_else(|| panic!("\n{:?} not found in \n{:?}", input, self.id_nr_outputs)); let index = *self .id_field_index .get(&(input.id.clone(), input.field.clone())) @@ -217,7 +230,9 @@ impl Simulator { .get(&(id.into(), field.into())) .unwrap_or_else(|| panic!("Component {}, field {} not found.", id, field)); let start_index = self.get_id_start_index(id); - self.set_value(start_index + index, value.into()); + let val: SignalValue = value.try_into().unwrap(); + //trace!("id:{}, field:{}, value:{:?}", id,field, SignalValue::try_from(val).unwrap()); + self.set_value(start_index + index, val); } /// set fmt by Id (instance) and Id (field) @@ -234,12 +249,15 @@ impl Simulator { pub fn clock(&mut self) { // push current state self.history.push(self.sim_state.clone()); - + trace!("cycle:{}", self.cycle); for component in self.ordered_components.clone() { + //trace!("evaling component:{}", component.get_id_ports().0); match component.clock(self) { Ok(_) => {} Err(cond) => match cond { - Condition::Warning(warn) => trace!("warning {}", warn), + Condition::Warning(warn) => { + trace!("warning {}", warn) + } Condition::Error(err) => panic!("err {}", err), Condition::Assert(assert) => { error!("assertion failed {}", assert); @@ -257,12 +275,18 @@ impl Simulator { /// free running mode until Halt condition pub fn run(&mut self) { - self.running = true; - while self.running { - self.clock() + use std::time::Instant; + let now = Instant::now(); + while now.elapsed().as_millis() < 1000 / 30 { + //60Hz + if self.running { + self.clock() + } } } + pub fn run_threaded(&mut self) {} + /// stop the simulator from gui or other external reason pub fn stop(&mut self) { self.running = false; @@ -290,6 +314,10 @@ impl Simulator { self.sim_state.iter_mut().for_each(|val| *val = 0.into()); self.stop(); self.clock(); + + for component in self.ordered_components.clone() { + component.reset(); + } } pub fn get_state(&self) -> bool { @@ -321,7 +349,7 @@ mod test { store: vec![Rc::new(ProbeOut::new("po1"))], }; - let simulator = Simulator::new(&cs); + let simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); } @@ -333,7 +361,7 @@ mod test { store: vec![Rc::new(ProbeOut::new("po1")), Rc::new(ProbeOut::new("po1"))], }; - let simulator = Simulator::new(&cs); + let simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); } @@ -344,7 +372,7 @@ mod test { store: vec![Rc::new(ProbeOut::new("po1"))], }; - let simulator = Simulator::new(&cs); + let simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); let _ = simulator.get_input_value(&Input::new("po1", "out")); @@ -357,7 +385,7 @@ mod test { store: vec![Rc::new(ProbeOut::new("po1"))], }; - let simulator = Simulator::new(&cs); + let simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); let _ = simulator.get_input_value(&Input::new("po1", "missing")); @@ -369,7 +397,7 @@ mod test { store: vec![Rc::new(Constant::new("c", (0.0, 0.0), 0))], }; - let simulator = Simulator::new(&cs); + let simulator = Simulator::new(cs).unwrap(); assert_eq!(simulator.cycle, 1); let _ = simulator.get_input_fmt(&Input::new("c", "out"));