diff --git a/platforms/Zephyr/boards/stm32l496g_disco.overlay b/platforms/Zephyr/boards/stm32l496g_disco.overlay index e5ec78c3..71d4ca49 100644 --- a/platforms/Zephyr/boards/stm32l496g_disco.overlay +++ b/platforms/Zephyr/boards/stm32l496g_disco.overlay @@ -107,7 +107,12 @@ pwms = <&pwm8 3 10000 PWM_POLARITY_NORMAL>, - <&pwm8 4 10000 PWM_POLARITY_NORMAL>; + <&pwm8 4 10000 PWM_POLARITY_NORMAL>, + <&pwm8 1 10000 PWM_POLARITY_NORMAL>, + <&pwm8 2 10000 PWM_POLARITY_NORMAL>; + + warduino-uarts = + <&usart1>; }; }; @@ -115,7 +120,7 @@ status = "okay"; pwm8: pwm { status = "okay"; - pinctrl-0 = <&tim8_ch3_pc8 &tim8_ch4_pc9>; + pinctrl-0 = <&tim8_ch3_pc8 &tim8_ch4_pc9 &tim8_ch1_pc6 &tim8_ch2_pc7>; pinctrl-names = "default"; }; diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index ff876d6f..0e1aa087 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -10,6 +10,7 @@ #endif #include "../Memory/mem.h" +#include "../Primitives/primitives.h" #include "../Utils//util.h" #include "../Utils/macros.h" #include "../WARDuino/CallbackHandler.h" @@ -714,11 +715,18 @@ bool Debugger::handlePushedEvent(char *bytes) const { } void Debugger::snapshot(Module *m) { - uint16_t numberBytes = 10; - uint8_t state[] = { - pcState, breakpointsState, callstackState, globalsState, - tableState, memoryState, branchingTableState, stackState, - callbacksState, eventsState}; + uint16_t numberBytes = 11; + uint8_t state[] = {pcState, + breakpointsState, + callstackState, + globalsState, + tableState, + memoryState, + branchingTableState, + stackState, + callbacksState, + eventsState, + ioState}; inspect(m, numberBytes, state); } @@ -843,6 +851,26 @@ void Debugger::inspect(Module *m, uint16_t sizeStateArray, uint8_t *state) { addComma = true; break; } + case ioState: { + this->channel->write("%s", addComma ? "," : ""); + this->channel->write("\"io\": ["); + bool comma = false; + std::vector external_state = get_io_state(m); + for (auto state_elem : external_state) { + this->channel->write("%s{", comma ? ", " : ""); + this->channel->write( + R"("key": "%s", "output": %s, "value": %d)", + state_elem->key.c_str(), + state_elem->output ? "true" : "false", + state_elem->value); + this->channel->write("}"); + comma = true; + delete state_elem; + } + this->channel->write("]"); + addComma = true; + break; + } default: { debug("dumpExecutionState: Received unknown state request\n"); break; @@ -953,7 +981,8 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) { uint8_t *program_state = nullptr; uint8_t *end_state = nullptr; program_state = interruptData + 1; // skip interruptLoadSnapshot - end_state = program_state + read_B32(&program_state); + uint32_t len = read_B32(&program_state); + end_state = program_state + len; debug("saving program_state\n"); while (program_state < end_state) { @@ -1169,6 +1198,29 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) { } break; } + case ioState: { + debug("receiving ioState\n"); + uint8_t io_state_count = *program_state++; + std::vector external_state; + external_state.reserve(io_state_count); + for (int i = 0; i < io_state_count; i++) { + IOStateElement state_elem; + state_elem.key = ""; + char c = (char)*program_state++; + while (c != '\0') { + state_elem.key += c; + c = (char)*program_state++; + } + state_elem.output = *program_state++; + state_elem.value = (int)read_B32(&program_state); + external_state.emplace_back(state_elem); + debug("pin %s(%s) = %d\n", state_elem.key.c_str(), + state_elem.output ? "output" : "input", + state_elem.value); + } + restore_external_state(m, external_state); + break; + } default: { FATAL("saveState: Received unknown program state\n"); } diff --git a/src/Debug/debugger.h b/src/Debug/debugger.h index 100fd1dd..444d03be 100644 --- a/src/Debug/debugger.h +++ b/src/Debug/debugger.h @@ -47,7 +47,8 @@ enum ExecutionState { branchingTableState = 0x07, stackState = 0x08, callbacksState = 0x09, - eventsState = 0x0A + eventsState = 0x0A, + ioState = 0x0B, }; enum InterruptTypes { diff --git a/src/Primitives/arduino.cpp b/src/Primitives/arduino.cpp index 0100451e..4a946d33 100644 --- a/src/Primitives/arduino.cpp +++ b/src/Primitives/arduino.cpp @@ -150,15 +150,32 @@ int prim_index = 0; PrimitiveEntry *p = &primitives[prim_index++]; \ p->name = #prim_name; \ p->f = &(prim_name); \ + p->f_reverse = nullptr; \ + p->f_serialize_state = nullptr; \ } else { \ FATAL("pim_index out of bounds"); \ } \ } +#define install_primitive_reverse(prim_name) \ + { \ + PrimitiveEntry *p = &primitives[prim_index - 1]; \ + p->f_reverse = &(prim_name##_reverse); \ + p->f_serialize_state = &(prim_name##_serialize); \ + } + #define def_prim(function_name, type) \ Type function_name##_type = type; \ bool function_name(Module *m) +#define def_prim_reverse(function_name) \ + void function_name##_reverse(Module *m, \ + std::vector external_state) + +#define def_prim_serialize(function_name) \ + void function_name##_serialize( \ + std::vector &external_state) + // TODO: use fp #define pop_args(n) m->sp -= n #define get_arg(m, arg) m->stack[(m)->sp - (arg)].value @@ -491,6 +508,36 @@ def_prim(chip_digital_write, twoToNoneU32) { return true; } +def_prim_reverse(chip_digital_write) { + for (IOStateElement state : external_state) { + if (!state.output) { + continue; + } + + if (state.key[0] == 'p') { + invoke_primitive(m, "chip_digital_write", stoi(state.key.substr(1)), + (uint32_t)state.value); + } + } +} + +def_prim_serialize(chip_digital_write) { + for (int i = 0; i < NUM_DIGITAL_PINS; i++) { + uint32_t bit_mask = digitalPinToBitMask(i); + auto *state = new IOStateElement(); + state->key = "p" + std::to_string(i); + uint8_t port = digitalPinToPort(i); + if (*portModeRegister(port) & bit_mask) { // DDR + state->output = true; + state->value = (*portOutputRegister(port) & bit_mask) > 0; + } else { + state->output = false; + state->value = (*portInputRegister(port) & bit_mask) > 0; + } + external_state.push_back(state); + } +} + def_prim(chip_delay, oneToNoneU32) { delay(arg0.uint32); pop_args(1); @@ -954,6 +1001,7 @@ void install_primitives() { install_primitive(chip_pin_mode); install_primitive(chip_digital_write); + install_primitive_reverse(chip_digital_write); install_primitive(chip_delay); install_primitive(chip_digital_read); install_primitive(chip_analog_read); @@ -1026,3 +1074,44 @@ bool resolve_external_memory(char *symbol, Memory **val) { FATAL("Could not find memory %s \n", symbol); return false; } + +//------------------------------------------------------ +// Restore external state when restoring a snapshot +//------------------------------------------------------ +void restore_external_state(Module *m, + std::vector external_state) { + uint8_t opcode = *m->pc_ptr; + // TODO: Maybe primitives can also be called using the other call + // instructions such as call_indirect + // maybe there should just be a function that checks if a certain function + // is being called that handles all these cases? + if (opcode == 0x10) { // call opcode + uint8_t *pc_copy = m->pc_ptr + 1; + uint32_t fidx = read_LEB_32(&pc_copy); + if (fidx < m->import_count) { + for (auto &primitive : primitives) { + if (!strcmp(primitive.name, m->functions[fidx].import_field)) { + if (primitive.f_reverse) { + debug("Reversing action for primitive %s\n", + primitive.name); + primitive.f_reverse(m, external_state); + } + return; + } + } + } + } +} + +//------------------------------------------------------ +// Serialize external state into a snapshot +//------------------------------------------------------ +std::vector get_io_state(Module *m) { + std::vector ioState; + for (auto &primitive : primitives) { + if (primitive.f_serialize_state) { + primitive.f_serialize_state(ioState); + } + } + return ioState; +} diff --git a/src/Primitives/emulated.cpp b/src/Primitives/emulated.cpp index 0a139ec8..71bab730 100644 --- a/src/Primitives/emulated.cpp +++ b/src/Primitives/emulated.cpp @@ -47,15 +47,32 @@ double sensor_emu = 0; PrimitiveEntry *p = &primitives[prim_index++]; \ p->name = #prim_name; \ p->f = &(prim_name); \ + p->f_reverse = nullptr; \ + p->f_serialize_state = nullptr; \ } else { \ FATAL("pim_index out of bounds"); \ } \ } +#define install_primitive_reverse(prim_name) \ + { \ + PrimitiveEntry *p = &primitives[prim_index - 1]; \ + p->f_reverse = &(prim_name##_reverse); \ + p->f_serialize_state = &(prim_name##_serialize); \ + } + #define def_prim(function_name, type) \ Type function_name##_type = type; \ bool function_name(Module *m) +#define def_prim_reverse(function_name) \ + void function_name##_reverse(Module *m, \ + std::vector external_state) + +#define def_prim_serialize(function_name) \ + void function_name##_serialize( \ + std::vector &external_state) + // TODO: use fp #define pop_args(n) m->sp -= n #define get_arg(m, arg) m->stack[(m)->sp - (arg)].value @@ -572,4 +589,42 @@ bool resolve_external_memory(char *symbol, Memory **val) { return false; } +//------------------------------------------------------ +// Restore external state when restoring a snapshot +//------------------------------------------------------ +void restore_external_state(Module *m, + std::vector external_state) { + uint8_t opcode = *m->pc_ptr; + // TODO: Maybe primitives can also be called using the other call + // instructions such as call_indirect + // maybe there should just be a function that checks if a certain function + // is being called that handles all these cases? + if (opcode == 0x10) { // call opcode + uint8_t *pc_copy = m->pc_ptr + 1; + uint32_t fidx = read_LEB_32(&pc_copy); + if (fidx < m->import_count) { + for (auto &primitive : primitives) { + if (!strcmp(primitive.name, m->functions[fidx].import_field)) { + if (primitive.f_reverse) { + debug("Reversing action for primitive %s\n", + primitive.name); + primitive.f_reverse(m, external_state); + } + return; + } + } + } + } +} + +std::vector get_io_state(Module *m) { + std::vector ioState; + for (auto &primitive : primitives) { + if (primitive.f_serialize_state) { + primitive.f_serialize_state(ioState); + } + } + return ioState; +} + #endif // ARDUINO diff --git a/src/Primitives/idf.cpp b/src/Primitives/idf.cpp index 883a6b18..9db8acee 100644 --- a/src/Primitives/idf.cpp +++ b/src/Primitives/idf.cpp @@ -48,15 +48,32 @@ double sensor_emu = 0; PrimitiveEntry *p = &primitives[prim_index++]; \ p->name = #prim_name; \ p->f = &(prim_name); \ + p->f_reverse = nullptr; \ + p->f_serialize_state = nullptr; \ } else { \ FATAL("pim_index out of bounds"); \ } \ } +#define install_primitive_reverse(prim_name) \ + { \ + PrimitiveEntry *p = &primitives[prim_index - 1]; \ + p->f_reverse = &(prim_name##_reverse); \ + p->f_serialize_state = &(prim_name##_serialize); \ + } + #define def_prim(function_name, type) \ Type function_name##_type = type; \ bool function_name(Module *m) +#define def_prim_reverse(function_name) \ + void function_name##_reverse(Module *m, \ + std::vector external_state) + +#define def_prim_serialize(function_name) \ + void function_name##_serialize( \ + std::vector &external_state) + // TODO: use fp #define pop_args(n) m->sp -= n #define get_arg(m, arg) m->stack[(m)->sp - (arg)].value @@ -277,4 +294,42 @@ bool resolve_external_memory(char *symbol, Memory **val) { return false; } +//------------------------------------------------------ +// Restore external state when restoring a snapshot +//------------------------------------------------------ +void restore_external_state(Module *m, + std::vector external_state) { + uint8_t opcode = *m->pc_ptr; + // TODO: Maybe primitives can also be called using the other call + // instructions such as call_indirect + // maybe there should just be a function that checks if a certain function + // is being called that handles all these cases? + if (opcode == 0x10) { // call opcode + uint8_t *pc_copy = m->pc_ptr + 1; + uint32_t fidx = read_LEB_32(&pc_copy); + if (fidx < m->import_count) { + for (auto &primitive : primitives) { + if (!strcmp(primitive.name, m->functions[fidx].import_field)) { + if (primitive.f_reverse) { + debug("Reversing action for primitive %s\n", + primitive.name); + primitive.f_reverse(m, external_state); + } + return; + } + } + } + } +} + +std::vector get_io_state(Module *m) { + std::vector ioState; + for (auto &primitive : primitives) { + if (primitive.f_serialize_state) { + primitive.f_serialize_state(ioState); + } + } + return ioState; +} + #endif // ARDUINO diff --git a/src/Primitives/primitives.h b/src/Primitives/primitives.h index 6ce1f4e8..03b08146 100644 --- a/src/Primitives/primitives.h +++ b/src/Primitives/primitives.h @@ -1,6 +1,7 @@ #ifndef WARDUINO_PRIM_H #define WARDUINO_PRIM_H +#include "../Utils/macros.h" #include "../WARDuino.h" /** @@ -26,4 +27,58 @@ bool resolve_external_memory(char *symbol, Memory **val); void install_primitives(); +std::vector get_io_state(Module *m); + +void restore_external_state(Module *m, + std::vector external_state); + +inline void create_stack(std::vector *stack) {} + +template +void create_stack(std::vector *stack, T value, Ts... args) { + StackValue stackValue; + if constexpr (std::is_same() || std::is_same()) { + stackValue.value.int32 = value; + stackValue.value_type = I32; + } else if constexpr (std::is_same()) { + stackValue.value.uint32 = value; + stackValue.value_type = I32; + } else if constexpr (std::is_same()) { + stackValue.value.int64 = value; + stackValue.value_type = I64; + } else if constexpr (std::is_same()) { + stackValue.value.uint64 = value; + stackValue.value_type = I64; + } else if constexpr (std::is_same()) { + stackValue.value.f32 = value; + stackValue.value_type = F32; + } else if constexpr (std::is_same()) { + stackValue.value.f64 = value; + stackValue.value_type = F64; + } else { + // This will trigger a compile time error if a different unsupported + // type is used. + static_assert( + sizeof(T) == 0, + "Unsupported argument type! Expected i32, i64, f32 or f64."); + } + stack->push_back(stackValue); + + create_stack(stack, args...); +} + +template +void invoke_primitive(Module *m, const std::string &function_name, Ts... args) { + Primitive primitive; + resolve_primitive((char *)function_name.c_str(), &primitive); + + std::vector argStack; + create_stack(&argStack, args...); + + for (auto arg : argStack) { + m->stack[++m->sp] = arg; + } + primitive(m); +} + #endif diff --git a/src/Primitives/zephyr.cpp b/src/Primitives/zephyr.cpp index 56ed58be..4da4f7d9 100644 --- a/src/Primitives/zephyr.cpp +++ b/src/Primitives/zephyr.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -32,10 +33,7 @@ #include "../Utils/util.h" #include "primitives.h" -#define NUM_PRIMITIVES 0 -#define NUM_PRIMITIVES_ARDUINO 5 - -#define ALL_PRIMITIVES (NUM_PRIMITIVES + NUM_PRIMITIVES_ARDUINO) +#define ALL_PRIMITIVES 5 // Global index for installing primitives int prim_index = 0; @@ -53,15 +51,32 @@ double sensor_emu = 0; PrimitiveEntry *p = &primitives[prim_index++]; \ p->name = #prim_name; \ p->f = &(prim_name); \ + p->f_reverse = nullptr; \ + p->f_serialize_state = nullptr; \ } else { \ FATAL("pim_index out of bounds"); \ } \ } +#define install_primitive_reverse(prim_name) \ + { \ + PrimitiveEntry *p = &primitives[prim_index - 1]; \ + p->f_reverse = &(prim_name##_reverse); \ + p->f_serialize_state = &(prim_name##_serialize); \ + } + #define def_prim(function_name, type) \ Type function_name##_type = type; \ bool function_name(Module *m) +#define def_prim_reverse(function_name) \ + void function_name##_reverse(Module *m, \ + std::vector external_state) + +#define def_prim_serialize(function_name) \ + void function_name##_serialize( \ + std::vector &external_state) + // TODO: use fp #define pop_args(n) m->sp -= n #define get_arg(m, arg) m->stack[(m)->sp - (arg)].value @@ -297,4 +312,42 @@ bool resolve_external_memory(char *symbol, Memory **val) { return false; } +//------------------------------------------------------ +// Restore external state when restoring a snapshot +//------------------------------------------------------ +void restore_external_state(Module *m, + std::vector external_state) { + uint8_t opcode = *m->pc_ptr; + // TODO: Maybe primitives can also be called using the other call + // instructions such as call_indirect + // maybe there should just be a function that checks if a certain function + // is being called that handles all these cases? + if (opcode == 0x10) { // call opcode + uint8_t *pc_copy = m->pc_ptr + 1; + uint32_t fidx = read_LEB_32(&pc_copy); + if (fidx < m->import_count) { + for (auto &primitive : primitives) { + if (!strcmp(primitive.name, m->functions[fidx].import_field)) { + if (primitive.f_reverse) { + debug("Reversing action for primitive %s\n", + primitive.name); + primitive.f_reverse(m, external_state); + } + return; + } + } + } + } +} + +std::vector get_io_state(Module *m) { + std::vector ioState; + for (auto &primitive : primitives) { + if (primitive.f_serialize_state) { + primitive.f_serialize_state(ioState); + } + } + return ioState; +} + #endif // ARDUINO diff --git a/src/WARDuino.h b/src/WARDuino.h index 3d2d08b1..8d04d160 100644 --- a/src/WARDuino.h +++ b/src/WARDuino.h @@ -63,9 +63,17 @@ extern char exception[512]; typedef bool (*Primitive)(Module *); +struct IOStateElement { + std::string key; + bool output; + int value; +}; + typedef struct PrimitiveEntry { const char *name; Primitive f; + void (*f_reverse)(Module *m, std::vector); + void (*f_serialize_state)(std::vector &); Type t; } PrimitiveEntry;