diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 0f539724..19c559d9 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -5,9 +5,6 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # Cancel in-flight jobs for the same branch or PR -env: - BENCHMARKS_CACHE: "build-benchmarks" - jobs: formatting-check: name: Formatting Check @@ -29,7 +26,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-18.04, ubuntu-20.04, macos-latest] + os: [ubuntu-20.04, ubuntu-22.04, macos-latest] steps: - name: Checkout uses: actions/checkout@v2 @@ -45,7 +42,7 @@ jobs: compile-with-arduino: name: (Arduino) Compile on ${{matrix.board.platform-name}} - needs: formatting-check + needs: [formatting-check] runs-on: ubuntu-latest env: SKETCHES_REPORTS_PATH: sketches-reports @@ -56,6 +53,7 @@ jobs: - name: HTTPClient - name: PubSubClient - name: Adafruit NeoPixel + - source-url: https://github.com/me-no-dev/AsyncTCP.git strategy: fail-fast: false matrix: @@ -72,7 +70,11 @@ jobs: sketches: | - platforms/Arduino steps: - - uses: actions/checkout@v2 + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Compile sketches uses: arduino/compile-sketches@v1 with: @@ -84,6 +86,7 @@ jobs: ${{ env.LIBRARIES }} enable-deltas-report: true sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} + - name: Upload sketches report to workflow artifact uses: actions/upload-artifact@v2 with: @@ -107,9 +110,10 @@ jobs: submodules: 'recursive' - name: Build WARDuino for ESP-IDF - uses: espressif/esp-idf-ci-action@main + uses: espressif/esp-idf-ci-action@v1 with: esp_idf_version: latest target: esp32 + command: idf.py build -DBUILD_ESP=ON path: . diff --git a/.gitignore b/.gitignore index ab0901f5..78858d21 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ _deps .AppleDouble .LSOverride +*.old + diff --git a/.gitmodules b/.gitmodules index db420bdd..c81691f9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "tests/sexpr-parser"] path = tests/integration/sexpr-parser url = git@github.com:benthepoet/c-sexpr-parser.git +[submodule "lib/json"] + path = lib/json + url = https://github.com/nlohmann/json diff --git a/CMakeLists.txt b/CMakeLists.txt index 2738064a..09efe83d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ if (BUILD_ESP) if (NOT EXISTS $ENV{IDF_PATH}/tools/cmake/project.cmake) message(FATAL_ERROR "Can't find $IDF_PATH/tools/cmake/project.cmake. Make sure ESP-IDF is installed and $IDF_PATH is set.") endif () - + message(VERBOSE "Using ESP-IDF toolchain") set(EXTRA_COMPONENT_DIRS "platforms/ESP-IDF") @@ -25,23 +25,29 @@ project(WARDuino) # Build the emulator version of WARDuino if (BUILD_EMULATOR) + set(EXTERNAL_LIB_HEADERS lib/json/single_include) + find_package(Threads REQUIRED) + set(SOURCE_FILES + src/WARDuino/WARDuino.cpp + src/WARDuino/CallbackHandler.cpp + src/Primitives/emulated.cpp + src/Interpreter/instructions.cpp src/Memory/mem.cpp src/Utils/util.cpp src/Utils/util_arduino.cpp - src/Debug/debugger.cpp src/Utils/macros.cpp - src/WARDuino/WARDuino.cpp - src/WARDuino/CallbackHandler.cpp - src/Primitives/primitives.cpp - src/Interpreter/instructions.cpp - ) + src/Utils/sockets.cpp + src/Debug/debugger.cpp + src/Edward/proxy.cpp + src/Edward/proxy_supervisor.cpp + src/Edward/RFC.cpp) + set(TEST_FRAMEWORK - tests/integration/wasm_tests.cpp - tests/integration/assertion.cpp - tests/integration/sexpr-parser/src/sexpr.c - ) + tests/integration/wasm_tests.cpp + tests/integration/assertion.cpp + tests/integration/sexpr-parser/src/sexpr.c) add_definitions(-DINFO=0) add_definitions(-DDEBUG=0) @@ -52,11 +58,12 @@ if (BUILD_EMULATOR) set(CMAKE_CXX_STANDARD 11) # Set default compile flags for GCC - if(CMAKE_COMPILER_IS_GNUCXX) + if (CMAKE_COMPILER_IS_GNUCXX) add_compile_options(-g -v -Wall -Wextra -Wunused) - endif(CMAKE_COMPILER_IS_GNUCXX) + endif (CMAKE_COMPILER_IS_GNUCXX) # WARDuino CLI add_executable(wdcli platforms/CLI-Emulator/main.cpp ${SOURCE_FILES} ${TEST_FRAMEWORK}) target_link_libraries(wdcli PRIVATE Threads::Threads) + target_include_directories(wdcli PRIVATE ${EXTERNAL_LIB_HEADERS}) endif (BUILD_EMULATOR) diff --git a/README.md b/README.md index 8a52c21b..cbbdc30d 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@

