diff --git a/.gitignore b/.gitignore index 412648eb..7f28aaaf 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,5 @@ Cargo.lock *.sublime-project *.sublime-workspace + +.vscode/ diff --git a/runtime/examples/CMakeLists.txt b/runtime/examples/CMakeLists.txt index 09c6aa7f..ec59ab6c 100644 --- a/runtime/examples/CMakeLists.txt +++ b/runtime/examples/CMakeLists.txt @@ -37,3 +37,4 @@ add_subdirectory(C++/svm) add_subdirectory(Rust/libtapasco_tests) add_subdirectory(Rust/libtapasco_svm) +add_subdirectory(Rust/tapasco-debug) diff --git a/runtime/examples/Rust/tapasco-debug/.gitignore b/runtime/examples/Rust/tapasco-debug/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/runtime/examples/Rust/tapasco-debug/.gitignore @@ -0,0 +1 @@ +/target diff --git a/runtime/examples/Rust/tapasco-debug/CMakeLists.txt b/runtime/examples/Rust/tapasco-debug/CMakeLists.txt new file mode 100644 index 00000000..37b338ac --- /dev/null +++ b/runtime/examples/Rust/tapasco-debug/CMakeLists.txt @@ -0,0 +1,57 @@ +# Copyright (c) 2014-2021 Embedded Systems and Applications, TU Darmstadt. +# +# This file is part of TaPaSCo +# (see https://github.com/esa-tu-darmstadt/tapasco). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +cmake_minimum_required(VERSION 3.5.1 FATAL_ERROR) +project(tapasco-debug) + +# Add a Rust/Cargo project to CMake watching all files in the current directory because Cargo already doesn't do anything if nothing has changed. +add_executable(tapasco-debug .) + +# Use Cargo to build this project in debug mode by defining a custom target running after the tapasco target has been built. +add_custom_target(tapasco_debug_cargo_build_debug + COMMAND CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} cargo build -q + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + DEPENDS tapasco + COMMENT "Building tapasco-debug with Cargo") + +# Use Cargo to build this project in release mode by defining a custom target running after the tapasco target has been built. +add_custom_target(tapasco_debug_cargo_build_release + COMMAND CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} cargo build -q --release + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + DEPENDS tapasco + COMMENT "Building tapasco-debug with Cargo") + +# Check if building should be in Debug or Release mode +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(TARGET_DIR "debug") + add_dependencies(tapasco-debug tapasco_debug_cargo_build_debug) +else() + set(TARGET_DIR "release") + add_dependencies(tapasco-debug tapasco_debug_cargo_build_release) +endif() + + +# This tells CMake that this is a C++ executable (but it's Rust) because CMake really wants to know how to link this executable +set_target_properties(tapasco-debug PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON LINKER_LANGUAGE CXX) +# but it has already been linked by Cargo, so we then tell CMake to use the most failure-proof linker available (none, it's just /usr/bin/true). +# You can't tell CMake not to link this at all, so this is the dirty workaround: +set(CMAKE_CXX_LINK_EXECUTABLE "true") + +# Install the executable in the TaPaSCo PATH +install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/tapasco-debug + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/Tapasco/bin) diff --git a/runtime/examples/Rust/tapasco-debug/Cargo.lock b/runtime/examples/Rust/tapasco-debug/Cargo.lock new file mode 100644 index 00000000..2b0ea4dc --- /dev/null +++ b/runtime/examples/Rust/tapasco-debug/Cargo.lock @@ -0,0 +1,1151 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "cbindgen" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e03a705df2e735cc5486f104a48e25a8f72ae06eaea5b7753a81270ed00859" +dependencies = [ + "clap", + "heck", + "log", + "proc-macro2", + "quote", + "serde 1.0.136", + "serde_json", + "syn", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits 0.2.14", + "time", + "winapi", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "config" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" +dependencies = [ + "lazy_static", + "nom", + "rust-ini", + "serde 1.0.136", + "serde-hjson", + "serde_json", + "toml", + "yaml-rust", +] + +[[package]] +name = "crossbeam" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" +dependencies = [ + "cfg-if 0.1.10", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "crossbeam-utils", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +dependencies = [ + "cfg-if 0.1.10", + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "lazy_static", +] + +[[package]] +name = "crossterm" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486d44227f71a1ef39554c0dc47e44b9f4139927c75043312690c3f476d1d788" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507" +dependencies = [ + "winapi", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime 1.3.0", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime 2.1.0", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "getset" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 1.0.0", + "ryu", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "linked-hash-map" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" +dependencies = [ + "serde 0.8.23", + "serde_test", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "lockfree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ee94b5ad113c7cb98c5a040f783d0952ee4fe100993881d1673c2cb002dd23" +dependencies = [ + "owned-alloc", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "memoffset" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "nix" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", + "void", +] + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "lexical-core", + "memchr", + "version_check", +] + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits 0.2.14", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.14", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "owned-alloc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30fceb411f9a12ff9222c5f824026be368ff15dc2f13468d850c7d3f502205d6" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "prost" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce49aefe0a6144a45de32927c77bd2859a5f7677b55f220ae5b744e87389c212" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b10678c913ecbd69350e8535c3aef91a8676c0773fc1d7b95cdd196d7f2f26" +dependencies = [ + "bytes", + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1834f67c0697c001304b75be76f67add9c89742eda3a085ad8ee0bb38c3417aa" +dependencies = [ + "bytes", + "prost", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rust-ini" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-hjson" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" +dependencies = [ + "lazy_static", + "linked-hash-map 0.3.0", + "num-traits 0.1.43", + "regex", + "serde 0.8.23", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde 1.0.136", +] + +[[package]] +name = "serde_test" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5" +dependencies = [ + "serde 0.8.23", +] + +[[package]] +name = "signal-hook" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "snafu" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tapasco" +version = "1.0.0" +dependencies = [ + "bytes", + "cbindgen", + "chrono", + "config", + "crossbeam", + "env_logger 0.7.1", + "getset", + "libc", + "lockfree", + "log", + "memmap", + "nix", + "prost", + "prost-build", + "serde 1.0.136", + "snafu", + "vfio-bindings", + "volatile", +] + +[[package]] +name = "tapasco-debug" +version = "0.1.0" +dependencies = [ + "chrono", + "crossterm 0.21.0", + "env_logger 0.9.0", + "log", + "memmap", + "snafu", + "structopt", + "tapasco", + "tui", + "unicode-width", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde 1.0.136", +] + +[[package]] +name = "tui" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c8ce4e27049eed97cfa363a5048b09d995e209994634a0efc26a14ab6c0c23" +dependencies = [ + "bitflags", + "cassowary", + "crossterm 0.20.0", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vfio-bindings" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a21f546f2bda37f5a8cfb138c87f95b8e34d2d78d6a7a92ba3785f4e08604a7" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b06ad3ed06fef1713569d547cdbdb439eafed76341820fb0e0344f29a41945" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map 0.5.4", +] diff --git a/runtime/examples/Rust/tapasco-debug/Cargo.toml b/runtime/examples/Rust/tapasco-debug/Cargo.toml new file mode 100644 index 00000000..62eb034c --- /dev/null +++ b/runtime/examples/Rust/tapasco-debug/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "tapasco-debug" +version = "0.1.0" +authors = ["zyno42 <83068959+zyno42@users.noreply.github.com>"] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tui = { version = "0.16.0", default-features = false, features = ['crossterm'] } +crossterm = "0.21.0" +tapasco = { path = "../../../libtapasco", features = ["tapasco-debug"] } +snafu = "0.6.10" +chrono = "0.4.19" +log = "0.4.14" +env_logger = "0.9.0" +structopt = "0.3.23" +memmap = "0.7.0" +unicode-width = "0.1.9" diff --git a/runtime/examples/Rust/tapasco-debug/src/app.rs b/runtime/examples/Rust/tapasco-debug/src/app.rs new file mode 100644 index 00000000..fa224b2c --- /dev/null +++ b/runtime/examples/Rust/tapasco-debug/src/app.rs @@ -0,0 +1,736 @@ +use std::collections::HashMap; + +use tui::widgets::ListState; + +use log::{error, trace, warn}; + +use tapasco::{ + device::PEParameter, + device::{status::Interrupt, Device}, + pe::PE, + tlkm::TLKM, +}; + +use chrono::{TimeZone, Utc}; + +use snafu::{ResultExt, Snafu}; + +#[derive(Debug, Snafu)] +pub enum Error { + #[snafu(display("Failed to initialize TLKM object: {}", source))] + TLKMInit { source: tapasco::tlkm::Error }, + + #[snafu(display("Failed to decode/acquire TLKM device or one of its PEs: {}", source))] + DeviceInit { source: tapasco::device::Error }, +} + +type Result = std::result::Result; + +// Import the Subcommand enum from super module as AccessMode to clarify intent +pub use super::Command as AccessMode; + +// TODO: It would be nice to see when/how many interrupts were triggered. + +#[derive(Debug, PartialEq)] +pub enum InputMode { + Navigation, + Edit, +} + +#[derive(Debug, PartialEq)] +pub enum InputFrame { + PEList, + RegisterList, +} + +pub struct App<'a> { + tlkm_device: Device, + bitstream_info: String, + platform_info: String, + pub access_mode: AccessMode, + pub input: String, + pub input_mode: InputMode, + pub focus: InputFrame, + pub title: String, + pub tabs: TabsState<'a>, + pub pe_infos: StatefulList, + pub pes: Vec<(usize, PE)>, // Plural of Processing Element (PEs) + pub register_list: ListState, + pub local_memory_list: ListState, + pub messages: Vec, +} + +impl<'a> App<'a> { + pub fn new(device_id: u32, access_mode: AccessMode) -> Result> { + trace!("Creating new App for Tapasco state"); + + // Get Tapasco Loadable Linux Kernel Module + let tlkm = TLKM::new().context(TLKMInit {})?; + // Allocate the device with the given ID + let tlkm_device = tlkm + .device_alloc(device_id, &HashMap::new()) + .context(TLKMInit {})?; + + // For some access modes we need to take some special care to use them + let access_mode_str = match access_mode { + AccessMode::Monitor {} => { + // Monitor Mode: In order to observe other Tapasco Host applications which need exclusive + // access we implement a monitor mode where registers cannot be modified. For this + // no special access is necessary. This is a no-op. + "Monitor" + } + // TODO: 3. When Issue #296 is fixed, enable debug mode here again, too. + //AccessMode::Debug {} => { + // // Change device access to exclusive to be able to acquire PEs + // tlkm_device + // .change_access(tapasco::tlkm::tlkm_access::TlkmAccessExclusive) + // .context(DeviceInit {})?; + // "Debug" + //} + AccessMode::Unsafe {} => { + // Change device access to exclusive to be able to acquire PEs + warn!("Running in Unsafe Mode"); + "Unsafe" + } + }; + trace!("Access Mode is: {}", access_mode_str); + + // Empty string where input is stored + let input = String::new(); + // Initialize App in Navigation mode + let input_mode = InputMode::Navigation; + // Initialize UI with focus on the PE list + let focus = InputFrame::PEList; + + let tabs = TabsState::new(vec![ + "Peek & Poke PEs", + "Platform Components", + "Bitstream & Device Info", + ]); + let title = format!("TaPaSCo Debugger - {} Mode", access_mode_str); + + let tlkm_version = tlkm.version().context(TLKMInit {})?; + let platform_base = tlkm_device + .status() + .platform_base + .clone() + .expect("Could not get platform_base!"); + let arch_base = tlkm_device + .status() + .arch_base + .clone() + .expect("Could not get arch_base!"); + + // Parse info about PEs from the status core + // Preallocate these vectors to set the acquired PE at the right position later + let mut pe_infos = Vec::with_capacity(tlkm_device.status().pe.len()); + let mut pes: Vec<(usize, PE)> = Vec::with_capacity(tlkm_device.status().pe.len()); + + for (index, pe) in tlkm_device.status().pe.iter().enumerate() { + // Calculate the "real" address using arch base address plus PE offset + let address = arch_base.base + pe.offset; + pe_infos.push( + format!("Slot {}: {} (ID: {}) Address: 0x{:016x} ({}), Size: 0x{:x} ({} Bytes), Interrupts: {}, Debug: {:?}", + index, pe.name, pe.id, address, address, pe.size, pe.size, App::parse_interrupts(&pe.interrupts), pe.debug)); + + // Acquire the PE to be able to show its registers etc. + // Warning: casting to usize can panic! On a <32-bit system.. + let pe = tlkm_device + .acquire_pe_without_job(pe.id as usize) + .context(DeviceInit {})?; + // TODO: There is no way to check that you really got the PE that you wanted + // so I have to use this workaround to set it at the ID of the PE struct which + // confusingly is NOT the pe.id from above which is stored at type_id inside the PE. + let pe_real_id = *pe.id(); + //pes[pe_real_id] = pe; + pes.push((pe_real_id, pe)); + } + + // Preselect the first element if the device's bitstream contains at least one PE + let pe_infos = if pe_infos.is_empty() { + StatefulList::with_items(pe_infos) + } else { + StatefulList::with_items_selected(pe_infos, 0) + }; + + // There are theoretically endless registers and local memory + let register_list = ListState::default(); + let local_memory_list = ListState::default(); + + // Parse bitstream info from the Status core + let mut bitstream_info = String::new(); + // TODO: decode vendor and product IDs + bitstream_info += &format!( + "Device ID: {} ({}),\nVendor: {}, Product: {},\n\n", + tlkm_device.id(), + tlkm_device.name(), + tlkm_device.vendor(), + tlkm_device.product() + ); + + bitstream_info += &format!( + "Bitstream generated at: {} ({})\n\n", + if let Ok(i) = tlkm_device.status().timestamp.try_into() { + format!("{}", Utc.timestamp(i, 0).format("%Y-%m-%d")) + } else { + "the future".to_string() + }, + tlkm_device.status().timestamp + ); + + for v in &tlkm_device.status().versions { + bitstream_info += &format!( + "{} Version: {}.{}{}\n", + v.software, v.year, v.release, v.extra_version + ); + } + bitstream_info += &format!("TLKM Version: {}\n\n", tlkm_version); + + for c in &tlkm_device.status().clocks { + bitstream_info += &format!("{} Clock Frequency: {} MHz\n", c.name, c.frequency_mhz); + } + + // Parse platform info from the Status core + let mut platform_info = String::new(); + + platform_info += &format!( + "Platform Base: 0x{:012x} (Size: 0x{:x} ({} Bytes))\n\n", + platform_base.base, platform_base.size, platform_base.size + ); + + for p in &tlkm_device.status().platform { + let address = platform_base.base + p.offset; + platform_info += &format!( + "{}:\n Address: 0x{:012x} ({}), Size: 0x{:x} ({} Bytes)\n Interrupts: {}\n\n", + p.name.trim_start_matches("PLATFORM_COMPONENT_"), + address, + address, + p.size, + p.size, + App::parse_interrupts(&p.interrupts) + ); + } + + // Setup a new Vector to store (event) messages. It's kind of like logging but as there + // already is a real logger and we cannot add another logging implementation, we have to + // provide something a little bit different and simpler to inform users about things like + // started PEs. + let messages: Vec = Vec::new(); + + trace!("Constructed App"); + + Ok(App { + tlkm_device, + bitstream_info, + platform_info, + access_mode, + input, + input_mode, + focus, + title, + tabs, + pe_infos, + pes, + register_list, + local_memory_list, + messages, + }) + } + + pub fn next_tab(&mut self) { + self.tabs.next(); + } + + pub fn previous_tab(&mut self) { + self.tabs.previous(); + } + + pub fn on_up(&mut self) { + if self.tabs.index == 0 { + match self.focus { + InputFrame::PEList => self.pe_infos.previous(), + InputFrame::RegisterList => self.register_list.previous(), + }; + }; + } + + pub fn on_down(&mut self) { + if self.tabs.index == 0 { + match self.focus { + InputFrame::PEList => self.pe_infos.next(), + InputFrame::RegisterList => self.register_list.next(), + }; + }; + } + + pub fn on_escape(&mut self) { + if self.tabs.index == 0 { + match self.input_mode { + InputMode::Navigation => { + match self.focus { + InputFrame::PEList => self.pe_infos.unselect(), + InputFrame::RegisterList => { + self.register_list.unselect(); + self.focus = InputFrame::PEList; + } + }; + } + InputMode::Edit => { + self.input_mode = InputMode::Navigation; + self.input.clear(); + } + }; + }; + } + + pub fn on_enter(&mut self) { + if self.tabs.index == 0 { + match self.access_mode { + AccessMode::Monitor {} => {} + // TODO: 4. Replace the second next line with the next line: + // AccessMode::Debug {} | AccessMode::Unsafe {} => { + AccessMode::Unsafe {} => { + match self.input_mode { + InputMode::Navigation => { + match self.focus { + // Change the focused component to the register list of the selected PE + InputFrame::PEList => match self.pe_infos.state.selected() { + Some(_) => { + self.focus = InputFrame::RegisterList; + self.register_list.next(); + } + _ => self.pe_infos.next(), + }, + // Enter Edit Mode for a new register value + InputFrame::RegisterList => { + self.input_mode = InputMode::Edit; + } + }; + } + InputMode::Edit => { + // If the input cannot be parsed correctly, simply do nothing until + // we either hit Escape or enter a valid decimal integer. + let new_value: Option = + if let Some(hex_string) = self.input.strip_prefix("0x") { + u64::from_str_radix(hex_string, 16).ok() + } else if let Ok(new_value) = self.input.parse::() { + Some(new_value) + } else if let Ok(new_value) = self.input.parse::() { + Some(new_value as u64) // explicitly use as casting + } else { + None + }; + + if let Some(new_value) = new_value { + self.input.clear(); + + // Ignore the error because unless the code changes, there will + // be no error returned by this function. + if let Err(e) = self.pes + .get_mut(self.pe_infos.state.selected() + .expect("There should have been a selected PE. This is a bug.")) + .expect("There should have been a PE for the selection. This is a bug.") + .1 // ignore the index, select the PE from the tuple + .set_arg(self.register_list.selected().unwrap(), + PEParameter::Single64(new_value)) { + // but log this error in case the code changes + error!("Error setting argument: {}. + This is probably due to libtapasco having changed something + important. You should fix this app.", e); + } + + self.messages.push(format!( + "In slot {} set argument register {} to new value: {}.", + self.pe_infos.state.selected().unwrap(), + self.register_list.selected().unwrap(), + new_value + )); + + self.input_mode = InputMode::Navigation; + } + } + }; + } + }; + }; + } + + pub fn get_bitstream_info(&self) -> &str { + &self.bitstream_info + } + + pub fn get_platform_info(&self) -> &str { + &self.platform_info + } + + fn get_current_pe(&self) -> Option<&PE> { + let pe_slot = match self.pe_infos.state.selected() { + Some(n) => n, + _ => return None, + }; + + // Get the PE with the real ID of the selected slot + let (_, pe) = self + .pes + .iter() + .filter(|(id, _)| *id == pe_slot) + .take(1) + .collect::>() + .get(0) + .expect("There should be a PE with the selected ID. This is a bug."); + + Some(pe) + } + + pub fn start_current_pe(&self) -> String { + assert!(self.access_mode == AccessMode::Unsafe {}, "Unsafe access mode necessary to start a PE! This function should not have been callable. This is a bug."); + + if let Some(pe) = self.get_current_pe() { + // This does not work because `libtapasco` does a really god job of protecting its PEs + // and access to them with Rust's ownership rules. + // + // Besides, this might not be the correct PE when there are multiple PEs with the same + // TypeID. + //match self.tlkm_device.acquire_pe(*pe.type_id()) { + // Ok(mut pe) => { + // trace!("Acquired PE: {:?}", pe); + // if let Ok(_) = pe.start(vec![]) { + // trace!("Started PE: {:?}", pe); + // if let Ok(_) = pe.release(true, true) { + // trace!("Starting PE: {:?}", pe); + // } + // } + // }, + // Err(e) => error!("Could not acquire PE: {}", e), + //} + // + trace!("Starting PE with ID: {}.", pe.id()); + + let offset = (*pe.offset()) + .try_into() + .expect("Expected to be able to cast the PE offset."); + + unsafe { + // Access PE memory just like in `libtapasco`: + //use volatile::Volatile; + //let ptr = pe.memory().as_ptr().offset(offset); + //let volatile_ptr: *mut Volatile = ptr as *mut Volatile; + //(*volatile_ptr).write(1); + // + // but instead of the `volatile` crate use std::ptr: + let ptr = pe.memory().as_ptr().offset(offset); + (ptr as *mut u32).write_volatile(1); + } + + return format!( + "Send start signal to PE in slot: {}.", + self.pe_infos + .state + .selected() + .expect("There needs to be a selected PE. This is a bug.") + ); + } + + "No PE selected.".to_string() + } + + pub fn get_status_registers(&mut self) -> String { + if let Some(pe) = self.get_current_pe() { + let (global_interrupt, local_interrupt) = pe + .interrupt_status() + .expect("Expected to get PE interrupt status."); + let return_value = pe.return_value(); + + // TODO: I have to get these from the runtime because there are no registers for that. + //let is_running = false; + //let interrupt_pending = false; + + let mut result = String::new(); + //result += &format!("PE is running: {}", is_running); + result += &format!("Local Interrupt Enabled: {}\n", local_interrupt); + result += &format!("Global Interrupt Enabled: {}\n", global_interrupt); + //result += &format!("Interrupt pending: {}", interrupt_pending); + result += &format!( + "Return: 0x{:16x} (i32: {:10})\n", + return_value, + return_value as i32 // explicitly use as casting + ); + + return result; + } + + "No PE selected.".to_string() + } + + pub fn get_argument_registers(&mut self, number_of_lines: usize) -> Vec { + let number_of_registers = self.register_list.selected().unwrap_or(0) + number_of_lines; + + if let Some(pe) = self.get_current_pe() { + let argument_registers = (0..number_of_registers) + .map(|i| { + match pe + .read_arg(i, 8) + .expect("Expected to be able to read PE registers!") + { + PEParameter::Single64(u) => u, + _ => unreachable!(), + } + }) + .collect::>(); + + let mut result = Vec::new(); + for (i, a) in argument_registers.iter().enumerate() { + result.push(format!("Arg#{:02}: 0x{:16x} ({:20})\n", i, a, a)); + } + + return result; + } + + vec!["No PE selected.".to_string()] + } + + pub fn dump_current_pe_local_memory(&self, number_of_lines: usize) -> Vec { + if let Some(pe) = self.get_current_pe() { + let local_memory = match pe.local_memory() { + Some(m) => m, + _ => return vec!["No local memory for this PE.".to_string()], + }; + + let mut memory_cells: Vec = vec![0_u8; 16 * number_of_lines]; + match local_memory.dma().copy_from(0, &mut memory_cells) { + Ok(_) => {} + _ => return vec!["Could not read PE Local Memory!".to_string()], + } + + let mut result: Vec = Vec::new(); + for (i, s) in memory_cells.chunks_exact(16).enumerate() { + // format bytes like hexdump + result.push(format!( + "{:08x}: {}\n", + 16 * i, + s.iter() + .map(|x| format!("{:02x}", x)) + .collect::>() + .join(" ") + )); + } + + result + } else { + vec!["No PE selected.".to_string()] + } + } + + fn parse_interrupts(interrupts: &[Interrupt]) -> String { + let mut result = String::new(); + + if interrupts.is_empty() { + result += "None"; + } else { + result += "[ "; + + for (index, interrupt) in interrupts.iter().enumerate() { + if index > 0 { + result += ", "; + } + + result += &format!("{}:{}", interrupt.mapping, interrupt.name); + } + + result += " ]"; + } + + result + } + + pub fn get_dmaengine_statistics(&self) -> String { + let dmaengine_memory = unsafe { + match self + .tlkm_device + .get_platform_component_memory("PLATFORM_COMPONENT_DMA0") + { + Ok(m) => m, + Err(_) => return "No DMAEngine found!".to_string(), + } + }; + + let status_registers: Vec<(&str, isize)> = vec![ + ("Number of Read Requests ", 48), + ("Number of Write Requests ", 56), + ("Cycles since last Read Request ", 64), + ("Cycles between Read Requests ", 72), + ("Cycles since last Write Request", 88), + ("Cycles between Write Requests ", 96), + ]; + + let mut result = String::new(); + for (index, r) in status_registers.iter().enumerate() { + unsafe { + // Create a const pointer to the u64 register at the offset in the platform address + // space of the DMAEngine + let dmaengine_register_ptr = dmaengine_memory.as_ptr().offset(r.1).cast::(); + // Read IO register with volatile, see: + // https://doc.rust-lang.org/std/ptr/fn.read_volatile.html + let dmaengine_register = dmaengine_register_ptr.read_volatile(); + + if index < 2 { + // Calculating ms doesn't make sense for the number of Reads/Writes + result += &format!( + "{} {:016x} ({:20})\n", + r.0, dmaengine_register, dmaengine_register + ); + } else { + // Warning: This assumes the host frequency to be 250MHz which should be the case + // everywhere. + result += &format!( + "{} {:016x} ({:20} = {:9} ns)\n", + r.0, + dmaengine_register, + dmaengine_register, + dmaengine_register * 4 + ); + } + } + + // Add a newline every second line + if index % 2 == 1 { + result += "\n"; + } + } + + result + } +} + +// The following code is taken from libtui-rs demo: +// https://github.com/fdehau/tui-rs/blob/v0.15.0/examples/util/mod.rs +// licensed under MIT License by Florian Dehau, see: +// https://spdx.org/licenses/MIT.html +pub struct TabsState<'a> { + pub titles: Vec<&'a str>, + pub index: usize, +} + +impl<'a> TabsState<'a> { + pub fn new(titles: Vec<&'a str>) -> TabsState { + TabsState { titles, index: 0 } + } + + pub fn next(&mut self) { + self.index = (self.index + 1) % self.titles.len(); + } + + pub fn previous(&mut self) { + self.index = if self.index == 0 { + self.titles.len() - 1 + } else { + self.index - 1 + } + } +} + +pub struct StatefulList { + pub state: ListState, + pub items: Vec, +} + +impl Default for StatefulList { + fn default() -> Self { + Self { + state: ListState::default(), + items: Vec::new(), + } + } +} + +impl StatefulList { + pub fn with_items(items: Vec) -> Self { + Self { + state: ListState::default(), + items, + } + } + + pub fn with_items_selected(items: Vec, selected: usize) -> Self { + let mut list = Self { + state: ListState::default(), + items, + }; + list.state.select(Some(selected)); + + list + } +} + +// Define a new trait so we can implement methods for ListState +pub trait Select { + fn next(&mut self); + fn previous(&mut self); + fn unselect(&mut self); +} + +impl Select for StatefulList { + fn next(&mut self) { + let n = match self.state.selected() { + Some(m) => { + if m >= self.items.len() - 1 { + 0 + } else { + m + 1 + } + } + None => 0, + }; + + self.state.select(Some(n)); + } + + fn previous(&mut self) { + let n = match self.state.selected() { + Some(m) => { + if m == 0 { + self.items.len() - 1 + } else { + m - 1 + } + } + None => self.items.len() - 1, + }; + + self.state.select(Some(n)); + } + + fn unselect(&mut self) { + self.state.select(None); + } +} + +impl Select for ListState { + fn next(&mut self) { + let n = match self.selected() { + Some(m) => m + 1, + None => 0, + }; + + self.select(Some(n)); + } + + fn previous(&mut self) { + let n = match self.selected() { + Some(m) => { + if m == 0 { + 0 + } else { + m - 1 + } + } + None => 0, + }; + + self.select(Some(n)); + } + + fn unselect(&mut self) { + self.select(None); + } +} diff --git a/runtime/examples/Rust/tapasco-debug/src/main.rs b/runtime/examples/Rust/tapasco-debug/src/main.rs new file mode 100644 index 00000000..96abb8e8 --- /dev/null +++ b/runtime/examples/Rust/tapasco-debug/src/main.rs @@ -0,0 +1,83 @@ +// The app module holds all state (of TaPaSCo) and interacts with PEs +mod app; +// The ui module handles (key press) events and displaying the app in the TUI +mod ui; + +use snafu::{ResultExt, Snafu}; + +#[derive(Debug, Snafu)] +enum Error { + #[snafu(display( + "Failed to initialize TLKM object: {}. Have you loaded the kernel module?", + source + ))] + TLKMInit { source: tapasco::tlkm::Error }, + + #[snafu(display( + "Failed to initialize App: {}. Have you loaded the kernel module?", + source + ))] + App { source: app::Error }, + + #[snafu(display("Failed to initialize UI: {}. Have you checked your Terminal?", source))] + UI { source: ui::Error }, +} + +type Result = std::result::Result; + +use structopt::StructOpt; + +// TODO: 1. When issue #296 is fixed, remove the paragraph about the `EMFILE` error. +/// The interactive `TaPaSCo` Debugger can be used to retrieve information about the loaded +/// bitstream, monitor other `TaPaSCo` runtimes and write values to the registers of your PEs +/// +/// Currently due to a `libtapasco` bug where DMA Buffers, Interrupts, etc. are allocated even in monitor mode, you will have to start your other runtime twice, where the first time the `EMFILE` error is to be expected. +#[derive(StructOpt, Debug)] +#[structopt(rename_all = "kebab-case")] +struct Opt { + /// The Device ID of the FPGA you want to use if you got more than one + #[structopt(short = "d", long = "device", default_value = "0")] + device_id: u32, + + /// Specify the Access Mode as subcommand + #[structopt(subcommand)] + pub subcommand: Command, +} + +#[derive(StructOpt, Debug, PartialEq)] +pub enum Command { + /// Enter Monitor Mode where values cannot be modified, e.g. to monitor another runtime + Monitor {}, + // TODO: 2. When issue #296 is fixed, enable debug mode again. + // /// Enter Debug Mode where values can only be modified interactively in this debugger + // Debug {}, + /// Enter Unsafe Mode where values can be modified by this debugger and another runtime + Unsafe {}, +} + +fn init() -> Result<()> { + // Parse command line arguments: + let Opt { + device_id, + subcommand, + } = Opt::from_args(); + + // Specify the Access Mode as subcommand and setup the App and UI + ui::setup(&mut app::App::new(device_id, subcommand).context(App {})?).context(UI {}) +} + +fn main() { + // Initialize the env logger. Export `RUST_LOG=debug` to see logs on stderr. + env_logger::init(); + + // Initialize app and error reporting with Snafu: + match init() { + Ok(_) => {} + Err(e) => { + eprintln!("An error occurred: {}", e); + if let Some(backtrace) = snafu::ErrorCompat::backtrace(&e) { + println!("{}", backtrace); + } + } + }; +} diff --git a/runtime/examples/Rust/tapasco-debug/src/ui.rs b/runtime/examples/Rust/tapasco-debug/src/ui.rs new file mode 100644 index 00000000..bd3c5e08 --- /dev/null +++ b/runtime/examples/Rust/tapasco-debug/src/ui.rs @@ -0,0 +1,427 @@ +use std::{io::stdout, sync::mpsc, thread, time::Duration}; + +use log::trace; + +use tui::{ + backend::{Backend, CrosstermBackend}, + layout::{Constraint, Direction, Layout, Rect}, + style::{Color, Modifier, Style}, + symbols::DOT, + text::{Span, Spans, Text}, + widgets::{Block, BorderType, Borders, List, ListItem, Paragraph, Tabs, Wrap}, + Frame, Terminal, +}; + +use crossterm::{ + event::{self, Event as CEvent, KeyCode, KeyEvent, KeyModifiers}, + execute, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, +}; + +use unicode_width::UnicodeWidthStr; + +use snafu::{ResultExt, Snafu}; + +#[derive(Debug, Snafu)] +pub enum Error { + #[snafu(display("Failure in Crossterm Terminal Backend: {}", source))] + Crossterm { source: std::io::Error }, + + #[snafu(display("Failed to receive Input event: {}", source))] + ReceiveInput { source: std::sync::mpsc::RecvError }, + + #[snafu(display("Failed to handle Input event: {}", source))] + HandleInput { source: std::io::Error }, +} + +pub type Result = std::result::Result; + +use crate::app::{AccessMode, App, InputFrame, InputMode}; + +// Define an Event which can consist of a pressed key or the terminal got resized. +enum Event { + Input(I), + Resize(H, H), + // TODO: Maybe add a tick event which occurs when the UI should be updated while no key got pressed? +} + +pub fn setup(app: &mut App) -> Result<()> { + // Raw mode disables some common terminal functions that are unnecessary in the TUI environment + enable_raw_mode().context(Crossterm {})?; + + // Enter the Alternate Screen, so we don't break terminal history (it's like opening vim) + let mut stdout = stdout(); + execute!(stdout, EnterAlternateScreen).context(Crossterm {})?; + + // Initialize Crossterm backend + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend).context(Crossterm {})?; + + // Clear the Alternate Screen if someone left it dirty + terminal.clear().context(Crossterm {})?; + + // Save the result of the main loop to return it after tearing down the backend + let result = run_event_loop(app, &mut terminal); + + // Leave Alternate Screen to shut down cleanly regardless of the result + disable_raw_mode().context(Crossterm {})?; + execute!(terminal.backend_mut(), LeaveAlternateScreen).context(Crossterm {})?; + terminal.show_cursor().context(Crossterm {})?; + + // Return the result of the main loop after restoring the previous terminal state in order to + // not be stuck in the Alternate Screen / or Raw Mode which would make a `reset` of the shell + // necessary + result +} + +fn run_event_loop(app: &mut App, terminal: &mut Terminal) -> Result<()> { + // Setup input handling as in the crossterm demo with a multi producer single consumer (mpsc) channel + let (tx, rx) = mpsc::channel(); + thread::spawn(move || loop { + if event::poll(Duration::from_millis(250)).expect("Event loop: could not poll for events!") + { + if let CEvent::Key(key) = + event::read().expect("Event loop: could not read a key event!") + { + tx.send(Event::Input(key)) + .expect("Event loop: could not send an input event!"); + } else if let CEvent::Resize(w, h) = + event::read().expect("Event loop: could not read a resize event!") + { + tx.send(Event::Resize(w, h)) + .expect("Event loop: could not send a resize event!"); + } + } + }); + + // Event loop + loop { + // Update UI + draw(app, terminal)?; + + // Handle events + match rx.recv().context(ReceiveInput {})? { + // Match key pressed events + Event::Input(event) => { + trace!("Input event: {:?}", event); + + // Match the input mode, either you're in line input mode where you enter new + // values for registers or you're in the default navigation mode. + match app.input_mode { + InputMode::Edit => match event.code { + KeyCode::Char(c) => app.input.push(c), + KeyCode::Backspace => { + if app.input.pop().is_none() { + app.input_mode = InputMode::Navigation; + } + } + KeyCode::Enter => app.on_enter(), + KeyCode::Esc => app.on_escape(), + _ => {} + }, + InputMode::Navigation => match event { + // Press 'Shift+Tab' to switch backward through tabs + KeyEvent { + modifiers: KeyModifiers::SHIFT, + code: KeyCode::BackTab, + } => app.previous_tab(), + // without any modifiers + KeyEvent { + modifiers: KeyModifiers::NONE, + code, + } => match code { + // Press 'q' to quit application + KeyCode::Char('q') => return Ok(()), + // Press 'r' to redraw the UI + KeyCode::Char('r') => continue, + // Press 'Tab' to switch forward through tabs + KeyCode::Tab => app.next_tab(), + // Press ↑ or 'k' to go up in the list of PEs/Registers + KeyCode::Up | KeyCode::Char('k') => app.on_up(), + // Press ↓ or 'j' to go down in the list of PEs/Registers + KeyCode::Down | KeyCode::Char('j') => app.on_down(), + // Press Escape or 'h' to return back to the list of PEs + KeyCode::Esc | KeyCode::Left | KeyCode::Char('h') => app.on_escape(), + // Press Enter or 'l' to select a PE/Register + KeyCode::Enter | KeyCode::Right | KeyCode::Char('l') => app.on_enter(), + // Press 's' on a selected PE to start a job + KeyCode::Char('s') => match app.access_mode { + AccessMode::Unsafe {} => app.messages.push(app.start_current_pe()), + AccessMode::Monitor {} => app.messages.push("Unsafe access mode necessary to start a PE. Restart the app with `unsafe` parameter.".to_string()) + }, + _ => {} + }, + _ => {} + }, + } + } + // TODO: When opening a new pane in Tmux this app is not redrawn. + Event::Resize(_, _) => continue, + } + } +} + +fn draw(app: &mut App, terminal: &mut Terminal) -> Result<()> { + terminal.draw(|f| { + // Create a layout with fixed space for the Tab bar and a flexible space where each tab can + // draw itself + let tabs_chunks = Layout::default() + .direction(Direction::Vertical) + .margin(0) + .constraints( + [ + Constraint::Length(2), + Constraint::Length(3), + Constraint::Min(0), + ].as_ref() + ) + .split(f.size()); + + // Render title and general user instructions + f.render_widget( + Paragraph::new( + Spans::from(vec![ + Span::raw(&app.title), + Span::raw(" (q: Quit. "), + Span::styled("Remember to quit before reprogramming the FPGA or reloading the kernel module!", + Style::default().add_modifier(Modifier::ITALIC)), + Span::raw(")"), + ])), tabs_chunks[0]); + + // Map the titles of the Tabs into Spans to be able to highlight the title of the + // selected Tab + let titles = app.tabs.titles + .iter() + .map(|t| Spans::from(Span::styled(*t, Style::default()))) + .collect(); + let tabs = Tabs::new(titles) + .block(Block::default() + .title(Span::styled("Tabs (Shift+Tab: \u{2190}, Tab: \u{2192})", + Style::default().add_modifier(Modifier::DIM))) + .border_type(BorderType::Rounded) + .border_style(Style::default().add_modifier(Modifier::DIM)) + .borders(Borders::ALL)) + .style(Style::default() + .fg(Color::White)) + .highlight_style(Style::default().add_modifier(Modifier::BOLD)) + .divider(DOT) + .select(app.tabs.index); + + f.render_widget(tabs, tabs_chunks[1]); + + // Call the specific draw function for the selected Tab + match app.tabs.index { + 0 => draw_tab_peek_and_poke_pes(f, app, tabs_chunks[2]), + 1 => draw_tab_platform_components(f, app, tabs_chunks[2]), + 2 => draw_tab_bitstream_and_device_info(f, app, tabs_chunks[2]), + _ => {}, + } + }).context(Crossterm {})?; + + Ok(()) +} + +fn draw_tab_peek_and_poke_pes(f: &mut Frame, app: &mut App, chunk: Rect) { + // Create a vertical layout (top to bottom) first to split the Tab into 3 rows with a + // bottom line for keyboard input that is only shown when in Edit Mode (that then replaces + // the messages view): + let vertical_chunks = Layout::default() + .direction(Direction::Vertical) + .margin(0) + .constraints(match app.input_mode { + InputMode::Edit => [ + Constraint::Length(15), + Constraint::Min(30), + Constraint::Length(3), + ] + .as_ref(), + InputMode::Navigation => [ + Constraint::Length(15), + Constraint::Min(30), + Constraint::Length(15), + ] + .as_ref(), + }) + .split(chunk); + + // Split the second row into half horizontally + let horizontal_chunks = Layout::default() + .direction(Direction::Horizontal) + .margin(0) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) + .split(vertical_chunks[1]); + + // Draw the PEs as stateful list to be able to select one + let pes_title = if (app.access_mode == AccessMode::Monitor {}) { + "PE List (j:\u{2193}, k:\u{2191})" + } else { + "PE List (j:\u{2193}, k:\u{2191}, s: start the selected PE, Enter/l: switch to Register List)" + }; + let pes: Vec = app + .pe_infos + .items + .iter() + .map(|i| ListItem::new(vec![Spans::from(Span::raw(i))])) + .collect(); + let pes = List::new(pes) + .block(focusable_block( + pes_title, + app.focus == InputFrame::PEList {}, + )) + .highlight_style(Style::default().add_modifier(Modifier::BOLD)) + .highlight_symbol("> "); + f.render_stateful_widget(pes, vertical_chunks[0], &mut app.pe_infos.state); + + // Split the PE's registers into status plus return value and arguments + let register_chunks = Layout::default() + .direction(Direction::Vertical) + .margin(0) + .constraints([Constraint::Length(5), Constraint::Min(10)].as_ref()) + .split(horizontal_chunks[0]); + + // Status registers + draw_block_with_paragraph( + f, + "Status Registers", + app.get_status_registers(), + register_chunks[0], + ); + + // Argument Register List (also stateful list for editing) + let registers_title = if (app.access_mode == AccessMode::Monitor {}) { + "Register List (r: Refresh)" + //} else if (app.access_mode == AccessMode::Debug {}) { + // "Register List (r: Refresh, Escape: back, j:\u{2193}, k:\u{2191}, Enter/l: set Register, s: Start PE)" + } else { + "Register List (r: Refresh, Escape: back, j:\u{2193}, k:\u{2191}, Enter/l: set Register)" + }; + let registers = app.get_argument_registers(register_chunks[1].height.saturating_sub(2).into()); + let registers: Vec = registers + .iter() + .map(|i| ListItem::new(vec![Spans::from(Span::raw(i))])) + .collect(); + let registers = List::new(registers) + .block(focusable_block( + registers_title, + app.focus == InputFrame::RegisterList, + )) + .highlight_style(Style::default().add_modifier(Modifier::BOLD)) + .highlight_symbol("> "); + f.render_stateful_widget(registers, register_chunks[1], &mut app.register_list); + + // TODO: Should local memory also be editable? + let local_memory = + app.dump_current_pe_local_memory(horizontal_chunks[1].height.saturating_sub(2).into()); + let local_memory: Vec = local_memory + .iter() + .map(|i| ListItem::new(vec![Spans::from(Span::raw(i))])) + .collect(); + let local_memory = List::new(local_memory) + .block(focusable_block("Local Memory (r: Refresh)", false)) + .highlight_style(Style::default().add_modifier(Modifier::BOLD)) + .highlight_symbol("> "); + f.render_stateful_widget( + local_memory, + horizontal_chunks[1], + &mut app.local_memory_list, + ); + + // Draw an input line if in Edit Mode or the messages view when not in Edit Mode + if app.input_mode == InputMode::Edit { + let input = Paragraph::new(app.input.as_ref()).block( + Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .title("Input (Escape: abort, Enter: try to parse input as signed decimal i64)") + .style(Style::default().fg(Color::Yellow)), + ); + let input_chunks = vertical_chunks[2]; + f.render_widget(input, input_chunks); + + // Make the cursor visible and ask tui-rs to put it at the specified coordinates after rendering + f.set_cursor( + // Put cursor past the end of the input text + input_chunks.x + (app.input.width() + 1).try_into().unwrap_or(0), + // Move one line down, from the border to the input line + input_chunks.y + 1, + ); + } else { + draw_block_with_paragraph( + f, + "Messages", + app.messages + .iter() + .rev() + .take(vertical_chunks[2].height.saturating_sub(2).into()) + .rev() + .cloned() + .collect::>() + .join("\n"), + vertical_chunks[2], + ); + } +} + +fn draw_tab_platform_components(f: &mut Frame, app: &App, chunk: Rect) { + // Create a vertical layout (top to bottom) first to split the Tab into 2 rows + let vertical_chunks = Layout::default() + .direction(Direction::Vertical) + .margin(0) + .constraints([Constraint::Min(15), Constraint::Length(10)].as_ref()) + .split(chunk); + + // Show general info about platform components + draw_block_with_paragraph(f, "Overview", app.get_platform_info(), vertical_chunks[0]); + + // and DMAEngine Statistics + draw_block_with_paragraph( + f, + "DMAEngine Statistics (r: Refresh)", + app.get_dmaengine_statistics(), + vertical_chunks[1], + ); +} + +fn draw_tab_bitstream_and_device_info(f: &mut Frame, app: &App, chunk: Rect) { + draw_block_with_paragraph(f, "", app.get_bitstream_info(), chunk); +} + +/// Draw a block with some text in it into the rectangular space given by chunk +fn draw_block_with_paragraph<'a, B: Backend, T>( + f: &mut Frame, + block_title: &str, + text: T, + chunk: Rect, +) where + Text<'a>: From, +{ + let block = dim_block(block_title); + let paragraph = Paragraph::new(text).block(block).wrap(Wrap { trim: false }); + f.render_widget(paragraph, chunk); +} + +/// Create a new Block with round corners and the given title and choose the border style +fn block_with_border_style(title: &str, style: Modifier) -> Block { + Block::default() + .title(Span::styled(title, Style::default().add_modifier(style))) + .border_type(BorderType::Rounded) + .border_style(Style::default().add_modifier(style)) + .borders(Borders::ALL) +} + +/// Create a new Block with round corners in dim colors and the given title +fn dim_block(title: &str) -> Block { + block_with_border_style(title, Modifier::DIM) +} + +/// Create a new Block with round corners which takes a boolean if it is focused +fn focusable_block(title: &str, focused: bool) -> Block { + block_with_border_style( + title, + if focused { + Modifier::BOLD + } else { + Modifier::DIM + }, + ) +} diff --git a/runtime/libtapasco/Cargo.toml b/runtime/libtapasco/Cargo.toml index ac073d27..4d654cf2 100644 --- a/runtime/libtapasco/Cargo.toml +++ b/runtime/libtapasco/Cargo.toml @@ -12,6 +12,12 @@ opt-level = 3 codegen-units = 1 lto = "fat" +[features] +# There are no default features: +default = [] +# Necessary when building `tapasco-debug` to make some things accessible that are usually hidden away: +tapasco-debug = [] + [dependencies] log = "0.4.8" chrono = "0.4.11" diff --git a/runtime/libtapasco/src/device.rs b/runtime/libtapasco/src/device.rs index 21af184c..b763f077 100644 --- a/runtime/libtapasco/src/device.rs +++ b/runtime/libtapasco/src/device.rs @@ -19,11 +19,12 @@ */ use crate::allocator::{Allocator, DriverAllocator, DummyAllocator, GenericAllocator, VfioAllocator}; -use crate::debug::DebugGenerator; +use crate::debug::{DebugGenerator, NonDebugGenerator}; use crate::dma::{DMAControl, DirectDMA, DriverDMA, VfioDMA, SVMDMA}; use crate::dma_user_space::UserSpaceDMA; use crate::job::Job; use crate::pe::PEId; +use crate::pe::PE; use crate::scheduler::Scheduler; use crate::tlkm::{tlkm_access, tlkm_ioctl_svm_launch, tlkm_svm_init_cmd}; use crate::tlkm::tlkm_ioctl_create; @@ -32,6 +33,7 @@ use crate::tlkm::tlkm_ioctl_device_cmd; use crate::tlkm::DeviceId; use crate::vfio::*; use config::Config; +use memmap::MmapMut; use memmap::MmapOptions; use prost::Message; use snafu::ResultExt; @@ -106,6 +108,21 @@ pub enum Error { #[snafu(display("Could not launch SVM support in the TLKM"))] SVMInitError { source: nix::Error }, + + #[snafu(display("Could not find component {}.", name))] + ComponentNotFound { name: String }, + + #[snafu(display( + "Component {} has no associated interrupt. Cannot be used as PE.", + name + ))] + MissingInterrupt { name: String }, + + #[snafu(display("PE Error: {}", source))] + PEError { source: crate::pe::Error }, + + #[snafu(display("Debug Error: {}", source))] + DebugError { source: crate::debug::Error }, } type Result = std::result::Result; @@ -226,8 +243,10 @@ pub struct Device { name: String, access: tlkm_access, scheduler: Arc, + platform: Arc, offchip_memory: Vec>, tlkm_file: Arc, + tlkm_device_file: Arc, } impl Device { @@ -473,8 +492,10 @@ impl Device { name, status: s, scheduler, + platform, offchip_memory: allocator, tlkm_file, + tlkm_device_file: tlkm_dma_file, }; device.change_access(tlkm_access::TlkmAccessMonitor)?; @@ -498,6 +519,25 @@ impl Device { Ok(Job::new(pe, &self.scheduler)) } + /// Request a PE from the device but don't create a Job for it. Usually [`acquire_pe`] is used + /// if you don't want to do things manually. + /// + /// # Arguments + /// * id: The ID of the desired PE. + /// + pub fn acquire_pe_without_job(&self, id: PEId) -> Result { + trace!( + "Trying to acquire PE of type {} without exclusive access.", + id + ); + let pe = self.scheduler.acquire_pe(id).context(SchedulerError)?; + trace!( + "Successfully acquired PE of type {} without exclusive access.", + id + ); + Ok(pe) + } + fn check_exclusive_access(&self) -> Result<()> { if self.access == tlkm_access::TlkmAccessExclusive { Ok(()) @@ -625,4 +665,86 @@ impl Device { pub fn get_pe_id(&self, name: &str) -> Result { self.scheduler.get_pe_id(name).context(SchedulerError) } + + /// Get a list of platform components names available on this device + pub fn get_available_platform_components(&self) -> Vec { + let mut platform_components = Vec::new(); + + for p in &self.status.platform { + trace!("Found platform component {}.", p.name); + platform_components.push(p.name.clone()); + } + + platform_components + } + + /// Get memory of a platform component + /// + /// # Safety + /// + /// Treated unsafe as a user can change anything about the memory without any checks + /// and might try using the memory after the device has been released. + pub unsafe fn get_platform_component_memory(&self, name: &str) -> Result<&mut [u8]> { + for p in &self.status.platform { + if p.name == name { + trace!( + "Found platform component {} at {:X} (Size {}).", + p.name, + p.offset, + p.size + ); + + let ptr = self.platform.as_ptr().offset(p.offset as isize) as *mut u8; + let s = std::slice::from_raw_parts_mut(ptr, p.size as usize); + return Ok(s); + } + } + Err(Error::ComponentNotFound { + name: name.to_string(), + }) + } + + /// Returns a PE interface to the platform component + /// Can be used like any other PE but is not integrated + /// into the scheduling and job mechanisms + pub fn get_platform_component_as_pe(&self, name: &str) -> Result { + for p in &self.status.platform { + if p.name == name { + trace!( + "Found platform component {} at {:X} (Size {}).", + p.name, + p.offset, + p.size + ); + + let d = NonDebugGenerator {}; + let debug = d + .new(&self.platform, "Unused".to_string(), 0, 0) + .context(DebugError)?; + + if !p.interrupts.is_empty() { + return PE::new( + // TODO: Should the ID of this PE really be 42? If it's necessarily a magic + // value, why not just 0? + 42, + 42, + p.offset, + self.platform.clone(), + &self.tlkm_device_file, + p.interrupts[0].mapping as usize, + debug, + false, // TODO: Is this correct? + ) + .context(PEError); + } else { + return Err(Error::MissingInterrupt { + name: name.to_string(), + }); + } + } + } + Err(Error::ComponentNotFound { + name: name.to_string(), + }) + } } diff --git a/runtime/libtapasco/src/pe.rs b/runtime/libtapasco/src/pe.rs index 73c069f1..74c56997 100644 --- a/runtime/libtapasco/src/pe.rs +++ b/runtime/libtapasco/src/pe.rs @@ -95,10 +95,14 @@ pub struct PE { id: usize, #[get = "pub"] type_id: PEId, + // This public getter is guarded behind conditional compilation for `tapasco-debug`: + #[cfg_attr(feature = "tapasco-debug", get = "pub")] offset: DeviceAddress, #[get = "pub"] active: bool, copy_back: Option>, + // This public getter is guarded behind conditional compilation for `tapasco-debug`: + #[cfg_attr(feature = "tapasco-debug", get = "pub")] memory: Arc, #[set = "pub"]