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"]