-This project is released under the Mozilla Public License 2.0, and is being developed as part of an active research project at the University of Ghent's [TOPL](https://github.com/TOPLLab) lab. +This project is released under the Mozilla Public License 2.0, and is being developed as part of an active research project at the University of Ghent's [TOPL Lab](https://github.com/TOPLLab). The WARDuino virtual machine is a WebAssembly runtime for microcontrollers, which runs both under the Arduino and ESP-IDF toolchains. -The WARDuino project also includes a [VS Code extension](https://github.com/TOPLLab/WARDuino-VSCode) to use the remote debugging facilities offered by the virtual machine. +The WARDuino project also includes a [VS Code extension](https://github.com/TOPLLab/WARDuino-VSCode) to use both the remote debugging and the out-of-place debugging facilities offered by the virtual machine.

Installation | Examples | Run Specification tests | Documentation @@ -22,7 +22,7 @@ Supported platforms: Linux (Ubuntu), macOS, ESP-IDF, Arduino The project uses CMake. Quick install looks like this: ```bash -git clone git@github.com:TOPLLab/WARDuino.git +git clone --recursive git@github.com:TOPLLab/WARDuino.git cd WARDuino mkdir build-emu cd build-emu @@ -36,6 +36,8 @@ The WARDuino VM can be compiled with both the Arduino and ESP-IDF toolchains, an ### Build for ESP-IDF +> warning: primitive support for IDF is under construction + Before you can compile and flash with ESP-IDF, you must install and enable [the toolchain](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/linux-macos-setup.html). You also need to disable the watchdog timer: @@ -62,6 +64,7 @@ Or simply run `idf.py flash`. ### Build for Arduino First, install the [arduino-cli](https://arduino.github.io/arduino-cli/0.21/installation/). +You will also need python3 with the pyserial pacakge. Second, create the config file: @@ -69,15 +72,27 @@ Second, create the config file: arduino-cli config init ``` -If you need additional boards, such as the esp32 boards, you can add them in the generated config file. More information [here](https://arduino.github.io/arduino-cli/0.21/getting-started/). +If you need additional boards, such as the esp32 boards, you can add them in the generated config file. More information on how to install the esp32 boards can be found here. +(_note: WARDuino requires at least version 2.0.2 of the esp32 board manager when using esp32 devices)_ Thirdly, make sure you install the `PubSubClient` and `Adafruit NeoPixel` library. (used for MQTT and pixel primitives) ```bash -arduino-cli lib install "PubSubClient" -arduino-cli lib install "Adafruit NeoPixel" +arduino-cli lib install "PubSubClient" # for MQTT +arduino-cli lib install "Adafruit NeoPixel" # for some primitives ``` +To build for Arduino with WIFI support you need to also install the following third-party libraries. +(Wou might need to set `enable_unsafe_install` to `true` in your arduino config ) + +```bash +arduino-cli lib install FreeRTOS +arduino-cli lib install --git-url https://github.com/me-no-dev/AsyncTCP.git +``` + +If you haven't done so already, clone (or symlink) this repository to `~/Arduino/libraries` to make WARDuino availible to Arduino. + + After this initial installation steps you can start using WARDuino with the Arduino toolchain. You can upload the example file as follows, starting from the project root: diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore index c4f050ed..cc37b2a9 100644 --- a/benchmarks/.gitignore +++ b/benchmarks/.gitignore @@ -3,3 +3,6 @@ bin/ output *.csv + +*.json +*.data diff --git a/benchmarks/all_bench.sh b/benchmarks/all_bench.sh index 9ed673bf..ce4dcd88 100755 --- a/benchmarks/all_bench.sh +++ b/benchmarks/all_bench.sh @@ -18,14 +18,18 @@ to_csv() { sed -i -n '0~2{N;s/\n/,/p}' $1 } -sleep 5 -./espruino_bench.sh $tmpdir/espruino -to_csv $tmpdir/espruino +#sleep 5 +#./espruino_bench.sh $tmpdir/espruino +#to_csv $tmpdir/espruino sleep 5 ./warduino_bench.sh $tmpdir/warduino to_csv $tmpdir/warduino +sleep 5 +./edward_bench.sh $tmpdir/edward +to_csv $tmpdir/edward + sleep 5 ./wasm3_bench.sh $tmpdir/wasm3 to_csv $tmpdir/wasm3 @@ -38,6 +42,8 @@ echo "# Espruino" cat $tmpdir/espruino echo "# Warduino" cat $tmpdir/warduino +echo "# Edward" +cat $tmpdir/edward echo "# Wasm3" cat $tmpdir/wasm3 echo "# Native" diff --git a/benchmarks/benchmarks.cpp b/benchmarks/benchmarks.cpp index a3f145a9..aafb2261 100644 --- a/benchmarks/benchmarks.cpp +++ b/benchmarks/benchmarks.cpp @@ -2,7 +2,8 @@ #include -#include "../debug.h" +#include "../src/Debug/debugger.h" +#include "../src/Utils/macros.h" #include "../src/WARDuino.h" #include "timer.h" @@ -48,7 +49,7 @@ int run_benchmarks(size_t num_benchmarks, string benchmarks[], char path[MAX_PATH]; unsigned char bytes[MAX_BYTE_CODE_SIZE]; unsigned int bytes_length; - auto *w = new WARDuino(); + auto *w = WARDuino::instance(); size_t correct = 0; for (size_t i = 0; i < num_benchmarks; i++) { string name = benchmarks[i]; diff --git a/benchmarks/edward.ino.template b/benchmarks/edward.ino.template new file mode 100644 index 00000000..e566c6b7 --- /dev/null +++ b/benchmarks/edward.ino.template @@ -0,0 +1,88 @@ +#include "Arduino.h" +#include "WARDuino.h" + +WARDuino* wac = WARDuino::instance(); + +SocketServer* server; +ServerCredentials serverCredentials = {"{{SSID}}", "{{Password}}"}; +uint16_t pullportno = 8080; +uint16_t pushportno = 8081; + +#define D1 5 + +volatile bool handelingInterrupt = false; +uint8_t buff[100] = {0}; +uint8_t buff_len = 0; + +void ICACHE_RAM_ATTR handleInput() { + if (handelingInterrupt) return; + handelingInterrupt = true; + interrupts(); + + while (Serial.available()) { + size_t buff_len = 0; + while (Serial.available()) { + buff[buff_len++] = (int8_t)Serial.read(); + } + if (buff_len) { + wac->handleInterrupt(buff_len, buff); + } + } + handelingInterrupt = false; +} + +void startDebuggerStd(void* pvParameter) { + int valread; + uint8_t buffer[1024] = {0}; + wac->debugger->setChannel(fileno(stdout)); + write(fileno(stdout), "Got a message ... \n", 19); + while (true) { + // taskYIELD(); + // vTaskDelay(100 / portTICK_PERIOD_MS); + yield(); + + while (Serial.available()) { + size_t buff_len = 0; + while (Serial.available()) { + buffer[buff_len++] = (int8_t)Serial.read(); + } + if (buff_len) { + write(fileno(stdout), "Reading message ..... \n", 19); + fflush(stdout); + wac->handleInterrupt(valread - 1, buffer); + write(fileno(stdout), buffer, valread); + fflush(stdout); + } + } + } +} + +void handleInterrupt(size_t len, uint8_t* buff) { + wac->handleInterrupt(len, buff); +} + +void setup() { + Serial.begin(115200); + attachInterrupt(D1, handleInput, CHANGE); + + // create & connect SocketServer + SocketServer::createServer(pullportno, pushportno, &handleInterrupt); + server = SocketServer::getServer(); + server->connect2Wifi(&serverCredentials); +} + +void loop() { + disableCore0WDT(); + Module* m = wac->load_module(impl_wasm, impl_wasm_len, {}); + server->begin(); + + printf("LOADED \n\n"); + xTaskCreate(startDebuggerStd, "Debug Thread", 5000, NULL, 1, NULL); + printf("START\n\n"); + for (int i = 0; i < 10; i++) { + wac->run_module(m); + printf("%d: %u\n", i, m->stack->value.uint32); + } + wac->unload_module(m); + printf("DONE\n\n"); +} \ No newline at end of file diff --git a/benchmarks/edward_bench.sh b/benchmarks/edward_bench.sh new file mode 100755 index 00000000..7efea3c9 --- /dev/null +++ b/benchmarks/edward_bench.sh @@ -0,0 +1,23 @@ +#!/usr/bin/sh +# Name: Upload all programs in bench.list to arduino (WARDuino) and time +# By Robbert Gurdeep Singh +################################################################################ +tmpfile="$(mktemp --tmpdir)" +trap "rm '$tmpfile'" EXIT +cd "$(dirname "$0")" +date >$1 +make clean all +make -C tasks all + +cat bench.list | while read l; do + echo $l | tee -a $1 + ../scripts/upload ${BOARD:-ESP32WROVER} ./tasks/$l/wast/edward/edward.ino -p /dev/ttyUSB0 2>&1 >"$tmpfile" + if [ "$?" -eq "0" ]; then + echo "flashed" + python3 flash_and_check.py | tee -a $1 + else + cat $tmpfile + echo "FAILED!" + exit 1 + fi +done diff --git a/benchmarks/flash_and_check.py b/benchmarks/flash_and_check.py index b5e29449..d83294e6 100644 --- a/benchmarks/flash_and_check.py +++ b/benchmarks/flash_and_check.py @@ -7,6 +7,7 @@ import serial.tools.list_ports import time import sys +import math def await_output(s: serial.Serial, target: str, failure=None): @@ -49,19 +50,17 @@ def await_output(s: serial.Serial, target: str, failure=None): port = ports[0] print(f"using {port}", file=sys.stderr) + with serial.Serial(port, 115200) as serial: if len(sys.argv) == 2: - with open(sys.argv[1], "rb") as inputText: - serial.write(inputText.read(-1)) - serial.write(b'\n\n') - print("Bytes sent", file=sys.stderr) - print("Await start", file=sys.stderr) - await_output(serial, "START\n") - startTime = time.monotonic() - print("START found, waiting for DONE", file=sys.stderr) - success = await_output(serial, "DONE\n", failure="Guru Meditation Error") - if success: - endTime = time.monotonic() - print(endTime - startTime) - else: - print("\nnan") + print("Await start", file=sys.stderr) + await_output(serial, "START\n") + startTime = time.monotonic() + print("START found, waiting for DONE", file=sys.stderr) + success = await_output(serial, "DONE\n", failure="Guru Meditation Error") + if success: + endTime = time.monotonic() + print(f"{sys.argv[1]}: iterations=1 runtime: {math.floor((endTime - startTime) * 1000000)}us") + else: + print("\nnan") + diff --git a/benchmarks/makefile b/benchmarks/makefile index 3ae306e9..95b6bc36 100644 --- a/benchmarks/makefile +++ b/benchmarks/makefile @@ -15,7 +15,7 @@ DEBUGFLAGS = -DDEBUG=$(DEBUG) -DTRACE=$(TRACE) -DINFO=$(INFO) -DWARN=$(WARN) CXX = g++ CC = gcc CFLAGS = -g -Wall -c -CXXFLAGS = -g -v -std=c++11 -Wall +CXXFLAGS = -g -v -std=c++11 -I ../lib/json/single_include -Wall @@ -26,15 +26,20 @@ TARGET = warduino_benchmark TASKS = $(notdir $(patsubst %/.,%,$(wildcard tasks/*/.))) CXXSOURCES = \ - ../mem.cpp \ - ../util.cpp \ - ../util_arduino.cpp \ - ../interrupt_operations.cpp \ - ../debug.cpp \ - ../WARDuino.cpp \ - ../primitives.cpp \ - ../instructions.cpp \ - ../glue.cpp \ + ../src/Memory/mem.cpp \ + ../src/Utils/util.cpp \ + ../src/Utils/util_arduino.cpp \ + ../src/Debug/debugger.cpp \ + ../src/Utils/macros.cpp \ + ../src/WARDuino/WARDuino.cpp \ + ../src/WARDuino/CallbackHandler.cpp \ + ../src/Primitives/emulated.cpp \ + ../src/Interpreter/instructions.cpp \ + ../src/Edward/RFC.cpp \ + ../src/Edward/proxy.cpp \ + ../src/Edward/proxy_supervisor.cpp \ + ../src/Utils/sockets.cpp \ + ../lib/json/single_include/nlohmann/json.hpp \ benchmarks.cpp all: $(OUTPUTDIR)$(TARGET) $(addprefix tasks/,$(addsuffix /wast/impl.wasm, $(TASKS))) @@ -51,8 +56,8 @@ run: all $(OUTPUTDIR)$(TARGET) clean: mostlyclean - $(MAKE) -C tasks clean + -$(MAKE) -C tasks clean mostlyclean: - $(RM) -rf $(OUTPUTDIR) - $(RM) $(COBJECTS) + -$(RM) -rf $(OUTPUTDIR) + -$(RM) $(COBJECTS) diff --git a/benchmarks/native_bench.sh b/benchmarks/native_bench.sh index 3d87de0d..9b71d803 100755 --- a/benchmarks/native_bench.sh +++ b/benchmarks/native_bench.sh @@ -1,24 +1,20 @@ #!/usr/bin/sh -# Name: Upload all programs in bench.list to arduino and time -# By Robbert Gurdeep Singh +# This scripts takes the name of a benchmark from the tasks folder and uploads +# it to arduino and times the execution ################################################################################ + tmpfile="$(mktemp --tmpdir)" trap "rm '$tmpfile'" EXIT cd "$(dirname "$0")" -outloc=${1:--} -date >$1 -make clean all -make -C tasks all +outloc=${2:--} -cat bench.list | while read l; do - echo $l | tee -a $1 - ../scripts/upload ${BOARD:-ESP32WROVER} ./tasks/$l/c/arduino.ino 2>&1 >"$tmpfile" - if [ "$?" -eq "0" ]; then - echo "flashed" - python flash_and_check.py | tee -a $1 - else - cat $tmpfile - echo "FAILED!" - exit 1 - fi -done +echo $1 | tee -a $2 +./upload ${BOARD:-ESP32WROVER} ./tasks/$1/c/c.ino 2>&1 >"$tmpfile" +if [ "$?" -eq "0" ]; then + echo "flashed" + python3 flash_and_check.py $1 | tee -a $2 +else + cat $tmpfile + echo "FAILED!" + exit 1 +fi diff --git a/benchmarks/rebench.conf b/benchmarks/rebench.conf new file mode 100644 index 00000000..84bc6310 --- /dev/null +++ b/benchmarks/rebench.conf @@ -0,0 +1,45 @@ +# this run definition will be chosen if no parameters are given to rebench +default_experiment: all +default_data_file: 'example.data' + +reporting: + rebenchdb: + db_url: https://rebench.stefan-marr.de/rebenchdb + repo_url: https://github.com/TOPLLab/WARDuino + record_all: true + project_name: WARDuino + +# a set of suites with different benchmarks and possibly different settings +benchmark_suites: + Microbenchmarks: + gauge_adapter: RebenchLog + command: "" + benchmarks: + - catalan + - fac + - fib + - gcd + - primes + - tak + - tak-mem + +# a set of executables for the benchmark execution +executors: + warduino: + path: . + executable: warduino_bench.sh + env: + PATH: /snap/bin/:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + args: " %(benchmark)s" + build: + - cd tasks; make + + +# combining benchmark suites and executions +experiments: + Example: + suites: + - Microbenchmarks + executions: + - warduino + diff --git a/benchmarks/tasks/.gitignore b/benchmarks/tasks/.gitignore index 60b2a44a..f97f5809 100644 --- a/benchmarks/tasks/.gitignore +++ b/benchmarks/tasks/.gitignore @@ -1,4 +1,5 @@ */wast/warduino/ +*/wast/edward/ */wast/wasm3/ */wast/*.wasm */wast/*.wast diff --git a/benchmarks/tasks/makefile b/benchmarks/tasks/makefile index 53f9096b..4d5c6deb 100644 --- a/benchmarks/tasks/makefile +++ b/benchmarks/tasks/makefile @@ -1,6 +1,6 @@ TASKS=$(patsubst %/.,%,$(wildcard */.)) -all: $(addsuffix /wast/warduino/warduino.ino,$(TASKS)) $(addsuffix /wast/wasm3/wasm3.ino,$(TASKS)) $(addsuffix /wast/impl.wast,$(TASKS)) $(addsuffix /wast/impl.wasm,$(TASKS)) $(addsuffix /c/arduino.ino,$(TASKS)) +all: $(addsuffix /wast/warduino/warduino.ino,$(TASKS)) $(addsuffix /wast/edward/edward.ino,$(TASKS)) $(addsuffix /wast/wasm3/wasm3.ino,$(TASKS)) $(addsuffix /wast/impl.wast,$(TASKS)) $(addsuffix /wast/impl.wasm,$(TASKS)) $(addsuffix /c/c.ino,$(TASKS)) echo $(TASKS) @@ -8,16 +8,22 @@ all: $(addsuffix /wast/warduino/warduino.ino,$(TASKS)) $(addsuffix /wast/wasm3/w %/wast/warduino/warduino.ino: %/wast/impl.wasm -mkdir $(@D) xxd -i $< $@ - sed -i 's/[^ ]*_impl_wasm/impl_wasm/' $@ + sed -i 's/[^ ]*_impl_wasm/impl_wasm/' $@ cat ../warduino.ino.template >> $@ +%/wast/edward/edward.ino: %/wast/impl.wasm + -mkdir $(@D) + xxd -i $< $@ + sed -i 's/[^ ]*_impl_wasm/impl_wasm/' $@ + cat ../edward.ino.template >> $@ + %/wast/wasm3/wasm3.ino: %/wast/impl.wasm -mkdir $(@D) xxd -i $< $@ sed -i 's/[^ ]*_impl_wasm/impl_wasm/' $@ cat ../wasm3.ino.template >> $@ -%/c/arduino.ino: %/wast/impl.c ../c.ino.template +%/c/c.ino: %/wast/impl.c ../c.ino.template -mkdir $(@D) echo '#include "Arduino.h"' > $@ echo '#pragma GCC optimize ("O0")' >> $@ @@ -43,5 +49,5 @@ all: $(addsuffix /wast/warduino/warduino.ino,$(TASKS)) $(addsuffix /wast/wasm3/w wasm2wat -f $< > $@ clean: - -find -iname "impl.wast" -o -iname "impl.wasm" -o -iname "arduino.ino" -o -iname "warduino.ino" -o -iname "wasm3.ino" | xargs rm + -find -iname "impl.wast" -o -iname "impl.wasm" -o -iname "c.ino" -o -iname "warduino.ino" -o -iname "edward.ino" -o -iname "wasm3.ino" | xargs rm diff --git a/scripts/upload b/benchmarks/upload similarity index 100% rename from scripts/upload rename to benchmarks/upload diff --git a/benchmarks/warduino.ino.template b/benchmarks/warduino.ino.template index f07a8a3c..d682df8d 100644 --- a/benchmarks/warduino.ino.template +++ b/benchmarks/warduino.ino.template @@ -1,8 +1,7 @@ #include "Arduino.h" #include "WARDuino.h" -WARDuino wac; - +WARDuino* wac = WARDuino::instance(); #define D1 5 @@ -18,10 +17,10 @@ void ICACHE_RAM_ATTR handleInput() { while (Serial.available()) { size_t buff_len = 0; while (Serial.available()) { - buff[buff_len++] = (int8_t) Serial.read(); + buff[buff_len++] = (int8_t)Serial.read(); } if (buff_len) { - wac.handleInterrupt(buff_len, buff); + wac->handleInterrupt(buff_len, buff); } } handelingInterrupt = false; @@ -33,13 +32,13 @@ void setup() { } void loop() { - Module *m = wac.load_module(impl_wasm, impl_wasm_len, {}); + Module *m = wac->load_module(impl_wasm, impl_wasm_len, {}); delay(1000); printf("START\n\n"); for (int i = 0; i < 10; i++) { - wac.run_module(m); + wac->run_module(m); printf("%d: %u\n", i, m->stack->value.uint32); } - wac.unload_module(m); + wac->unload_module(m); printf("DONE\n\n"); -} \ No newline at end of file +} diff --git a/benchmarks/warduino_bench.sh b/benchmarks/warduino_bench.sh index 6ebf364f..4589840c 100755 --- a/benchmarks/warduino_bench.sh +++ b/benchmarks/warduino_bench.sh @@ -1,23 +1,20 @@ #!/usr/bin/sh -# Name: Upload all programs in bench.list to arduino (WARDuino) and time -# By Robbert Gurdeep Singh +# This scripts takes the name of a benchmark from the tasks folder and uploads +# it to arduino and times the execution ################################################################################ + tmpfile="$(mktemp --tmpdir)" trap "rm '$tmpfile'" EXIT cd "$(dirname "$0")" -date >$1 -make clean all -make -C tasks all +outloc=${2:--} -cat bench.list | while read l; do - echo $l | tee -a $1 - ../scripts/upload ${BOARD:-ESP32WROVER} ./tasks/$l/wast/warduino/warduino.ino 2>&1 >"$tmpfile" - if [ "$?" -eq "0" ]; then - echo "flashed" - python flash_and_check.py | tee -a $1 - else - cat $tmpfile - echo "FAILED!" - exit 1 - fi -done +echo $1 | tee -a $2 +./upload ${BOARD:-ESP32WROVER} ./tasks/$1/wast/warduino/warduino.ino 2>&1 >"$tmpfile" +if [ "$?" -eq "0" ]; then + echo "flashed" + python3 flash_and_check.py $1 | tee -a $2 +else + cat $tmpfile + echo "FAILED!" + exit 1 +fi diff --git a/documentation/DumpFormat.md b/documentation/DumpFormat.md index 95c970c1..2b09b7db 100644 --- a/documentation/DumpFormat.md +++ b/documentation/DumpFormat.md @@ -2,7 +2,7 @@ WARDuino sends its information dumps as json. -## Full dump (0x12) +## Full dump (0x12) Lists the following items: @@ -11,11 +11,13 @@ Lists the following items: - breakpoints (list of pointers to instrs in program buffer) - functions (all declared functions) - callstack (the current callstack, bottom to top) +- locals +- events (async events currently in CallbackHandler queue) ### Callstack -The callstack is printed as a list of objects, with the first object the bottom of the stack. -Each object represents a block with a type: +The callstack is printed as a list of objects, with the first object the bottom of the stack. Each object represents a +block with a type: - Function 0x00 - Init expression 0x01 @@ -29,46 +31,45 @@ When the block is a function it also holds the function index in the `fidx` fiel ```json { - "pc":"0x3ffbdc00", - "start":[ - "0x3ffbdb70" - ], - "breakpoints":[ - - ], - "functions":[ + "pc": "0x3ffbdc00", + "start": [ + "0x3ffbdb70" + ], + "breakpoints": [ + ], + "functions": [ + { + "fidx": "0x3", + "from": "0x3ffbdbee", + "to": "0x3ffbdbf4" + }, + { + "fidx": "0x4", + "from": "0x3ffbdbf9", + "to": "0x3ffbdc19" + } + ], + "callstack": [ + { + "type": 0, + "fidx": "0x4", + "sp": -1, + "fp": -1, + "start": "0x3ffbdbf9", + "ra": "0x3ffbdbdf", + "callsite": "0x3ffbdbdd" + } + ], + "locals": { + "count": 1, + "locals": [ { - "fidx":"0x3", - "from":"0x3ffbdbee", - "to":"0x3ffbdbf4" - }, - { - "fidx":"0x4", - "from":"0x3ffbdbf9", - "to":"0x3ffbdc19" - } - ], - "callstack":[ - { - "type":0, - "fidx":"0x4", - "sp":-1, - "fp":-1, - "start":"0x3ffbdbf9", - "ra":"0x3ffbdbdf", - "callsite":"0x3ffbdbdd" + "type": "i32", + "value": 1000, + "index": 0 } - ], - "locals":{ - "count":1, - "locals":[ - { - "type":"i32", - "value":1000, - "index":0 - } - ] - } + ] + } } ``` @@ -80,14 +81,108 @@ The locals can also be retreived on their own with the 0x11 byte. ```json { - "count":1, - "locals":[ - { - "type":"i32", - "value":1000, - "index":0 - } - ] + "count": 1, + "locals": [ + { + "type": "i32", + "value": 1000, + "index": 0 + } + ] } ``` +## WOOD Dump (0x60) + +```json +{ + "pc": "0x60000272806e", + "start": [ + "0x600002728000" + ], + "breakpoints": [ + ], + "stack": [ + { + "idx": 0, + "type": "i32", + "value": 5 + }, + { + "idx": 1, + "type": "i32", + "value": 5 + }, + { + "idx": 2, + "type": "i32", + "value": 5 + } + ], + "callstack": [ + { + "type": 0, + "fidx": "0x3", + "sp": -1, + "fp": -1, + "block_key": "0x0", + "ra": "0x60000272805a", + "idx": 0 + }, + { + "type": 3, + "fidx": "0x0", + "sp": 0, + "fp": 0, + "block_key": "0x600002728083", + "ra": "0x600002728085", + "idx": 1 + }, + { + "type": 0, + "fidx": "0x2", + "sp": 0, + "fp": 0, + "block_key": "0x0", + "ra": "0x600002728089", + "idx": 2 + }, + { + "type": 4, + "fidx": "0x0", + "sp": 2, + "fp": 1, + "block_key": "0x60000272806a", + "ra": "0x60000272806c", + "idx": 3 + } + ], + "globals": [ + { + "idx": 0, + "type": "i32", + "value": 0 + }, + { + "idx": 1, + "type": "i32", + "value": 0 + } + ], + "table": { + "max": 2, + "init": 2, + "elements": "" + }, + "memory": { + "pages": 2, + "max": 32768, + "init": 2, + "bytes": "" + }, + "br_table": { + "size": "0x100", + "labels": "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00" + } +} +``` diff --git a/documentation/InstallArduinoESP32.md b/documentation/InstallArduinoESP32.md new file mode 100644 index 00000000..fe2c662f --- /dev/null +++ b/documentation/InstallArduinoESP32.md @@ -0,0 +1,51 @@ +# Install instructions for Arduino ESP32 + +To use ESP32 boards with the WARDuino project you need to install the correct board manager for ESP32. + +## Installing the board manager for ESP32 + +To use the ESP32 boards with `arduino-cli` perform the following steps: + +1. Init the config file, if you have not done so yet. + +``` +arduino-cli config init +``` + +2. To find the location of your config file you can run: + +``` +arduino-cli config dump --verbose +``` + +3. Add the ESP32 board manager URL to the config file: + +``` +board_manager: + additional_urls: + - https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json +``` + +4. Update index + +``` +arduino-cli core update-index +``` + +5. Install the ESP32 platform: + +``` +arduino-cli core install esp32:esp32 +``` + +To use ESP32 boards with the WARDuino Project you need at least version 2.0.2 of the board manager. +you can check your version with: + +``` +arduino-cli core list +``` + +## Additional information + +More information on how to use `arduino-cli` can be found [here](https://arduino.github.io/arduino-cli/0.21/getting-started/). + diff --git a/documentation/Interrupts.md b/documentation/Interrupts.md index 477e9649..0f2766b4 100644 --- a/documentation/Interrupts.md +++ b/documentation/Interrupts.md @@ -13,4 +13,4 @@ character, it should match `([0-9A-F][0-9A-F])+` The first two character of the HEX sequence (that is the first byte of the translated binary data) differentiates between the various interrup types. -See: [interrupt_operations.cpp](../interrupt_operations.cpp) \ No newline at end of file +See: [src/Debug/Debugger.cpp](../src/Debug/Debugger.cpp) diff --git a/documentation/OutOfPlaceDebugging.md b/documentation/OutOfPlaceDebugging.md new file mode 100644 index 00000000..1259c5c6 --- /dev/null +++ b/documentation/OutOfPlaceDebugging.md @@ -0,0 +1,37 @@ +# Out-of-place debugging + +Aside from traditional remote debugging, the WARDuino virtual machine also supports pull-push debugging. + +## Pull-based OOP Debugging + +With pull debugging the current application is debugged in a local emulated WARDuino instance, but with live actuator +and sensor values from a drone device. To get current values, the emulated debugger initiates proxy calls to the drone +device. The debugger will wait until this call has completed and the result is returned by the drone. + +Communication happens through a minimal byte format. + +## Push-based OOP Debugging + +A drone device can also push live values to the emulated debugger. These will typically be asynchronous events such as +hardware interrupts, exceptions, ... + +Those are represented by `Events` in WARDuino and send by the drone device as simple json: + +```json +{ + "topic": "topic string", + "payload": "payload as a string" +} +``` + +The supported interrupt messages: + +1. `interruptDUMPAllEvents (0x70)` dumps all events. +2. `interruptDUMPEvents (0x71)` this message' code is followed by two bytes, `a` and `b`. The command dumps at most `b` + events, starting in the event queue from index `a`. +3. `interruptPOPEvent (0x72)` tells the VM to remove the event at the front of the queue and process it. +4. `interruptPUSHEvent (0x73)` this messages' code is followed by a json representation of an event to be pushed on the + stack. +5. `interruptDUMPCallbackmapping (0x74)` requests a dump of the current callback mapping as json. +6. `interruptRecvCallbackmapping (0x75)` sends a callback mapping as json to replace the current callback mapping. + diff --git a/examples/assemblyscript/lib/warduino.ts b/examples/assemblyscript/lib/warduino.ts index d827eff5..4d712a07 100644 --- a/examples/assemblyscript/lib/warduino.ts +++ b/examples/assemblyscript/lib/warduino.ts @@ -11,7 +11,7 @@ @external("env", "wifi_connect") declare function _wifi_connect(ssid: ArrayBuffer, length: i32, password: ArrayBuffer, size: i32): void; @external("env", "wifi_status") export declare function wifi_status(): i32; -@external("env", "wifi_connected") declare function _wifi_status(): i32; +@external("env", "wifi_connected") declare function _wifi_connected(): i32; @external("env", "wifi_localip") declare function _wifi_localip(buff: ArrayBuffer, buffer_size: u32): i32; @external("env", "http_get") declare function _http_get(url: ArrayBuffer, url_len: u32, buffer: ArrayBuffer, buffer_size: u32): i32; @@ -56,7 +56,7 @@ export function print(text: string): void { export function wifi_connected(): bool { - return _wifi_status() == 1; + return _wifi_connected() === 1; } export function wifi_connect(ssid: string, password: string): void { diff --git a/examples/assemblyscript/main/CMakeLists.txt b/examples/assemblyscript/main/CMakeLists.txt index f9742f5a..3fdb8c1d 100644 --- a/examples/assemblyscript/main/CMakeLists.txt +++ b/examples/assemblyscript/main/CMakeLists.txt @@ -1,14 +1,14 @@ set(SOURCE_FILES - ../../../src/Memory/mem.cpp - ../../../src/Utils/util.cpp - ../../../src/Utils/util_arduino.cpp - ../../../src/Debug/debugger.cpp - ../../../src/Utils/macros.cpp - ../../../src/WARDuino/WARDuino.cpp - ../../../src/Primitives/primitives.cpp - ../../../src/Interpreter/instructions.cpp - ../../../src/WARDuino/CallbackHandler.cpp - ) + ../../../src/Memory/mem.cpp + ../../../src/Utils/util.cpp + ../../../src/Utils/util_arduino.cpp + ../../../src/Debug/debugger.cpp + ../../../src/Utils/macros.cpp + ../../../src/WARDuino/WARDuino.cpp + ../../../src/Primitives/emulated.cpp + ../../../src/Interpreter/instructions.cpp + ../../../src/WARDuino/CallbackHandler.cpp + ) idf_component_register(SRCS "main.cpp" ${SOURCE_FILES} INCLUDE_DIRS "" REQUIRES driver) diff --git a/examples/assemblyscript/main/smartlamp.ts b/examples/assemblyscript/main/smartlamp.ts index 67bf90fa..b46e3431 100644 --- a/examples/assemblyscript/main/smartlamp.ts +++ b/examples/assemblyscript/main/smartlamp.ts @@ -7,10 +7,12 @@ const PASSWORD = "network-password"; const CLIENT_ID = "random-mqtt-client-id"; function until_connected(connect: () => void, - connected: () => boolean): void { + connected: () => boolean, + retry: () => boolean): void { + connect(); while (!connected()) { wd.delay(1000); - connect(); + if (retry) connect(); } } @@ -40,7 +42,8 @@ export function main(): void { // Connect to Wi-Fi until_connected( () => { wd.wifi_connect(SSID, PASSWORD); }, - wd.wifi_connected); + wd.wifi_connected, + () => { return wd.wifi_status() === 6; }); let message = "Connected to wifi network with ip: "; wd.print(message.concat(wd.wifi_localip())); @@ -48,7 +51,8 @@ export function main(): void { wd.mqtt_init("192.168.0.24", 1883); until_connected( () => { wd.mqtt_connect(CLIENT_ID); wd.mqtt_loop(); }, - () => { return wd.mqtt_connected(); }); + () => { return wd.mqtt_connected(); }, + () => { return true; }); // Subscribe to MQTT topic and turn on LED wd.mqtt_subscribe("LED", callback); @@ -60,7 +64,8 @@ export function main(): void { while (true) { until_connected( () => { wd.mqtt_connect(CLIENT_ID); wd.mqtt_loop(); }, - () => { return wd.mqtt_connected(); }); + () => { return wd.mqtt_connected(); }, + () => { return true; }); wd.sleep(5); // Sleep for 5 seconds } diff --git a/examples/assemblyscript/package-lock.json b/examples/assemblyscript/package-lock.json index 7d6be6f1..4cfe17db 100644 --- a/examples/assemblyscript/package-lock.json +++ b/examples/assemblyscript/package-lock.json @@ -1,8 +1,59 @@ { "name": "smartlamp", - "version": "1.0.0", - "lockfileVersion": 1, + "version": "0.1.0", + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "smartlamp", + "version": "0.1.0", + "license": "ISC", + "dependencies": { + "@assemblyscript/loader": "^0.17.1" + }, + "devDependencies": { + "assemblyscript": "^0.17.14" + } + }, + "node_modules/@assemblyscript/loader": { + "version": "0.17.14", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.17.14.tgz", + "integrity": "sha512-+PVTOfla/0XMLRTQLJFPg4u40XcdTfon6GGea70hBGi8Pd7ZymIXyVUR+vK8wt5Jb4MVKTKPIz43Myyebw5mZA==" + }, + "node_modules/assemblyscript": { + "version": "0.17.14", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.17.14.tgz", + "integrity": "sha512-TLuwNvZAIH26wu2puKpAJokzLp10kJkVXxbgDjFFmbW9VF/qg7rkmi0hjsiu41bjoH1UaVgY4vYvbbUeOHtKyg==", + "dev": true, + "dependencies": { + "binaryen": "98.0.0-nightly.20201109", + "long": "^4.0.0" + }, + "bin": { + "asc": "bin/asc", + "asinit": "bin/asinit" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/assemblyscript" + } + }, + "node_modules/binaryen": { + "version": "98.0.0-nightly.20201109", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-98.0.0-nightly.20201109.tgz", + "integrity": "sha512-iRarAqdH5lMWlMBzrDuJgLYJR2g4QXk93iYE2zpr6gEZkb/jCgDpPUXdhuN11Ge1zZ/6By4DwA1mmifcx7FWaw==", + "dev": true, + "bin": { + "wasm-opt": "bin/wasm-opt" + } + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + } + }, "dependencies": { "@assemblyscript/loader": { "version": "0.17.14", diff --git a/examples/blink/build.sh b/examples/blink/build.sh index 05a05915..fb7e1ae3 100755 --- a/examples/blink/build.sh +++ b/examples/blink/build.sh @@ -17,7 +17,7 @@ if [[ $src == *.wat ]] || [[ $extension == *.wast ]]; then fi # Optimize (optional) -wasm-opt -O3 "${src}.wasm" -o "src.wasm" +#wasm-opt -O3 "${src}.wasm" -o "src.wasm" wasm-strip "src.wasm" echo -e "> optimized src.wasm" diff --git a/examples/blink/main/CMakeLists.txt b/examples/blink/main/CMakeLists.txt index f9742f5a..7ddf2d42 100644 --- a/examples/blink/main/CMakeLists.txt +++ b/examples/blink/main/CMakeLists.txt @@ -1,16 +1,19 @@ set(SOURCE_FILES - ../../../src/Memory/mem.cpp - ../../../src/Utils/util.cpp - ../../../src/Utils/util_arduino.cpp - ../../../src/Debug/debugger.cpp - ../../../src/Utils/macros.cpp - ../../../src/WARDuino/WARDuino.cpp - ../../../src/Primitives/primitives.cpp - ../../../src/Interpreter/instructions.cpp - ../../../src/WARDuino/CallbackHandler.cpp - ) + ../../src/Memory/mem.cpp + ../../src/Utils/util.cpp + ../../src/Utils/util_arduino.cpp + ../../src/Debug/debugger.cpp + ../../src/Utils/macros.cpp + ../../src/WARDuino/WARDuino.cpp + ../../src/Primitives/idf.cpp + ../../src/Interpreter/instructions.cpp + ../../src/RFC/rfc.cpp + ../../src/RFC/proxy_server.cpp + ../../src/RFC/SocketServer.cpp + ../../src/WARDuino/CallbackHandler.cpp + ) -idf_component_register(SRCS "main.cpp" ${SOURCE_FILES} INCLUDE_DIRS "" REQUIRES driver) +idf_component_register(SRCS "main.cpp" ${SOURCE_FILES} INCLUDE_DIRS ../../lib/json/single_include/ REQUIRES driver) add_definitions(-DESP=1) diff --git a/examples/rust/.cargo/config b/examples/rust/.cargo/config new file mode 100644 index 00000000..e4f6290f --- /dev/null +++ b/examples/rust/.cargo/config @@ -0,0 +1,5 @@ +[build] +target = "wasm32-unknown-unknown" +rustflags = [ + "-C", "link-args=-zstack-size=2048 -s", +] diff --git a/examples/rust/.gitignore b/examples/rust/.gitignore new file mode 100644 index 00000000..40fac1ff --- /dev/null +++ b/examples/rust/.gitignore @@ -0,0 +1,17 @@ +.idea +idf/ + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/examples/rust/CMakeLists.txt b/examples/rust/CMakeLists.txt new file mode 100644 index 00000000..bb72b053 --- /dev/null +++ b/examples/rust/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(rust) + diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml new file mode 100644 index 00000000..86d0c3f6 --- /dev/null +++ b/examples/rust/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "button" +version = "0.1.0" +authors = ["Tom Lauwaerts "] +edition = "2018" + +[lib] +path = "main/button.rs" +crate-type = ["cdylib"] + +[dependencies] +warduino = { path = "lib/warduino" } + +[profile.dev] +panic = "abort" +opt-level = 2 + +[profile.release] +panic = "abort" +opt-level = 3 +debug = true diff --git a/examples/rust/README.md b/examples/rust/README.md new file mode 100644 index 00000000..259df2ce --- /dev/null +++ b/examples/rust/README.md @@ -0,0 +1,14 @@ +# Button demo + +This demo is used to show case that interrupts on pins can also be subscribed on with the WARDuino callback system. + +## Running the demo + +Flash the wasm code and WARDuino VM onto the esp with the following commands: + +``` +./build.sh +``` + +The program listens for changes on pin 26. + diff --git a/examples/rust/button.wat b/examples/rust/button.wat new file mode 100644 index 00000000..e7eefecf --- /dev/null +++ b/examples/rust/button.wat @@ -0,0 +1,57 @@ +(module + (type $t0 (func (param i32 i32))) + (type $t1 (func (param i32) (result i32))) + (type $t2 (func (param i32 i32 i32))) + (type $t3 (func (param i32 i32 i32 i32 i32))) + (type $t4 (func)) + (import "env" "chip_pin_mode" (func $env.chip_pin_mode (type $t0))) + (import "env" "chip_digital_write" (func $env.chip_digital_write (type $t0))) + (import "env" "chip_digital_read" (func $env.chip_digital_read (type $t1))) + (import "env" "subscribe_interrupt" (func $env.subscribe_interrupt (type $t2))) + (func $f4 (type $t3) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (param $p4 i32) + i32.const 26 + i32.const 26 + call $f8 + i32.const 1 + i32.ne + call $f7) + (func $main (type $t4) + i32.const 25 + i32.const 0 + call $f6 + i32.const 26 + i32.const 2 + call $f6 + i32.const 25 + i32.const 1 + i32.const 2 + call $f9 + loop $L0 + br $L0 + end) + (func $f6 (type $t0) (param $p0 i32) (param $p1 i32) + local.get $p0 + local.get $p1 + call $env.chip_pin_mode) + (func $f7 (type $t0) (param $p0 i32) (param $p1 i32) + local.get $p0 + local.get $p1 + call $env.chip_digital_write) + (func $f8 (type $t1) (param $p0 i32) (result i32) + local.get $p0 + call $env.chip_digital_read) + (func $f9 (type $t2) (param $p0 i32) (param $p1 i32) (param $p2 i32) + local.get $p0 + local.get $p1 + local.get $p2 + call $env.subscribe_interrupt) + (table $T0 2 2 funcref) + (memory $memory 1) + (global $g0 (mut i32) (i32.const 2048)) + (global $__data_end i32 (i32.const 2048)) + (global $__heap_base i32 (i32.const 2048)) + (export "memory" (memory $memory)) + (export "main" (func $main)) + (export "__data_end" (global $__data_end)) + (export "__heap_base" (global $__heap_base)) + (elem $e0 (i32.const 1) func $f4)) diff --git a/examples/rust/lib/warduino/.gitignore b/examples/rust/lib/warduino/.gitignore new file mode 100644 index 00000000..20694291 --- /dev/null +++ b/examples/rust/lib/warduino/.gitignore @@ -0,0 +1,5 @@ +target/ +*.wasm* +*.wat +*.log +*.h diff --git a/examples/rust/lib/warduino/Cargo.toml b/examples/rust/lib/warduino/Cargo.toml new file mode 100644 index 00000000..524c4d2e --- /dev/null +++ b/examples/rust/lib/warduino/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "warduino" +version = "0.1.0" +authors = ["Tom Lauwaerts "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/examples/rust/lib/warduino/src/lib.rs b/examples/rust/lib/warduino/src/lib.rs new file mode 100644 index 00000000..63d7a4d2 --- /dev/null +++ b/examples/rust/lib/warduino/src/lib.rs @@ -0,0 +1,127 @@ +#![allow(dead_code)] + +use std::mem; + +#[link(wasm_import_module = "env")] +//#[link(wasm_import_module = "arduino")] +extern { + #[link_name = "test"] pub fn test(f: fn(&str, &str, u32)); + + #[link_name = "millis"] fn _millis() -> u32; + #[link_name = "chip_delay"] fn _delay(ms: u32); + #[link_name = "getPinLED"] fn _getPinLED() -> u32; + #[link_name = "chip_pin_mode"] fn _pinMode(pin: u32, mode: u32); + #[link_name = "chip_digital_write"] fn _digitalWrite(pin: u32, value: u32); + #[link_name = "chip_digital_read"] fn _digitalRead(pin: u32) -> u32; + #[link_name = "chip_analog_read"] fn _analogRead(pin: u32) -> i32; +//} + + +//#[link(wasm_import_module = "serial")] +//extern { + #[link_name = "print_string"] fn _print_buffer(text: *const u8, length: usize); + #[link_name = "print_int"] fn _print_int(integer: i32); +//} + +//#[link(wasm_import_module = "wifi")] +//extern { + #[link_name = "wifi_connect"] fn _connect(ssid: &str, password: &str); + #[link_name = "wifi_status"] fn _status() -> i32; + #[link_name = "wifi_localip"] fn _localip(buffer: *const u8, buffer_length: usize) -> i32; +//} + +//#[link(wasm_import_module = "http")] +//extern { + #[link_name = "http_get"] fn _get(url: *const u8, url_len: usize, buffer: *const u8, buffer_size: usize) -> i32; + #[link_name = "http_post"] fn _post(url: *const u8, url_len: usize, + body: *const u8, body_len: usize, + content_type: *const u8, content_type_len: usize, + authorization: *const u8, authorization_len: usize, + buffer: *const u8, buffer_size: usize) + -> i32; + + +//#[link(wasm_import_module = "interrupt")] +//extern { + #[link_name = "subscribe_interrupt"] fn _sub_interrupt(pin: u32, f: fn(&str, &str, u32), mode: u32); + #[link_name = "unsubscribe_interrupt"] fn _unsub_interrupt(pin: u32); + +//#[link(wasm_import_module = "mqtt")] +//extern { + #[link_name = "mqtt_init"] fn _mqtt_init(server: *const u8, server_length: usize, port: u32); + #[link_name = "mqtt_connect"] fn _mqtt_connect(client_id: *const u8, client_id_length: usize) -> i32; + #[link_name = "mqtt_connected"] fn _mqtt_connected() -> i32; + #[link_name = "mqtt_state"] fn _mqtt_state() -> i32; + #[link_name = "mqtt_publish"] fn _mqtt_publish(topic: *const u8, topic_length: usize, payload: *const u8, payload_length: usize) -> i32; + #[link_name = "mqtt_subscribe"] fn _mqtt_subscribe(topic: *const u8, topic_length: usize, f: fn(&str, &str, u32)) -> i32; + #[link_name = "mqtt_unsubscribe"] fn _mqtt_unsubscribe(topic: *const u8, topic_length: usize, f: fn(&str, &str, u32)) -> i32; + #[link_name = "mqtt_loop"] fn _mqtt_loop() -> i32; +} + +#[repr(C)] +pub struct Headers { + pub content_type: &'static str, + pub authorization: &'static str, // TODO make optional +} + +#[repr(C)] +pub struct PostOptions { + pub uri: &'static str, + pub body: String, + pub headers: Headers, +} + +fn size_of_post_options(options: &PostOptions) -> usize { + options.uri.len() + options.body.len() + options.headers.content_type.len() +} + +pub static LOW : u32 = 0x0; +pub static HIGH : u32 = 0x1; + +pub static CHANGE : u32 = 1; +pub static FALLING : u32 = 2; +pub static RISING : u32 = 3; + +pub static INPUT : u32 = 0x0; +pub static OUTPUT : u32 = 0x2; + +pub fn millis () -> u32 { unsafe { _millis() } } +pub fn delay (ms: u32) { unsafe { _delay(ms); } } +pub fn get_pin_led () -> u32 { unsafe { _getPinLED() } } +pub fn pin_mode (pin: u32, mode: u32) { unsafe { _pinMode(pin, mode) } } +pub fn digital_write(pin: u32, value: u32) { unsafe { _digitalWrite(pin, value) } } +pub fn digital_read (pin: u32) -> u32 { unsafe { _digitalRead(pin) } } +pub fn analog_read (pin: u32) -> i32 { unsafe { _analogRead(pin) } } + +pub fn wifi_connect (ssid: &str, password: &str) { unsafe { _connect(ssid, password) } } +pub fn wifi_status () -> i32 { unsafe { _status() } } +pub fn wifi_localip () -> String { unsafe { let buffer: [u8; 100] = [0; 100]; + _localip(buffer.as_ptr(), mem::size_of_val(&buffer) / mem::size_of::()); + std::str::from_utf8(&buffer).unwrap().to_owned() + } } + +pub fn get (url: &str, buffer: &[u8]) -> i32 { unsafe { _get(url.as_ptr(), url.len(), buffer.as_ptr(), mem::size_of_val(buffer) / mem::size_of::()) } } +pub fn post (options: &PostOptions, buffer: &[u8]) -> i32 { unsafe { + _post(options.uri.as_ptr(), options.uri.len(), + options.body.as_ptr(), options.body.len(), + options.headers.content_type.as_ptr(), options.headers.content_type.len(), + options.headers.authorization.as_ptr(), options.headers.authorization.len(), + buffer.as_ptr(), mem::size_of_val(buffer) / mem::size_of::()) + } +} + +pub fn print (text: &[u8]) { unsafe { _print_buffer(text.as_ptr(), text.len()) } } +pub fn print_int (integer: i32) { unsafe { _print_int(integer) } } + +pub fn sub_interrupt (pin: u32, f: fn(&str, &str, u32), mode: u32) { unsafe { _sub_interrupt(pin, f, mode) } } +pub fn unsub_interrupt (pin: u32) { unsafe { _unsub_interrupt(pin) } } + +pub fn mqtt_init (server: &str, port: u32) { unsafe { _mqtt_init(server.as_ptr(), server.len(), port) } } +pub fn mqtt_connect (client_id: &str) -> bool { unsafe { _mqtt_connect(client_id.as_ptr(), client_id.len()) != 0 } } +pub fn mqtt_connected () -> bool { unsafe { _mqtt_connected() > 0 } } +pub fn mqtt_state () -> i32 { unsafe { _mqtt_state() } } +pub fn mqtt_publish (topic: &str, payload: &str) -> i32 { unsafe { _mqtt_publish(topic.as_ptr(), topic.len(), payload.as_ptr(), payload.len()) } } +pub fn mqtt_subscribe (topic: &str, f: fn(&str, &str, u32)) -> i32 { unsafe { _mqtt_subscribe(topic.as_ptr(), topic.len(), f) } } +pub fn mqtt_unsubscribe (topic: &str, f: fn(&str, &str, u32)) -> i32 { unsafe { _mqtt_unsubscribe(topic.as_ptr(), topic.len(), f) } } +pub fn mqtt_loop () -> i32 { unsafe { _mqtt_loop() } } + diff --git a/examples/rust/main/CMakeLists.txt b/examples/rust/main/CMakeLists.txt new file mode 100644 index 00000000..3fdb8c1d --- /dev/null +++ b/examples/rust/main/CMakeLists.txt @@ -0,0 +1,16 @@ +set(SOURCE_FILES + ../../../src/Memory/mem.cpp + ../../../src/Utils/util.cpp + ../../../src/Utils/util_arduino.cpp + ../../../src/Debug/debugger.cpp + ../../../src/Utils/macros.cpp + ../../../src/WARDuino/WARDuino.cpp + ../../../src/Primitives/emulated.cpp + ../../../src/Interpreter/instructions.cpp + ../../../src/WARDuino/CallbackHandler.cpp + ) + +idf_component_register(SRCS "main.cpp" ${SOURCE_FILES} INCLUDE_DIRS "" REQUIRES driver) + +add_definitions(-DESP=1) + diff --git a/examples/rust/main/button.rs b/examples/rust/main/button.rs new file mode 100644 index 00000000..6688a6b2 --- /dev/null +++ b/examples/rust/main/button.rs @@ -0,0 +1,27 @@ +use warduino::*; + +static BUTTON : u32 = 25; +static LED : u32 = 26; + +fn callback(_topic: &str, _payload: &str, _length: u32) { + let val = digital_read(LED); + //print(format!("Switch LED to {}\n", (val + 1) % 2).as_bytes()); + if val == HIGH { + digital_write(LED, LOW); + } else { + digital_write(LED, HIGH); + } +} + +#[no_mangle] +pub fn main() { + pin_mode(BUTTON, INPUT); + pin_mode(LED, OUTPUT); + + sub_interrupt(BUTTON, callback, FALLING); + + loop { + //delay(1); + } +} + diff --git a/examples/rust/main/main.cpp b/examples/rust/main/main.cpp new file mode 100644 index 00000000..133c2ad3 --- /dev/null +++ b/examples/rust/main/main.cpp @@ -0,0 +1,52 @@ +// +// WARDuino - WebAssembly interpreter for embedded devices. +// +// +#include + +#include "../../../../src/WARDuino.h" +#include "driver/gpio.h" +#include "driver/uart.h" +#include "esp_err.h" +#include "esp_task_wdt.h" +#include "esp_vfs_dev.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sdkconfig.h" + +volatile bool handelingInterrupt = false; + +#include "src.wasm.h" + +extern "C" { +extern void app_main(void); +} + +WARDuino wac; +Module* m; + +void startDebuggerStd(void* pvParameter) { + int valread; + uint8_t buffer[1024] = {0}; + wac.debugger->socket = fileno(stdout); + while (true) { + taskYIELD(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + + while ((valread = read(fileno(stdin), buffer, 1024)) != -1) { + write(fileno(stdout), "got a message ... \n", 19); + wac.handleInterrupt(valread - 1, buffer); + write(fileno(stdout), buffer, valread); + fflush(stdout); + } + } +} + +void app_main(void) { + m = wac.load_module(src_wasm, src_wasm_len, {}); + xTaskCreate(startDebuggerStd, "Debug Thread", 5000, NULL, 1, NULL); + printf("START\n\n"); + wac.run_module(m); + printf("END\n\n"); + wac.unload_module(m); +} diff --git a/lib/json b/lib/json new file mode 160000 index 00000000..b205361d --- /dev/null +++ b/lib/json @@ -0,0 +1 @@ +Subproject commit b205361d8652759b6d850a37b227c8d57ee19005 diff --git a/library.properties b/library.properties index 2eab09ef..3c19512d 100644 --- a/library.properties +++ b/library.properties @@ -1,6 +1,6 @@ name=WARDuino -version=0.9.2 -author=Robbert Gurdeep Singh , Christophe Scholliers , Tom Lauwaerts , Joel Martin +version=0.2.1 +author=Robbert Gurdeep Singh , Christophe Scholliers , Tom Lauwaerts , Carlos Rojas Castillo , Joel Martin maintainer=Robbert Gurdeep Singh , Christophe Scholliers , Tom Lauwaerts sentence=A library that enables the use of WebAssembly on Arduino boards with debugging support paragraph= diff --git a/platforms/Arduino/Arduino.ino b/platforms/Arduino/Arduino.ino index 919bee00..20f3514c 100644 --- a/platforms/Arduino/Arduino.ino +++ b/platforms/Arduino/Arduino.ino @@ -15,19 +15,18 @@ unsigned int wasm_len = upload_wasm_len; unsigned char* wasm = upload_wasm; -WARDuino wac; +WARDuino* wac = WARDuino::instance(); Module* m; #define UART_PIN 3 void startDebuggerStd(void* pvParameter) { - int valread; + Channel* sink = new Sink(stdout); + wac->debugger->setChannel(sink); + sink->open(); + uint8_t buffer[1024] = {0}; - wac.debugger->socket = fileno(stdout); - write(fileno(stdout), "Got a message ... \n", 19); while (true) { - // taskYIELD(); - // vTaskDelay(100 / portTICK_PERIOD_MS); yield(); while (Serial.available()) { @@ -36,11 +35,8 @@ void startDebuggerStd(void* pvParameter) { buffer[buff_len++] = (int8_t)Serial.read(); } if (buff_len) { - write(fileno(stdout), "Reading message ..... \n", 19); - fflush(stdout); - wac.handleInterrupt(valread - 1, buffer); - write(fileno(stdout), buffer, valread); - fflush(stdout); + buffer[buff_len] = '\0'; + wac->handleInterrupt(buff_len, buffer); } } } @@ -63,18 +59,18 @@ void setup(void) { void loop() { disableCore0WDT(); - m = wac.load_module(wasm, wasm_len, {}); + m = wac->load_module(wasm, wasm_len, {}); printf("LOADED \n\n"); uint8_t command[] = {'0', '3', '\n'}; - wac.handleInterrupt(3, command); + wac->handleInterrupt(3, command); xTaskCreate(startDebuggerStd, "Debug Thread", 5000, NULL, 1, NULL); printf("START\n\n"); Serial.println("\nFree heap:"); Serial.println(ESP.getFreeHeap()); - wac.run_module(m); + wac->run_module(m); printf("END\n\n"); - wac.unload_module(m); + wac->unload_module(m); } diff --git a/platforms/Arduino/Makefile b/platforms/Arduino/Makefile index 4b1f2ff8..3e189220 100644 --- a/platforms/Arduino/Makefile +++ b/platforms/Arduino/Makefile @@ -1,8 +1,14 @@ +# Arduino Platform + +PORT = /dev/ttyUSB0 +FQBN = esp32:esp32:esp32wrover + flash: - arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:esp32wrover Arduino.ino + arduino-cli upload -p $(PORT) --fqbn $(FQBN) Arduino.ino compile: - arduino-cli -v compile --fqbn esp32:esp32:esp32wrover Arduino.ino + arduino-cli compile --fqbn $(FQBN) Arduino.ino monitor: - arduino-cli monitor -p /dev/ttyUSB0 -c baudrate=115200 + arduino-cli monitor -p $(PORT) -c baudrate=115200 + diff --git a/platforms/Arduino/examples/blink.c b/platforms/Arduino/examples/blink.c new file mode 100644 index 00000000..c64c74a4 --- /dev/null +++ b/platforms/Arduino/examples/blink.c @@ -0,0 +1,20 @@ +unsigned char blink_wasm[] = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0d, 0x03, 0x60, + 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, + 0x4f, 0x04, 0x03, 0x65, 0x6e, 0x76, 0x0a, 0x63, 0x68, 0x69, 0x70, 0x5f, + 0x64, 0x65, 0x6c, 0x61, 0x79, 0x00, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x0d, + 0x63, 0x68, 0x69, 0x70, 0x5f, 0x70, 0x69, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x63, 0x68, 0x69, 0x70, + 0x5f, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x09, 0x70, 0x72, 0x69, + 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x00, 0x01, 0x03, 0x03, 0x02, 0x02, + 0x02, 0x06, 0x10, 0x03, 0x7f, 0x00, 0x41, 0x0a, 0x0b, 0x7f, 0x00, 0x41, + 0x01, 0x0b, 0x7f, 0x00, 0x41, 0x00, 0x0b, 0x07, 0x08, 0x01, 0x04, 0x6d, + 0x61, 0x69, 0x6e, 0x00, 0x05, 0x0a, 0x33, 0x02, 0x08, 0x00, 0x23, 0x00, + 0x41, 0x02, 0x10, 0x01, 0x0b, 0x28, 0x01, 0x01, 0x7f, 0x41, 0xe8, 0x07, + 0x21, 0x00, 0x10, 0x04, 0x03, 0x40, 0x23, 0x00, 0x23, 0x00, 0x10, 0x03, + 0x23, 0x01, 0x10, 0x02, 0x20, 0x00, 0x10, 0x00, 0x23, 0x00, 0x23, 0x02, + 0x10, 0x02, 0x20, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x0b, 0x0b}; +unsigned int blink_wasm_len = 190; +/* + */ diff --git a/platforms/Arduino/examples/blink.wast b/platforms/Arduino/examples/blink.wast new file mode 100644 index 00000000..0e0ed2df --- /dev/null +++ b/platforms/Arduino/examples/blink.wast @@ -0,0 +1,54 @@ +(module + ;; Type declarations + (type $int32->int32->void (func (param i32 i32))) + (type $int32->void (func (param i32))) + (type $void->void (func)) + + ;; Imports from the WARDuino VM + (import "env" "chip_delay" (func $env.chip_delay (type $int32->void))) + (import "env" "chip_pin_mode" (func $env.chip_pin_mode (type $int32->int32->void))) + (import "env" "chip_digital_write" (func $env.chip_digital_write (type $int32->int32->void))) + (import "env" "print_int" (func $env.print_int (type $int32->void))) + + + ;; Non-mutable globals + (global $led i32 (i32.const 10)) + (global $on i32 (i32.const 1)) + (global $off i32 (i32.const 0)) + + ;; Initialise function (private) + (func $init (type $void->void) + ;; Set pin mode + global.get $led + i32.const 2 + call $env.chip_pin_mode) + + ;; Blink function (public) + (func $blink (type $void->void) + ;; Declare local $delay + (local $delay i32) + i32.const 1000 + local.set $delay + + ;; Initialise + call $init + + ;; Blink in infinite loop + loop $infinite + global.get $led + global.get $led + call $env.print_int ;;print led nr + global.get $on + call $env.chip_digital_write ;; turn led on + local.get $delay + call $env.chip_delay ;; wait + global.get $led + global.get $off + call $env.chip_digital_write ;; turn led off + local.get $delay + call $env.chip_delay ;; wait + br $infinite ;; jump back to start of loop + end) + + ;; Export blink as function + (export "main" (func $blink))) diff --git a/platforms/Arduino/upload b/platforms/Arduino/upload deleted file mode 100644 index abef5100..00000000 --- a/platforms/Arduino/upload +++ /dev/null @@ -1 +0,0 @@ -arduino-cli upload -p $1 --fqbn esp32:esp32:esp32wrover Arduino.ino diff --git a/platforms/CLI-Emulator/main.cpp b/platforms/CLI-Emulator/main.cpp index 689fca8d..4414c6f3 100644 --- a/platforms/CLI-Emulator/main.cpp +++ b/platforms/CLI-Emulator/main.cpp @@ -1,17 +1,19 @@ // // WARDuino - WebAssembly interpreter for embedded devices. // +#include #include #include -#include #include #include #include #include +#include #include "../../src/Debug/debugger.h" #include "../../src/Utils/macros.h" +#include "../../src/Utils/sockets.h" #include "../../src/WARDuino.h" #include "../../tests/integration/wasm_tests.h" @@ -32,13 +34,13 @@ } void print_help() { - fprintf(stdout, "WARDuino WebAssembly Runtime - 0.1.0\n\n"); + fprintf(stdout, "WARDuino WebAssembly Runtime - 0.2.1\n\n"); fprintf(stdout, "Usage:\n"); fprintf(stdout, " warduino [options] \n"); fprintf(stdout, "Options:\n"); - fprintf( - stdout, - " --loop Let the runtime loop infinitely on exceptions\n"); + fprintf(stdout, + " --loop Let the runtime loop infinitely on exceptions " + "(default: false)\n"); fprintf(stdout, " --asserts Name of file containing asserts to run against " "loaded module\n"); @@ -47,6 +49,24 @@ void print_help() { "binaries (default: wat2wasm)\n"); fprintf(stdout, " --file Wasm file (module) to load and execute\n"); + fprintf(stdout, + " --no-debug Run without debug thread" + "(default: false)\n"); + fprintf(stdout, + " --no-socket Run debug on stdout" + "(default: false)\n"); + fprintf(stdout, + " --socket Port number for debug socket (ignored if " + "'--no-socket' is true)" + "(default: 8192)\n"); + fprintf(stdout, + " --paused Pause program on entry (default: false)\n"); + fprintf(stdout, + " --proxy Localhost port or serial port (ignored if mode " + "is 'proxy')\n"); + fprintf(stdout, + " --mode The mode to run in: interpreter, proxy " + "(default: interpreter)\n"); } Module *load(WARDuino wac, const char *file_name, Options opt) { @@ -89,106 +109,64 @@ Module *load(WARDuino wac, const char *file_name, Options opt) { return nullptr; } -// Socket Debugger Interface -void setFileDescriptorOptions(int socket_fd) { - int opt = 1; - if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { - perror("Failed to set socket file descriptor options"); - exit(EXIT_FAILURE); +void *startDebuggerCommunication(void *arg) { + Channel *duplex = WARDuino::instance()->debugger->channel; + if (duplex == nullptr) { + return nullptr; } -} -int createSocketFileDescriptor() { - int socket_fd; - if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { - perror("Failed to make a new socket file descriptor"); - exit(EXIT_FAILURE); - } - setFileDescriptorOptions(socket_fd); - return socket_fd; -} + duplex->open(); -void bindSocketToAddress(int socket_fd, struct sockaddr_in address) { - if (bind(socket_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { - perror("Binding socket to address failed"); - exit(EXIT_FAILURE); + ssize_t valread; + uint8_t buffer[1024] = {0}; + while (true) { + while ((valread = duplex->read(buffer, 1024)) != -1) { + WARDuino::instance()->handleInterrupt(valread - 1, buffer); + } } } -struct sockaddr_in createAddress(int port) { - struct sockaddr_in address; - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(port); - return address; -} +// Connect to proxy via a web socket +int connectToProxySocket(int proxy) { + int channel; + struct sockaddr_in address = createLocalhostAddress(proxy); -void startListening(int socket_fd) { - if (listen(socket_fd, 1) < 0) { - perror("listen"); - exit(EXIT_FAILURE); + if ((channel = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + dbg_info("Socket creation error\n"); + return -1; } -} -int listenForIncomingConnection(int socket_fd, struct sockaddr_in address) { - int new_socket; - int size = sizeof(address); - if ((new_socket = accept(socket_fd, (struct sockaddr *)&address, - (socklen_t *)&size)) < 0) { - perror("Failed to listen for incoming connections"); - exit(EXIT_FAILURE); + if (connect(channel, (struct sockaddr *)&address, sizeof(address)) < 0) { + dbg_info("Connection failed\n"); + return -1; } - return new_socket; -} -void startDebuggerStd(WARDuino *wac, Module *m) { - int valread; - uint8_t buffer[1024] = {0}; - wac->debugger->socket = fileno(stdout); - while (true) { - debug("waiting for debug command\n"); - while ((valread = read(fileno(stdin), buffer, 1024)) != -1) { - write(fileno(stdout), "got a message ... \n", 19); - wac->handleInterrupt(valread - 1, buffer); - write(fileno(stdout), buffer, valread); - fflush(stdout); - } - } + return channel; } -void startDebuggerSocket(WARDuino *wac, Module *m) { - int socket_fd = createSocketFileDescriptor(); - struct sockaddr_in address = createAddress(8192); - bindSocketToAddress(socket_fd, address); - startListening(socket_fd); - - int valread; - uint8_t buffer[1024] = {0}; - while (true) { - int socket = listenForIncomingConnection(socket_fd, address); - wac->debugger->socket = socket; - // wac->debugger->socket = fileno(stdout); // todo remove - while ((valread = read(socket, buffer, 1024)) != -1) { - write(socket, "got a message ... \n", 19); - wac->handleInterrupt(valread - 1, buffer); - // runningstate program_state = warduinorun; - write(socket, buffer, valread); - // while (checkdebugmessages(m, &program_state)) { - // printf("checkdebugmessages \n"); - //}; - // fflush(stdout); - } - } -} +// Connect to proxy via file descriptor +int connectToProxyFd(const char *proxyfd) { return open(proxyfd, O_RDWR); } -WARDuino wac; +WARDuino *wac = WARDuino::instance(); Module *m; -void *runWAC(void *p) { - // Print value received as argument: - dbg_info("\n=== STARTED INTERPRETATION (in separate thread) ===\n"); - wac.run_module(m); - wac.unload_module(m); +struct debugger_options { + const char *socket; + bool no_socket; +}; + +void *setupDebuggerCommunication(debugger_options *options) { + dbg_info("\n=== STARTED DEBUGGER (in separate thread) ===\n"); + // Start debugger + Channel *duplex; + if (options->no_socket) { + duplex = new Duplex(stdin, stdout); + } else { + int port = std::stoi(options->socket); + duplex = new WebSocket(port); + } + + wac->debugger->setChannel(duplex); } int main(int argc, const char *argv[]) { @@ -196,8 +174,13 @@ int main(int argc, const char *argv[]) { bool return_exception = true; bool run_tests = false; + bool no_debug = false; bool no_socket = false; + const char *socket = "8192"; + bool paused = false; const char *file_name = nullptr; + const char *proxy = nullptr; + const char *mode = "interpreter"; const char *asserts_file = nullptr; const char *watcompiler = "wat2wasm"; @@ -222,8 +205,18 @@ int main(int argc, const char *argv[]) { ARGV_GET(asserts_file); } else if (!strcmp("--watcompiler", arg)) { ARGV_GET(watcompiler); + } else if (!strcmp("--no-debug", arg)) { + no_debug = true; } else if (!strcmp("--no-socket", arg)) { no_socket = true; + } else if (!strcmp("--socket", arg)) { + ARGV_GET(socket); + } else if (!strcmp("--paused", arg)) { + wac->program_state = WARDUINOpause; + } else if (!strcmp("--proxy", arg)) { + ARGV_GET(proxy); // /dev/ttyUSB0 + } else if (!strcmp("--mode", arg)) { + ARGV_GET(mode); } } @@ -235,10 +228,10 @@ int main(int argc, const char *argv[]) { if (argc == 0 && file_name != nullptr) { if (run_tests) { dbg_info("=== STARTING SPEC TESTS ===\n"); - return run_wasm_test(wac, file_name, asserts_file, watcompiler); + return run_wasm_test(*wac, file_name, asserts_file, watcompiler); } dbg_info("=== LOAD MODULE INTO WARDUINO ===\n"); - m = load(wac, file_name, + m = load(*wac, file_name, {.disable_memory_bounds = false, .mangle_table_index = false, .dlsym_trim_underscore = false, @@ -249,19 +242,60 @@ int main(int argc, const char *argv[]) { } if (m) { + m->warduino = wac; + + if (strcmp(mode, "proxy") == 0) { + // Run in proxy mode + wac->debugger->proxify(); + } else if (proxy) { + // Connect to proxy device + Channel *connection = nullptr; + try { + int port = std::stoi(proxy); + connection = new WebSocket(port); + } catch (std::invalid_argument const &ex) { + // argument is not a port + // treat as filename + connection = new FileDescriptorChannel(open(proxy, O_RDWR)); + } catch (std::out_of_range const &ex) { + // argument is an integer but is out of range + fprintf(stderr, + "wdcli: out of range integer argument for --proxy\n"); + return 1; + } + + if (connection == nullptr) { + // Failed to connect stop program + fprintf(stderr, "wdcli: failed to connect to proxy device\n"); + return 1; + } + + // Start supervising proxy device (new thread) + wac->debugger->startProxySupervisor(connection); + } + + // Start debugger (new thread) pthread_t id; - uint8_t command[] = {'0', '3', '\n'}; - // wac.handleInterrupt(3, command); - m->warduino = &wac; - pthread_create(&id, nullptr, runWAC, nullptr); - if (no_socket) { - startDebuggerStd(&wac, m); - } else { - startDebuggerSocket(&wac, m); + if (!no_debug) { + auto *options = + (debugger_options *)malloc(sizeof(struct debugger_options)); + options->no_socket = no_socket; + options->socket = socket; + setupDebuggerCommunication(options); + free(options); + + pthread_create(&id, nullptr, startDebuggerCommunication, nullptr); } + + // Run Wasm module + dbg_info("\n=== STARTED INTERPRETATION (main thread) ===\n"); + wac->run_module(m); + wac->unload_module(m); + wac->debugger->stop(); + int *ptr; pthread_join(id, (void **)&ptr); } return 0; -} +} \ No newline at end of file diff --git a/platforms/ESP-IDF/CMakeLists.txt b/platforms/ESP-IDF/CMakeLists.txt index 694cad97..335bdc5c 100644 --- a/platforms/ESP-IDF/CMakeLists.txt +++ b/platforms/ESP-IDF/CMakeLists.txt @@ -1,16 +1,20 @@ set(SOURCE_FILES - ../../src/Memory/mem.cpp - ../../src/Utils/util.cpp - ../../src/Utils/util_arduino.cpp - ../../src/Debug/debugger.cpp - ../../src/Utils/macros.cpp - ../../src/WARDuino/WARDuino.cpp - ../../src/Primitives/primitives.cpp - ../../src/Interpreter/instructions.cpp - ../../src/WARDuino/CallbackHandler.cpp - ) + ../../src/Debug/debugger.cpp + ../../src/Interpreter/instructions.cpp + ../../src/Memory/mem.cpp + ../../src/Primitives/idf.cpp + ../../src/Edward/proxy.cpp + ../../src/Edward/proxy_supervisor.cpp + ../../src/Edward/RFC.cpp + ../../src/Utils/macros.cpp + ../../src/Utils/sockets.cpp + ../../src/Utils/util.cpp + ../../src/Utils/util_arduino.cpp + ../../src/WARDuino/CallbackHandler.cpp + ../../src/WARDuino/WARDuino.cpp + ) -idf_component_register(SRCS "main.cpp" ${SOURCE_FILES} INCLUDE_DIRS "" REQUIRES driver) +idf_component_register(SRCS "main.cpp" ${SOURCE_FILES} INCLUDE_DIRS ../../lib/json/single_include/ REQUIRES driver) add_definitions(-DINFO=0) add_definitions(-DDEBUG=0) diff --git a/platforms/ESP-IDF/main.cpp b/platforms/ESP-IDF/main.cpp index bbbed898..a4df9175 100644 --- a/platforms/ESP-IDF/main.cpp +++ b/platforms/ESP-IDF/main.cpp @@ -2,8 +2,6 @@ // WARDuino - WebAssembly interpreter for embedded devices. // // -//#include - #include #include "../../src/WARDuino.h" @@ -11,76 +9,48 @@ #include "driver/uart.h" #include "esp_err.h" #include "esp_task_wdt.h" -#include "esp_vfs_dev.h" +//#include "esp_vfs_dev.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "sdkconfig.h" +#include "upload.h" volatile bool handelingInterrupt = false; -unsigned char wasm[] = { - 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x03, 0x60, - 0x01, 0x7f, 0x00, 0x60, 0x00, 0x00, 0x60, 0x02, 0x7e, 0x7f, 0x01, 0x7f, - 0x03, 0x04, 0x03, 0x00, 0x02, 0x01, 0x04, 0x04, 0x01, 0x70, 0x00, 0x03, - 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x23, 0x04, 0x7f, 0x01, 0x41, 0x00, - 0x0b, 0x7f, 0x01, 0x41, 0x00, 0x0b, 0x7c, 0x01, 0x44, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x7c, 0x01, 0x44, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, - 0x69, 0x6e, 0x00, 0x02, 0x09, 0x09, 0x01, 0x00, 0x41, 0x00, 0x0b, 0x03, - 0x02, 0x01, 0x00, 0x0a, 0x54, 0x03, 0x02, 0x00, 0x0b, 0x39, 0x00, 0x20, - 0x01, 0x41, 0x01, 0x4a, 0x04, 0x7f, 0x20, 0x00, 0x42, 0x01, 0x7c, 0x20, - 0x01, 0x41, 0x01, 0x6b, 0x10, 0x01, 0x20, 0x01, 0x6c, 0x05, 0x41, 0x20, - 0x24, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x24, - 0x02, 0x23, 0x00, 0x23, 0x00, 0x6a, 0x24, 0x01, 0x23, 0x02, 0x23, 0x02, - 0xa0, 0x24, 0x03, 0x41, 0x01, 0x0b, 0x0b, 0x15, 0x01, 0x01, 0x7f, 0x41, - 0x06, 0x21, 0x00, 0x03, 0x40, 0x42, 0x0d, 0x20, 0x00, 0x10, 0x01, 0x10, - 0x00, 0x0c, 0x00, 0x0b, 0x0b, 0x00, 0x31, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x01, 0x13, 0x03, 0x00, 0x05, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x01, 0x03, - 0x66, 0x61, 0x63, 0x02, 0x04, 0x66, 0x61, 0x63, 0x35, 0x02, 0x15, 0x03, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x06, 0x69, 0x6e, 0x74, 0x5f, 0x33, 0x32}; -unsigned int wasm_len = 236; +unsigned int wasm_len = upload_wasm_len; +unsigned char* wasm = upload_wasm; extern "C" { extern void app_main(void); } -WARDuino wac; +WARDuino* wac = WARDuino::instance(); Module* m; void startDebuggerStd(void* pvParameter) { + Channel* duplex = new Duplex(stdin, stdout); + wac->debugger->setChannel(duplex); + duplex->open(); + int valread; uint8_t buffer[1024] = {0}; - wac.debugger->socket = fileno(stdout); while (true) { taskYIELD(); vTaskDelay(1000 / portTICK_PERIOD_MS); - while ((valread = read(fileno(stdin), buffer, 1024)) != -1) { - write(fileno(stdout), "got a message ... \n", 19); - wac.handleInterrupt(valread - 1, buffer); - write(fileno(stdout), buffer, valread); - fflush(stdout); + while ((valread = duplex->read(buffer, 1024)) != -1) { + wac->handleInterrupt(valread - 1, buffer); } } } void app_main(void) { - m = wac.load_module(wasm, wasm_len, {}); - uint8_t command[] = {'0', '3', '\n'}; - wac.handleInterrupt(3, command); + m = wac->load_module(wasm, wasm_len, {}); + // uint8_t command[] = {'0', '3', '\n'}; + // wac->handleInterrupt(3, command); xTaskCreate(startDebuggerStd, "Debug Thread", 5000, NULL, 1, NULL); printf("START\n\n"); - wac.run_module(m); + wac->run_module(m); printf("END\n\n"); - wac.unload_module(m); + wac->unload_module(m); } - -/*void app_main(void) { - m = wac.load_module(wasm, wasm_len, {}); - printf("START\n\n"); - //wac.run_module(m); - //printf("END\n\n"); - //wac.unload_module(m); - while(true); -}*/ diff --git a/platforms/ESP-IDF/upload.h b/platforms/ESP-IDF/upload.h new file mode 100644 index 00000000..753e37df --- /dev/null +++ b/platforms/ESP-IDF/upload.h @@ -0,0 +1,17 @@ +unsigned char upload_wasm[] = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0d, 0x03, 0x60, + 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, + 0x3f, 0x03, 0x03, 0x65, 0x6e, 0x76, 0x0a, 0x63, 0x68, 0x69, 0x70, 0x5f, + 0x64, 0x65, 0x6c, 0x61, 0x79, 0x00, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x0d, + 0x63, 0x68, 0x69, 0x70, 0x5f, 0x70, 0x69, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x63, 0x68, 0x69, 0x70, + 0x5f, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x00, 0x00, 0x03, 0x03, 0x02, 0x02, 0x02, 0x06, 0x10, 0x03, + 0x7f, 0x00, 0x41, 0x17, 0x0b, 0x7f, 0x00, 0x41, 0x01, 0x0b, 0x7f, 0x00, + 0x41, 0x00, 0x0b, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, + 0x04, 0x0a, 0x2f, 0x02, 0x08, 0x00, 0x23, 0x00, 0x41, 0x02, 0x10, 0x01, + 0x0b, 0x24, 0x01, 0x01, 0x7f, 0x41, 0xe8, 0x07, 0x21, 0x00, 0x10, 0x03, + 0x03, 0x40, 0x23, 0x00, 0x23, 0x01, 0x10, 0x02, 0x20, 0x00, 0x10, 0x00, + 0x23, 0x00, 0x23, 0x02, 0x10, 0x02, 0x20, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x0b, 0x0b}; +unsigned int upload_wasm_len = 170; diff --git a/platforms/README.md b/platforms/README.md new file mode 100644 index 00000000..20d0e805 --- /dev/null +++ b/platforms/README.md @@ -0,0 +1,9 @@ +# Platforms + +This folder contains the code necessary to compile WARDuino for the different supported platforms. + +- Arduino: WARDuino with primitives implemented for the Arduino platform +- Arduino-socket: WARDuino with primitives implemented for the Arduino platform and a socket server to debug over Wi-Fi +- CLI-EMULATOR: a cli for WARDuino with emulated primitives, to run on desktop environments +- ESP-IDF: WARDuino compiled with the ESP-IDF toolchain + diff --git a/scripts/signal b/scripts/signal deleted file mode 100755 index 4138bed4..00000000 --- a/scripts/signal +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/sh - -if test -n "$WARDUINO_DEV"; then - echo "USING: $WARDUINO_DEV" - target="$WARDUINO_DEV" - write() { - ( - tr -d '\n\t ' - echo"" - ) | tr '[:lower:]' '[:upper:]' | tee $WARDUINO_DEV - echo "Written to $WARDUINO_DEV" - } -else - write() { - # xxd -r -p > /tmp/change - ( - tr -d '\n\t ' - echo"" - ) | tr '[:lower:]' '[:upper:]' | tee /tmp/change - kill -USR1 "$(pgrep -i warduino)" - } -fi - -#echo "AAcAQeQAEAIL" | base64 -d > /tmp/change -case "$1" in -"REPLACE") - cat </dev/null | sort | uniq | sed 's/^/ export WARDUINO_DEV=/') - - Communication via /tmp/change and kill -USR1 - export WARDUINO_DEV="" -HELP - - ;; -esac diff --git a/sdkconfig b/sdkconfig index 88079570..2d5dd7d3 100644 --- a/sdkconfig +++ b/sdkconfig @@ -10,6 +10,7 @@ CONFIG_SOC_DAC_SUPPORTED=y CONFIG_SOC_MCPWM_SUPPORTED=y CONFIG_SOC_SDMMC_HOST_SUPPORTED=y CONFIG_SOC_BT_SUPPORTED=y +CONFIG_SOC_BLUEDROID_SUPPORTED=y CONFIG_SOC_CLASSIC_BT_SUPPORTED=y CONFIG_SOC_PCNT_SUPPORTED=y CONFIG_SOC_WIFI_SUPPORTED=y @@ -19,23 +20,32 @@ CONFIG_SOC_EMAC_SUPPORTED=y CONFIG_SOC_CPU_CORES_NUM=2 CONFIG_SOC_ULP_SUPPORTED=y CONFIG_SOC_CCOMP_TIMER_SUPPORTED=y -CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=y CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED=y CONFIG_SOC_I2S_SUPPORTED=y CONFIG_SOC_RMT_SUPPORTED=y CONFIG_SOC_SIGMADELTA_SUPPORTED=y +CONFIG_SOC_SUPPORT_COEXISTENCE=y +CONFIG_SOC_AES_SUPPORTED=y +CONFIG_SOC_MPI_SUPPORTED=y +CONFIG_SOC_SHA_SUPPORTED=y +CONFIG_SOC_FLASH_ENC_SUPPORTED=y +CONFIG_SOC_SECURE_BOOT_SUPPORTED=y CONFIG_SOC_ADC_RTC_CTRL_SUPPORTED=y CONFIG_SOC_ADC_DIG_CTRL_SUPPORTED=y CONFIG_SOC_ADC_PERIPH_NUM=2 CONFIG_SOC_ADC_MAX_CHANNEL_NUM=10 +CONFIG_SOC_ADC_ATTEN_NUM=4 CONFIG_SOC_ADC_DIGI_CONTROLLER_NUM=2 CONFIG_SOC_ADC_PATT_LEN_MAX=16 CONFIG_SOC_ADC_DIGI_MIN_BITWIDTH=9 CONFIG_SOC_ADC_DIGI_MAX_BITWIDTH=12 CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH=2 CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=2000 -CONFIG_SOC_ADC_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=9 +CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12 +CONFIG_SOC_RTC_SLOW_CLOCK_SUPPORT_8MD256=y +CONFIG_SOC_SHARED_IDCACHE_SUPPORTED=y CONFIG_SOC_CPU_BREAKPOINTS_NUM=2 CONFIG_SOC_CPU_WATCHPOINTS_NUM=2 CONFIG_SOC_CPU_WATCHPOINT_SIZE=64 @@ -56,17 +66,22 @@ CONFIG_SOC_APLL_MULTIPLIER_OUT_MAX_HZ=500000000 CONFIG_SOC_APLL_MIN_HZ=5303031 CONFIG_SOC_APLL_MAX_HZ=125000000 CONFIG_SOC_I2S_NUM=2 +CONFIG_SOC_I2S_HW_VERSION_1=y CONFIG_SOC_I2S_SUPPORTS_APLL=y +CONFIG_SOC_I2S_SUPPORTS_PDM=y CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y +CONFIG_SOC_I2S_SUPPORTS_ADC_DAC=y CONFIG_SOC_I2S_SUPPORTS_ADC=y CONFIG_SOC_I2S_SUPPORTS_DAC=y +CONFIG_SOC_I2S_SUPPORTS_LCD_CAMERA=y CONFIG_SOC_I2S_TRANS_SIZE_ALIGN_WORD=y CONFIG_SOC_I2S_LCD_I80_VARIANT=y CONFIG_SOC_LCD_I80_SUPPORTED=y -CONFIG_SOC_LCD_I80_BUSES=y +CONFIG_SOC_LCD_I80_BUSES=2 CONFIG_SOC_LCD_I80_BUS_WIDTH=24 CONFIG_SOC_LEDC_HAS_TIMER_SPECIFIC_MUX=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y CONFIG_SOC_LEDC_SUPPORT_REF_TICK=y CONFIG_SOC_LEDC_SUPPORT_HS_MODE=y CONFIG_SOC_LEDC_CHANNEL_NUM=8 @@ -94,6 +109,7 @@ CONFIG_SOC_RMT_RX_CANDIDATES_PER_GROUP=8 CONFIG_SOC_RMT_CHANNELS_PER_GROUP=8 CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=64 CONFIG_SOC_RMT_SUPPORT_REF_TICK=y +CONFIG_SOC_RMT_SUPPORT_APB=y CONFIG_SOC_RMT_CHANNEL_CLK_INDEPENDENT=y CONFIG_SOC_RTCIO_PIN_COUNT=18 CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y @@ -107,10 +123,15 @@ CONFIG_SOC_SPI_PERIPH_NUM=3 CONFIG_SOC_SPI_DMA_CHAN_NUM=2 CONFIG_SOC_SPI_MAXIMUM_BUFFER_SIZE=64 CONFIG_SOC_SPI_MAX_PRE_DIVIDER=8192 +CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_26M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y CONFIG_SOC_TIMER_GROUPS=2 CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2 CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=64 CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4 +CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y CONFIG_SOC_TOUCH_VERSION_1=y CONFIG_SOC_TOUCH_SENSOR_NUM=10 CONFIG_SOC_TOUCH_PAD_MEASURE_WAIT_MAX=0xFF @@ -130,10 +151,13 @@ CONFIG_SOC_RSA_MAX_BIT_LEN=4096 CONFIG_SOC_AES_SUPPORT_AES_128=y CONFIG_SOC_AES_SUPPORT_AES_192=y CONFIG_SOC_AES_SUPPORT_AES_256=y +CONFIG_SOC_SECURE_BOOT_V1=y +CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=y CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=32 CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21 CONFIG_SOC_PM_SUPPORT_EXT_WAKEUP=y CONFIG_SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_RTC_PERIPH_PD=y CONFIG_SOC_SDMMC_USE_IOMUX=y CONFIG_SOC_SDMMC_NUM_SLOTS=2 CONFIG_SOC_BLE_DONT_UPDATE_OWN_RPA=y @@ -154,6 +178,8 @@ CONFIG_APP_BUILD_BOOTLOADER=y CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y # CONFIG_APP_REPRODUCIBLE_BUILD is not set # CONFIG_APP_NO_BLOBS is not set +# CONFIG_APP_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set +# CONFIG_APP_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set # end of Build type # @@ -185,6 +211,7 @@ CONFIG_BOOTLOADER_LOG_LEVEL=3 CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y # CONFIG_BOOTLOADER_FACTORY_RESET is not set # CONFIG_BOOTLOADER_APP_TEST is not set +CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y CONFIG_BOOTLOADER_WDT_ENABLE=y # CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set CONFIG_BOOTLOADER_WDT_TIME_MS=9000 @@ -200,6 +227,7 @@ CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y # # Security features # +CONFIG_SECURE_BOOT_V1_SUPPORTED=y # CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set # CONFIG_SECURE_BOOT is not set # CONFIG_SECURE_FLASH_ENC_ENABLED is not set @@ -267,10 +295,12 @@ CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 # CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set CONFIG_COMPILER_HIDE_PATHS_MACROS=y -# CONFIG_COMPILER_CXX_EXCEPTIONS is not set +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 # CONFIG_COMPILER_CXX_RTTI is not set CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y # CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set @@ -290,6 +320,10 @@ CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y # # CONFIG_APPTRACE_DEST_JTAG is not set CONFIG_APPTRACE_DEST_NONE=y +# CONFIG_APPTRACE_DEST_UART1 is not set +# CONFIG_APPTRACE_DEST_UART2 is not set +CONFIG_APPTRACE_DEST_UART_NONE=y +CONFIG_APPTRACE_UART_TASK_PRIO=1 CONFIG_APPTRACE_LOCK_ENABLE=y # end of Application Level Tracing @@ -351,6 +385,7 @@ CONFIG_SPI_SLAVE_ISR_IN_IRAM=y # GPIO Configuration # # CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL is not set +# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set # end of GPIO Configuration # @@ -370,6 +405,14 @@ CONFIG_SPI_SLAVE_ISR_IN_IRAM=y # CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set # CONFIG_PCNT_ENABLE_DEBUG_LOG is not set # end of PCNT Configuration + +# +# RMT Configuration +# +# CONFIG_RMT_ISR_IRAM_SAFE is not set +# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_RMT_ENABLE_DEBUG_LOG is not set +# end of RMT Configuration # end of Driver configurations # @@ -388,50 +431,12 @@ CONFIG_EFUSE_MAX_BLK_LEN=192 # CONFIG_ESP_TLS_USING_MBEDTLS=y # CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set -# CONFIG_ESP_TLS_SERVER is not set # CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER is not set # CONFIG_ESP_TLS_PSK_VERIFICATION is not set # CONFIG_ESP_TLS_INSECURE is not set # end of ESP-TLS -# -# ESP32-specific -# -CONFIG_ESP32_REV_MIN_0=y -# CONFIG_ESP32_REV_MIN_1 is not set -# CONFIG_ESP32_REV_MIN_2 is not set -# CONFIG_ESP32_REV_MIN_3 is not set -CONFIG_ESP32_REV_MIN=0 -CONFIG_ESP32_DPORT_WORKAROUND=y -# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set -CONFIG_ESP32_DEFAULT_CPU_FREQ_160=y -# CONFIG_ESP32_DEFAULT_CPU_FREQ_240 is not set -CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160 -# CONFIG_ESP32_SPIRAM_SUPPORT is not set -# CONFIG_ESP32_TRAX is not set -CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 -CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT=y -# CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set -# CONFIG_ESP32_TIME_SYSCALL_USE_HRT is not set -# CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set -CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y -# CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set -# CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set -# CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set -CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 -CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 -CONFIG_ESP32_XTAL_FREQ_40=y -# CONFIG_ESP32_XTAL_FREQ_26 is not set -# CONFIG_ESP32_XTAL_FREQ_AUTO is not set -CONFIG_ESP32_XTAL_FREQ=40 -# CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set -# CONFIG_ESP32_NO_BLOBS is not set -# CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set -# CONFIG_ESP32_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set -# CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set -CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL=5 -# end of ESP32-specific - # # ADC-Calibration # @@ -463,6 +468,7 @@ CONFIG_ETH_USE_SPI_ETHERNET=y # CONFIG_ETH_SPI_ETHERNET_W5500 is not set # CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL is not set # CONFIG_ETH_USE_OPENETH is not set +# CONFIG_ETH_TRANSMIT_MUTEX is not set # end of Ethernet # @@ -495,6 +501,7 @@ CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=32 # CONFIG_HTTPD_LOG_PURGE_DATA is not set # CONFIG_HTTPD_WS_SUPPORT is not set +# CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set # end of HTTP Server # @@ -513,6 +520,7 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32 # # Hardware Settings # +# CONFIG_SPIRAM is not set # # MAC Config @@ -533,12 +541,36 @@ CONFIG_ESP_SLEEP_POWER_DOWN_FLASH=y CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND=y # CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND is not set # CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND is not set +CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 # end of Sleep Config # # RTC Clock Config # +CONFIG_RTC_CLK_SRC_INT_RC=y +# CONFIG_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_RTC_CLK_CAL_CYCLES=1024 # end of RTC Clock Config + +# +# Peripheral Control +# +# CONFIG_PERIPH_CTRL_FUNC_IN_IRAM is not set +# end of Peripheral Control + +CONFIG_ESP32_REV_MIN_0=y +# CONFIG_ESP32_REV_MIN_1 is not set +# CONFIG_ESP32_REV_MIN_2 is not set +# CONFIG_ESP32_REV_MIN_3 is not set +CONFIG_ESP32_REV_MIN=0 +CONFIG_ESP32_DPORT_WORKAROUND=y +CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL=5 +CONFIG_ESP32_XTAL_FREQ_40=y +# CONFIG_ESP32_XTAL_FREQ_26 is not set +# CONFIG_ESP32_XTAL_FREQ_AUTO is not set +CONFIG_ESP32_XTAL_FREQ=40 # end of Hardware Settings # @@ -549,6 +581,7 @@ CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND=y # LCD Peripheral Configuration # CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE=32 +# CONFIG_LCD_ENABLE_DEBUG_LOG is not set # end of LCD Peripheral Configuration # end of LCD and Touch Panel @@ -558,7 +591,6 @@ CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE=32 CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120 CONFIG_ESP_NETIF_TCPIP_LWIP=y # CONFIG_ESP_NETIF_LOOPBACK is not set -CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=y # CONFIG_ESP_NETIF_L2_TAP is not set # end of ESP NETIF Adapter @@ -581,6 +613,24 @@ CONFIG_ESP_PHY_REDUCE_TX_POWER=y # # ESP System Settings # +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160 + +# +# Memory +# +# CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set +# end of Memory + +# +# Trace memory +# +# CONFIG_ESP32_TRAX is not set +CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 +# end of Trace memory + # CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y # CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set @@ -608,7 +658,11 @@ CONFIG_ESP_CONSOLE_MULTIPLE_UART=y CONFIG_ESP_CONSOLE_UART_NUM=0 CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 # CONFIG_ESP_INT_WDT is not set -# CONFIG_ESP_TASK_WDT is not set +CONFIG_ESP_TASK_WDT=y +# CONFIG_ESP_TASK_WDT_PANIC is not set +CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y # CONFIG_ESP_PANIC_HANDLER_IRAM is not set # CONFIG_ESP_DEBUG_STUBS_ENABLE is not set CONFIG_ESP_DEBUG_OCDAWARE=y @@ -629,6 +683,8 @@ CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0=y # CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7 is not set CONFIG_ESP_BROWNOUT_DET_LVL=0 # end of Brownout Detector + +# CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set # end of ESP System Settings # @@ -740,75 +796,56 @@ CONFIG_FATFS_PER_FILE_CACHE=y # end of FAT Filesystem support # -# Modbus configuration -# -CONFIG_FMB_COMM_MODE_TCP_EN=y -CONFIG_FMB_TCP_PORT_DEFAULT=502 -CONFIG_FMB_TCP_PORT_MAX_CONN=5 -CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20 -CONFIG_FMB_COMM_MODE_RTU_EN=y -CONFIG_FMB_COMM_MODE_ASCII_EN=y -CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150 -CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 -CONFIG_FMB_QUEUE_LENGTH=20 -CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 -CONFIG_FMB_SERIAL_BUF_SIZE=256 -CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8 -CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000 -CONFIG_FMB_PORT_TASK_PRIO=10 -# CONFIG_FMB_PORT_TASK_AFFINITY_NO_AFFINITY is not set -CONFIG_FMB_PORT_TASK_AFFINITY_CPU0=y -# CONFIG_FMB_PORT_TASK_AFFINITY_CPU1 is not set -CONFIG_FMB_PORT_TASK_AFFINITY=0x0 -CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT=y -CONFIG_FMB_CONTROLLER_SLAVE_ID=0x00112233 -CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 -CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 -CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 -CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 -# CONFIG_FMB_TIMER_PORT_ENABLED is not set -# CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD is not set -# end of Modbus configuration +# FreeRTOS +# # -# FreeRTOS +# Kernel # +# CONFIG_FREERTOS_SMP is not set # CONFIG_FREERTOS_UNICORE is not set -CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF -CONFIG_FREERTOS_TICK_SUPPORT_CORETIMER=y -CONFIG_FREERTOS_CORETIMER_0=y -# CONFIG_FREERTOS_CORETIMER_1 is not set -CONFIG_FREERTOS_SYSTICK_USES_CCOUNT=y CONFIG_FREERTOS_HZ=100 -CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y -# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set -CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 -CONFIG_FREERTOS_ISR_STACKSIZE=1536 # CONFIG_FREERTOS_USE_IDLE_HOOK is not set # CONFIG_FREERTOS_USE_TICK_HOOK is not set CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 # CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set -CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y -# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 # CONFIG_FREERTOS_USE_TRACE_FACILITY is not set # CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +# end of Kernel + +# +# Port +# CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set +# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y -# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set -# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set -CONFIG_FREERTOS_DEBUG_OCDAWARE=y +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y # CONFIG_FREERTOS_FPU_IN_ISR is not set -CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y +CONFIG_FREERTOS_TICK_SUPPORT_CORETIMER=y +CONFIG_FREERTOS_CORETIMER_0=y +# CONFIG_FREERTOS_CORETIMER_1 is not set +CONFIG_FREERTOS_SYSTICK_USES_CCOUNT=y +# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set # CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH is not set +# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set +CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y +CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y +# end of Port + +CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y +CONFIG_FREERTOS_DEBUG_OCDAWARE=y # end of FreeRTOS # @@ -1025,6 +1062,7 @@ CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y # CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set # CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set # CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 # end of Certificate Bundle # CONFIG_MBEDTLS_ECP_RESTARTABLE is not set @@ -1036,6 +1074,7 @@ CONFIG_MBEDTLS_ROM_MD5=y # CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set # CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set CONFIG_MBEDTLS_HAVE_TIME=y +# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set # CONFIG_MBEDTLS_HAVE_TIME_DATE is not set CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y CONFIG_MBEDTLS_SHA512_C=y @@ -1123,6 +1162,7 @@ CONFIG_MBEDTLS_ECP_NIST_OPTIM=y # # mDNS # +CONFIG_MDNS_MAX_INTERFACES=3 CONFIG_MDNS_MAX_SERVICES=10 CONFIG_MDNS_TASK_PRIORITY=1 CONFIG_MDNS_TASK_STACK_SIZE=4096 @@ -1135,6 +1175,14 @@ CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000 CONFIG_MDNS_TIMER_PERIOD_MS=100 # CONFIG_MDNS_NETWORKING_SOCKET is not set CONFIG_MDNS_MULTIPLE_INSTANCE=y + +# +# MDNS Predefined interfaces +# +CONFIG_MDNS_PREDEF_NETIF_STA=y +CONFIG_MDNS_PREDEF_NETIF_AP=y +CONFIG_MDNS_PREDEF_NETIF_ETH=y +# end of MDNS Predefined interfaces # end of mDNS # @@ -1162,6 +1210,10 @@ CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y # CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y # CONFIG_NEWLIB_NANO_FORMAT is not set +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set # end of Newlib # @@ -1301,7 +1353,6 @@ CONFIG_VFS_SUPPORT_TERMIOS=y # Host File System I/O (Semihosting) # CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS=1 -CONFIG_VFS_SEMIHOSTFS_HOST_PATH_MAX_LEN=128 # end of Host File System I/O (Semihosting) # end of Virtual file system @@ -1324,6 +1375,7 @@ CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 # Supplicant # CONFIG_WPA_MBEDTLS_CRYPTO=y +CONFIG_WPA_MBEDTLS_TLS_CLIENT=y # CONFIG_WPA_WAPI_PSK is not set # CONFIG_WPA_SUITE_B_192 is not set # CONFIG_WPA_DEBUG_PRINT is not set @@ -1332,11 +1384,16 @@ CONFIG_WPA_MBEDTLS_CRYPTO=y # CONFIG_WPA_11KV_SUPPORT is not set # CONFIG_WPA_MBO_SUPPORT is not set # CONFIG_WPA_DPP_SUPPORT is not set +# CONFIG_WPA_11R_SUPPORT is not set +# CONFIG_WPA_WPS_SOFTAP_REGISTRAR is not set # end of Supplicant # end of Component config # Deprecated options for backward compatibility # CONFIG_NO_BLOBS is not set +# CONFIG_ESP32_NO_BLOBS is not set +# CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set +# CONFIG_ESP32_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set # CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set # CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set # CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set @@ -1359,7 +1416,8 @@ CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y # CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set # CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 -# CONFIG_CXX_EXCEPTIONS is not set +CONFIG_CXX_EXCEPTIONS=y +CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 CONFIG_STACK_CHECK_NONE=y # CONFIG_STACK_CHECK_NORM is not set # CONFIG_STACK_CHECK_STRONG is not set @@ -1370,30 +1428,37 @@ CONFIG_STACK_CHECK_NONE=y CONFIG_ESP32_APPTRACE_DEST_NONE=y CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y CONFIG_ADC2_DISABLE_DAC=y -# CONFIG_SPIRAM_SUPPORT is not set -CONFIG_TRACEMEM_RESERVE_DRAM=0x0 -CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y -# CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set -CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y -# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set -# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set -# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set -# CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set -# CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set # CONFIG_EVENT_LOOP_PROFILING is not set CONFIG_POST_EVENTS_FROM_ISR=y CONFIG_POST_EVENTS_FROM_IRAM_ISR=y # CONFIG_OTA_ALLOW_HTTP is not set +# CONFIG_SPIRAM_SUPPORT is not set +# CONFIG_ESP32_SPIRAM_SUPPORT is not set # CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 CONFIG_ESP_SYSTEM_PD_FLASH=y +CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y +CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y +# CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set +# CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set +# CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set +CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 CONFIG_ESP32_PHY_MAX_TX_POWER=20 CONFIG_REDUCE_PHY_TX_POWER=y CONFIG_ESP32_REDUCE_PHY_TX_POWER=y +# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_160=y +# CONFIG_ESP32_DEFAULT_CPU_FREQ_240 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_TRACEMEM_RESERVE_DRAM=0x0 # CONFIG_ESP32_PANIC_PRINT_HALT is not set CONFIG_ESP32_PANIC_PRINT_REBOOT=y # CONFIG_ESP32_PANIC_SILENT_REBOOT is not set @@ -1409,7 +1474,11 @@ CONFIG_CONSOLE_UART=y CONFIG_CONSOLE_UART_NUM=0 CONFIG_CONSOLE_UART_BAUDRATE=115200 # CONFIG_INT_WDT is not set -# CONFIG_TASK_WDT is not set +CONFIG_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y # CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set CONFIG_ESP32_DEBUG_OCDAWARE=y CONFIG_BROWNOUT_DET=y @@ -1432,28 +1501,16 @@ CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set CONFIG_BROWNOUT_DET_LVL=0 CONFIG_ESP32_BROWNOUT_DET_LVL=0 +# CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set CONFIG_IPC_TASK_STACK_SIZE=1024 CONFIG_TIMER_TASK_STACK_SIZE=3584 # CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set # CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y -CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND=150 -CONFIG_MB_MASTER_DELAY_MS_CONVERT=200 -CONFIG_MB_QUEUE_LENGTH=20 -CONFIG_MB_SERIAL_TASK_STACK_SIZE=4096 -CONFIG_MB_SERIAL_BUF_SIZE=256 -CONFIG_MB_SERIAL_TASK_PRIO=10 -CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT=y -CONFIG_MB_CONTROLLER_SLAVE_ID=0x00112233 -CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT=20 -CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 -CONFIG_MB_CONTROLLER_STACK_SIZE=4096 -CONFIG_MB_EVENT_QUEUE_TIMEOUT=20 -# CONFIG_MB_TIMER_PORT_ENABLED is not set -# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set CONFIG_TIMER_TASK_PRIORITY=1 CONFIG_TIMER_TASK_STACK_DEPTH=2048 CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set # CONFIG_L2_TO_L3_COPY is not set CONFIG_ESP_GRATUITOUS_ARP=y CONFIG_GARP_TMR_INTERVAL=60 @@ -1476,6 +1533,12 @@ CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y # CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF # CONFIG_PPP_SUPPORT is not set +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y +# CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_HRT is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 CONFIG_ESP32_PTHREAD_STACK_MIN=768 @@ -1491,5 +1554,4 @@ CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y CONFIG_SUPPORT_TERMIOS=y CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 -CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN=128 # End of deprecated options diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index 00aaedc9..85e5d51d 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -1,23 +1,41 @@ #include "debugger.h" -#include - +#include +#include #include +#ifndef ARDUINO +#include +#else +#include "../../lib/json/single_include/nlohmann/json.hpp" +#endif +#include "../Edward/proxy.h" +#include "../Edward/proxy_supervisor.h" #include "../Memory/mem.h" #include "../Utils//util.h" #include "../Utils/macros.h" -#include "../WARDuino.h" // Debugger -Debugger::Debugger(int socket) { this->socket = socket; } +Debugger::Debugger(Channel *duplex) { this->channel = duplex; } // Public methods +void Debugger::setChannel(Channel *duplex) { + delete this->channel; + this->channel = duplex; +} + void Debugger::addDebugMessage(size_t len, const uint8_t *buff) { uint8_t *data = this->parseDebugBuffer(len, buff); - if (data != nullptr) { + if (data != nullptr && *data == interruptRecvCallbackmapping) { + std::string text = (char *)buff; + auto *msg = + (uint8_t *)acalloc(sizeof(uint8_t), len, "interrupt buffer"); + memcpy(msg, buff, len * sizeof(uint8_t)); + *msg = *data; + this->debugMessages.push_back(msg); + } else if (data != nullptr) { this->debugMessages.push_back(data); } } @@ -35,6 +53,9 @@ uint8_t *Debugger::parseDebugBuffer(size_t len, const uint8_t *buff) { case 'A' ... 'F': r = buff[i] - 'A' + 10; break; + case 'a' ... 'f': + r = buff[i] - 'a' + 10; + break; default: success = false; } @@ -89,8 +110,8 @@ bool Debugger::isBreakpoint(uint8_t *loc) { return this->breakpoints.find(loc) != this->breakpoints.end(); } -void Debugger::notifyBreakpoint(uint8_t *pc_ptr) { - dprintf(this->socket, "AT %p!\n", (void *)pc_ptr); +void Debugger::notifyBreakpoint(uint8_t *pc_ptr) const { + this->channel->write("AT %p!\n", (void *)pc_ptr); } /** @@ -121,34 +142,35 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { return false; } - dprintf(this->socket, "Interrupt: %x\n", *interruptData); + this->channel->write("Interrupt: %x\n", *interruptData); + + long start = 0, size = 0; switch (*interruptData) { case interruptRUN: this->handleInterruptRUN(m, program_state); free(interruptData); break; case interruptHALT: - dprintf(this->socket, "STOP!\n"); + this->channel->write("STOP!\n"); + this->channel->close(); free(interruptData); exit(0); case interruptPAUSE: *program_state = WARDUINOpause; - dprintf(this->socket, "PAUSE!\n"); + this->channel->write("PAUSE!\n"); free(interruptData); break; case interruptSTEP: - dprintf(this->socket, "STEP!\n"); + this->channel->write("STEP!\n"); *program_state = WARDUINOstep; this->skipBreakpoint = m->pc_ptr; free(interruptData); break; case interruptBPAdd: // Breakpoint case interruptBPRem: // Breakpoint remove - { this->handleInterruptBP(interruptData); free(interruptData); break; - } case interruptDUMP: *program_state = WARDUINOpause; this->dump(m); @@ -165,20 +187,93 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { free(interruptData); break; case interruptUPDATEFun: - dprintf(this->socket, "CHANGE function!\n"); - this->handleChangedFunction(m, interruptData); + this->channel->write("CHANGE function!\n"); + Debugger::handleChangedFunction(m, interruptData); // do not free(interruptData); // we need it to run that code // TODO: free double replacements break; case interruptUPDATELocal: - dprintf(this->socket, "CHANGE local!\n"); + this->channel->write("CHANGE local!\n"); this->handleChangedLocal(m, interruptData); free(interruptData); break; + case interruptWOODDUMP: + *program_state = WARDUINOpause; + free(interruptData); + woodDump(m); + break; + case interruptOffset: + free(interruptData); + this->channel->write("{\"offset\":\"%p\"}\n", (void *)m->bytes); + break; + case interruptRecvState: + if (!this->receivingData) { + *program_state = WARDUINOpause; + debug("paused program execution\n"); + CallbackHandler::manual_event_resolution = true; + dbg_info("Manual event resolution is on."); + this->receivingData = true; + this->freeState(m, interruptData); + free(interruptData); + this->channel->write("ack!\n"); + } else { + printf("receiving state\n"); + debug("receiving state\n"); + receivingData = !this->saveState(m, interruptData); + free(interruptData); + debug("sending %s!\n", receivingData ? "ack" : "done"); + this->channel->write("%s!\n", receivingData ? "ack" : "done"); + if (!this->receivingData) { + debug("receiving state done\n"); + } + } + break; + case interruptProxyCall: { + this->handleProxyCall(m, program_state, interruptData + 1); + free(interruptData); + } break; + case interruptMonitorProxies: { + printf("receiving functions list to proxy\n"); + this->handleMonitorProxies(m, interruptData + 1); + free(interruptData); + } break; + case interruptProxify: { + dbg_info("Converting to proxy settings.\n"); + this->proxify(); + free(interruptData); + break; + } + case interruptDUMPAllEvents: + printf("InterruptDUMPEvents\n"); + size = (long)CallbackHandler::event_count(); + case interruptDUMPEvents: + // TODO get start and size from message + this->channel->write("{"); + this->dumpEvents(start, size); + this->channel->write("}\n"); + free(interruptData); + break; + case interruptPOPEvent: + CallbackHandler::resolve_event(true); + free(interruptData); + break; + case interruptPUSHEvent: + this->handlePushedEvent(reinterpret_cast(interruptData)); + free(interruptData); + break; + case interruptRecvCallbackmapping: + Debugger::updateCallbackmapping( + m, reinterpret_cast(interruptData + 2)); + free(interruptData); + break; + case interruptDUMPCallbackmapping: + this->dumpCallbackmapping(); + free(interruptData); + break; default: // handle later - dprintf(this->socket, "COULD not parse interrupt data!\n"); + this->channel->write("COULD not parse interrupt data!\n"); free(interruptData); break; } @@ -187,16 +282,57 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { } // Private methods +void Debugger::printValue(StackValue *v, uint32_t idx, bool end = false) const { + char buff[256]; + + switch (v->value_type) { + case I32: + snprintf(buff, 255, R"("type":"i32","value":%)" PRIi32, + v->value.uint32); + break; + case I64: + snprintf(buff, 255, R"("type":"i64","value":%)" PRIi64, + v->value.uint64); + break; + case F32: + snprintf(buff, 255, R"("type":"F32","value":%.7f)", v->value.f32); + break; + case F64: + snprintf(buff, 255, R"("type":"F64","value":%.7f)", v->value.f64); + break; + default: + snprintf(buff, 255, R"("type":"%02x","value":"%)" PRIx64 "\"", + v->value_type, v->value.uint64); + } + this->channel->write(R"({"idx":%d,%s}%s)", idx, buff, end ? "" : ","); +} + +uint8_t *Debugger::findOpcode(Module *m, Block *block) { + auto find = + std::find_if(std::begin(m->block_lookup), std::end(m->block_lookup), + [&](const std::pair &pair) { + return pair.second == block; + }); + uint8_t *opcode = nullptr; + if (find != std::end(m->block_lookup)) { + opcode = find->first; + } else { + // FIXME FATAL? + debug("find_opcode: not found\n"); + exit(33); + } + return opcode; +} void Debugger::handleInterruptRUN(Module *m, RunningState *program_state) { - dprintf(this->socket, "GO!\n"); + this->channel->write("GO!\n"); if (*program_state == WARDUINOpause && this->isBreakpoint(m->pc_ptr)) { this->skipBreakpoint = m->pc_ptr; } *program_state = WARDUINOrun; } -void Debugger::handleInterruptBP(uint8_t *interruptData) { +void Debugger::handleInterruptBP(const uint8_t *interruptData) { // TODO: segfault may happen here! uint8_t len = interruptData[1]; uintptr_t bp = 0x0; @@ -205,7 +341,7 @@ void Debugger::handleInterruptBP(uint8_t *interruptData) { bp |= interruptData[i + 2]; } auto *bpt = (uint8_t *)bp; - dprintf(this->socket, "BP %p!\n", static_cast(bpt)); + this->channel->write("BP %p!\n", static_cast(bpt)); if (*interruptData == 0x06) { this->addBreakpoint(bpt); @@ -215,50 +351,52 @@ void Debugger::handleInterruptBP(uint8_t *interruptData) { } void Debugger::dump(Module *m, bool full) const { - dprintf(this->socket, "{"); + this->channel->write("{"); // current PC - dprintf(this->socket, R"("pc":"%p",)", (void *)m->pc_ptr); + this->channel->write(R"("pc":"%p",)", (void *)m->pc_ptr); // start of bytes - dprintf(this->socket, R"("start":["%p"],)", (void *)m->bytes); + this->channel->write(R"("start":["%p"],)", (void *)m->bytes); - this->dumpBreakpoints(m); + this->dumpBreakpoints(); this->dumpFunctions(m); this->dumpCallstack(m); if (full) { - dprintf(this->socket, R"(, "locals": )"); + this->channel->write(R"(, "locals": )"); this->dumpLocals(m); + this->channel->write(", "); + this->dumpEvents(0, CallbackHandler::event_count()); } - dprintf(this->socket, "}\n\n"); - fflush(stdout); + this->channel->write("}\n\n"); + // fflush(stdout); } -void Debugger::dumpBreakpoints(Module *m) const { - dprintf(this->socket, "\"breakpoints\":["); +void Debugger::dumpBreakpoints() const { + this->channel->write("\"breakpoints\":["); { size_t i = 0; for (auto bp : this->breakpoints) { - dprintf(this->socket, R"("%p"%s)", bp, - (++i < this->breakpoints.size()) ? "," : ""); + this->channel->write(R"("%p"%s)", bp, + (++i < this->breakpoints.size()) ? "," : ""); } } - dprintf(this->socket, "],"); + this->channel->write("],"); } void Debugger::dumpFunctions(Module *m) const { - dprintf(this->socket, "\"functions\":["); + this->channel->write("\"functions\":["); for (size_t i = m->import_count; i < m->function_count; i++) { - dprintf(this->socket, R"({"fidx":"0x%x","from":"%p","to":"%p"}%s)", - m->functions[i].fidx, - static_cast(m->functions[i].start_ptr), - static_cast(m->functions[i].end_ptr), - (i < m->function_count - 1) ? "," : "],"); + this->channel->write(R"({"fidx":"0x%x","from":"%p","to":"%p"}%s)", + m->functions[i].fidx, + static_cast(m->functions[i].start_ptr), + static_cast(m->functions[i].end_ptr), + (i < m->function_count - 1) ? "," : "],"); } } @@ -266,12 +404,11 @@ void Debugger::dumpFunctions(Module *m) const { * {"type":%u,"fidx":"0x%x","sp":%d,"fp":%d,"ra":"%p"}%s */ void Debugger::dumpCallstack(Module *m) const { - dprintf(this->socket, "\"callstack\":["); + this->channel->write("\"callstack\":["); for (int i = 0; i <= m->csp; i++) { Frame *f = &m->callstack[i]; uint8_t *callsite = f->ra_ptr - 2; // callsite of function (if type 0) - dprintf( - this->socket, + this->channel->write( R"({"type":%u,"fidx":"0x%x","sp":%d,"fp":%d,"start":"%p","ra":"%p","callsite":"%p"}%s)", f->block->block_type, f->block->fidx, f->sp, f->fp, f->block->start_ptr, static_cast(f->ra_ptr), @@ -280,7 +417,7 @@ void Debugger::dumpCallstack(Module *m) const { } void Debugger::dumpLocals(Module *m) const { - fflush(stdout); + // fflush(stdout); int firstFunFramePtr = m->csp; while (m->callstack[firstFunFramePtr].block->block_type != 0) { firstFunFramePtr--; @@ -289,10 +426,10 @@ void Debugger::dumpLocals(Module *m) const { } } Frame *f = &m->callstack[firstFunFramePtr]; - dprintf(this->socket, R"({"count":%u,"locals":[)", 0); - fflush(stdout); // FIXME: this is needed for ESP to propery print + this->channel->write(R"({"count":%u,"locals":[)", 0); + // fflush(stdout); // FIXME: this is needed for ESP to properly print char _value_str[256]; - for (size_t i = 0; i < f->block->local_count; i++) { + for (uint32_t i = 0; i < f->block->local_count; i++) { auto v = &m->stack[m->fp + i]; switch (v->value_type) { case I32: @@ -304,11 +441,11 @@ void Debugger::dumpLocals(Module *m) const { v->value.uint64); break; case F32: - snprintf(_value_str, 255, R"("type":"i64","value":%.7f)", + snprintf(_value_str, 255, R"("type":"F32","value":%.7f)", v->value.f32); break; case F64: - snprintf(_value_str, 255, R"("type":"i64","value":%.7f)", + snprintf(_value_str, 255, R"("type":"F64","value":%.7f)", v->value.f64); break; default: @@ -317,12 +454,40 @@ void Debugger::dumpLocals(Module *m) const { v->value_type, v->value.uint64); } - dprintf(this->socket, "{%s, \"index\":%i}%s", _value_str, - i + f->block->type->param_count, - (i + 1 < f->block->local_count) ? "," : ""); + this->channel->write("{%s, \"index\":%u}%s", _value_str, + i + f->block->type->param_count, + (i + 1 < f->block->local_count) ? "," : ""); } - dprintf(this->socket, "]}"); - fflush(stdout); + this->channel->write("]}"); + // fflush(stdout); +} + +void Debugger::dumpEvents(long start, long size) const { + bool previous = CallbackHandler::resolving_event; + CallbackHandler::resolving_event = true; + if (size > EVENTS_SIZE) { + size = EVENTS_SIZE; + } + + this->channel->write(R"("events": [)"); + long index = start, end = start + size; + std::for_each(CallbackHandler::event_begin() + start, + CallbackHandler::event_begin() + end, + [this, &index, &end](const Event &e) { + this->channel->write( + R"({"topic": "%s", "payload": "%s"})", + e.topic.c_str(), e.payload.c_str()); + if (++index < end) { + this->channel->write(", "); + } + }); + this->channel->write("]"); + + CallbackHandler::resolving_event = previous; +} + +void Debugger::dumpCallbackmapping() const { + this->channel->write("%s\n", CallbackHandler::dump_callbacks().c_str()); } /** @@ -394,10 +559,10 @@ bool Debugger::handleChangedFunction(Module *m, uint8_t *bytes) { bool Debugger::handleChangedLocal(Module *m, uint8_t *bytes) const { if (*bytes != interruptUPDATELocal) return false; uint8_t *pos = bytes + 1; - dprintf(this->socket, "Local updates: %x\n", *pos); + this->channel->write("Local updates: %x\n", *pos); uint32_t localId = read_LEB_32(&pos); - dprintf(this->socket, "Local %u being changed\n", localId); + this->channel->write("Local %u being changed\n", localId); auto v = &m->stack[m->fp + localId]; switch (v->value_type) { case I32: @@ -413,6 +578,478 @@ bool Debugger::handleChangedLocal(Module *m, uint8_t *bytes) const { memcpy(&v->value.uint64, pos, 8); break; } - dprintf(this->socket, "Local %u changed to %u\n", localId, v->value.uint32); + this->channel->write("Local %u changed to %u\n", localId, v->value.uint32); + return true; +} + +void Debugger::notifyPushedEvent() const { + this->channel->write("new pushed event"); +} + +bool Debugger::handlePushedEvent(char *bytes) const { + if (*bytes != interruptPUSHEvent) return false; + auto parsed = nlohmann::json::parse(bytes); + printf("handle pushed event: %s", bytes); + auto *event = new Event(*parsed.find("topic"), *parsed.find("payload")); + CallbackHandler::push_event(event); + this->notifyPushedEvent(); return true; } + +void Debugger::woodDump(Module *m) { + debug("asked for doDump\n"); + printf("asked for woodDump\n"); + this->channel->write("DUMP!\n"); + this->channel->write("{"); + + // current PC + this->channel->write(R"("pc":"%p",)", (void *)m->pc_ptr); + + // start of bytes + this->channel->write(R"("start":["%p"],)", (void *)m->bytes); + + this->channel->write("\"breakpoints\":["); + size_t i = 0; + for (auto bp : this->breakpoints) { + this->channel->write(R"("%p"%s)", bp, + (++i < this->breakpoints.size()) ? "," : ""); + } + this->channel->write("],"); + + // stack + this->channel->write("\"stack\":["); + for (int j = 0; j <= m->sp; j++) { + auto v = &m->stack[j]; + printValue(v, j, j == m->sp); + } + this->channel->write("],"); + + // Callstack + this->channel->write("\"callstack\":["); + for (int j = 0; j <= m->csp; j++) { + Frame *f = &m->callstack[j]; + uint8_t *block_key = + f->block->block_type == 0 ? nullptr : findOpcode(m, f->block); + this->channel->write( + R"({"type":%u,"fidx":"0x%x","sp":%d,"fp":%d,"block_key":"%p", "ra":"%p", "idx":%d}%s)", + f->block->block_type, f->block->fidx, f->sp, f->fp, block_key, + static_cast(f->ra_ptr), j, (j < m->csp) ? "," : ""); + } + + // Globals + this->channel->write("],\"globals\":["); + for (uint32_t j = 0; j < m->global_count; j++) { + auto v = m->globals + j; + printValue(v, j, j == (m->global_count - 1)); + } + this->channel->write("]"); // closing globals + + this->channel->write(R"(,"table":{"max":%d, "init":%d, "elements":[)", + m->table.maximum, m->table.initial); + + for (uint32_t j = 0; j < m->table.size; j++) { + this->channel->write("%" PRIu32 "%s", m->table.entries[j], + (j + 1) == m->table.size ? "" : ","); + } + this->channel->write("]}"); // closing table + + // memory + uint32_t total_elems = + m->memory.pages * (uint32_t)PAGE_SIZE; // TODO debug PAGE_SIZE + this->channel->write( + R"(,"memory":{"pages":%d,"max":%d,"init":%d,"bytes":[)", + m->memory.pages, m->memory.maximum, m->memory.initial); + for (uint32_t j = 0; j < total_elems; j++) { + this->channel->write("%" PRIu8 "%s", m->memory.bytes[j], + (j + 1) == total_elems ? "" : ","); + } + this->channel->write("]}"); // closing memory + + this->channel->write(R"(,"br_table":{"size":"0x%x","labels":[)", + BR_TABLE_SIZE); + for (uint32_t j = 0; j < BR_TABLE_SIZE; j++) { + this->channel->write("%" PRIu32 "%s", m->br_table[j], + (j + 1) == BR_TABLE_SIZE ? "" : ","); + } + this->channel->write("]}}\n"); +} + +enum ReceiveState { + pcState = 0x01, + breakpointsState = 0x02, + callstackState = 0x03, + globalsState = 0x04, + tblState = 0x05, + memState = 0x06, + brtblState = 0x07, + stackvalsState = 0x08, + pcErrorState = 0x09 +}; + +void Debugger::freeState(Module *m, uint8_t *interruptData) { + debug("freeing the program state\n"); + uint8_t *first_msg = nullptr; + uint8_t *endfm = nullptr; + first_msg = interruptData + 1; // skip interruptRecvState + endfm = first_msg + read_B32(&first_msg); + + // nullify state + this->breakpoints.clear(); + m->csp = -1; + m->sp = -1; + memset(m->br_table, 0, BR_TABLE_SIZE); + + while (first_msg < endfm) { + switch (*first_msg++) { + case globalsState: { + debug("receiving globals info\n"); + uint32_t amount = read_B32(&first_msg); + debug("total globals %d\n", amount); + // TODO if global_count != amount Otherwise set all to zero + if (m->global_count != amount) { + debug("globals freeing state and then allocating\n"); + if (m->global_count > 0) free(m->globals); + if (amount > 0) + m->globals = (StackValue *)acalloc( + amount, sizeof(StackValue), "globals"); + } else { + debug("globals setting existing state to zero\n"); + for (uint32_t i = 0; i < m->global_count; i++) { + debug("decreasing global_count\n"); + StackValue *sv = &m->globals[i]; + sv->value_type = 0; + sv->value.uint32 = 0; + } + } + m->global_count = 0; + break; + } + case tblState: { + debug("receiving table info\n"); + m->table.initial = read_B32(&first_msg); + m->table.maximum = read_B32(&first_msg); + uint32_t size = read_B32(&first_msg); + debug("init %d max %d size %d\n", m->table.initial, + m->table.maximum, size); + if (m->table.size != size) { + debug("old table size %d\n", m->table.size); + if (m->table.size != 0) free(m->table.entries); + m->table.entries = (uint32_t *)acalloc( + size, sizeof(uint32_t), "Module->table.entries"); + } + m->table.size = 0; // allows to accumulatively add entries + break; + } + case memState: { + debug("receiving memory info\n"); + // FIXME: init & max not needed + m->memory.maximum = read_B32(&first_msg); + m->memory.initial = read_B32(&first_msg); + uint32_t pages = read_B32(&first_msg); + debug("max %d init %d current page %d\n", m->memory.maximum, + m->memory.initial, pages); + // if(pages !=m->memory.pages){ + // if(m->memory.pages !=0) + if (m->memory.bytes != nullptr) { + free(m->memory.bytes); + } + m->memory.bytes = + (uint8_t *)acalloc(pages * PAGE_SIZE, sizeof(uint32_t), + "Module->memory.bytes"); + m->memory.pages = pages; + // } + // else{ + // //TODO fill memory.bytes with zeros + // memset(m->memory.bytes, 0, m->memory.pages * PAGE_SIZE) ; + // } + break; + } + default: { + FATAL("freeState: receiving unknown command\n"); + break; + } + } + } + debug("done with first msg\n"); +} + +bool Debugger::saveState(Module *m, uint8_t *interruptData) { + uint8_t *program_state = nullptr; + uint8_t *end_state = nullptr; + program_state = interruptData + 1; // skip interruptRecvState + end_state = program_state + read_B32(&program_state); + + debug("saving program_state\n"); + while (program_state < end_state) { + switch (*program_state++) { + case pcState: { // PC + m->pc_ptr = (uint8_t *)readPointer(&program_state); + /* printf("receiving pc %p\n", static_cast(m->pc_ptr)); + */ + break; + } + case breakpointsState: { // breakpoints + uint8_t quantity_bps = *program_state++; + debug("receiving breakpoints %" PRIu8 "\n", quantity_bps); + for (size_t i = 0; i < quantity_bps; i++) { + auto bp = (uint8_t *)readPointer(&program_state); + this->addBreakpoint(bp); + } + break; + } + case callstackState: { + debug("receiving callstack\n"); + uint16_t quantity = read_B16(&program_state); + debug("quantity frames %" PRIu16 "\n", quantity); + /* printf("quantity frames %" PRIu16 "\n", quantity); */ + for (size_t i = 0; i < quantity; i++) { + /* printf("frame IDX: %lu\n", i); */ + uint8_t block_type = *program_state++; + m->csp += 1; + Frame *f = m->callstack + m->csp; + f->sp = read_B32_signed(&program_state); + f->fp = read_B32_signed(&program_state); + f->ra_ptr = (uint8_t *)readPointer(&program_state); + if (block_type == 0) { // a function + debug("function block\n"); + uint32_t fidx = read_B32(&program_state); + /* debug("function block idx=%" PRIu32 "\n", fidx); */ + f->block = m->functions + fidx; + + if (f->block->fidx != fidx) { + FATAL("incorrect fidx: exp %" PRIu32 " got %" PRIu32 + ". Exiting program\n", + fidx, f->block->fidx); + } + m->fp = f->sp + 1; + } else { + debug("non function block\n"); + auto *block_key = + (uint8_t *)readPointer(&program_state); + /* debug("block_key=%p\n", static_cast(block_key)); */ + f->block = m->block_lookup[block_key]; + if (f->block == nullptr) { + FATAL("block_lookup cannot be nullptr\n"); + } + } + } + break; + } + case globalsState: { // TODO merge globalsState stackvalsState into + // one case + debug("receiving global state\n"); + uint32_t quantity_globals = read_B32(&program_state); + uint8_t valtypes[] = {I32, I64, F32, F64}; + + debug("receiving #%" PRIu32 " globals\n", quantity_globals); + for (uint32_t q = 0; q < quantity_globals; q++) { + uint8_t type_index = *program_state++; + if (type_index >= sizeof(valtypes)) { + FATAL("received unknown type %" PRIu8 "\n", type_index); + } + StackValue *sv = &m->globals[m->global_count++]; + size_t qb = type_index == 0 || type_index == 2 ? 4 : 8; + debug("receiving type %" PRIu8 " and %d bytes \n", + type_index, + type_index == 0 || type_index == 2 ? 4 : 8); + + sv->value_type = valtypes[type_index]; + memcpy(&sv->value, program_state, qb); + program_state += qb; + } + break; + } + case tblState: { + program_state++; // for now only funcref + uint32_t quantity = read_B32(&program_state); + for (size_t i = 0; i < quantity; i++) { + uint32_t ne = read_B32(&program_state); + m->table.entries[m->table.size++] = ne; + } + break; + } + case memState: { + debug("receiving memory\n"); + uint32_t start = read_B32(&program_state); + uint32_t limit = read_B32(&program_state); + debug("memory offsets start=%" PRIu32 " , limit=%" PRIu32 "\n", + start, limit); + if (start > limit) { + FATAL("incorrect memory offsets\n"); + } + uint32_t total_bytes = limit - start + 1; + uint8_t *mem_end = + m->memory.bytes + m->memory.pages * (uint32_t)PAGE_SIZE; + debug("will copy #%" PRIu32 " bytes\n", total_bytes); + if ((m->memory.bytes + start) + total_bytes > mem_end) { + FATAL("memory overflow %p > %p\n", + static_cast((m->bytes + start) + total_bytes), + static_cast(mem_end)); + } + memcpy(m->memory.bytes + start, program_state, total_bytes + 1); + for (auto i = start; i <= (start + total_bytes - 1); i++) { + debug("GOT byte idx %" PRIu32 " =%" PRIu8 "\n", i, + m->memory.bytes[i]); + } + debug("outside the out\n"); + program_state += total_bytes; + break; + } + case brtblState: { + debug("receiving br_table\n"); + uint16_t begin_index = read_B16(&program_state); + uint16_t end_index = read_B16(&program_state); + debug("br_table offsets begin=%" PRIu16 " , end=%" PRIu16 "\n", + begin_index, end_index); + if (begin_index > end_index) { + FATAL("incorrect br_table offsets\n"); + } + if (end_index >= BR_TABLE_SIZE) { + FATAL("br_table overflow\n"); + } + for (auto idx = begin_index; idx <= end_index; idx++) { + // FIXME speedup with memcpy? + uint32_t el = read_B32(&program_state); + m->br_table[idx] = el; + } + break; + } + case stackvalsState: { + // FIXME the float does add numbers at the end. The extra + // numbers are present in the send information when dump occurs + debug("receiving stack\n"); + uint16_t quantity_sv = read_B16(&program_state); + uint8_t valtypes[] = {I32, I64, F32, F64}; + for (size_t i = 0; i < quantity_sv; i++) { + uint8_t type_index = *program_state++; + if (type_index >= sizeof(valtypes)) { + FATAL("received unknown type %" PRIu8 "\n", type_index); + } + m->sp += 1; + StackValue *sv = &m->stack[m->sp]; + sv->value.uint64 = 0; // init whole union to 0 + size_t qb = type_index == 0 || type_index == 2 ? 4 : 8; + sv->value_type = valtypes[type_index]; + memcpy(&sv->value, program_state, qb); + program_state += qb; + } + break; + } + default: { + FATAL("saveState: Received unknown program state\n"); + } + } + } + auto done = (uint8_t)*program_state; + return done == (uint8_t)1; +} + +uintptr_t Debugger::readPointer(uint8_t **data) { + uint8_t len = (*data)[0]; + uintptr_t bp = 0x0; + for (size_t i = 0; i < len; i++) { + bp <<= sizeof(uint8_t) * 8; + bp |= (*data)[i + 1]; + } + *data += 1 + len; // skip pointer + return bp; +} + +void Debugger::proxify() { + WARDuino::instance()->program_state = PROXYhalt; + this->proxy = new Proxy(); // TODO delete +} + +void Debugger::handleProxyCall(Module *m, RunningState *program_state, + uint8_t *interruptData) { + if (this->proxy == nullptr) { + dbg_info("No proxy available to send proxy call to.\n"); + // TODO how to handle this error? + return; + } + uint8_t *data = interruptData; + uint32_t fidx = read_L32(&data); + dbg_info("Proxycall func %" PRIu32 "\n", fidx); + + Block *func = &m->functions[fidx]; + StackValue *args = Proxy::readRFCArgs(func, data); + dbg_trace("Enqueuing callee %" PRIu32 "\n", func->fidx); + + auto *rfc = new RFC(fidx, func->type, args); + this->proxy->pushRFC(m, rfc); +} + +RFC *Debugger::topProxyCall() { + if (proxy == nullptr) { + return nullptr; + } + return this->proxy->topRFC(); +} + +void Debugger::sendProxyCallResult(Module *m) { + if (proxy == nullptr) { + return; + } + this->proxy->returnResult(m); +} + +bool Debugger::isProxied(uint32_t fidx) const { + return this->supervisor != nullptr && this->supervisor->isProxied(fidx); +} + +void Debugger::handleMonitorProxies(Module *m, uint8_t *interruptData) { + uint32_t amount_funcs = read_B32(&interruptData); + printf("funcs_total %" PRIu32 "\n", amount_funcs); + + m->warduino->debugger->supervisor->unregisterAllProxiedCalls(); + for (uint32_t i = 0; i < amount_funcs; i++) { + uint32_t fidx = read_B32(&interruptData); + printf("registering fid=%" PRIu32 "\n", fidx); + m->warduino->debugger->supervisor->registerProxiedCall(fidx); + } + + this->channel->write("done!\n"); +} + +void Debugger::startProxySupervisor(Channel *socket) { + this->connected_to_proxy = true; + pthread_mutex_init(&this->supervisor_mutex, nullptr); + pthread_mutex_lock(&this->supervisor_mutex); + + this->supervisor = new ProxySupervisor(socket, &this->supervisor_mutex); + printf("Connected to proxy.\n"); +} + +bool Debugger::proxy_connected() const { return this->connected_to_proxy; } + +void Debugger::disconnect_proxy() { + if (!this->proxy_connected()) { + return; + } + int *ptr; + // TODO close file + pthread_mutex_unlock(&this->supervisor_mutex); + pthread_join(this->supervisor->getThreadID(), (void **)&ptr); +} + +void Debugger::updateCallbackmapping(Module *m, const char *data) { + nlohmann::basic_json<> parsed = nlohmann::json::parse(data); + CallbackHandler::clear_callbacks(); + nlohmann::basic_json<> callbacks = *parsed.find("callbacks"); + for (auto &array : callbacks.items()) { + auto callback = array.value().begin(); + for (auto &functions : callback.value().items()) { + CallbackHandler::add_callback( + Callback(m, callback.key(), functions.value())); + } + } +} + +// Stop the debugger +void Debugger::stop() { + if (this->channel != nullptr) { + this->channel->close(); + this->channel = nullptr; + } +} diff --git a/src/Debug/debugger.h b/src/Debug/debugger.h index d0ee12c4..cfa99646 100644 --- a/src/Debug/debugger.h +++ b/src/Debug/debugger.h @@ -1,15 +1,37 @@ #pragma once +#include +#include #include // std::queue #include #include #include -struct Module; +#include "../Edward/proxy.h" +#include "../Edward/proxy_supervisor.h" +#include "../Utils/sockets.h" -enum RunningState { WARDUINOrun, WARDUINOpause, WARDUINOstep }; +#ifndef ARDUINO +#include +#endif +struct Module; +struct Block; +struct StackValue; + +enum RunningState { + WARDUINOrun, + WARDUINOpause, + WARDUINOstep, + PROXYrun, // Running state used when executing a proxy call. During + // this state the call is set up and executed by the main + // loop. After execution, the state is restored to + // PROXYhalt + PROXYhalt // Do not run the program (program runs on computer, which + // sends messages for primitives, do forward interrupts) +}; enum InterruptTypes { + // Remote Debugging interruptRUN = 0x01, interruptHALT = 0x02, interruptPAUSE = 0x03, @@ -20,7 +42,23 @@ enum InterruptTypes { interruptDUMPLocals = 0x11, interruptDUMPFull = 0x12, interruptUPDATEFun = 0x20, - interruptUPDATELocal = 0x21 + interruptUPDATELocal = 0x21, + + // Pull Debugging + interruptWOODDUMP = 0x60, + interruptOffset = 0x61, + interruptRecvState = 0x62, + interruptMonitorProxies = 0x63, + interruptProxyCall = 0x64, + interruptProxify = 0x65, // wifi SSID \0 wifi PASS \0 + + // Push Debugging + interruptDUMPAllEvents = 0x70, + interruptDUMPEvents = 0x71, + interruptPOPEvent = 0x72, + interruptPUSHEvent = 0x73, + interruptDUMPCallbackmapping = 0x74, + interruptRecvCallbackmapping = 0x75 }; class Debugger { @@ -28,15 +66,24 @@ class Debugger { std::deque debugMessages = {}; // Help variables + volatile bool interruptWrite{}; volatile bool interruptRead{}; bool interruptEven = true; uint8_t interruptLastChar{}; std::vector interruptBuffer; long interruptSize{}; + bool receivingData = false; + + Proxy *proxy = nullptr; // proxy module for debugger + + bool connected_to_proxy = false; + pthread_mutex_t supervisor_mutex; // Private methods + void printValue(StackValue *v, uint32_t idx, bool end) const; + // TODO Move parsing to WARDuino class? uint8_t *parseDebugBuffer(size_t len, const uint8_t *buff); @@ -44,7 +91,7 @@ class Debugger { void handleInterruptRUN(Module *m, RunningState *program_state); - void handleInterruptBP(uint8_t *interruptData); + void handleInterruptBP(const uint8_t *interruptData); //// Information dumps @@ -52,26 +99,51 @@ class Debugger { void dumpLocals(Module *m) const; - void dumpBreakpoints(Module *m) const; + void dumpBreakpoints() const; void dumpFunctions(Module *m) const; void dumpCallstack(Module *m) const; + void dumpEvents(long start, long size) const; + + void dumpCallbackmapping() const; + //// Handle live code update - bool handleChangedFunction(Module *m, uint8_t *bytes); + static bool handleChangedFunction(Module *m, uint8_t *bytes); bool handleChangedLocal(Module *m, uint8_t *bytes) const; + //// Handle out-of-place debugging + + void freeState(Module *m, uint8_t *interruptData); + + static uint8_t *findOpcode(Module *m, Block *block); + + bool saveState(Module *m, uint8_t *interruptData); + + static uintptr_t readPointer(uint8_t **data); + + static void updateCallbackmapping(Module *m, const char *interruptData); + public: - int socket; + // Public fields + Channel *channel; + ProxySupervisor *supervisor = nullptr; std::set breakpoints = {}; // Vector, we expect few breakpoints uint8_t *skipBreakpoint = nullptr; // Breakpoint to skip in the next interpretation step - explicit Debugger(int socket); + // Constructor + explicit Debugger(Channel *duplex); + + void setChannel(Channel *duplex); + + // Public methods + + void stop(); // Interrupts @@ -89,5 +161,36 @@ class Debugger { bool isBreakpoint(uint8_t *loc); - void notifyBreakpoint(uint8_t *pc_ptr); + void notifyBreakpoint(uint8_t *pc_ptr) const; + + // Out-of-place debugging + + void woodDump(Module *m); + + void proxify(); + + void handleProxyCall(Module *m, RunningState *program_state, + uint8_t *interruptData); + + RFC *topProxyCall(); + + void sendProxyCallResult(Module *m); + + bool isProxied(uint32_t fidx) const; + + void startProxySupervisor(Channel *socket); + + bool proxy_connected() const; + + void disconnect_proxy(); + + // Pull-based + + void handleMonitorProxies(Module *m, uint8_t *interruptData); + + // Push-based + + void notifyPushedEvent() const; + + bool handlePushedEvent(char *bytes) const; }; diff --git a/src/Edward/RFC.cpp b/src/Edward/RFC.cpp new file mode 100644 index 00000000..8bcf131e --- /dev/null +++ b/src/Edward/RFC.cpp @@ -0,0 +1,4 @@ +#include "RFC.h" + +RFC::RFC(uint32_t id, Type *t_type, StackValue *t_args) + : fidx(id), args(t_args), type(t_type) {} diff --git a/src/Edward/RFC.h b/src/Edward/RFC.h new file mode 100644 index 00000000..2ed7b9c8 --- /dev/null +++ b/src/Edward/RFC.h @@ -0,0 +1,24 @@ +#pragma once + +#include +struct StackValue; +struct Type; + +struct SerializeData { + const unsigned char *raw; + uint32_t size; +}; + +class RFC { + public: + const uint32_t fidx; + StackValue *args; + const Type *type; + StackValue *result; + + bool success = true; + char *exception; + uint16_t exception_size; + + RFC(uint32_t id, Type *t_type, StackValue *t_args = nullptr); +}; diff --git a/src/Edward/proxy.cpp b/src/Edward/proxy.cpp new file mode 100644 index 00000000..b03dbc60 --- /dev/null +++ b/src/Edward/proxy.cpp @@ -0,0 +1,165 @@ +#include "proxy.h" + +#include +#include +#include +#include +#include + +#include "../Interpreter/instructions.h" +#include "../Utils/macros.h" +#include "../Utils/util.h" + +// TODO tests with exceptions +////TODO test with many args proxy +////TODO test with no return proxy + +void arguments_copy(unsigned char *, StackValue *, uint32_t); +char *printValue(StackValue *v); +/* + * Proxy methods + */ + +Proxy::Proxy() = default; + +void Proxy::pushRFC(Module *m, RFC *rfc) { + // keep RFC in queue + this->calls->push(rfc); + + // push RFC arguments to stack + this->setupCalleeArgs(m, rfc); + + if (rfc->fidx < m->import_count) { + // execute primitives directly + ((Primitive)m->functions[rfc->fidx].func_ptr)(m); + // send result directly + m->warduino->program_state = PROXYhalt; + m->warduino->debugger->sendProxyCallResult(m); + return; + } + + // push proxy guard block to stack + this->pushProxyGuard(m); + // push function to stack + setup_call(m, rfc->fidx); + + m->warduino->program_state = PROXYrun; +} + +RFC *Proxy::topRFC() { return this->calls->top(); } + +void Proxy::returnResult(Module *m) { + RFC *rfc = this->calls->top(); + + // remove call from lifo queue + this->calls->pop(); + + if (!rfc->success) { + // TODO exception msg + WARDuino::instance()->debugger->channel->write(R"({"success":false})"); + return; + } + + if (rfc->type->result_count == 0) { + // reading result from stack + WARDuino::instance()->debugger->channel->write(R"({"success":true})"); + return; + } + + // send the result to the client + rfc->result = &m->stack[m->sp]; + char *val = printValue(rfc->result); + WARDuino::instance()->debugger->channel->write(R"({"success":true,%s})", + val); + free(val); +} + +char *printValue(StackValue *v) { + char *buff = (char *)malloc(256); + switch (v->value_type) { + case I32: + snprintf(buff, 255, R"("type":%d,"value":%)" PRIi32, I32, + v->value.uint32); + break; + case I64: + snprintf(buff, 255, R"("type":%d,"value":%)" PRIi64, I64, + v->value.uint64); + break; + case F32: + snprintf(buff, 255, R"("type":%d,"value":%.7f)", F32, v->value.f32); + break; + case F64: + snprintf(buff, 255, R"("type":%d,"value":%.7f)", F64, v->value.f64); + break; + default: + snprintf(buff, 255, R"("type":%02x,"value":%)" PRIx64, + v->value_type, v->value.uint64); + } + return buff; +} + +StackValue *Proxy::readRFCArgs(Block *func, uint8_t *data) { + if (func->type->param_count == 0) { + printf("ProxyFunc %" PRIu32 "takes no arg\n", func->fidx); + return nullptr; + } + + auto *args = new StackValue[func->type->param_count]; + uint32_t *params = func->type->params; + for (uint32_t i = 0; i < func->type->param_count; i++) { + args[i].value.uint64 = 0; // init whole union to 0 + args[i].value_type = params[i]; + + switch (params[i]) { + case I32: { + memcpy(&args[i].value.uint32, data, sizeof(uint32_t)); + data += sizeof(uint32_t); + printf("arg %" PRIu32 ": i32 value %" PRIu32 "\n", i, + args[i].value.uint32); + break; + } + case F32: { + memcpy(&args[i].value.f32, data, sizeof(float)); + data += sizeof(float); + printf("arg %" PRIu32 ": F32 value %.7f \n", i, + args[i].value.f32); + break; + } + case I64: { + memcpy(&args[i].value.uint64, data, sizeof(uint64_t)); + data += sizeof(uint64_t); + printf("arg %" PRIu32 ": I64 value %" PRIu64 "\n", i, + args[i].value.uint64); + break; + } + case F64: { + memcpy(&args[i].value.f64, data, sizeof(double)); + data += sizeof(double); + printf("arg %" PRIu32 ": f64 value %.7f \n", i, + args[i].value.f64); + break; + } + default: { + FATAL("incorrect argument type: %" PRIu32 "\n", params[i]); + break; + } + } + } + return args; +} + +void Proxy::setupCalleeArgs(Module *m, RFC *callee) { + // adding arguments to the stack + StackValue *args = callee->args; + for (uint32_t i = 0; i < callee->type->param_count; i++) + m->stack[++m->sp] = args[i]; +} + +void Proxy::pushProxyGuard(Module *m) { + if (m == nullptr) { + return; + } + auto *guard = new Block(); + guard->block_type = 255; // 0xfe proxy guard + push_block(m, guard, m->sp); +} diff --git a/src/Edward/proxy.h b/src/Edward/proxy.h new file mode 100644 index 00000000..20993f50 --- /dev/null +++ b/src/Edward/proxy.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include +#include + +#include "RFC.h" +struct Module; +struct Block; + +class Proxy { + private: + std::stack *calls = new std::stack(); // lifo queue + + void setupCalleeArgs(Module *m, RFC *callee); + void pushProxyGuard(Module *m); + + public: + Proxy(); + + void pushRFC(Module *m, RFC *rfc); + RFC *topRFC(); + void returnResult(Module *m); + + // Server side ( arduino side ) + static StackValue *readRFCArgs(Block *func, uint8_t *data); +}; diff --git a/src/Edward/proxy_supervisor.cpp b/src/Edward/proxy_supervisor.cpp new file mode 100644 index 00000000..7a15f026 --- /dev/null +++ b/src/Edward/proxy_supervisor.cpp @@ -0,0 +1,281 @@ +#include "proxy_supervisor.h" + +#include +#include + +#include +#include +#include +#include +#ifndef ARDUINO +#include +#else +#include "../../lib/json/single_include/nlohmann/json.hpp" +#endif + +#include "../Utils/macros.h" +#include "../Utils/util.h" + +// TODO exception msg +const char SUCCESS[] = ""; // Empty denotes success +const char NO_HOST_ERR[] = "No host and port set"; +const char CREATE_SOCK_ERR[] = "Could not create Socket"; +const char INVALID_HOST[] = "Invalid host"; +const char CONNECT_ERR[] = "Socket failed to connect"; +const char WRITE_ERR[] = "ERROR writing to socket"; +const char READ_ERR[] = "ERROR reading from socket"; + +bool is_success(const char *msg) { + return (msg != nullptr) && (msg[0] == '\0'); // check if string is empty +} + +bool continuing(pthread_mutex_t *mutex) { + switch (pthread_mutex_trylock(mutex)) { + case 0: /* if we got the lock, unlock and return false */ + pthread_mutex_unlock(mutex); + return false; + case EBUSY: /* return true if the mutex was locked */ + default: + return true; + } +} + +void *runSupervisor(void *input) { + // Print value received as argument: + dbg_info("\n=== LISTENING TO SOCKET (in separate thread) ===\n"); + auto *supervisor = (ProxySupervisor *)input; + supervisor->listenToSocket(); + pthread_exit(nullptr); +} + +Event *parseJSON(char *buff) { + // TODO duplicate code in Debugger::handlePushedEvent + nlohmann::basic_json<> parsed = nlohmann::json::parse(buff); + printf("parseJSON: %s\n", parsed.dump().c_str()); + std::string payload = *parsed.find("payload"); + return new Event(*parsed.find("topic"), payload); +} + +ProxySupervisor::ProxySupervisor(Channel *duplex, pthread_mutex_t *mutex) { + debug("Starting supervisor.\n"); + this->channel = duplex; + this->mutex = mutex; + this->proxyResult = nullptr; + + pthread_create(&this->threadid, nullptr, runSupervisor, this); +} + +bool isEvent(nlohmann::basic_json<> parsed) { + return parsed.find("topic") != parsed.end(); +} + +bool isReply(nlohmann::basic_json<> parsed) { + return parsed.find("success") != parsed.end(); +} + +void ProxySupervisor::listenToSocket() { + char _char; + uint32_t buf_idx = 0; + const uint32_t start_size = 1024; + uint32_t current_size = start_size; + char *buffer = (char *)malloc(start_size); + + dbg_info("Proxy supervisor listening to remote device...\n"); + while (continuing(this->mutex)) { + if (this->channel->read(&_char, 1) != -1) { + // increase buffer size if needed + if (current_size <= (buf_idx + 1)) { + char *new_buff = (char *)malloc(current_size + start_size); + memcpy((void *)new_buff, (void *)buffer, current_size); + free(buffer); + buffer = new_buff; + current_size += start_size; + printf("increasing PushClient's buffer size to %" PRId32 "\n", + current_size); + } + buffer[buf_idx++] = _char; + // manual null-termination is needed because parseJSON does not use + // first len argument + buffer[buf_idx] = '\0'; + try { + nlohmann::basic_json<> parsed = nlohmann::json::parse(buffer); + debug("parseJSON: %s\n", parsed.dump().c_str()); + + if (isEvent(parsed)) { + CallbackHandler::push_event(new Event( + *parsed.find("topic"), *parsed.find("payload"))); + WARDuino::instance()->debugger->notifyPushedEvent(); + } + + if (isReply(parsed)) { + this->hasReplied = true; + this->proxyResult = parsed; + } + + buf_idx = 0; + } catch (const nlohmann::detail::parse_error &e) { + if (_char == '\n') { + // discard buffer + buf_idx = 0; + } + } + } + } + dbg_info("Proxy supervisor shutting down.\n"); +} + +bool ProxySupervisor::send( + void *buffer, int size) { // TODO buffer needs to be null terminated + int n = this->channel->write(static_cast(buffer)); + return n == size; +} + +nlohmann::basic_json<> ProxySupervisor::readReply() { + while (!this->hasReplied) + ; + WARDuino::instance()->debugger->channel->write("read reply: succeeded\n"); + this->hasReplied = false; + return this->proxyResult; +} + +pthread_t ProxySupervisor::getThreadID() { return this->threadid; } + +/* + * returns the quantity of bytes needed to serialize a Proxy. + * The size includes: Interrupt + Id of the function + parameters + */ +unsigned short int sizeSerializationRFC(const Type *type) { + short unsigned int paramSize = 0; + for (uint32_t i = 0; i < type->param_count; i++) + paramSize += sizeof_valuetype(type->params[i]); + + return 1 + sizeof(uint32_t) + paramSize; +} + +void arguments_copy(unsigned char *dest, StackValue *args, + uint32_t param_count) { + uint32_t offset = 0; + for (uint32_t i = 0; i < param_count; i++) { + switch (args[i].value_type) { + case I32: { + memcpy(dest + offset, &args[i].value.uint32, sizeof(uint32_t)); + offset += sizeof(uint32_t); + break; + } + case F32: { + memcpy(dest + offset, &args[i].value.f32, sizeof(float)); + offset += sizeof(float); + break; + } + case I64: { + memcpy(dest + offset, &args[i].value.uint64, sizeof(uint64_t)); + offset += sizeof(uint64_t); + break; + } + case F64: { + memcpy(dest + offset, &args[i].value.f64, sizeof(double)); + offset += sizeof(double); + break; + } + default: + FATAL("incorrect StackValue type\n"); + break; + } + } +} + +/* + * + * output: 1 byte (interrupt code) | 4 bytes (fidx) | sizeof(arg_1) arg_1 ... + sizeof(arg_n) arg_n + + * Output is also transformed to hexa + * + */ +struct SerializeData *ProxySupervisor::serializeRFC(RFC *callee) { + const unsigned short serializationSize = sizeSerializationRFC(callee->type); + auto *buffer = new unsigned char[serializationSize]; + + // write to array: interrupt, function identifier and arguments + const unsigned char interrupt = interruptProxyCall; + memcpy(buffer, &interrupt, sizeof(unsigned char)); + memcpy(buffer + 1, &callee->fidx, sizeof(uint32_t)); + arguments_copy(buffer + 5, callee->args, callee->type->param_count); + + // array as hexa + const uint32_t hexa_size = serializationSize * 2; + auto *hexa = + new unsigned char[hexa_size + 2]; //+2 for '\n' and '0' termination + chars_as_hexa(hexa, buffer, serializationSize); + hexa[hexa_size] = '\n'; + hexa[hexa_size + 1] = '\0'; // TODO remove zero termination and +2 above + + delete[] buffer; + auto *ser = new SerializeData; + ser->size = hexa_size + 1; + ser->raw = hexa; + return ser; +} + +void ProxySupervisor::deserializeRFCResult(RFC *rfc) { + nlohmann::basic_json<> call_result = this->readReply(); // blocking + rfc->success = *call_result.find("success"); + + if (rfc->type->result_count == 0) { + return; + } + + // if (!rfc->success) { + // uint16_t msg_size = 0; + // memcpy(&msg_size, call_result + 1, sizeof(uint16_t)); + // if (msg_size > rfc->exception_size) { + // delete[] rfc->exception; + // rfc->exception = new char[msg_size]; + // rfc->exception_size = msg_size; + // } + // memcpy(rfc->exception, call_result + 1 + sizeof(uint16_t), + // msg_size); return; + // } + + uint8_t type = *call_result.find("type"); + auto *result = (StackValue *)malloc(sizeof(struct StackValue)); + result->value_type = type; + result->value = {*call_result.find("value")}; + rfc->result = result; +} + +bool ProxySupervisor::call(RFC *callee) { + struct SerializeData *rfc_request = this->serializeRFC(callee); + + bool sent = this->send((void *)rfc_request->raw, rfc_request->size); + if (!sent) { + callee->success = false; + + delete[] rfc_request->raw; + delete rfc_request; + dbg_trace("Sending RFC: FAILED\n"); + return false; + } + // Fetch new callback mapping + // convert message to hex TODO: move to proxyserver + char cmdBuffer[10] = ""; + int cmdBufferLen = 0; + sprintf(cmdBuffer, "%x\n%n", interruptDUMPCallbackmapping, &cmdBufferLen); + WARDuino::instance()->debugger->supervisor->send(cmdBuffer, cmdBufferLen); + this->deserializeRFCResult(callee); + return true; +} + +void ProxySupervisor::registerProxiedCall(uint32_t fidx) { + this->proxied->insert(fidx); +} + +void ProxySupervisor::unregisterProxiedCall(uint32_t fidx) { + this->proxied->erase(fidx); +} + +void ProxySupervisor::unregisterAllProxiedCalls() { this->proxied->clear(); } + +bool ProxySupervisor::isProxied(uint32_t fidx) { + return this->proxied->count(fidx) > 0; +} diff --git a/src/Edward/proxy_supervisor.h b/src/Edward/proxy_supervisor.h new file mode 100644 index 00000000..33e40b9b --- /dev/null +++ b/src/Edward/proxy_supervisor.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +#include "../Utils/sockets.h" +#include "RFC.h" +#ifndef ARDUINO +#include +#else +#include "../../lib/json/single_include/nlohmann/json.hpp" +#endif +#include "pthread.h" +#include "sys/types.h" + +class ProxySupervisor { + private: + Channel *channel; + pthread_t threadid; + pthread_mutex_t *mutex; + std::set *proxied = new std::set(); + + bool hasReplied = false; + nlohmann::basic_json<> proxyResult; + + struct SerializeData *serializeRFC(RFC *callee); + void deserializeRFCResult(RFC *rfc); + + public: + ProxySupervisor(Channel *duplex, pthread_mutex_t *mutex); + + void listenToSocket(); + + bool send(void *t_buffer, int t_size); + nlohmann::basic_json<> readReply(); + + pthread_t getThreadID(); + + bool call(RFC *callee); + + void registerProxiedCall(uint32_t fidx); + void unregisterProxiedCall(uint32_t fidx); + void unregisterAllProxiedCalls(); + bool isProxied(uint32_t fidx); +}; diff --git a/src/Interpreter/instructions.cpp b/src/Interpreter/instructions.cpp index aef60494..da68d394 100644 --- a/src/Interpreter/instructions.cpp +++ b/src/Interpreter/instructions.cpp @@ -3,7 +3,6 @@ #include #include -#include "../Debug/debugger.h" #include "../Memory/mem.h" #include "../Utils/macros.h" #include "../Utils/util.h" @@ -29,6 +28,19 @@ Block *pop_block(Module *m) { Frame *frame = &m->callstack[m->csp--]; Type *t = frame->block->type; + if (frame->block->block_type == 0xff) { + CallbackHandler::resolving_event = false; + frame = &m->callstack[m->csp--]; + t = frame->block->type; + } + + if (frame->block->block_type == 0xfe) { + m->warduino->program_state = PROXYhalt; + m->warduino->debugger->sendProxyCallResult(m); + frame = &m->callstack[m->csp--]; + t = frame->block->type; + } + // TODO: validate return value if there is one m->fp = frame->fp; // Restore frame pointer @@ -69,15 +81,16 @@ void setup_call(Module *m, uint32_t fidx) { // Push current frame on the call stack push_block(m, func, m->sp - type->param_count); - if (TRACE) { - dbg_warn(" >> fn0x%x(%d) %s(", fidx, fidx, - func->export_name ? func->export_name : ""); - for (int p = ((int)type->param_count) - 1; p >= 0; p--) { - dbg_warn("%s%s", value_repr(&m->stack[m->sp - p]), p ? " " : ""); - } - dbg_warn("), %d locals, %d results\n", func->local_count, - type->result_count); +#if TRACE + dbg_warn(" >> fn0x%x(%d) %s(", fidx, fidx, + func->export_name + ? func->export_name + : "") for (int p = ((int)type->param_count) - 1; p >= 0; p--) { + dbg_warn("%s%s", value_repr(&m->stack[m->sp - p]), p ? " " : ""); } + dbg_warn("), %d locals, %d results\n", func->local_count, + type->result_count); +#endif // Push locals (dropping extras) m->fp = m->sp - ((int)type->param_count) + 1; @@ -94,6 +107,37 @@ void setup_call(Module *m, uint32_t fidx) { m->pc_ptr = func->start_ptr; } +// performs proxy calls to an MCU +bool proxy_call(Module *m, uint32_t fidx) { + dbg_info("Remote Function Call %d\n", fidx); + ProxySupervisor *supervisor = m->warduino->debugger->supervisor; + RFC *rfc; + Type *type = m->functions[fidx].type; + if (type->param_count > 0) { + m->sp -= type->param_count; + StackValue *args = &m->stack[m->sp + 1]; + rfc = new RFC(fidx, type, args); + } else { + rfc = new RFC(fidx, type); + } + + if (!supervisor->call(rfc)) { + dbg_info(": FAILED TO SEND\n", fidx); + return false; + } + + if (!rfc->success) { + // TODO exception bugger might be too small and msg not null terminated? + memcpy(&exception, rfc->exception, strlen(rfc->exception)); + return false; + } + + if (rfc->type->result_count > 0) { + m->stack[++m->sp] = *rfc->result; + } + return true; +} + /* * WebAssembly Instructions * @@ -199,10 +243,10 @@ bool i_instr_if(Module *m, uint8_t *block_ptr) { } } // if true, keep going - if (TRACE) { - debug(" - cond: 0x%x jump to 0x%x, block: %s\n", cond, - (uint32_t)(m->pc_ptr - m->bytes), block_repr(block)); - } +#if TRACE + debug(" - cond: 0x%x jump to 0x%x, block: %s\n", cond, + (uint32_t)(m->pc_ptr - m->bytes), block_repr(block)); +#endif return true; } @@ -212,9 +256,9 @@ bool i_instr_if(Module *m, uint8_t *block_ptr) { bool i_instr_else(Module *m) { Block *block = m->callstack[m->csp].block; m->pc_ptr = block->br_ptr; - if (TRACE) { - debug(" - of %s jump to 0x%p\n", block_repr(block), m->pc_ptr); - } +#if TRACE + debug(" - of %s jump to 0x%p\n", block_repr(block), m->pc_ptr); +#endif return true; } @@ -226,17 +270,16 @@ bool i_instr_end(Module *m, bool *prog_done) { if (block == nullptr) { return false; // an exception (set by pop_block) } - if (TRACE) { - debug(" - of %s\n", block_repr(block)); - } +#if TRACE + debug(" - of %s\n", block_repr(block)); +#endif if (block->block_type == 0x00) { // Function - if (TRACE) { - dbg_warn(" << fn0x%x(%d) %s = %s\n", block->fidx, block->fidx, - block->export_name ? block->export_name : "", - block->type->result_count > 0 - ? value_repr(&m->stack[m->sp]) - : "_"); - } +#if TRACE + dbg_warn( + " << fn0x%x(%d) %s = %s\n", block->fidx, block->fidx, + block->export_name ? block->export_name : "", + block->type->result_count > 0 ? value_repr(&m->stack[m->sp]) : "_"); +#endif if (m->csp == -1) { // Return to top-level *prog_done = true; @@ -261,9 +304,9 @@ bool i_instr_br(Module *m) { m->csp -= depth; // set to end for pop_block m->pc_ptr = m->callstack[m->csp].block->br_ptr; - if (TRACE) { - debug(" - to: 0x%p\n", m->pc_ptr); - } +#if TRACE + debug(" - to: 0x%p\n", m->pc_ptr); +#endif return true; } @@ -279,11 +322,10 @@ bool i_instr_br_if(Module *m) { // set to end for pop_block m->pc_ptr = m->callstack[m->csp].block->br_ptr; } - if (TRACE) { - debug(" - depth: 0x%x, cond: 0x%x, to: 0x%p\n", depth, cond, - m->pc_ptr); - } - +#if TRACE + debug(" - depth: 0x%x, cond: 0x%x, to: 0x%p\n", depth, cond, + m->pc_ptr); +#endif return true; } @@ -294,7 +336,7 @@ bool i_instr_br_table(Module *m) { uint32_t count = read_LEB_32(&m->pc_ptr); if (count > BR_TABLE_SIZE) { // TODO: check this prior to runtime - sprintf(exception, "br_table size %d exceeds max %d\n", count, + sprintf(exception, "br_table size %" PRIu32 " exceeds max %d\n", count, BR_TABLE_SIZE); return false; } @@ -311,10 +353,9 @@ bool i_instr_br_table(Module *m) { m->csp -= depth; // set to end for pop_block m->pc_ptr = m->callstack[m->csp].block->br_ptr; - if (TRACE) { - debug(" - count: %d, didx: %d, to: 0x%p\n", count, didx, - m->pc_ptr); - } +#if TRACE + debug(" - count: %d, didx: %d, to: 0x%p\n", count, didx, m->pc_ptr); +#endif return true; } @@ -328,9 +369,9 @@ bool i_instr_return(Module *m) { // Set the program count to the end of the function // The actual pop_block and return is handled by the end opcode. m->pc_ptr = m->callstack[0].block->end_ptr; - if (TRACE) { - debug(" - to: 0x%p\n", m->pc_ptr); - } +#if TRACE + debug(" - to: 0x%p\n", m->pc_ptr); +#endif return true; } @@ -339,6 +380,11 @@ bool i_instr_return(Module *m) { */ bool i_instr_call(Module *m) { uint32_t fidx = read_LEB_32(&m->pc_ptr); + + if (m->warduino->debugger->isProxied(fidx)) { + return proxy_call(m, fidx); + } + if (fidx < m->import_count) { return ((Primitive)m->functions[fidx].func_ptr)(m); } else { @@ -347,10 +393,9 @@ bool i_instr_call(Module *m) { return false; } setup_call(m, fidx); // regular function call - if (TRACE) { - debug(" - calling function fidx: %d at: 0x%p\n", fidx, - m->pc_ptr); - } +#if TRACE + debug(" - calling function fidx: %d at: 0x%p\n", fidx, m->pc_ptr); +#endif } return true; } @@ -366,25 +411,26 @@ bool i_instr_call_indirect(Module *m) { if (m->options.mangle_table_index) { // val is the table address + the index (not sized for the // pointer size) so get the actual (sized) index - if (TRACE) { - debug(" - entries: %p, original val: 0x%x, new val: 0x%x\n", - m->table.entries, val, - (uint32_t)((uint64_t)m->table.entries) - val); - } +#if TRACE + debug(" - entries: %p, original val: 0x%x, new val: 0x%x\n", + m->table.entries, val, + (uint32_t)((uint64_t)m->table.entries) - val); +#endif // val = val - (uint32_t)((uint64_t)m->table.entries & 0xFFFFFFFF); val = val - (uint32_t)((uint64_t)m->table.entries); } if (val >= m->table.maximum) { - sprintf(exception, "undefined element 0x%x (max: 0x%x) in table", val, - m->table.maximum); + sprintf(exception, + "undefined element 0x%" PRIx32 " (max: 0x%" PRIx32 ") in table", + val, m->table.maximum); return false; } uint32_t fidx = m->table.entries[val]; - if (TRACE) { - debug(" - call_indirect tidx: %d, val: 0x%x, fidx: 0x%x\n", tidx, - val, fidx); - } +#if TRACE + debug(" - call_indirect tidx: %d, val: 0x%x, fidx: 0x%x\n", tidx, val, + fidx); +#endif if (fidx < m->import_count) { // THUNK thunk_out(m, fidx); // import/thunk call @@ -420,19 +466,19 @@ bool i_instr_call_indirect(Module *m) { } } - if (TRACE) { - debug( - " - tidx: %d, table idx: %d, " - "calling function fidx: %d at: 0x%p\n", - tidx, val, fidx, m->pc_ptr); - } +#if TRACE + debug( + " - tidx: %d, table idx: %d, " + "calling function fidx: %d at: 0x%p\n", + tidx, val, fidx, m->pc_ptr); +#endif } return true; } /** * 0x1a drop - * remvove a value from the stack + * remove a value from the stack */ bool i_instr_drop(Module *m) { m->sp--; @@ -463,10 +509,10 @@ bool i_instr_select(Module *m) { */ bool i_instr_get_local(Module *m) { int32_t arg = read_LEB_32(&m->pc_ptr); - if (TRACE) { - debug(" - arg: 0x%x, got %s\n", arg, - value_repr(&m->stack[m->fp + arg])); - } +#if TRACE + debug(" - arg: 0x%x, got %s\n", arg, + value_repr(&m->stack[m->fp + arg])); +#endif m->stack[++m->sp] = m->stack[m->fp + arg]; return true; } @@ -477,10 +523,10 @@ bool i_instr_get_local(Module *m) { bool i_instr_set_local(Module *m) { int32_t arg = read_LEB_32(&m->pc_ptr); m->stack[m->fp + arg] = m->stack[m->sp--]; - if (TRACE) { - debug(" - arg: 0x%x, to %s (stack loc: %d)\n", arg, - value_repr(&m->stack[m->sp + 1]), m->fp + arg); - } +#if TRACE + debug(" - arg: 0x%x, to %s (stack loc: %d)\n", arg, + value_repr(&m->stack[m->sp + 1]), m->fp + arg); +#endif return true; } @@ -490,9 +536,9 @@ bool i_instr_set_local(Module *m) { bool i_instr_tee_local(Module *m) { int32_t arg = read_LEB_32(&m->pc_ptr); m->stack[m->fp + arg] = m->stack[m->sp]; - if (TRACE) { - debug(" - arg: 0x%x, to %s\n", arg, value_repr(&m->stack[m->sp])); - } +#if TRACE + debug(" - arg: 0x%x, to %s\n", arg, value_repr(&m->stack[m->sp])); +#endif return true; } @@ -501,9 +547,9 @@ bool i_instr_tee_local(Module *m) { */ bool i_instr_get_global(Module *m) { int32_t arg = read_LEB_32(&m->pc_ptr); - if (TRACE) { - debug(" - arg: 0x%x, got %s\n", arg, value_repr(&m->globals[arg])); - } +#if TRACE + debug(" - arg: 0x%x, got %s\n", arg, value_repr(&m->globals[arg])); +#endif m->stack[++m->sp] = m->globals[arg]; return true; } @@ -514,10 +560,9 @@ bool i_instr_get_global(Module *m) { bool i_instr_set_global(Module *m) { uint32_t arg = read_LEB_32(&m->pc_ptr); m->globals[arg] = m->stack[m->sp--]; - if (TRACE) { - debug(" - arg: 0x%x, got %s\n", arg, - value_repr(&m->stack[m->sp + 1])); - } +#if TRACE + debug(" - arg: 0x%x, got %s\n", arg, value_repr(&m->stack[m->sp + 1])); +#endif return true; } @@ -1464,35 +1509,36 @@ bool interpret(Module *m) { // set to true when finishes successfully bool program_done = false; - // TODO: this is actually a property of warduino - RunningState program_state = WARDUINOrun; + m->warduino->program_state = WARDUINOrun; while (!program_done && success) { - if (program_state == WARDUINOstep) { - program_state = WARDUINOpause; + if (m->warduino->program_state == WARDUINOstep) { + m->warduino->program_state = WARDUINOpause; } - while (m->warduino->debugger->checkDebugMessages(m, &program_state)) { - }; + while (m->warduino->debugger->checkDebugMessages( + m, &m->warduino->program_state)) { + } fflush(stdout); reset_wdt(); - if (program_state == WARDUINOpause) { + // Resolve 1 callback event if queue is not empty and VM not paused, and + // no event currently resolving + CallbackHandler::resolve_event(); + + // Skip the main loop if paused or drone + if (m->warduino->program_state == WARDUINOpause || + m->warduino->program_state == PROXYhalt) { continue; } // Program state is not paused - // Resolve 1 callback event if queue is not empty and no event currently - // resolving - // if (!CallbackHandler::resolving_event) { - CallbackHandler::resolve_event(); - // } - - // if BP and not the one we just unpaused + // If BP and not the one we just unpaused if (m->warduino->debugger->isBreakpoint(m->pc_ptr) && - m->warduino->debugger->skipBreakpoint != m->pc_ptr) { - program_state = WARDUINOpause; + m->warduino->debugger->skipBreakpoint != m->pc_ptr && + m->warduino->program_state != PROXYrun) { + m->warduino->program_state = WARDUINOpause; m->warduino->debugger->notifyBreakpoint(m->pc_ptr); continue; } @@ -1553,10 +1599,10 @@ bool interpret(Module *m) { success &= i_instr_call(m); continue; } - case 0x11: // call_indirect + case 0x11: { // call_indirect success &= i_instr_call_indirect(m); continue; - + } // // Parametric operators // @@ -1692,9 +1738,17 @@ bool interpret(Module *m) { } } + if (m->warduino->program_state == PROXYrun) { + dbg_info("Trap was thrown during proxy call.\n"); + RFC *rfc = m->warduino->debugger->topProxyCall(); + rfc->success = false; + rfc->exception = strdup(exception); + rfc->exception_size = strlen(exception); + m->warduino->debugger->sendProxyCallResult(m); + } + // Resolve all unhandled callback events - while (/*!CallbackHandler::resolving_event &&*/ - CallbackHandler::resolve_event()) + while (CallbackHandler::resolving_event && CallbackHandler::resolve_event()) ; dbg_trace("Interpretation ended %s with status %s\n", diff --git a/src/Primitives/primitives.cpp b/src/Primitives/arduino.cpp similarity index 72% rename from src/Primitives/primitives.cpp rename to src/Primitives/arduino.cpp index c84d7c56..9d162948 100644 --- a/src/Primitives/primitives.cpp +++ b/src/Primitives/arduino.cpp @@ -9,8 +9,10 @@ * 4) Extend the install_primitives function * */ -#include "primitives.h" +#include "Arduino.h" +#include +#include #include #include @@ -20,12 +22,7 @@ #include "../Memory/mem.h" #include "../Utils/macros.h" #include "../Utils/util.h" - -#ifdef ARDUINO -#include -#include - -#include "Arduino.h" +#include "primitives.h" // NEOPIXEL #include @@ -38,7 +35,7 @@ Adafruit_NeoPixel pixels = #include SPIClass *spi = new SPIClass(); -// Hardeware SPI +// Hardware SPI void write_spi_byte(unsigned char c) { spi->beginTransaction(SPISettings(20000000, MSBFIRST, SPI_MODE0)); spi->transfer(c); @@ -55,29 +52,93 @@ void write_spi_bytes_16_prim(int times, uint32_t color) { spi->endTransaction(); } -#else +// Hardware Interrupts -#include -#include +#define ALL_ISRS 25 // number of installed ISRs -#endif +typedef struct ISREntry { + int pin; + void (*ISR_callback)(); +} ISREntry; + +ISREntry ISRs[ALL_ISRS]; +int isr_index = 0; + +/* Private macro to install an ISR */ +#define install_isr(number) \ + { \ + dbg_info("installing isr number: %d of %d with name: %s\n", \ + isr_index + 1, ALL_ISRS, isr_##number); \ + if (isr_index < ALL_ISRS) { \ + ISREntry *p = &ISRs[isr_index++]; \ + p->pin = number; \ + p->ISR_callback = &(isr_##number); \ + } else { \ + FATAL("isr_index out of bounds"); \ + } \ + } + +#define INTERRUPT_TOPIC_PREFIX "interrupt_" + +/* Private macro to create an ISR for a specific pin*/ +#define def_isr(pin) \ + void isr_##pin() { \ + CallbackHandler::push_event(INTERRUPT_TOPIC_PREFIX #pin, "", 0); \ + } + +/* Common GPIO pins on ESP32 devices:*/ +def_isr(1); +def_isr(2); +def_isr(3); +def_isr(4); +def_isr(5); +def_isr(12); +def_isr(13); +def_isr(14); +def_isr(15); +def_isr(16); +def_isr(17); +def_isr(18); +def_isr(19); +def_isr(21); +def_isr(22); +def_isr(23); +def_isr(25); +def_isr(26); +def_isr(27); +def_isr(32); +def_isr(33); +def_isr(34); +def_isr(35); +def_isr(36); +def_isr(39); + +int resolve_isr(int pin) { + debug("Resolve ISR (%d) for %i \n", ALL_ISRS, pin); + + for (int i = 0; i < ALL_ISRS; i++) { + auto &isr = ISRs[i]; + debug("Checking entry %i of %i: pin = %i \n", i, ALL_ISRS, isr.pin); + if (pin == isr.pin) { + debug("FOUND ISR\n"); + return i; + } + } + return -1; +} + +// Primitives #define NUM_PRIMITIVES 0 -#ifdef ARDUINO -#define NUM_PRIMITIVES_ARDUINO 34 -#else -#define NUM_PRIMITIVES_ARDUINO 24 -#endif +#define NUM_PRIMITIVES_ARDUINO 37 #define ALL_PRIMITIVES (NUM_PRIMITIVES + NUM_PRIMITIVES_ARDUINO) // Global index for installing primitives int prim_index = 0; -double sensor_emu = 0; - /* - Private macros to install a primitive + Private macros to install a primitive */ #define install_primitive(prim_name) \ { \ @@ -242,7 +303,6 @@ Type NoneToOneU64 = {.form = FUNC, .mask = 0x82000}; // Util function declarations -#ifdef ARDUINO void connect(const String ssid, const String password); @@ -254,12 +314,9 @@ int32_t http_post_request(Module *m, const String url, const String body, const String authorization_parsed, uint32_t response, uint32_t size); -#endif - //------------------------------------------------------ // Arduino Specific Functions //------------------------------------------------------ -#ifdef ARDUINO def_prim(abort, NoneToNoneU32) { sprintf(exception, "Trap: assertion failed."); @@ -324,7 +381,6 @@ def_prim(wifi_connected, NoneToOneU32) { def_prim(wifi_localip, twoToOneU32) { uint32_t buff = arg1.uint32; - uint32_t size = arg0.uint32; IPAddress ip = WiFi.localIP(); String ipString = String(ip[0]); @@ -509,47 +565,57 @@ def_prim(clear_pixels, NoneToNoneU32) { return true; } -// INTERRUPTS +// LED Control primitives -class Interrupt { - public: - void setup(uint8_t pin, void (*ISR_callback)(void), uint8_t mode) { - this->pin = pin; - this->mode = mode; - Serial.print("Attaching to "); - Serial.print(pin); - Serial.println(""); - attachInterrupt(digitalPinToInterrupt(pin), ISR_callback, mode); - } +def_prim(chip_analog_write, threeToNoneU32) { + uint8_t channel = arg2.uint32; + uint32_t value = arg1.uint32; + uint32_t maxValue = arg0.uint32; - void handleInterrupt(); - uint8_t pin; + // printf("chip_ledc_analog_write(%u, %u, %u)\n", channel, value, maxValue); + // calculate duty, 4095 from 2 ^ 12 - 1 + uint32_t duty = (4095 / maxValue) * min(value, maxValue); - private: - uint8_t mode; - void (*ISR_callback)(); -}; + ledcWrite(channel, duty); + pop_args(3); + return true; +} + +def_prim(chip_ledc_setup, threeToNoneU32) { + uint32_t channel = arg2.uint32; + uint32_t freq = arg1.uint32; + uint32_t ledc_timer = arg0.uint32; + // printf("chip_ledc_setup(%u, %u, %u)\n", channel, freq, ledc_timer); + ledcSetup(channel, freq, ledc_timer); + pop_args(3); + return true; +} -void Interrupt::handleInterrupt() { - String topic = "interrupt"; - topic += String(pin); - auto *empty = reinterpret_cast(""); - CallbackHandler::push_event(topic.c_str(), empty, 0); +def_prim(chip_ledc_attach_pin, twoToNoneU32) { + uint32_t pin = arg1.uint32; + uint32_t channel = arg0.uint32; + // printf("chip_ledc_attach_pin(%u,%u)\n", pin, channel); + ledcAttachPin(pin, channel); + pop_args(2); + return true; } -std::vector handlers; +// INTERRUPTS def_prim(subscribe_interrupt, threeToNoneU32) { uint8_t pin = arg2.uint32; // GPIOPin uint8_t fidx = arg1.uint32; // Callback function uint8_t mode = arg0.uint32; - Interrupt *handler = new Interrupt(); - handlers.push_back(handler); - handler->setup( - pin, [] { handlers.back()->handleInterrupt(); }, mode); + int index = resolve_isr(pin); + if (index < 0) { + dbg_info("subscribe_interrupt: no ISR found for pin %i", pin); + return false; + } + + attachInterrupt(digitalPinToInterrupt(pin), ISRs[index].ISR_callback, mode); - String callback_id = "interrupt"; + String callback_id = INTERRUPT_TOPIC_PREFIX; callback_id += String(pin); Callback c = Callback(m, callback_id.c_str(), fidx); CallbackHandler::add_callback(c); @@ -561,14 +627,7 @@ def_prim(subscribe_interrupt, threeToNoneU32) { def_prim(unsubscribe_interrupt, oneToNoneU32) { uint8_t pin = arg0.uint32; - auto it = std::remove_if( - handlers.begin(), handlers.end(), - [pin](Interrupt *handler) { return handler->pin == pin; }); - - if (it != handlers.end()) { - handlers.erase(it, handlers.end()); - detachInterrupt(digitalPinToInterrupt(pin)); - } + detachInterrupt(digitalPinToInterrupt(pin)); pop_args(1); return true; @@ -592,7 +651,7 @@ def_prim(mqtt_init, threeToNoneU32) { mqttClient.setServer(server, port); mqttClient.setCallback([](const char *topic, const unsigned char *payload, unsigned int length) { - CallbackHandler::push_event(topic, payload, length); + CallbackHandler::push_event(topic, (const char *)payload, length); }); #if DEBUG @@ -743,240 +802,9 @@ def_prim(mqtt_loop, NoneToOneU32) { return true; } -#else - -def_prim(init_pixels, NoneToNoneU32) { - printf("init_pixels \n"); - return true; -} - -def_prim(set_pixel_color, fourToOneU32) { - printf("set_pixel_color \n"); - pop_args(4); - return true; -} - -def_prim(show_pixels, NoneToNoneU32) { - printf("show pixels \n"); - return true; -} - -def_prim(clear_pixels, NoneToNoneU32) { - printf("clear pixels \n"); - return true; -} - -def_prim(abort, NoneToNoneU32) { - debug("EMU: abort\n"); - return false; -} - -def_prim(micros, NoneToOneU64) { - struct timeval tv {}; - gettimeofday(&tv, nullptr); - unsigned long micros = 1000000 * tv.tv_sec + tv.tv_usec; - pushUInt64(micros); - return true; -} - -// call callback test function (temporary) -def_prim(test, oneToNoneU32) { - uint32_t fidx = arg0.uint32; - - std::string topic = "interrupt"; - topic.append(std::to_string(fidx)); - - Callback c = Callback(m, topic, fidx); - CallbackHandler::add_callback(c); - auto *payload = reinterpret_cast("TestPayload"); - CallbackHandler::push_event(topic, payload, 11); - pop_args(1); - return true; -} - -def_prim(print_int, oneToNoneU32) { - debug("EMU: print "); - printf("%u\n", arg0.uint32); - pop_args(1); - return true; -} - -def_prim(print_string, twoToNoneU32) { - uint32_t addr = arg1.uint32; - uint32_t size = arg0.uint32; - std::string text = parse_utf8_string(m->memory.bytes, size, addr); - debug("EMU: print string at %i: ", addr); - printf("%s\n", text.c_str()); - pop_args(2); - return true; -} - -def_prim(wifi_connect, fourToNoneU32) { - uint32_t ssid = arg3.uint32; - uint32_t len0 = arg2.uint32; - uint32_t pass = arg1.uint32; - uint32_t len1 = arg0.uint32; - - std::string ssid_str = parse_utf8_string(m->memory.bytes, len0, ssid); - std::string pass_str = parse_utf8_string(m->memory.bytes, len1, pass); - debug("EMU: connect to %s with password %s\n", ssid_str.c_str(), - pass_str.c_str()); - pop_args(4); - return true; -} - -def_prim(wifi_status, NoneToOneU32) { - pushInt32(3); // return WL_CONNECTED - return true; -} - -def_prim(wifi_connected, NoneToOneU32) { - pushInt32(1); // return that we are connected - return true; -} - -def_prim(wifi_localip, twoToOneU32) { - uint32_t buff = arg1.uint32; - uint32_t size = arg0.uint32; - std::string ip = "192.168.0.181"; - - for (unsigned long i = 0; i < ip.length(); i++) { - m->memory.bytes[buff + i] = (uint32_t)ip[i]; - } - pop_args(2); - pushInt32(buff); - return true; -} - -def_prim(http_get, fourToOneU32) { - // Get arguments - uint32_t url = arg3.uint32; - uint32_t length = arg2.uint32; - int32_t response = arg1.uint32; - uint32_t size = arg0.uint32; - // Parse url - std::string text = parse_utf8_string(m->memory.bytes, length, url); - debug("EMU: http get request %s\n", text.c_str()); - // Construct response - std::string answer = "Response code: 200."; - if (answer.length() > size) { - sprintf(exception, "GET: buffer size is too small for response."); - return false; // TRAP - } - for (unsigned long i = 0; i < answer.length(); i++) { - m->memory.bytes[response + i] = (uint32_t)answer[i]; - } - - // Pop args and return response address - pop_args(4); - pushInt32(response); - return true; -} - -def_prim(http_post, tenToOneU32) { - // Get arguments - uint32_t url = arg9.uint32; - uint32_t url_len = arg8.uint32; - uint32_t body = arg7.uint32; - uint32_t body_len = arg6.uint32; - uint32_t content_type = arg5.uint32; - uint32_t content_type_len = arg4.uint32; - uint32_t authorization = arg3.uint32; - uint32_t authorization_len = arg2.uint32; - int32_t response = arg1.uint32; - uint32_t size = arg0.uint32; - - std::string url_parsed = parse_utf8_string(m->memory.bytes, url_len, url); - std::string body_parsed = - parse_utf8_string(m->memory.bytes, body_len, body); - std::string content_type_parsed = - parse_utf8_string(m->memory.bytes, content_type_len, content_type); - std::string authorization_parsed = - parse_utf8_string(m->memory.bytes, authorization_len, authorization); - debug( - "EMU: POST %s\n\t Content-type: '%s'\n\t Authorization: '%s'\n\t " - "'%s'\n", - url_parsed.c_str(), content_type_parsed.c_str(), - authorization_parsed.c_str(), body_parsed.c_str()); - - pop_args(10); - pushInt32(response); - return true; -} - -def_prim(chip_pin_mode, twoToNoneU32) { - debug("EMU: chip_pin_mode(%u,%u) \n", arg1.uint32, arg0.uint32); - pop_args(2); - return true; -} - -def_prim(chip_digital_write, twoToNoneU32) { - debug("EMU: chip_digital_write(%u,%u) \n", arg1.uint32, arg0.uint32); - pop_args(2); - return true; -} - -def_prim(chip_digital_read, oneToOneU32) { - uint8_t pin = arg0.uint32; - pop_args(1); - pushUInt32(1); // HIGH - return true; -} - -def_prim(chip_analog_read, oneToOneI32) { - uint8_t pin = arg0.uint32; - pop_args(1); - pushInt32(sin(sensor_emu) * 100); - sensor_emu += .25; - return true; -} - -def_prim(chip_delay, oneToNoneU32) { - using namespace std::this_thread; // sleep_for, sleep_until - using namespace std::chrono; // nanoseconds, system_clock, seconds - debug("EMU: chip_delay(%u) \n", arg0.uint32); - sleep_for(milliseconds(arg0.uint32)); - debug("EMU: .. done\n"); - pop_args(1); - return true; -} - -def_prim(chip_delay_us, oneToNoneU32) { - using namespace std::this_thread; // sleep_for, sleep_until - using namespace std::chrono; // nanoseconds, system_clock, seconds - debug("EMU: chip_delay(%u ms) \n", arg0.uint32); - sleep_for(microseconds(arg0.uint32)); - debug("EMU: .. done\n"); - pop_args(1); - return true; -} - -// warning: undefined symbol: write_spi_byte -def_prim(write_spi_byte, oneToNoneU32) { - debug("EMU: write_spi_byte(%u) \n", arg0.uint32); - pop_args(1); - return true; -} - -// warning: undefined symbol: spi_begin -def_prim(spi_begin, NoneToNoneU32) { - debug("EMU: spi_begin \n"); - return true; -} - -def_prim(write_spi_bytes_16, twoToNoneU32) { - debug("EMU: write_spi_byte_16(%u, %u) \n", arg1.uint32, arg0.uint32); - pop_args(2); - return true; -} - -#endif - //------------------------------------------------------ // Util functions //------------------------------------------------------ -#ifdef ARDUINO - void connect(const String ssid, const String password) { char *ssid_buf = (char *)acalloc(ssid.length() + 1, sizeof(char), "ssid_buf"); @@ -1063,24 +891,39 @@ int32_t http_post_request(Module *m, const String url, const String body, return httpResponseCode; } -#endif - -/* -int analogRead(uint8_t pin) -void analogReference(uint8_t mode) -void analogWrite(uint8_t pin, int val) -void analogWriteFreq(uint32_t freq) -void analogWriteRange(uint32_t range) -*/ - //------------------------------------------------------ -// Installing all the primitives +// Installing all the primitives & ISRs //------------------------------------------------------ +void install_isrs() { + install_isr(1); + install_isr(2); + install_isr(3); + install_isr(4); + install_isr(5); + install_isr(12); + install_isr(13); + install_isr(14); + install_isr(15); + install_isr(16); + install_isr(17); + install_isr(18); + install_isr(19); + install_isr(21); + install_isr(22); + install_isr(23); + install_isr(25); + install_isr(26); + install_isr(27); + install_isr(32); + install_isr(33); + install_isr(34); + install_isr(35); + install_isr(36); + install_isr(39); +} + void install_primitives() { dbg_info("INSTALLING PRIMITIVES\n"); - // install_primitive(rand); -#ifdef ARDUINO - dbg_info("INSTALLING ARDUINO\n"); install_primitive(abort); install_primitive(millis); install_primitive(micros); @@ -1124,35 +967,13 @@ void install_primitives() { install_primitive(clear_pixels); install_primitive(show_pixels); -#else - dbg_info("INSTALLING FAKE ARDUINO\n"); - install_primitive(abort); - install_primitive(micros); - install_primitive(test); - install_primitive(print_int); - install_primitive(print_string); - install_primitive(wifi_connect); - install_primitive(wifi_connected); - install_primitive(wifi_status); - install_primitive(wifi_localip); - install_primitive(http_get); - install_primitive(http_post); - install_primitive(chip_pin_mode); - install_primitive(chip_digital_write); - install_primitive(chip_delay); - install_primitive(chip_digital_read); - install_primitive(chip_analog_read); - install_primitive(chip_delay_us); - install_primitive(spi_begin); - install_primitive(write_spi_byte); - install_primitive(write_spi_bytes_16); + // temporary primitives needed for analogWrite in ESP32 + install_primitive(chip_analog_write); + install_primitive(chip_ledc_setup); + install_primitive(chip_ledc_attach_pin); - install_primitive(init_pixels); - install_primitive(set_pixel_color); - install_primitive(clear_pixels); - install_primitive(show_pixels); - -#endif + dbg_info("INSTALLING ISRs\n"); + install_isrs(); } //------------------------------------------------------ diff --git a/src/Primitives/emulated.cpp b/src/Primitives/emulated.cpp new file mode 100644 index 00000000..45a9b13d --- /dev/null +++ b/src/Primitives/emulated.cpp @@ -0,0 +1,560 @@ +#ifndef ARDUINO + +/** + * This file lists the primitives of the language and stores them in the + * primitives + * + * Adding a primitive: + * 1) Bump up the NUM_PRIMITIVES constant + * 2) Define _type + * 3) Define a function void primitive(Module* m) + * 4) Extend the install_primitives function + * + */ +#include + +#include +#include +#include +#include +#include + +#include "../Memory/mem.h" +#include "../Utils/macros.h" +#include "../Utils/util.h" +#include "primitives.h" + +#define NUM_PRIMITIVES 0 +#define NUM_PRIMITIVES_ARDUINO 28 + +#define ALL_PRIMITIVES (NUM_PRIMITIVES + NUM_PRIMITIVES_ARDUINO) + +// Global index for installing primitives +int prim_index = 0; + +double sensor_emu = 0; + +/* + Private macros to install a primitive +*/ +#define install_primitive(prim_name) \ + { \ + dbg_info("installing primitive number: %d of %d with name: %s\n", \ + prim_index + 1, ALL_PRIMITIVES, #prim_name); \ + if (prim_index < ALL_PRIMITIVES) { \ + PrimitiveEntry *p = &primitives[prim_index++]; \ + p->name = #prim_name; \ + p->f = &(prim_name); \ + } else { \ + FATAL("pim_index out of bounds"); \ + } \ + } + +#define def_prim(function_name, type) \ + Type function_name##_type = type; \ + bool function_name(Module *m) + +// TODO: use fp +#define pop_args(n) m->sp -= n +#define get_arg(m, arg) m->stack[(m)->sp - (arg)].value +#define pushUInt32(arg) m->stack[++m->sp].value.uint32 = arg +#define pushInt32(arg) m->stack[++m->sp].value.int32 = arg +#define pushUInt64(arg) \ + m->stack[++m->sp].value_type = I64; \ + m->stack[m->sp].value.uint64 = arg +#define arg0 get_arg(m, 0) +#define arg1 get_arg(m, 1) +#define arg2 get_arg(m, 2) +#define arg3 get_arg(m, 3) +#define arg4 get_arg(m, 4) +#define arg5 get_arg(m, 5) +#define arg6 get_arg(m, 6) +#define arg7 get_arg(m, 7) +#define arg8 get_arg(m, 8) +#define arg9 get_arg(m, 9) + +// The primitive table +PrimitiveEntry primitives[ALL_PRIMITIVES]; + +// +uint32_t param_arr_len0[0] = {}; +uint32_t param_I32_arr_len1[1] = {I32}; +uint32_t param_I32_arr_len2[2] = {I32, I32}; +uint32_t param_I32_arr_len3[3] = {I32, I32, I32}; +uint32_t param_I32_arr_len4[4] = {I32, I32, I32, I32}; +uint32_t param_I32_arr_len10[10] = {I32, I32, I32, I32, I32, + I32, I32, I32, I32, I32}; + +uint32_t param_I64_arr_len1[1] = {I64}; + +Type oneToNoneU32 = { + .form = FUNC, + .param_count = 1, + .params = param_I32_arr_len1, + .result_count = 0, + .results = nullptr, + .mask = 0x8001 /* 0x800 = no return ; 1 = I32*/ +}; + +Type twoToNoneU32 = { + .form = FUNC, + .param_count = 2, + .params = param_I32_arr_len2, + .result_count = 0, + .results = nullptr, + .mask = 0x80011 /* 0x800 = no return ; 1 = I32; 1 = I32*/ +}; + +Type threeToNoneU32 = { + .form = FUNC, + .param_count = 3, + .params = param_I32_arr_len3, + .result_count = 0, + .results = nullptr, + .mask = 0x800111 /* 0x800 = no return ; 1=I32; 1=I32; 1=I32*/ +}; + +Type fourToNoneU32 = { + .form = FUNC, + .param_count = 4, + .params = param_I32_arr_len4, + .result_count = 0, + .results = nullptr, + .mask = + 0x8001111 /* 0x800 = no return ; 1 = I32; 1 = I32; 1 = I32; 1 = I32*/ +}; + +Type oneToOneU32 = { + .form = FUNC, + .param_count = 1, + .params = param_I32_arr_len1, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x80011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ +}; + +Type oneToOneI32 = { + .form = FUNC, + .param_count = 1, + .params = param_I32_arr_len1, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x80011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ +}; + +Type twoToOneU32 = { + .form = FUNC, + .param_count = 2, + .params = param_I32_arr_len2, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x81011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ +}; + +Type threeToOneU32 = { + .form = FUNC, + .param_count = 3, + .params = param_I32_arr_len3, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x810111 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32; 1=I32*/ +}; + +Type fourToOneU32 = { + .form = FUNC, + .param_count = 4, + .params = param_I32_arr_len4, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x8101111 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32; 1=I32; 1=I32*/ +}; + +Type tenToOneU32 = { + .form = FUNC, + .param_count = 10, + .params = param_I32_arr_len10, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x8101111111111 /* 0x8 1=I32 0=endRet ; 10 params 1=I32*/ +}; + +Type NoneToNoneU32 = {.form = FUNC, + .param_count = 0, + .params = nullptr, + .result_count = 0, + .results = nullptr, + .mask = 0x80000}; + +Type NoneToOneU32 = {.form = FUNC, + .param_count = 0, + .params = nullptr, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x81000}; + +Type NoneToOneU64 = {.form = FUNC, + .param_count = 0, + .params = nullptr, + .result_count = 1, + .results = param_I64_arr_len1, + .mask = 0x82000}; + +def_prim(init_pixels, NoneToNoneU32) { + printf("init_pixels \n"); + return true; +} + +def_prim(set_pixel_color, fourToOneU32) { + printf("set_pixel_color \n"); + pop_args(4); + return true; +} + +def_prim(show_pixels, NoneToNoneU32) { + printf("show pixels \n"); + return true; +} + +def_prim(clear_pixels, NoneToNoneU32) { + printf("clear pixels \n"); + return true; +} + +def_prim(abort, NoneToNoneU32) { + debug("EMU: abort\n"); + return false; +} + +def_prim(millis, NoneToOneU64) { + struct timeval tv {}; + gettimeofday(&tv, nullptr); + unsigned long millis = 1000 * tv.tv_sec + tv.tv_usec; + pushUInt64(millis); + return true; +} + +def_prim(micros, NoneToOneU64) { + struct timeval tv {}; + gettimeofday(&tv, nullptr); + unsigned long micros = 1000000 * tv.tv_sec + tv.tv_usec; + pushUInt64(micros); + return true; +} + +// call callback test function (temporary) +def_prim(test, oneToNoneU32) { + uint32_t fidx = arg0.uint32; + + std::string topic = "interrupt"; + topic.append(std::to_string(fidx)); + + Callback c = Callback(m, topic, fidx); + CallbackHandler::add_callback(c); + auto *payload = reinterpret_cast("TestPayload"); + CallbackHandler::push_event(topic, (const char *)payload, 11); + pop_args(1); + return true; +} + +def_prim(print_int, oneToNoneU32) { + debug("EMU: print "); + printf("%u\n", arg0.uint32); + pop_args(1); + return true; +} + +def_prim(print_string, twoToNoneU32) { + uint32_t addr = arg1.uint32; + uint32_t size = arg0.uint32; + std::string text = parse_utf8_string(m->memory.bytes, size, addr); + debug("EMU: print string at %i: ", addr); + printf("%s", text.c_str()); + pop_args(2); + return true; +} + +def_prim(wifi_connect, fourToNoneU32) { + uint32_t ssid = arg3.uint32; + uint32_t len0 = arg2.uint32; + uint32_t pass = arg1.uint32; + uint32_t len1 = arg0.uint32; + + std::string ssid_str = parse_utf8_string(m->memory.bytes, len0, ssid); + std::string pass_str = parse_utf8_string(m->memory.bytes, len1, pass); + debug("EMU: connect to %s with password %s\n", ssid_str.c_str(), + pass_str.c_str()); + pop_args(4); + return true; +} + +def_prim(wifi_status, NoneToOneU32) { + pushInt32(3); // return WL_CONNECTED + return true; +} + +def_prim(wifi_connected, NoneToOneU32) { + pushInt32(1); // return that we are connected + return true; +} + +def_prim(wifi_localip, twoToOneU32) { + uint32_t buff = arg1.uint32; + uint32_t size = arg0.uint32; + std::string ip = "192.168.0.181"; + + for (unsigned long i = 0; i < ip.length(); i++) { + m->memory.bytes[buff + i] = (uint32_t)ip[i]; + } + pop_args(2); + pushInt32(buff); + return true; +} + +def_prim(http_get, fourToOneU32) { + // Get arguments + uint32_t url = arg3.uint32; + uint32_t length = arg2.uint32; + int32_t response = arg1.uint32; + uint32_t size = arg0.uint32; + // Parse url + std::string text = parse_utf8_string(m->memory.bytes, length, url); + debug("EMU: http get request %s\n", text.c_str()); + // Construct response + std::string answer = "Response code: 200."; + if (answer.length() > size) { + sprintf(exception, "GET: buffer size is too small for response."); + return false; // TRAP + } + for (unsigned long i = 0; i < answer.length(); i++) { + m->memory.bytes[response + i] = (uint32_t)answer[i]; + } + + // Pop args and return response address + pop_args(4); + pushInt32(response); + return true; +} + +def_prim(http_post, tenToOneU32) { + // Get arguments + uint32_t url = arg9.uint32; + uint32_t url_len = arg8.uint32; + uint32_t body = arg7.uint32; + uint32_t body_len = arg6.uint32; + uint32_t content_type = arg5.uint32; + uint32_t content_type_len = arg4.uint32; + uint32_t authorization = arg3.uint32; + uint32_t authorization_len = arg2.uint32; + int32_t response = arg1.uint32; + uint32_t size = arg0.uint32; + + std::string url_parsed = parse_utf8_string(m->memory.bytes, url_len, url); + std::string body_parsed = + parse_utf8_string(m->memory.bytes, body_len, body); + std::string content_type_parsed = + parse_utf8_string(m->memory.bytes, content_type_len, content_type); + std::string authorization_parsed = + parse_utf8_string(m->memory.bytes, authorization_len, authorization); + debug( + "EMU: POST %s\n\t Content-type: '%s'\n\t Authorization: '%s'\n\t " + "'%s'\n", + url_parsed.c_str(), content_type_parsed.c_str(), + authorization_parsed.c_str(), body_parsed.c_str()); + + pop_args(10); + pushInt32(response); + return true; +} + +def_prim(chip_pin_mode, twoToNoneU32) { + debug("EMU: chip_pin_mode(%u,%u) \n", arg1.uint32, arg0.uint32); + pop_args(2); + return true; +} + +def_prim(chip_digital_write, twoToNoneU32) { + debug("EMU: chip_digital_write(%u,%u) \n", arg1.uint32, arg0.uint32); + pop_args(2); + return true; +} + +def_prim(chip_digital_read, oneToOneU32) { + uint8_t pin = arg0.uint32; + pop_args(1); + pushUInt32(1); // HIGH + return true; +} + +def_prim(chip_analog_read, oneToOneI32) { + uint8_t pin = arg0.uint32; + pop_args(1); + pushInt32(sin(sensor_emu) * 100); + sensor_emu += .25; + return true; +} + +def_prim(chip_delay, oneToNoneU32) { + using namespace std::this_thread; // sleep_for, sleep_until + using namespace std::chrono; // nanoseconds, system_clock, seconds + debug("EMU: chip_delay(%u) \n", arg0.uint32); + sleep_for(milliseconds(arg0.uint32)); + debug("EMU: .. done\n"); + pop_args(1); + return true; +} + +def_prim(chip_delay_us, oneToNoneU32) { + using namespace std::this_thread; // sleep_for, sleep_until + using namespace std::chrono; // nanoseconds, system_clock, seconds + debug("EMU: chip_delay(%u ms) \n", arg0.uint32); + sleep_for(microseconds(arg0.uint32)); + debug("EMU: .. done\n"); + pop_args(1); + return true; +} + +// warning: undefined symbol: write_spi_byte +def_prim(write_spi_byte, oneToNoneU32) { + debug("EMU: write_spi_byte(%u) \n", arg0.uint32); + pop_args(1); + return true; +} + +// warning: undefined symbol: spi_begin +def_prim(spi_begin, NoneToNoneU32) { + debug("EMU: spi_begin \n"); + return true; +} + +def_prim(write_spi_bytes_16, twoToNoneU32) { + debug("EMU: write_spi_byte_16(%u, %u) \n", arg1.uint32, arg0.uint32); + pop_args(2); + return true; +} + +def_prim(subscribe_interrupt, threeToNoneU32) { + uint8_t pin = arg2.uint32; // GPIOPin + uint8_t fidx = arg1.uint32; // Callback function + uint8_t mode = arg0.uint32; + + debug("EMU: subscribe_interrupt(%u, %u, %u) \n", pin, fidx, mode); + std::string topic = "interrupt"; + topic.append(std::to_string(pin)); + + Callback c = Callback(m, topic, fidx); + CallbackHandler::add_callback(c); + pop_args(3); + return true; +} + +// Temporary Primitives needed for analogWrite in ESP32 +def_prim(chip_analog_write, threeToNoneU32) { + uint8_t channel = arg2.uint32; + uint32_t value = arg1.uint32; + uint32_t maxValue = arg0.uint32; + // calculate duty, 4095 from 2 ^ 12 - 1 + printf("chip_analog_write(%u, %u, %u)\n", channel, value, maxValue); + pop_args(3); + return true; +} + +def_prim(chip_ledc_setup, threeToNoneU32) { + uint32_t channel = arg2.uint32; + uint32_t freq = arg1.uint32; + uint32_t ledc_timer = arg0.uint32; + printf("chip_ledc_setup(%u, %u, %u)\n", channel, freq, ledc_timer); + pop_args(3); + return true; +} + +def_prim(chip_ledc_attach_pin, twoToNoneU32) { + uint32_t pin = arg1.uint32; + uint32_t channel = arg0.uint32; + printf("chip_ledc_attach_pin(%u,%u)\n", pin, channel); + pop_args(2); + return true; +} +//------------------------------------------------------ +// Installing all the primitives +//------------------------------------------------------ +void install_primitives() { + dbg_info("INSTALLING PRIMITIVES\n"); + dbg_info("INSTALLING FAKE ARDUINO\n"); + install_primitive(abort); + install_primitive(millis); + install_primitive(micros); + + install_primitive(print_int); + install_primitive(print_string); + + install_primitive(wifi_connect); + install_primitive(wifi_status); + install_primitive(wifi_connected); + install_primitive(wifi_localip); + + install_primitive(http_get); + install_primitive(http_post); + + install_primitive(chip_pin_mode); + install_primitive(chip_digital_write); + install_primitive(chip_delay); + install_primitive(chip_digital_read); + install_primitive(chip_analog_read); + install_primitive(chip_delay_us); + + install_primitive(spi_begin); + install_primitive(write_spi_byte); + install_primitive(write_spi_bytes_16); + + install_primitive(subscribe_interrupt); + + install_primitive(init_pixels); + install_primitive(set_pixel_color); + install_primitive(clear_pixels); + install_primitive(show_pixels); + + // temporary mock primitives needed for analogWrite in ESP32 + install_primitive(chip_analog_write); + install_primitive(chip_ledc_setup); + install_primitive(chip_ledc_attach_pin); +} + +//------------------------------------------------------ +// resolving the primitives +//------------------------------------------------------ +bool resolve_primitive(char *symbol, Primitive *val) { + debug("Resolve primitives (%d) for %s \n", ALL_PRIMITIVES, symbol); + + for (auto &primitive : primitives) { + // printf("Checking %s = %s \n", symbol, primitive.name); + if (!strcmp(symbol, primitive.name)) { + debug("FOUND PRIMITIVE\n"); + *val = primitive.f; + return true; + } + } + FATAL("Could not find primitive %s \n", symbol); + return false; +} + +Memory external_mem = {0, 0, 0, nullptr}; + +bool resolve_external_memory(char *symbol, Memory **val) { + if (!strcmp(symbol, "memory")) { + if (external_mem.bytes == nullptr) { + external_mem.initial = 256; + external_mem.maximum = 256; + external_mem.pages = 256; + external_mem.bytes = (uint8_t *)acalloc( + external_mem.pages * PAGE_SIZE, sizeof(uint32_t), + "Module->memory.bytes primitive"); + } + *val = &external_mem; + return true; + } + + FATAL("Could not find memory %s \n", symbol); + return false; +} + +#endif // ARDUINO diff --git a/src/Primitives/idf.cpp b/src/Primitives/idf.cpp new file mode 100644 index 00000000..883a6b18 --- /dev/null +++ b/src/Primitives/idf.cpp @@ -0,0 +1,280 @@ +#ifndef ARDUINO + +/** + * This file lists the primitives of the language and stores them in the + * primitives + * + * Adding a primitive: + * 1) Bump up the NUM_PRIMITIVES constant + * 2) Define _type + * 3) Define a function void primitive(Module* m) + * 4) Extend the install_primitives function + * + */ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../Memory/mem.h" +#include "../Utils/macros.h" +#include "../Utils/util.h" +#include "driver/gpio.h" +#include "primitives.h" + +#define NUM_PRIMITIVES 0 +#define NUM_PRIMITIVES_ARDUINO 4 + +#define ALL_PRIMITIVES (NUM_PRIMITIVES + NUM_PRIMITIVES_ARDUINO) + +// Global index for installing primitives +int prim_index = 0; + +double sensor_emu = 0; + +/* + Private macros to install a primitive +*/ +#define install_primitive(prim_name) \ + { \ + dbg_info("installing primitive number: %d of %d with name: %s\n", \ + prim_index + 1, ALL_PRIMITIVES, #prim_name); \ + if (prim_index < ALL_PRIMITIVES) { \ + PrimitiveEntry *p = &primitives[prim_index++]; \ + p->name = #prim_name; \ + p->f = &(prim_name); \ + } else { \ + FATAL("pim_index out of bounds"); \ + } \ + } + +#define def_prim(function_name, type) \ + Type function_name##_type = type; \ + bool function_name(Module *m) + +// TODO: use fp +#define pop_args(n) m->sp -= n +#define get_arg(m, arg) m->stack[(m)->sp - (arg)].value +#define pushUInt32(arg) m->stack[++m->sp].value.uint32 = arg +#define pushInt32(arg) m->stack[++m->sp].value.int32 = arg +#define pushUInt64(arg) \ + m->stack[++m->sp].value_type = I64; \ + m->stack[m->sp].value.uint64 = arg +#define arg0 get_arg(m, 0) +#define arg1 get_arg(m, 1) +#define arg2 get_arg(m, 2) +#define arg3 get_arg(m, 3) +#define arg4 get_arg(m, 4) +#define arg5 get_arg(m, 5) +#define arg6 get_arg(m, 6) +#define arg7 get_arg(m, 7) +#define arg8 get_arg(m, 8) +#define arg9 get_arg(m, 9) + +// The primitive table +PrimitiveEntry primitives[ALL_PRIMITIVES]; + +// +uint32_t param_arr_len0[0] = {}; +uint32_t param_I32_arr_len1[1] = {I32}; +uint32_t param_I32_arr_len2[2] = {I32, I32}; +uint32_t param_I32_arr_len3[3] = {I32, I32, I32}; +uint32_t param_I32_arr_len4[4] = {I32, I32, I32, I32}; +uint32_t param_I32_arr_len10[10] = {I32, I32, I32, I32, I32, + I32, I32, I32, I32, I32}; + +uint32_t param_I64_arr_len1[1] = {I64}; + +Type oneToNoneU32 = { + .form = FUNC, + .param_count = 1, + .params = param_I32_arr_len1, + .result_count = 0, + .results = nullptr, + .mask = 0x8001 /* 0x800 = no return ; 1 = I32*/ +}; + +Type twoToNoneU32 = { + .form = FUNC, + .param_count = 2, + .params = param_I32_arr_len2, + .result_count = 0, + .results = nullptr, + .mask = 0x80011 /* 0x800 = no return ; 1 = I32; 1 = I32*/ +}; + +Type threeToNoneU32 = { + .form = FUNC, + .param_count = 3, + .params = param_I32_arr_len3, + .result_count = 0, + .results = nullptr, + .mask = 0x800111 /* 0x800 = no return ; 1=I32; 1=I32; 1=I32*/ +}; + +Type fourToNoneU32 = { + .form = FUNC, + .param_count = 4, + .params = param_I32_arr_len4, + .result_count = 0, + .results = nullptr, + .mask = + 0x8001111 /* 0x800 = no return ; 1 = I32; 1 = I32; 1 = I32; 1 = I32*/ +}; + +Type oneToOneU32 = { + .form = FUNC, + .param_count = 1, + .params = param_I32_arr_len1, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x80011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ +}; + +Type oneToOneI32 = { + .form = FUNC, + .param_count = 1, + .params = param_I32_arr_len1, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x80011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ +}; + +Type twoToOneU32 = { + .form = FUNC, + .param_count = 2, + .params = param_I32_arr_len2, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x81011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ +}; + +Type threeToOneU32 = { + .form = FUNC, + .param_count = 3, + .params = param_I32_arr_len3, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x810111 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32; 1=I32*/ +}; + +Type fourToOneU32 = { + .form = FUNC, + .param_count = 4, + .params = param_I32_arr_len4, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x8101111 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32; 1=I32; 1=I32*/ +}; + +Type tenToOneU32 = { + .form = FUNC, + .param_count = 10, + .params = param_I32_arr_len10, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x8101111111111 /* 0x8 1=I32 0=endRet ; 10 params 1=I32*/ +}; + +Type NoneToNoneU32 = {.form = FUNC, + .param_count = 0, + .params = nullptr, + .result_count = 0, + .results = nullptr, + .mask = 0x80000}; + +Type NoneToOneU32 = {.form = FUNC, + .param_count = 0, + .params = nullptr, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x81000}; + +Type NoneToOneU64 = {.form = FUNC, + .param_count = 0, + .params = nullptr, + .result_count = 1, + .results = param_I64_arr_len1, + .mask = 0x82000}; + +def_prim(chip_delay, oneToNoneU32) { + vTaskDelay(arg0.uint32 / portTICK_PERIOD_MS); + pop_args(1); + return true; +} + +def_prim(chip_pin_mode, twoToNoneU32) { + gpio_set_direction(gpio_num_t(arg1.uint32), gpio_mode_t(arg0.uint32)); + pop_args(2); + return true; +} + +def_prim(chip_digital_write, twoToNoneU32) { + gpio_set_level(gpio_num_t(arg1.uint32), gpio_mode_t(arg0.uint32)); + pop_args(2); + return true; +} + +def_prim(chip_digital_read, oneToOneU32) { + gpio_num_t pin = gpio_num_t(arg0.uint32); + uint8_t res = (uint8_t)gpio_get_level(pin); + pop_args(1); + pushUInt32(res); + return true; +} + +//------------------------------------------------------ +// Installing all the primitives +//------------------------------------------------------ +void install_primitives() { + dbg_info("INSTALLING PRIMITIVES\n"); + install_primitive(chip_delay); + install_primitive(chip_pin_mode); + install_primitive(chip_digital_write); + install_primitive(chip_digital_read); +} + +//------------------------------------------------------ +// resolving the primitives +//------------------------------------------------------ +bool resolve_primitive(char *symbol, Primitive *val) { + debug("Resolve primitives (%d) for %s \n", ALL_PRIMITIVES, symbol); + + for (auto &primitive : primitives) { + // printf("Checking %s = %s \n", symbol, primitive.name); + if (!strcmp(symbol, primitive.name)) { + debug("FOUND PRIMITIVE\n"); + *val = primitive.f; + return true; + } + } + FATAL("Could not find primitive %s \n", symbol); + return false; +} + +Memory external_mem = {0, 0, 0, nullptr}; + +bool resolve_external_memory(char *symbol, Memory **val) { + if (!strcmp(symbol, "memory")) { + if (external_mem.bytes == nullptr) { + external_mem.initial = 256; + external_mem.maximum = 256; + external_mem.pages = 256; + external_mem.bytes = (uint8_t *)acalloc( + external_mem.pages * PAGE_SIZE, sizeof(uint32_t), + "Module->memory.bytes primitive"); + } + *val = &external_mem; + return true; + } + + FATAL("Could not find memory %s \n", symbol); + return false; +} + +#endif // ARDUINO diff --git a/src/Utils/macros.cpp b/src/Utils/macros.cpp index d6be73e3..9f6a28d9 100644 --- a/src/Utils/macros.cpp +++ b/src/Utils/macros.cpp @@ -1,4 +1,3 @@ - #include "macros.h" #include diff --git a/src/Utils/macros.h b/src/Utils/macros.h index 581d2d40..89c21acb 100644 --- a/src/Utils/macros.h +++ b/src/Utils/macros.h @@ -1,4 +1,3 @@ - #pragma once #include diff --git a/src/Utils/sockets.cpp b/src/Utils/sockets.cpp new file mode 100644 index 00000000..516604b2 --- /dev/null +++ b/src/Utils/sockets.cpp @@ -0,0 +1,160 @@ +#include "sockets.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// Socket Debugger Interface +void setFileDescriptorOptions(int socket_fd) { + int opt = 1; + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { + perror("Failed to set socket file descriptor options"); + exit(EXIT_FAILURE); + } +} + +int createSocketFileDescriptor() { + int socket_fd; + if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + perror("Failed to make a new socket file descriptor"); + exit(EXIT_FAILURE); + } + setFileDescriptorOptions(socket_fd); + return socket_fd; +} + +void bindSocketToAddress(int socket_fd, struct sockaddr_in address) { + if (bind(socket_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { + perror("Binding socket to address failed"); + exit(EXIT_FAILURE); + } +} + +struct sockaddr_in createAddress(int port) { + struct sockaddr_in address {}; + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + return address; +} + +struct sockaddr_in createLocalhostAddress(int port) { + struct sockaddr_in address = createAddress(port); + const char hostname[] = "localhost"; + struct hostent *resolvedhost = gethostbyname(hostname); + memcpy(&address.sin_addr, resolvedhost->h_addr_list[0], + resolvedhost->h_length); + return address; +} + +void startListening(int socket_fd) { + if (listen(socket_fd, 1) < 0) { + perror("listen"); + exit(EXIT_FAILURE); + } +} + +int listenForIncomingConnection(int socket_fd, struct sockaddr_in address) { + int new_socket; + int size = sizeof(address); + if ((new_socket = accept(socket_fd, (struct sockaddr *)&address, + (socklen_t *)&size)) < 0) { + perror("Failed to listen for incoming connections"); + exit(EXIT_FAILURE); + } + return new_socket; +} + +Sink::Sink(FILE *out) { + this->outStream = out; + this->outDescriptor = fileno(out); +} + +int Sink::write(const char *fmt, ...) const { + va_list args; + va_start(args, fmt); + int written = vdprintf(this->outDescriptor, fmt, args); + va_end(args); + fflush(this->outStream); + return written; +} + +Duplex::Duplex(FILE *inStream, FILE *outStream) : Sink(outStream) { + this->inDescriptor = fileno(inStream); +} + +ssize_t Duplex::read(void *out, size_t size) { + return ::read(this->inDescriptor, out, size); +} + +FileDescriptorChannel::FileDescriptorChannel(int fileDescriptor) { + this->fd = fileDescriptor; +} + +int FileDescriptorChannel::write(const char *fmt, ...) const { + va_list args; + va_start(args, fmt); + int written = vdprintf(this->fd, fmt, args); + va_end(args); + return written; +} + +ssize_t FileDescriptorChannel::read(void *out, size_t size) { + return ::read(this->fd, out, size); +} + +WebSocket::WebSocket(int port) { + this->port = port; + this->fileDescriptor = -1; + this->socket = -1; +} + +void WebSocket::open() { + // bind socket to address + this->fileDescriptor = createSocketFileDescriptor(); + struct sockaddr_in address = createAddress(this->port); + bindSocketToAddress(this->fileDescriptor, address); + startListening(this->fileDescriptor); + printf("Listening on port 172.0.0.1:%i\n", this->port); + + // block until a connection is established + this->socket = listenForIncomingConnection(this->fileDescriptor, address); +} + +int WebSocket::write(const char *fmt, ...) const { + if (this->socket < 0) { + return 0; + } + va_list args; + va_start(args, fmt); + int written = vdprintf(this->socket, fmt, args); + va_end(args); + return written; +} + +ssize_t WebSocket::read(void *out, size_t size) { + if (this->socket < 0) { + return 0; + } + return ::read(this->socket, out, size); +} + +void sendAlarm() { + struct sigaction sact {}; + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; + sigaction(SIGALRM, &sact, nullptr); + alarm(0); +} + +void WebSocket::close() { + sendAlarm(); // stop possible blocking accept call + shutdown(this->fileDescriptor, SHUT_RDWR); // shutdown connection +} diff --git a/src/Utils/sockets.h b/src/Utils/sockets.h new file mode 100644 index 00000000..b13b575b --- /dev/null +++ b/src/Utils/sockets.h @@ -0,0 +1,71 @@ +#pragma once + +#include +void setFileDescriptorOptions(int socket_fd); + +int createSocketFileDescriptor(); + +void bindSocketToAddress(int socket_fd, struct sockaddr_in address); + +struct sockaddr_in createAddress(int port); + +struct sockaddr_in createLocalhostAddress(int port); + +void startListening(int socket_fd); + +int listenForIncomingConnection(int socket_fd, struct sockaddr_in address); + +class Channel { + public: + virtual void open() {} + virtual int write(char const *fmt, ...) const { return 0; } + virtual ssize_t read(void *out, size_t size) { return 0; } + virtual void close() {} + virtual ~Channel() = default; +}; + +class Sink : public Channel { + private: + FILE *outStream; + int outDescriptor; + + public: + explicit Sink(FILE *out); + int write(char const *fmt, ...) const override; +}; + +class Duplex : public Sink { + private: + int inDescriptor; + + public: + explicit Duplex(FILE *inStream, FILE *outStream); + + ssize_t read(void *out, size_t size) override; +}; + +class FileDescriptorChannel : public Channel { + private: + int fd; + + public: + explicit FileDescriptorChannel(int fileDescriptor); + + int write(char const *fmt, ...) const override; + ssize_t read(void *out, size_t size) override; +}; + +class WebSocket : public Channel { + private: + int port; + int fileDescriptor; + int socket; + + public: + explicit WebSocket(int port); + + void open() override; + int write(char const *fmt, ...) const override; + ssize_t read(void *out, size_t size) override; + void close() override; +}; \ No newline at end of file diff --git a/src/Utils/util.cpp b/src/Utils/util.cpp index c67b1ed0..5323bafb 100644 --- a/src/Utils/util.cpp +++ b/src/Utils/util.cpp @@ -161,3 +161,56 @@ double wa_fmin(double a, double b) { } return c; } + +// WOOD +uint32_t read_B32(uint8_t **bytes) { + uint8_t *b = *bytes; + uint32_t n = (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]; + *bytes += 4; + return n; +} + +uint16_t read_B16(uint8_t **bytes) { + uint8_t *b = *bytes; + uint32_t n = (b[0] << 8) + b[1]; + *bytes += 2; + return n; +} + +int read_B32_signed(uint8_t **bytes) { + uint8_t *b = *bytes; + int n = (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]; + *bytes += 4; + return n; +} // TODO replace with read_LEB_32? If keep Big endian use memcpy? + +uint32_t read_L32(uint8_t **bytes) { + // uint8_t *b = *bytes; + uint32_t n = 0; + memcpy(&n, *bytes, sizeof(uint32_t)); + *bytes += 4; + return n; +} // TODO replace with read_LEB_32? If keep Big endian use memcpy? + +void chars_as_hexa(unsigned char *dest, unsigned char *source, + uint32_t len_source) { + for (uint32_t i = 0; i < len_source; i++) { + unsigned c = source[i] >> 4; + unsigned c2 = source[i] & 0xF; + dest[i * 2 + 0] = c > 9 ? (c - 10 + 'A') : (c + '0'); + dest[i * 2 + 1] = c2 > 9 ? (c2 - 10 + 'A') : (c2 + '0'); + } +} + +unsigned short int sizeof_valuetype(uint32_t vt) { + switch (vt) { + case I32: + return 4; + case I64: + return 8; + case F32: + return sizeof(float); + default: + return sizeof(double); + } +} diff --git a/src/Utils/util.h b/src/Utils/util.h index 900a5565..c64618bf 100644 --- a/src/Utils/util.h +++ b/src/Utils/util.h @@ -67,4 +67,14 @@ double wa_fmax(double a, double b); double wa_fmin(double a, double b); +// WOOD +uint32_t read_B32(uint8_t **bytes); +uint16_t read_B16(uint8_t **bytes); +int read_B32_signed(uint8_t **bytes); +uint32_t read_L32(uint8_t **bytes); +void chars_as_hexa(unsigned char *dest, unsigned char *source, + uint32_t len_source); + +unsigned short int sizeof_valuetype(uint32_t); + #endif diff --git a/src/WARDuino.h b/src/WARDuino.h index c91f36ea..4744bdce 100644 --- a/src/WARDuino.h +++ b/src/WARDuino.h @@ -1,5 +1,4 @@ -#ifndef WAC_H -#define WAC_H +#pragma once #include #include @@ -10,6 +9,7 @@ #include #include "Debug/debugger.h" +#include "Edward/proxy_supervisor.h" #include "WARDuino/CallbackHandler.h" // Constants @@ -30,8 +30,11 @@ #define FUNC 0x60 // -0x20 #define BLOCK 0x40 // -0x40 -#define EVENTS_SIZE 1 - +#ifdef ARDUINO +#define EVENTS_SIZE 10 +#else +#define EVENTS_SIZE 50 +#endif #define KIND_FUNCTION 0 #define KIND_TABLE 1 #define KIND_MEMORY 2 @@ -65,8 +68,9 @@ typedef union FuncPtr { // A block or function typedef struct Block { - uint8_t block_type; // 0x00: function, 0x01: init_exp - // 0x02: block, 0x03: loop, 0x04: if + uint8_t block_type; // 0x00: function, 0x01: init_exp, 0x02: block, + // 0x03: loop, 0x04: if, 0xfe: proxy guard, + // 0xff: cbk guard uint32_t fidx; // function only (index) Type *type; // params/results type uint32_t local_count; // function only @@ -178,12 +182,16 @@ typedef struct PrimitiveEntry { class WARDuino { private: + static WARDuino *singleton; std::vector modules = {}; + WARDuino(); + public: Debugger *debugger; + RunningState program_state; - WARDuino(); + static WARDuino *instance(); int run_module(Module *m); @@ -195,7 +203,5 @@ class WARDuino { uint32_t get_export_fidx(Module *m, const char *name); - void handleInterrupt(size_t len, uint8_t *buff); + void handleInterrupt(size_t len, uint8_t *buff) const; }; - -#endif diff --git a/src/WARDuino/CallbackHandler.cpp b/src/WARDuino/CallbackHandler.cpp index 40b305e8..08a1ec8e 100644 --- a/src/WARDuino/CallbackHandler.cpp +++ b/src/WARDuino/CallbackHandler.cpp @@ -2,16 +2,43 @@ #include +#include "../Debug/debugger.h" #include "../Interpreter/instructions.h" #include "../Utils/macros.h" +void push_guard(Module *m) { + if (m == nullptr) { + return; + } + auto *guard = (Block *)malloc(sizeof(struct Block)); + guard->block_type = 255; + guard->type = nullptr; + guard->local_value_type = nullptr; + guard->start_ptr = nullptr; + guard->end_ptr = nullptr; + guard->else_ptr = nullptr; + guard->export_name = nullptr; + guard->import_field = nullptr; + guard->import_module = nullptr; + guard->func_ptr = nullptr; + push_block(m, guard, m->sp); +} + // CallbackHandler class -// bool CallbackHandler::resolving_event = false; +bool CallbackHandler::manual_event_resolution = false; +bool CallbackHandler::resolving_event = false; +size_t CallbackHandler::pushed_cursor = 0; + +bool should_push_event() { + return WARDuino::instance()->program_state == PROXYrun || + WARDuino::instance()->program_state == PROXYhalt; +} + std::unordered_map *> *CallbackHandler::callbacks = new std::unordered_map *>(); -std::queue *CallbackHandler::events = new std::queue(); +std::deque *CallbackHandler::events = new std::deque(); void CallbackHandler::add_callback(const Callback &c) { auto item = callbacks->find(c.topic); @@ -39,44 +66,107 @@ size_t CallbackHandler::callback_count(const std::string &topic) { return callbacks->find(topic)->second->size(); } -void CallbackHandler::push_event(std::string topic, - const unsigned char *payload, +// WARNING: Push event functions should not use IO functions, since they can be +// called from ISR callbacks +void CallbackHandler::push_event(std::string topic, const char *payload, unsigned int length) { + char *message = (char *)(malloc(sizeof(char) * length + 1)); + snprintf(message, length + 1, "%s", payload); + auto event = + new Event(std::move(topic), reinterpret_cast(message)); + CallbackHandler::push_event(event); +} + +void CallbackHandler::push_event(Event *event) { if (events->size() < EVENTS_SIZE) { - char *message = (char *)(malloc(sizeof(char) * length + 1)); - snprintf(message, length + 1, "%s", payload); - auto e = new Event(std::move(topic), - reinterpret_cast(message)); - dbg_info("Push Event(%s, %s)\n", e->topic.c_str(), e->payload); - events->push(*e); + events->push_back(*event); } } -bool CallbackHandler::resolve_event() { - if (CallbackHandler::events->empty()) { +bool CallbackHandler::resolve_event(bool force) { + if ((!force && CallbackHandler::resolving_event) || + CallbackHandler::events->empty()) { + if (force) { + printf("No events to be processed!\n"); + WARDuino::instance()->debugger->channel->write( + "no events to be processed"); + } return false; } - // CallbackHandler::resolving_event = true; - Event event = CallbackHandler::events->front(); - CallbackHandler::events->pop(); - dbg_info("Resolving an event. (%lu remaining)\n", - CallbackHandler::events->size()); + if (should_push_event()) { + Event e = CallbackHandler::events->at(CallbackHandler::pushed_cursor++); + WARDuino::instance()->debugger->channel->write( + R"({"topic":"%s","payload":"%s"})", e.topic.c_str(), + e.payload.c_str()); + + CallbackHandler::events->pop_front(); + CallbackHandler::pushed_cursor--; + CallbackHandler::resolving_event = false; + return !CallbackHandler::events->empty(); + // no further execution if drone + } + + if (!force && (CallbackHandler::manual_event_resolution || + WARDuino::instance()->program_state == WARDUINOpause)) { + return true; + } + + CallbackHandler::resolving_event = true; + CallbackHandler::events->pop_front(); + + debug("Resolving an event. (%lu remaining)\n", + CallbackHandler::events->size()); auto iterator = CallbackHandler::callbacks->find(event.topic); if (iterator != CallbackHandler::callbacks->end()) { + Module *module = nullptr; std::string key = iterator->first; for (Callback cbs : *iterator->second) { cbs.resolve_event(event); + module = cbs.module; } + push_guard(module); } else { // TODO handle error: event for non-existing iterator + printf("No handler found for %s (in %u items)!\n", event.topic.c_str(), + CallbackHandler::callbacks->size()); } - // CallbackHandler::resolving_event = false; return !CallbackHandler::events->empty(); } +size_t CallbackHandler::event_count() { + return CallbackHandler::events->size(); +} + +std::deque::const_iterator CallbackHandler::event_begin() { + return CallbackHandler::events->cbegin(); +} + +std::deque::const_iterator CallbackHandler::event_end() { + return CallbackHandler::events->cend(); +} + +void CallbackHandler::clear_callbacks() { CallbackHandler::callbacks->clear(); } + +std::string CallbackHandler::dump_callbacks() { + std::string repr = R"({"callbacks": [)"; + auto iterator = CallbackHandler::callbacks->begin(); + while (iterator != CallbackHandler::callbacks->end()) { + repr += R"({")" + iterator->first + R"(": [)"; + auto callback = std::begin(*iterator->second); + while (callback != std::end(*iterator->second)) { + repr += std::to_string(callback->table_index); + repr += (++callback != iterator->second->end()) ? ", " : ""; + } + repr += "]}"; + repr += (++iterator != CallbackHandler::callbacks->end()) ? ", " : ""; + } + repr += "]}"; + return repr; +} + // Callback class Callback::Callback(Module *m, std::string id, uint32_t tidx) { @@ -125,7 +215,12 @@ Callback::Callback(const Callback &c) { // Event class -Event::Event(std::string topic, const char *payload) { +Event::Event(std::string topic, std::string payload) { this->topic = topic; this->payload = payload; } + +std::string Event::serialized() const { + return R"({"topic": ")" + this->topic + R"(", "payload": ")" + + this->payload + R"("})"; +} diff --git a/src/WARDuino/CallbackHandler.h b/src/WARDuino/CallbackHandler.h index 3d3ea289..7e5bb3d2 100644 --- a/src/WARDuino/CallbackHandler.h +++ b/src/WARDuino/CallbackHandler.h @@ -9,38 +9,49 @@ struct Module; class Callback; -class Event; +class Event { + public: + std::string topic; + std::string payload; + + Event(std::string topic, std::string payload); + + std::string serialized() const; +}; class CallbackHandler { private: static std::unordered_map *> *callbacks; - static std::queue *events; + static std::deque *events; CallbackHandler() = default; // Disallow creation public: - // static bool resolving_event; + static size_t pushed_cursor; + + static size_t event_count(); + static std::deque::const_iterator event_begin(); + static std::deque::const_iterator event_end(); + + static bool resolving_event; static void add_callback(const Callback &c); static void remove_callback(const Callback &c); + static void clear_callbacks(); + static std::string dump_callbacks(); static size_t callback_count(const std::string &topic); - static void push_event(std::string topic, const unsigned char *payload, + static void push_event(std::string topic, const char *payload, unsigned int length); - static bool resolve_event(); -}; - -class Event { - public: - std::string topic; - const char *payload; + static void push_event(Event *event); + static bool resolve_event(bool force = false); - Event(std::string topic, const char *payload); + // WOOD needed to know when to push events + static bool manual_event_resolution; }; class Callback { - private: - Module *module; // reference to module public: + Module *module; // reference to module std::string topic; uint32_t table_index{}; diff --git a/src/WARDuino/WARDuino.cpp b/src/WARDuino/WARDuino.cpp index 8d93bce2..09e2871a 100644 --- a/src/WARDuino/WARDuino.cpp +++ b/src/WARDuino/WARDuino.cpp @@ -3,7 +3,6 @@ #include // std::find #include #include -#include #include "../Interpreter/instructions.h" #include "../Memory/mem.h" @@ -325,9 +324,9 @@ Module *WARDuino::load_module(uint8_t *bytes, uint32_t byte_count, uint8_t *pos = bytes; word = read_uint32(&pos); debug("Magic number is 0x%x\n", word); - ASSERT(word == WA_MAGIC, "Wrong module magic 0x%x\n", word); + ASSERT(word == WA_MAGIC, "Wrong module magic 0x%" PRIx32 "\n", word); word = read_uint32(&pos); - ASSERT(word == WA_VERSION, "Wrong module version 0x%x\n", word); + ASSERT(word == WA_VERSION, "Wrong module version 0x%" PRIx32 "\n", word); // Read the sections uint8_t *bytes_end = bytes + byte_count; @@ -367,7 +366,8 @@ Module *WARDuino::load_module(uint8_t *bytes, uint32_t byte_count, Type *type = &m->types[c]; type->form = read_LEB(&pos, 7); ASSERT(type->form == FUNC, - "%u-th type def was not a function type", c); + "%" PRIu32 " -th type def was not a function type", + c); // read vector params type->param_count = read_LEB_32(&pos); @@ -508,7 +508,7 @@ Module *WARDuino::load_module(uint8_t *bytes, uint32_t byte_count, { ASSERT(!m->table.entries, "More than 1 table not supported\n"); - Table *tval = (Table *)val; + auto *tval = (Table *)val; m->table.entries = (uint32_t *)val; ASSERT(m->table.initial <= tval->maximum, "Imported table is not large enough\n"); @@ -720,8 +720,9 @@ Module *WARDuino::load_module(uint8_t *bytes, uint32_t byte_count, m->table.entries, offset); if (!m->options.disable_memory_bounds) { ASSERT(offset + num_elem <= m->table.size, - "table overflow %d+%d > %d\n", offset, num_elem, - m->table.size); + "table overflow %" PRIu32 "+%" PRIu32 + " > %" PRIu32 "\n", + offset, num_elem, m->table.size); } for (uint32_t n = 0; n < num_elem; n++) { debug( @@ -753,7 +754,9 @@ Module *WARDuino::load_module(uint8_t *bytes, uint32_t byte_count, uint32_t size = read_LEB_32(&pos); if (!m->options.disable_memory_bounds) { ASSERT(offset + size <= m->memory.pages * PAGE_SIZE, - "memory overflow %d+%d > %d\n", offset, size, + "memory overflow %" PRIu32 "+%" PRIu32 + " > %" PRIu32 "\n", + offset, size, (uint32_t)(m->memory.pages * PAGE_SIZE)); } dbg_info( @@ -817,7 +820,7 @@ Module *WARDuino::load_module(uint8_t *bytes, uint32_t byte_count, break; } default: - FATAL("Section %d unimplemented\n", id); + FATAL("Section %" PRIu32 " unimplemented\n", id); pos += section_len; } } @@ -835,7 +838,7 @@ Module *WARDuino::load_module(uint8_t *bytes, uint32_t byte_count, // dbg_dump_stack(m); ASSERT(m->functions[fidx].type->result_count == 0, - "start function 0x%x must not have arguments!", fidx); + "start function 0x%" PRIx32 " must not have arguments!", fidx); if (fidx < m->import_count) { // THUNK thunk_out(m, fidx); // import/thunk call @@ -863,6 +866,10 @@ Module *WARDuino::load_module(uint8_t *bytes, uint32_t byte_count, } void WARDuino::unload_module(Module *m) { +#ifndef ARDUINO + this->debugger + ->disconnect_proxy(); // TODO should this be in unload module? +#endif auto it = std::find(this->modules.begin(), this->modules.end(), m); if (it != this->modules.end()) this->modules.erase(it); @@ -935,6 +942,13 @@ int WARDuino::run_module(Module *m) { // ntly the same function) // parse numer per 2 chars (HEX) (stop if non-hex) // Don't use print in interrupt handlers -void WARDuino::handleInterrupt(size_t len, uint8_t *buff) { +void WARDuino::handleInterrupt(size_t len, uint8_t *buff) const { this->debugger->addDebugMessage(len, buff); } + +WARDuino *WARDuino::singleton = nullptr; + +WARDuino *WARDuino::instance() { + if (singleton == nullptr) singleton = new WARDuino(); + return singleton; +}