diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 878c3de4..360b8121 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,9 +22,8 @@ jobs: - name: Install deps run: | sudo apt-get install lcov - # Commented out because we dont do LFC tests at the moment. - # - name: Setup java and gradle for compiling lfc - # uses: ./.github/actions/prepare-build-env + - name: Setup java and gradle for compiling lfc + uses: ./.github/actions/prepare-build-env # Uncomment to SSH into the runner. # - name: Setup upterm session # uses: lhotari/action-upterm@v1 @@ -33,6 +32,7 @@ jobs: source env.bash make format-check make unit-test + make lf-test make examples make coverage - name: Coverage diff --git a/CMakeLists.txt b/CMakeLists.txt index 73099e3f..1e27d757 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,18 +40,16 @@ if(BUILD_TESTS) endif() endif() - file(GLOB SOURCES "src/*.c" "external/proto/*.c") - set(NANOPB_PATH external/nanopb) if (PLATFORM STREQUAL "POSIX") add_library(reactor-uc STATIC ${SOURCES}) - # Add nanopb library manually - add_library(nanopb ${NANOPB_PATH}/pb_common.c ${NANOPB_PATH}/pb_encode.c ${NANOPB_PATH}/pb_decode.c) - set_target_properties(nanopb PROPERTIES C_CLANG_TIDY "") # Disable clang-tidy for this external lib. - - target_link_libraries(reactor-uc PRIVATE pthread nanopb) + target_link_libraries(reactor-uc PRIVATE pthread) +elseif (PLATFORM STREQUAL "FLEXPRET") + add_library(reactor-uc STATIC ${SOURCES}) + add_subdirectory($ENV{FP_SDK_PATH} BINARY_DIR) + target_link_libraries(reactor-uc PUBLIC fp-sdk) elseif (PLATFORM STREQUAL "ZEPHYR") zephyr_library_named(reactor-uc) zephyr_library_sources(${SOURCES}) @@ -68,6 +66,13 @@ else () message(FATAL_ERROR "No valid platform specified") endif () +# Add nanopb library. Note that for Zephyr it is built customly above. +if (NOT PLATFORM STREQUAL "ZEPHYR") + add_library(nanopb ${NANOPB_PATH}/pb_common.c ${NANOPB_PATH}/pb_encode.c ${NANOPB_PATH}/pb_decode.c) + set_target_properties(nanopb PROPERTIES C_CLANG_TIDY "") # Disable clang-tidy for this external lib. + target_link_libraries(reactor-uc PRIVATE nanopb) +endif() + # Add compile definitions for platform and network channel specifics. target_compile_definitions(reactor-uc PRIVATE "PLATFORM_${PLATFORM}") @@ -80,6 +85,7 @@ if(NETWORK_CHANNEL_TCP_POSIX) endif() target_compile_options(reactor-uc PRIVATE -Wall -Wextra -Werror) +target_compile_options(reactor-uc PUBLIC -Wno-zero-length-bounds -Wno-stack-usage) add_compile_options (-fdiagnostics-color=always) diff --git a/Makefile b/Makefile index b82d4a84..9c720462 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: clean test coverage asan format format-check ci lf-test lib proto examples -test: unit-test examples +test: unit-test lf-test examples # Generate protobuf code proto: diff --git a/examples/common/timer_source.h b/examples/common/timer_source.h index e6d0aefa..6a569243 100644 --- a/examples/common/timer_source.h +++ b/examples/common/timer_source.h @@ -1,8 +1,8 @@ #include "reactor-uc/reactor-uc.h" -DEFINE_TIMER_STRUCT(TimerSource, t, 1); -DEFINE_TIMER_CTOR(TimerSource, t, 1); -DEFINE_REACTION_STRUCT(TimerSource, r, 1); +DEFINE_TIMER_STRUCT(TimerSource, t, 1, 0); +DEFINE_TIMER_CTOR(TimerSource, t, 1, 0); +DEFINE_REACTION_STRUCT(TimerSource, r, 0); DEFINE_REACTION_CTOR(TimerSource, r, 0); typedef struct { diff --git a/examples/flexpret/CMakeLists.txt b/examples/flexpret/CMakeLists.txt new file mode 100644 index 00000000..15e595b9 --- /dev/null +++ b/examples/flexpret/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.22) + +if(NOT DEFINED ENV{FP_SDK_PATH}) + message(FATAL_ERROR "FP_SDK_PATH environment variable not set!") +endif() + +include($ENV{FP_SDK_PATH}/cmake/riscv-toolchain.cmake) +include($ENV{FP_SDK_PATH}/cmake/fp-app.cmake) + +project(fp-lf) + +# add_executable(fp-hello src/main.c) +# add_subdirectory(src-gen/HelloFlexPRET) +# target_link_libraries(fp-hello PUBLIC HelloFlexPRET) +# fp_add_outputs(fp-hello) + + +add_executable(fp-smoke src/main.c) +add_subdirectory(src-gen/Smoke) +target_link_libraries(fp-smoke PUBLIC Smoke) +fp_add_outputs(fp-smoke) \ No newline at end of file diff --git a/examples/flexpret/src/HelloFlexPRET.lf b/examples/flexpret/src/HelloFlexPRET.lf new file mode 100644 index 00000000..871cff83 --- /dev/null +++ b/examples/flexpret/src/HelloFlexPRET.lf @@ -0,0 +1,9 @@ +target uC { + platform: FlexPRET +} + +main reactor { + reaction(startup) {= + printf("Hello, FlexPRET!\n"); + =} +} \ No newline at end of file diff --git a/examples/flexpret/src/Smoke.lf b/examples/flexpret/src/Smoke.lf new file mode 100644 index 00000000..ca277812 --- /dev/null +++ b/examples/flexpret/src/Smoke.lf @@ -0,0 +1,30 @@ +target uC { + platform: FlexPRET +} + +reactor R1 { + output out: int + + reaction(startup) -> out {= + lf_set(out, 42); + =} +} + +reactor R2 { + input in: int + + reaction(startup) {= + printf("Hello from R2, FlexPRET!\n"); + =} + + reaction(in) {= + printf("Received: %d\n", in->value); + validate(in->value == 42); + =} +} + +main reactor { + r1 = new R1() + r2 = new R2() + r1.out -> r2.in +} \ No newline at end of file diff --git a/examples/flexpret/src/main.c b/examples/flexpret/src/main.c new file mode 100644 index 00000000..306ba3a2 --- /dev/null +++ b/examples/flexpret/src/main.c @@ -0,0 +1,5 @@ +#include "lf_main.h" + +int main() { + lf_start(); +} \ No newline at end of file diff --git a/examples/lf/src/HelloUc.lf b/examples/lf/src/HelloUc.lf deleted file mode 100644 index 44fbce45..00000000 --- a/examples/lf/src/HelloUc.lf +++ /dev/null @@ -1,9 +0,0 @@ -target uC - -main reactor { - timer t(0, 500 msec) - - reaction(t) {= - printf("Reaction triggered at %ld\n", env->get_elapsed_physical_time(env)); - =} -} \ No newline at end of file diff --git a/examples/posix/federated/receiver.c b/examples/posix/federated/receiver.c index a2e688d4..0bec91fb 100644 --- a/examples/posix/federated/receiver.c +++ b/examples/posix/federated/receiver.c @@ -21,8 +21,8 @@ lf_ret_t deserialize_msg_t(void *user_struct, const unsigned char *msg_buf, size DEFINE_REACTION_STRUCT(Receiver, r, 0); DEFINE_REACTION_CTOR(Receiver, r, 0); -DEFINE_INPUT_STRUCT(Receiver, in, 1, msg_t, 0); -DEFINE_INPUT_CTOR(Receiver, in, 1, msg_t, 0); +DEFINE_INPUT_STRUCT(Receiver, in, 1, 0, msg_t, 0); +DEFINE_INPUT_CTOR(Receiver, in, 1, 0, msg_t, 0); typedef struct { Reactor super; @@ -40,14 +40,14 @@ DEFINE_REACTION_BODY(Receiver, r) { in->value.size); } -REACTOR_CTOR_SIGNATURE(Receiver) { +REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs in_external) { REACTOR_CTOR_PREAMBLE(); REACTOR_CTOR(Receiver); INITIALIZE_REACTION(Receiver, r); - INITIALIZE_INPUT(Receiver, in); + INITIALIZE_INPUT(Receiver, in, in_external); // Register reaction as an effect of in - INPUT_REGISTER_EFFECT(in, r); + PORT_REGISTER_EFFECT(in, r); } DEFINE_FEDERATED_INPUT_CONNECTION(Receiver, in, msg_t, 5, MSEC(100), false); @@ -71,12 +71,14 @@ typedef struct { CHILD_REACTOR_INSTANCE(Receiver, receiver); FEDERATED_CONNECTION_BUNDLE_INSTANCE(Receiver, Sender); FEDERATE_BOOKKEEPING_INSTANCES(0,0,1,1); + CHILD_INPUT_SOURCES(receiver, in, 0); } MainRecv; REACTOR_CTOR_SIGNATURE(MainRecv) { FEDERATE_CTOR_PREAMBLE(); REACTOR_CTOR(MainRecv); - INITIALIZE_CHILD_REACTOR(Receiver, receiver); + DEFINE_CHILD_INPUT_ARGS(receiver, in); + INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, _receiver_in_args); INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Receiver, Sender); BUNDLE_REGISTER_DOWNSTREAM(Receiver, Sender, receiver, in); } diff --git a/examples/posix/federated/sender.c b/examples/posix/federated/sender.c index 3e8ad225..e240dc03 100644 --- a/examples/posix/federated/sender.c +++ b/examples/posix/federated/sender.c @@ -21,11 +21,11 @@ size_t serialize_msg_t(const void *user_struct, size_t user_struct_size, unsigne return sizeof(msg->size) + msg->size; } -DEFINE_TIMER_STRUCT(Sender, t, 1) -DEFINE_TIMER_CTOR(Sender, t, 1) +DEFINE_TIMER_STRUCT(Sender, t, 1, 0) +DEFINE_TIMER_CTOR(Sender, t, 1, 0) DEFINE_REACTION_STRUCT(Sender, r, 1) DEFINE_REACTION_CTOR(Sender, r, 0) -DEFINE_OUTPUT_STRUCT(Sender, out, 1) +DEFINE_OUTPUT_STRUCT(Sender, out, 1, interval_t) DEFINE_OUTPUT_CTOR(Sender, out, 1) typedef struct { @@ -48,16 +48,16 @@ DEFINE_REACTION_BODY(Sender, r) { lf_set(out, val); } -REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, Connection **conn_out, size_t conn_out_num) { +REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs out_external) { REACTOR_CTOR_PREAMBLE(); REACTOR_CTOR(Sender); INITIALIZE_REACTION(Sender, r); INITIALIZE_TIMER(Sender, t, MSEC(0), SEC(1)); - INITIALIZE_OUTPUT(Sender, out, conn_out, conn_out_num); + INITIALIZE_OUTPUT(Sender, out, out_external); TIMER_REGISTER_EFFECT(t, r); REACTION_REGISTER_EFFECT(r, out); - OUTPUT_REGISTER_SOURCE(out, r); + PORT_REGISTER_SOURCE(out, r); } DEFINE_FEDERATED_OUTPUT_CONNECTION(Sender, out, msg_t, 1) @@ -85,12 +85,15 @@ typedef struct { FEDERATED_CONNECTION_BUNDLE_INSTANCE(Sender, Receiver); TcpIpChannel channel; FEDERATE_BOOKKEEPING_INSTANCES(0,0,1,1); - CONTAINED_OUTPUT_CONNECTIONS(sender, out, 1); + CHILD_OUTPUT_CONNECTIONS(sender, out, 1); + CHILD_OUTPUT_EFFECTS(sender, out, 0); + CHILD_OUTPUT_OBSERVERS(sender, out, 0); } MainSender; REACTOR_CTOR_SIGNATURE(MainSender) { FEDERATE_CTOR_PREAMBLE(); - INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, self->_conns_sender_out_out, 1); + DEFINE_CHILD_OUTPUT_ARGS(sender, out); + INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, _sender_out_args); INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver); BUNDLE_REGISTER_UPSTREAM(Sender, Receiver, sender, out); REACTOR_CTOR(MainSender); diff --git a/examples/zephyr/basic_federated/common/receiver.h b/examples/zephyr/basic_federated/common/receiver.h index b32253a3..5068e885 100644 --- a/examples/zephyr/basic_federated/common/receiver.h +++ b/examples/zephyr/basic_federated/common/receiver.h @@ -35,8 +35,8 @@ typedef struct { DEFINE_REACTION_STRUCT(Receiver, r, 0); DEFINE_REACTION_CTOR(Receiver, r, 0) -DEFINE_INPUT_STRUCT(Receiver, in, 1, msg_t, 0) -DEFINE_INPUT_CTOR(Receiver, in, 1, msg_t, 0) +DEFINE_INPUT_STRUCT(Receiver, in, 1, 0, msg_t, 0) +DEFINE_INPUT_CTOR(Receiver, in, 1, 0, msg_t, 0) typedef struct { Reactor super; @@ -54,14 +54,14 @@ DEFINE_REACTION_BODY(Receiver, r) { env->get_logical_time(env), env->get_physical_time(env)); } -REACTOR_CTOR_SIGNATURE(Receiver) { +REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs in_external) { REACTOR_CTOR_PREAMBLE(); REACTOR_CTOR(Receiver); INITIALIZE_REACTION(Receiver, r); - INITIALIZE_INPUT(Receiver, in); + INITIALIZE_INPUT(Receiver, in, in_external); // Register reaction as an effect of in - INPUT_REGISTER_EFFECT(in, r); + PORT_REGISTER_EFFECT(in, r); } @@ -87,12 +87,14 @@ typedef struct { CHILD_REACTOR_INSTANCE(Receiver, receiver); FEDERATED_CONNECTION_BUNDLE_INSTANCE(Receiver, Sender); FEDERATE_BOOKKEEPING_INSTANCES(0,0,1,1); + CHILD_INPUT_SOURCES(receiver, in, 0); } MainRecv; REACTOR_CTOR_SIGNATURE(MainRecv) { FEDERATE_CTOR_PREAMBLE(); REACTOR_CTOR(MainRecv); - INITIALIZE_CHILD_REACTOR(Receiver, receiver); + DEFINE_CHILD_INPUT_ARGS(receiver, in); + INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, _receiver_in_args); INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Receiver, Sender); BUNDLE_REGISTER_DOWNSTREAM(Receiver, Sender, receiver, in); } diff --git a/examples/zephyr/basic_federated/federated_sender/src/sender.c b/examples/zephyr/basic_federated/federated_sender/src/sender.c index 98fca6ba..ffe1ec10 100644 --- a/examples/zephyr/basic_federated/federated_sender/src/sender.c +++ b/examples/zephyr/basic_federated/federated_sender/src/sender.c @@ -17,12 +17,16 @@ static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, { static struct gpio_callback button_cb_data; static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios); -DEFINE_ACTION_STRUCT(Sender, act, PHYSICAL_ACTION, 1, 0, 10, bool); -DEFINE_ACTION_CTOR(Sender, act, PHYSICAL_ACTION, 1, 0, 10, bool); +typedef struct { + char msg[32]; +} msg_t; + +DEFINE_ACTION_STRUCT(Sender, act, PHYSICAL_ACTION, 1, 0, 0, 10, bool); +DEFINE_ACTION_CTOR(Sender, act, PHYSICAL_ACTION, 1, 0, 0, 10, bool); DEFINE_REACTION_STRUCT(Sender, r, 1); DEFINE_REACTION_CTOR(Sender, r, 0); -DEFINE_OUTPUT_STRUCT(Sender, out, 1) +DEFINE_OUTPUT_STRUCT(Sender, out, 1, msg_t) DEFINE_OUTPUT_CTOR(Sender, out, 1) Sender_act *action_ptr = NULL; @@ -62,9 +66,6 @@ void setup_led() { gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); } -typedef struct { - char msg[32]; -} msg_t; typedef struct { Reactor super; @@ -86,16 +87,16 @@ DEFINE_REACTION_BODY(Sender, r) { lf_set(out, val); } -REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, Connection **conn_out, size_t conn_out_num) { +REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs out_external) { REACTOR_CTOR_PREAMBLE(); REACTOR_CTOR(Sender); INITIALIZE_REACTION(Sender, r); INITIALIZE_ACTION(Sender, act, MSEC(0)); - INITIALIZE_OUTPUT(Sender, out, conn_out, conn_out_num); + INITIALIZE_OUTPUT(Sender, out, out_external); ACTION_REGISTER_EFFECT(act, r); REACTION_REGISTER_EFFECT(r, out); - OUTPUT_REGISTER_SOURCE(out, r); + PORT_REGISTER_SOURCE(out, r); } DEFINE_FEDERATED_OUTPUT_CONNECTION(Sender, out, msg_t, 1) @@ -138,15 +139,18 @@ typedef struct { CHILD_REACTOR_INSTANCE(Sender, sender); FEDERATED_CONNECTION_BUNDLE_INSTANCE(Sender, Receiver1); FEDERATED_CONNECTION_BUNDLE_INSTANCE(Sender, Receiver2); - CONTAINED_OUTPUT_CONNECTIONS(sender, out, 2); + CHILD_OUTPUT_CONNECTIONS(sender, out, 2); + CHILD_OUTPUT_EFFECTS(sender, out, 0); + CHILD_OUTPUT_OBSERVERS(sender, out, 0); FEDERATE_BOOKKEEPING_INSTANCES(0,0,1,2); } MainSender; REACTOR_CTOR_SIGNATURE(MainSender) { FEDERATE_CTOR_PREAMBLE(); REACTOR_CTOR(MainSender); - - INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, self->_conns_sender_out_out, 2); + + DEFINE_CHILD_OUTPUT_ARGS(sender, out); + INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, _sender_out_args); INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver1); INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver2); diff --git a/examples/zephyr/hello_lf/CMakeLists.txt b/examples/zephyr/hello_lf/CMakeLists.txt new file mode 100644 index 00000000..825b89bb --- /dev/null +++ b/examples/zephyr/hello_lf/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(HelloLF) + +target_sources(app PRIVATE src/main.c) +add_subdirectory(src-gen/HelloLF) +target_link_libraries(app PRIVATE HelloLF) diff --git a/examples/zephyr/hello_lf/README.md b/examples/zephyr/hello_lf/README.md new file mode 100644 index 00000000..9ce67dfd --- /dev/null +++ b/examples/zephyr/hello_lf/README.md @@ -0,0 +1,7 @@ +# Simple example of using the reactor-uc with LF and Zephyr + +``` +lfc src/HelloLF.lf -c +west build -b qemu_cortex_m3 +west build -t run +``` \ No newline at end of file diff --git a/examples/zephyr/hello_lf/prj.conf b/examples/zephyr/hello_lf/prj.conf new file mode 100644 index 00000000..e69de29b diff --git a/examples/zephyr/hello_lf/src/HelloLF.lf b/examples/zephyr/hello_lf/src/HelloLF.lf new file mode 100644 index 00000000..a884a08c --- /dev/null +++ b/examples/zephyr/hello_lf/src/HelloLF.lf @@ -0,0 +1,9 @@ +target uC { + platform: Zephyr +} + +main reactor { + reaction(startup) {= + printf("Hello from LF!\n"); + =} +} \ No newline at end of file diff --git a/examples/zephyr/hello_lf/src/main.c b/examples/zephyr/hello_lf/src/main.c new file mode 100644 index 00000000..306ba3a2 --- /dev/null +++ b/examples/zephyr/hello_lf/src/main.c @@ -0,0 +1,5 @@ +#include "lf_main.h" + +int main() { + lf_start(); +} \ No newline at end of file diff --git a/include/reactor-uc/action.h b/include/reactor-uc/action.h index 31cde18c..a6184b84 100644 --- a/include/reactor-uc/action.h +++ b/include/reactor-uc/action.h @@ -18,6 +18,7 @@ struct Action { void *value_ptr; TriggerEffects effects; // The reactions triggered by this Action. TriggerSources sources; // The reactions that can write to this Action. + TriggerObservers observers; // The reactions that can observe this Action. EventPayloadPool payload_pool; // Pool of memory for the data associated with the events scheduled on this action. size_t max_pending_events; size_t events_scheduled; @@ -28,7 +29,8 @@ struct Action { }; void Action_ctor(Action *self, ActionType type, interval_t min_offset, Reactor *parent, Reaction **sources, - size_t sources_size, Reaction **effects, size_t effects_size, void *value_ptr, size_t value_size, - void *payload_buf, bool *payload_used_buf, size_t event_bound); + size_t sources_size, Reaction **effects, size_t effects_size, Reaction **observers, + size_t observers_size, void *value_ptr, size_t value_size, void *payload_buf, bool *payload_used_buf, + size_t event_bound); #endif diff --git a/include/reactor-uc/builtin_triggers.h b/include/reactor-uc/builtin_triggers.h index be03ef15..88880adf 100644 --- a/include/reactor-uc/builtin_triggers.h +++ b/include/reactor-uc/builtin_triggers.h @@ -10,10 +10,11 @@ typedef struct BuiltinTrigger BuiltinTrigger; struct BuiltinTrigger { Trigger super; TriggerEffects effects; + TriggerObservers observers; BuiltinTrigger *next; }; void BuiltinTrigger_ctor(BuiltinTrigger *self, TriggerType type, Reactor *parent, Reaction **effects, - size_t effects_size); + size_t effects_size, Reaction **observers, size_t observers_size); #endif diff --git a/include/reactor-uc/connection.h b/include/reactor-uc/connection.h index af6d2912..be6de2b6 100644 --- a/include/reactor-uc/connection.h +++ b/include/reactor-uc/connection.h @@ -24,7 +24,7 @@ struct Connection { size_t downstreams_size; // Size of downstreams array size_t downstreams_registered; // Number of downstreams currently registered void (*register_downstream)(Connection *, Port *); - Output *(*get_final_upstream)(Connection *); + Port *(*get_final_upstream)(Connection *); void (*trigger_downstreams)(Connection *, const void *value_ptr, size_t value_size); }; diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 6c13936f..1d688975 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -6,10 +6,7 @@ do { \ __typeof__(val) __val = (val); \ Port *_port = (Port *)(port); \ - for (size_t i = 0; i < _port->conns_out_registered; i++) { \ - Connection *__conn = _port->conns_out[i]; \ - __conn->trigger_downstreams(__conn, (const void *)&__val, sizeof(__val)); \ - } \ + _port->set(_port, &__val); \ } while (0) /** @@ -44,6 +41,9 @@ #define lf_schedule(...) LF_SCHEDULE_CHOOSER(__VA_ARGS__)(__VA_ARGS__) +// FIXME: Separate user-facing vs internal macros +// FIXME: Group macros either by functionality or by element they operate on + /** * @brief Convenience macro for registering a reaction as an effect of a trigger. * The input must be a pointer to a derived Trigger type with an effects field. @@ -64,6 +64,14 @@ (trigger)->sources.reactions[(trigger)->sources.num_registered++] = (source); \ } while (0) +// Register a reaction as a source of a trigger. `trigger` must be a pointer to +// a derived Trigger type. +#define TRIGGER_REGISTER_OBSERVER(trigger, observer) \ + do { \ + assert((trigger)->observers.num_registered < (trigger)->observers.size); \ + (trigger)->observers.reactions[(trigger)->observers.num_registered++] = (observer); \ + } while (0) + // The following macros casts the inputs into the correct types before calling TRIGGER_REGISTER_EFFECTs #define ACTION_REGISTER_EFFECT(TheAction, TheEffect) \ TRIGGER_REGISTER_EFFECT((Action *)&self->TheAction, (Reaction *)&self->TheEffect) @@ -71,18 +79,34 @@ #define ACTION_REGISTER_SOURCE(TheAction, TheSource) \ TRIGGER_REGISTER_SOURCE((Action *)&self->TheAction, (Reaction *)&self->TheSource) +#define ACTION_REGISTER_OBSERVER(TheAction, TheObserver) \ + TRIGGER_REGISTER_OBSERVER((Action *)&self->TheAction, (Reaction *)&self->TheObserver) + #define TIMER_REGISTER_EFFECT(TheTimer, TheEffect) \ TRIGGER_REGISTER_EFFECT((Timer *)(&(self->TheTimer)), (Reaction *)(&(self->TheEffect))) +#define TIMER_REGISTER_OBSERVER(TheTimer, TheObserver) \ + TRIGGER_REGISTER_OBSERVER((Timer *)(&(self->TheTimer)), (Reaction *)(&(self->TheObserver))) + #define STARTUP_REGISTER_EFFECT(effect) \ TRIGGER_REGISTER_EFFECT((BuiltinTrigger *)&(self->startup), (Reaction *)&self->effect) -#define INPUT_REGISTER_EFFECT(_Input, _Effect) \ - TRIGGER_REGISTER_EFFECT((Input *)&self->_Input, (Reaction *)&self->_Effect) +#define STARTUP_REGISTER_OBSERVER(observer) \ + TRIGGER_REGISTER_OBSERVER((BuiltinTrigger *)&(self->startup), (Reaction *)&self->observer) + +#define PORT_REGISTER_EFFECT(_Port, _Effect) TRIGGER_REGISTER_EFFECT((Port *)&self->_Port, (Reaction *)&self->_Effect) + +#define PORT_REGISTER_SOURCE(_Port, _Source) TRIGGER_REGISTER_SOURCE((Port *)&self->_Port, (Reaction *)&self->_Source) + +#define PORT_REGISTER_OBSERVER(_Port, _Observer) \ + TRIGGER_REGISTER_OBSERVER((Port *)&self->_Port, (Reaction *)&self->_Observer) #define SHUTDOWN_REGISTER_EFFECT(effect) \ TRIGGER_REGISTER_EFFECT((BuiltinTrigger *)&(self->shutdown), (Reaction *)&self->effect) +#define SHUTDOWN_REGISTER_OBSERVER(observer) \ + TRIGGER_REGISTER_OBSERVER((BuiltinTrigger *)&(self->shutdown), (Reaction *)&self->observer) + /** * @brief Convenience macro for registering a trigger as an effect of a reaction. * @@ -94,10 +118,6 @@ (__reaction)->effects[(__reaction)->effects_registered++] = (Trigger *)&self->_Effect; \ } while (0) -// Convenient translation from a user trigger to a pointer to the derived Trigger type. -#define OUTPUT_REGISTER_SOURCE(_Output, _Source) \ - TRIGGER_REGISTER_SOURCE((Output *)&self->_Output, (Reaction *)&self->_Source); - // Convenience macro to register a downstream port on a connection. // TODO: Replace entirely the need for `register_downstream`. #define CONN_REGISTER_DOWNSTREAM(conn, down) \ @@ -105,12 +125,6 @@ ((Connection *)&(conn))->register_downstream((Connection *)&(conn), (Port *)&(down)); \ } while (0) -#define BUNDLE_REGISTER_DOWNSTREAM(ReactorName, OtherName, InstanceName, Port) \ - CONN_REGISTER_DOWNSTREAM(self->ReactorName##_##OtherName##_bundle.conn_##Port, self->InstanceName.Port); - -#define BUNDLE_REGISTER_UPSTREAM(ReactorName, OtherName, InstanceName, Port) \ - CONN_REGISTER_UPSTREAM(self->ReactorName##_##OtherName##_bundle.conn_##Port, self->InstanceName.Port); - // Convenience macro to register an upstream port on a connection #define CONN_REGISTER_UPSTREAM(conn, up) \ do { \ @@ -120,14 +134,11 @@ _up->conns_out[_up->conns_out_registered++] = (Connection *)&(conn); \ } while (0) -// Convenience macro to register upstream and downstream on a connection -#define LOGICAL_CONNECT(SourceReactor, SourcePort, DestReactor, DestPort) \ - CONN_REGISTER_UPSTREAM(self->conn_##SourcePort, self->SourceReactor.SourcePort); \ - CONN_REGISTER_DOWNSTREAM(self->conn_##SourcePort, self->DestReactor.DestPort); +#define BUNDLE_REGISTER_DOWNSTREAM(ReactorName, OtherName, InstanceName, Port) \ + CONN_REGISTER_DOWNSTREAM(self->ReactorName##_##OtherName##_bundle.conn_##Port, self->InstanceName.Port); -#define DELAYED_CONNECT(SourceReactor, SourcePort, DestReactor, DestPort) \ - CONN_REGISTER_UPSTREAM(self->delayed_conn_##SourcePort, self->SourceReactor.SourcePort); \ - CONN_REGISTER_DOWNSTREAM(self->delayed_conn_##SourcePort, self->DestReactor.DestPort); +#define BUNDLE_REGISTER_UPSTREAM(ReactorName, OtherName, InstanceName, Port) \ + CONN_REGISTER_UPSTREAM(self->ReactorName##_##OtherName##_bundle.conn_##Port, self->InstanceName.Port); // Macros for creating the structs and ctors @@ -162,51 +173,60 @@ REACTOR_BOOKKEEPING_INSTANCES(NumReactions, NumTriggers, NumChildren); \ FederatedConnectionBundle *_bundles[NumBundles]; -#define DEFINE_OUTPUT_STRUCT(ReactorName, PortName, SourceSize) \ +#define DEFINE_OUTPUT_STRUCT(ReactorName, PortName, SourceSize, BufferType) \ typedef struct { \ - Output super; \ + Port super; \ Reaction *sources[(SourceSize)]; \ + BufferType value; \ } ReactorName##_##PortName; #define DEFINE_OUTPUT_CTOR(ReactorName, PortName, SourceSize) \ - void ReactorName##_##PortName##_ctor(ReactorName##_##PortName *self, Reactor *parent, Connection **conn_out, \ - size_t conn_num) { \ - Output_ctor(&self->super, parent, self->sources, SourceSize, conn_out, conn_num); \ + void ReactorName##_##PortName##_ctor(ReactorName##_##PortName *self, Reactor *parent, \ + OutputExternalCtorArgs external) { \ + Port_ctor(&self->super, TRIG_OUTPUT, parent, &self->value, sizeof(self->value), external.parent_effects, \ + external.parent_effects_size, self->sources, SourceSize, external.parent_observers, \ + external.parent_observers_size, external.conns_out, external.conns_out_size); \ } #define PORT_INSTANCE(ReactorName, PortName) ReactorName##_##PortName PortName; +#define PORT_PTR_INSTANCE(ReactorName, PortName) ReactorName##_##PortName *PortName; -#define INITIALIZE_OUTPUT(ReactorName, PortName, Conns, ConnSize) \ - ReactorName##_##PortName##_ctor(&self->PortName, &self->super, Conns, ConnSize) +#define INITIALIZE_OUTPUT(ReactorName, PortName, External) \ + ReactorName##_##PortName##_ctor(&self->PortName, &self->super, External); -#define INITIALIZE_INPUT(ReactorName, PortName) \ +#define INITIALIZE_INPUT(ReactorName, PortName, External) \ self->_triggers[_triggers_idx++] = (Trigger *)&self->PortName; \ - ReactorName##_##PortName##_ctor(&self->PortName, &self->super) + ReactorName##_##PortName##_ctor(&self->PortName, &self->super, External); -#define DEFINE_INPUT_STRUCT(ReactorName, PortName, EffectSize, BufferType, NumConnsOut) \ +#define DEFINE_INPUT_STRUCT(ReactorName, PortName, EffectSize, ObserversSize, BufferType, NumConnsOut) \ typedef struct { \ - Input super; \ + Port super; \ Reaction *effects[(EffectSize)]; \ + Reaction *observers[(ObserversSize)]; \ BufferType value; \ Connection *conns_out[(NumConnsOut)]; \ } ReactorName##_##PortName; -#define DEFINE_INPUT_CTOR(ReactorName, PortName, EffectSize, BufferType, NumConnsOut) \ - void ReactorName##_##PortName##_ctor(ReactorName##_##PortName *self, Reactor *parent) { \ - Input_ctor(&self->super, parent, self->effects, (EffectSize), (Connection **)&self->conns_out, NumConnsOut, \ - &self->value, sizeof(BufferType)); \ +#define DEFINE_INPUT_CTOR(ReactorName, PortName, EffectSize, ObserverSize, BufferType, NumConnsOut) \ + void ReactorName##_##PortName##_ctor(ReactorName##_##PortName *self, Reactor *parent, \ + InputExternalCtorArgs external) { \ + Port_ctor(&self->super, TRIG_INPUT, parent, &self->value, sizeof(self->value), self->effects, (EffectSize), \ + external.parent_sources, external.parent_sources_size, self->observers, ObserverSize, \ + (Connection **)&self->conns_out, NumConnsOut); \ } -#define DEFINE_TIMER_STRUCT(ReactorName, TimerName, EffectSize) \ +#define DEFINE_TIMER_STRUCT(ReactorName, TimerName, EffectSize, ObserversSize) \ typedef struct { \ Timer super; \ Reaction *effects[(EffectSize)]; \ + Reaction *observers[(ObserversSize)]; \ } ReactorName##_##TimerName; -#define DEFINE_TIMER_CTOR(ReactorName, TimerName, EffectSize) \ +// TODO: Dont need sizes +#define DEFINE_TIMER_CTOR(ReactorName, TimerName, EffectSize, ObserverSize) \ void ReactorName##_##TimerName##_ctor(ReactorName##_##TimerName *self, Reactor *parent, interval_t offset, \ interval_t period) { \ - Timer_ctor(&self->super, parent, offset, period, self->effects, EffectSize); \ + Timer_ctor(&self->super, parent, offset, period, self->effects, EffectSize, self->observers, ObserverSize); \ } #define TIMER_INSTANCE(ReactorName, TimerName) ReactorName##_##TimerName TimerName; @@ -237,16 +257,18 @@ sizeof(self->effects) / sizeof(self->effects[0]), Priority); \ } -#define DEFINE_STARTUP_STRUCT(ReactorName, EffectSize) \ +#define DEFINE_STARTUP_STRUCT(ReactorName, EffectSize, ObserversSize) \ typedef struct { \ BuiltinTrigger super; \ Reaction *effects[(EffectSize)]; \ + Reaction *observers[(ObserversSize)]; \ } ReactorName##_Startup; #define DEFINE_STARTUP_CTOR(ReactorName) \ void ReactorName##_Startup_ctor(ReactorName##_Startup *self, Reactor *parent) { \ BuiltinTrigger_ctor(&self->super, TRIG_STARTUP, parent, self->effects, \ - sizeof(self->effects) / sizeof(self->effects[0])); \ + sizeof(self->effects) / sizeof(self->effects[0]), self->observers, \ + sizeof(self->observers) / sizeof(self->observers[0])); \ } #define STARTUP_INSTANCE(ReactorName) ReactorName##_Startup startup; @@ -254,16 +276,18 @@ self->_triggers[_triggers_idx++] = (Trigger *)&self->startup; \ ReactorName##_Startup_ctor(&self->startup, &self->super) -#define DEFINE_SHUTDOWN_STRUCT(ReactorName, EffectSize) \ +#define DEFINE_SHUTDOWN_STRUCT(ReactorName, EffectSize, ObserversSize) \ typedef struct { \ BuiltinTrigger super; \ Reaction *effects[(EffectSize)]; \ + Reaction *observers[(ObserversSize)]; \ } ReactorName##_Shutdown; #define DEFINE_SHUTDOWN_CTOR(ReactorName) \ void ReactorName##_Shutdown_ctor(ReactorName##_Shutdown *self, Reactor *parent) { \ BuiltinTrigger_ctor(&self->super, TRIG_SHUTDOWN, parent, self->effects, \ - sizeof(self->effects) / sizeof(self->effects[0])); \ + sizeof(self->effects) / sizeof(self->effects[0]), self->observers, \ + sizeof(self->observers) / sizeof(self->observers[0])); \ } #define SHUTDOWN_INSTANCE(ReactorName) ReactorName##_Shutdown shutdown; @@ -272,8 +296,8 @@ self->_triggers[_triggers_idx++] = (Trigger *)&self->shutdown; \ ReactorName##_Shutdown_ctor(&self->shutdown, &self->super) -#define DEFINE_ACTION_STRUCT(ReactorName, ActionName, ActionType, EffectSize, SourceSize, MaxPendingEvents, \ - BufferType) \ +#define DEFINE_ACTION_STRUCT(ReactorName, ActionName, ActionType, EffectSize, SourceSize, ObserverSize, \ + MaxPendingEvents, BufferType) \ typedef struct { \ Action super; \ BufferType value; \ @@ -281,26 +305,31 @@ bool payload_used_buf[(MaxPendingEvents)]; \ Reaction *sources[(SourceSize)]; \ Reaction *effects[(EffectSize)]; \ + Reaction *observers[(ObserverSize)]; \ } ReactorName##_##ActionName; -#define DEFINE_ACTION_STRUCT_VOID(ReactorName, ActionName, ActionType, EffectSize, SourceSize, MaxPendingEvents) \ +#define DEFINE_ACTION_STRUCT_VOID(ReactorName, ActionName, ActionType, EffectSize, SourceSize, ObserverSize, \ + MaxPendingEvents) \ typedef struct { \ Action super; \ Reaction *sources[(SourceSize)]; \ Reaction *effects[(EffectSize)]; \ + Reaction *observers[(ObserverSize)]; \ } ReactorName##_##ActionName; -#define DEFINE_ACTION_CTOR(ReactorName, ActionName, ActionType, EffectSize, SourceSize, MaxPendingEvents, BufferType) \ +#define DEFINE_ACTION_CTOR(ReactorName, ActionName, ActionType, EffectSize, SourceSize, ObserverSize, \ + MaxPendingEvents, BufferType) \ void ReactorName##_##ActionName##_ctor(ReactorName##_##ActionName *self, Reactor *parent, interval_t min_delay) { \ Action_ctor(&self->super, ActionType, min_delay, parent, self->sources, (SourceSize), self->effects, (EffectSize), \ - &self->value, sizeof(BufferType), (void *)&self->payload_buf, self->payload_used_buf, \ - (MaxPendingEvents)); \ + self->observers, ObserverSize, &self->value, sizeof(BufferType), (void *)&self->payload_buf, \ + self->payload_used_buf, (MaxPendingEvents)); \ } -#define DEFINE_ACTION_CTOR_VOID(ReactorName, ActionName, ActionType, EffectSize, SourceSize, MaxPendingEvents) \ +#define DEFINE_ACTION_CTOR_VOID(ReactorName, ActionName, ActionType, EffectSize, SourceSize, ObserverSize, \ + MaxPendingEvents) \ void ReactorName##_##ActionName##_ctor(ReactorName##_##ActionName *self, Reactor *parent, interval_t min_delay) { \ Action_ctor(&self->super, ActionType, min_delay, parent, self->sources, (SourceSize), self->effects, (EffectSize), \ - NULL, 0, NULL, NULL, (MaxPendingEvents)); \ + self->observers, ObserverSize, NULL, 0, NULL, NULL, (MaxPendingEvents)); \ } #define ACTION_INSTANCE(ReactorName, ActionName) ReactorName##_##ActionName ActionName; @@ -309,55 +338,97 @@ self->_triggers[_triggers_idx++] = (Trigger *)&self->ActionName; \ ReactorName##_##ActionName##_ctor(&self->ActionName, &self->super, MinDelay) -#define SCOPE_ACTION(ReactorName, ActionName) ReactorName##_##ActionName *ActionName = &self->ActionName - -#define SCOPE_PORT(ReactorName, PortName) ReactorName##_##PortName *PortName = &self->PortName -#define SCOPE_SELF(ReactorName) ReactorName *self = (ReactorName *)_self->parent -#define SCOPE_ENV() Environment *env = self->super.env - -#define DEFINE_LOGICAL_CONNECTION_STRUCT(ParentName, ReactorName, OutputPort, DownstreamSize) \ +#define SCOPE_ACTION(ReactorName, ActionName) \ + ReactorName##_##ActionName *ActionName = &self->ActionName; \ + (void ActionName); +#define SCOPE_TIMER(ReactorName, TimerName) \ + ReactorName##_##TimerName *TimerName = &self->TimerName; \ + (void)TimerName; + +#define SCOPE_PORT(ReactorName, PortName) \ + ReactorName##_##PortName *PortName = &self->PortName; \ + (void)PortName +#define SCOPE_SELF(ReactorName) \ + ReactorName *self = (ReactorName *)_self->parent; \ + (void)self; +#define SCOPE_ENV() \ + Environment *env = self->super.env; \ + (void)env + +#define SCOPE_STARTUP(ReactorName) \ + ReactorName##_Startup *startup = &self->startup; \ + (void)startup; +#define SCOPE_SHUTDOWN(ReactorName) \ + ReactorName##_Shutdown *shutdown = &self->shutdown; \ + (void)shutdown; + + +#define DEFINE_LOGICAL_CONNECTION_STRUCT(ParentName, ConnName, DownstreamSize) \ typedef struct { \ LogicalConnection super; \ - Input *downstreams[(DownstreamSize)]; \ - } ParentName##_##ReactorName##_conn_##OutputPort; + Port *downstreams[(DownstreamSize)]; \ + } ParentName##_##ConnName; -#define DEFINE_LOGICAL_CONNECTION_CTOR(ParentName, ReactorName, OutputPort, DownstreamSize) \ - void ParentName##_##ReactorName##_conn_##OutputPort##_ctor(ParentName##_##ReactorName##_conn_##OutputPort *self, \ - Reactor *parent) { \ - LogicalConnection_ctor(&self->super, parent, (Port **)self->downstreams, \ +#define DEFINE_LOGICAL_CONNECTION_CTOR(ParentName, ConnName, DownstreamSize) \ + void ParentName##_##ConnName##_ctor(ParentName##_##ConnName *self, Reactor *parent) { \ + LogicalConnection_ctor(&self->super, parent, self->downstreams, \ sizeof(self->downstreams) / sizeof(self->downstreams[0])); \ } -#define LOGICAL_CONNECTION_INSTANCE(ParentName, ReactorName, OutputPort) \ - ParentName##_##ReactorName##_conn_##OutputPort conn_##OutputPort; -#define CONTAINED_OUTPUT_CONNECTIONS(ReactorName, OutputPort, NumConnsOut) \ - Connection *_conns_##ReactorName##_##OutputPort##_out[NumConnsOut]; +#define LOGICAL_CONNECTION_INSTANCE(ParentName, ConnName) ParentName##_##ConnName ConnName; + +#define CHILD_OUTPUT_CONNECTIONS(ReactorName, OutputPort, NumConnsOut) \ + Connection *_conns_##ReactorName##_##OutputPort[NumConnsOut]; + +#define CHILD_INPUT_SOURCES(ReactorName, InputPort, NumSources) \ + Reaction *_sources_##ReactorName##_##InputPort[NumSources]; + +#define CHILD_OUTPUT_EFFECTS(ReactorName, OutputPort, NumEffects) \ + Reaction *_effects_##ReactorName##_##OutputPort[NumEffects]; -#define INITIALIZE_LOGICAL_CONNECTION(ParentName, ReactorName, OutputPort) \ - ParentName##_##ReactorName##_conn_##OutputPort##_ctor(&self->conn_##OutputPort, &self->super) +#define CHILD_OUTPUT_OBSERVERS(ReactorName, OutputPort, NumObservers) \ + Reaction *_observers_##ReactorName##_##OutputPort[NumObservers]; -#define DEFINE_DELAYED_CONNECTION_STRUCT(ParentName, ReactorName, OutputPort, DownstreamSize, BufferType, BufferSize, \ - Delay) \ +#define DEFINE_CHILD_OUTPUT_ARGS(ReactorName, OutputPort) \ + OutputExternalCtorArgs _##ReactorName##_##OutputPort##_args = { \ + .parent_effects = self->_effects_##ReactorName##_##OutputPort, \ + .parent_effects_size = sizeof(self->_effects_##ReactorName##_##OutputPort) / \ + sizeof(self->_effects_##ReactorName##_##OutputPort[0]), \ + .parent_observers = self->_observers_##ReactorName##_##OutputPort, \ + .parent_observers_size = sizeof(self->_observers_##ReactorName##_##OutputPort) / \ + sizeof(self->_observers_##ReactorName##_##OutputPort[0]), \ + .conns_out = self->_conns_##ReactorName##_##OutputPort, \ + .conns_out_size = \ + sizeof(self->_conns_##ReactorName##_##OutputPort) / sizeof(self->_conns_##ReactorName##_##OutputPort[0])} + +#define DEFINE_CHILD_INPUT_ARGS(ReactorName, InputPort) \ + InputExternalCtorArgs _##ReactorName##_##InputPort##_args = { \ + .parent_sources = self->_sources_##ReactorName##_##InputPort, \ + .parent_sources_size = \ + sizeof(self->_sources_##ReactorName##_##InputPort) / sizeof(self->_sources_##ReactorName##_##InputPort[0])} + +#define INITIALIZE_LOGICAL_CONNECTION(ParentName, ConnName) \ + ParentName##_##ConnName##_ctor(&self->ConnName, &self->super) + +#define DEFINE_DELAYED_CONNECTION_STRUCT(ParentName, ConnName, DownstreamSize, BufferType, BufferSize, Delay) \ typedef struct { \ DelayedConnection super; \ BufferType payload_buf[(BufferSize)]; \ bool payload_used_buf[(BufferSize)]; \ - Input *downstreams[(BufferSize)]; \ - } ParentName##_##ReactorName##_delayed_conn_##OutputPort; - -#define DEFINE_DELAYED_CONNECTION_CTOR(ParentName, ReactorName, OutputPort, DownstreamSize, BufferType, BufferSize, \ - Delay, IsPhysical) \ - void ParentName##_##ReactorName##_delayed_conn_##OutputPort##_ctor( \ - ParentName##_##ReactorName##_delayed_conn_##OutputPort *self, Reactor *parent) { \ - DelayedConnection_ctor(&self->super, parent, (Port **)self->downstreams, DownstreamSize, Delay, IsPhysical, \ + Port *downstreams[(BufferSize)]; \ + } ParentName##_##ConnName; + +#define DEFINE_DELAYED_CONNECTION_CTOR(ParentName, ConnName, DownstreamSize, BufferType, BufferSize, Delay, \ + IsPhysical) \ + void ParentName##_##ConnName##_ctor(ParentName##_##ConnName *self, Reactor *parent) { \ + DelayedConnection_ctor(&self->super, parent, self->downstreams, DownstreamSize, Delay, IsPhysical, \ sizeof(BufferType), (void *)self->payload_buf, self->payload_used_buf, BufferSize); \ } -#define DELAYED_CONNECTION_INSTANCE(ParentName, ReactorName, OutputPort) \ - ParentName##_##ReactorName##_delayed_conn_##OutputPort delayed_conn_##OutputPort; +#define DELAYED_CONNECTION_INSTANCE(ParentName, ConnName) ParentName##_##ConnName ConnName; -#define INITIALIZE_DELAYED_CONNECTION(ParentName, ReactorName, OutputPort) \ - ParentName##_##ReactorName##_delayed_conn_##OutputPort##_ctor(&self->delayed_conn_##OutputPort, &self->super) +#define INITIALIZE_DELAYED_CONNECTION(ParentName, ConnName) \ + ParentName##_##ConnName##_ctor(&self->ConnName, &self->super) typedef struct FederatedOutputConnection FederatedOutputConnection; #define DEFINE_FEDERATED_OUTPUT_CONNECTION(ReactorName, OutputName, BufferType, BufferSize) \ @@ -384,7 +455,6 @@ typedef struct FederatedOutputConnection FederatedOutputConnection; #define FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(ReactorName, OtherName) \ void ReactorName##_##OtherName##_Bundle_ctor(ReactorName##_##OtherName##_Bundle *self, Reactor *parent) -// FIXME: Add parent pointer #define REACTOR_CTOR_SIGNATURE(ReactorName) \ void ReactorName##_ctor(ReactorName *self, Reactor *parent, Environment *env) @@ -413,7 +483,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; FederatedInputConnection super; \ BufferType payload_buf[(BufferSize)]; \ bool payload_used_buf[(BufferSize)]; \ - Input *downstreams[1]; \ + Port *downstreams[1]; \ } ReactorName##_##InputName##_conn; \ \ void ReactorName##_##InputName##_conn_ctor(ReactorName##_##InputName##_conn *self, Reactor *parent) { \ diff --git a/include/reactor-uc/platform/flexpret/flexpret.h b/include/reactor-uc/platform/flexpret/flexpret.h new file mode 100644 index 00000000..ab3e8a4b --- /dev/null +++ b/include/reactor-uc/platform/flexpret/flexpret.h @@ -0,0 +1,15 @@ +#ifndef REACTOR_UC_PLATFORM_FLEXPRET_H +#define REACTOR_UC_PLATFORM_FLEXPRET_H + +#include "reactor-uc/platform.h" +#include + +typedef struct { + Platform super; + volatile bool async_event_occurred; + bool in_critical_section; + fp_lock_t lock; +} PlatformFlexpret; + +void PlatformFlexpret_ctor(Platform *self); +#endif diff --git a/include/reactor-uc/port.h b/include/reactor-uc/port.h index 01586d9c..36ccbceb 100644 --- a/include/reactor-uc/port.h +++ b/include/reactor-uc/port.h @@ -6,38 +6,45 @@ #include "reactor-uc/reactor.h" #include "reactor-uc/trigger.h" -typedef struct Input Input; -typedef struct Output Output; typedef struct Connection Connection; typedef struct Port Port; struct Port { Trigger super; + void *value_ptr; // Pointer to the `buffer` field in the user Input port struct. + size_t value_size; // Size of the data stored in this Port. + TriggerEffects effects; // The reactions triggered by this Port + TriggerSources sources; // The reactions that can write to this Port. + TriggerObservers observers; // The reactions that can observe this Port. Connection *conn_in; // Connection coming into the port. Connection **conns_out; // Connections going out of the port. size_t conns_out_size; // Number of connections going out of the port. size_t conns_out_registered; // Number of connections that have been registered for cleanup. -}; - -struct Input { - Port super; - TriggerEffects effects; // The reactions triggered by this Input port. - void *value_ptr; // Pointer to the `buffer` field in the user Input port struct. - size_t value_size; // Size of the data stored in this Input Port. -}; -struct Output { - Port super; - TriggerSources sources; // The reactions that can write to this Output port. + void (*set)(Port *self, const void *value); }; -void Input_ctor(Input *self, Reactor *parent, Reaction **effects, size_t effects_size, Connection **conns_out, - size_t conns_out_size, void *value_ptr, size_t value_size); - -void Output_ctor(Output *self, Reactor *parent, Reaction **sources, size_t sources_size, Connection **conns_out, - size_t conns_out_size); - -void Port_ctor(Port *self, TriggerType type, Reactor *parent, Connection **conns_out, size_t conns_out_size, - void (*prepare)(Trigger *, Event *), void (*cleanup)(Trigger *)); +// Output ports need pointers to arrats of effects, observers and connections which are not +// located within the same reactor, but in the parent reactor. This struct has all the arguments +// that are needed to initialize an Output port. This is only used in the code-generated code. +typedef struct { + Reaction **parent_effects; + size_t parent_effects_size; + Reaction **parent_observers; + size_t parent_observers_size; + Connection **conns_out; + size_t conns_out_size; +} OutputExternalCtorArgs; + +// Likewise, Input ports need pointers to arrays of sources, which are not located within the same reactor +// this struct captures all those arguments. +typedef struct { + Reaction **parent_sources; + size_t parent_sources_size; +} InputExternalCtorArgs; + +void Port_ctor(Port *self, TriggerType type, Reactor *parent, void *value_ptr, size_t value_size, Reaction **effects, + size_t effects_size, Reaction **sources, size_t sources_size, Reaction **observers, + size_t observers_size, Connection **conns_out, size_t conns_out_size); #endif diff --git a/include/reactor-uc/timer.h b/include/reactor-uc/timer.h index 81548179..8e91af95 100644 --- a/include/reactor-uc/timer.h +++ b/include/reactor-uc/timer.h @@ -12,9 +12,10 @@ struct Timer { instant_t offset; interval_t period; TriggerEffects effects; + TriggerObservers observers; } __attribute__((aligned(MEM_ALIGNMENT))); void Timer_ctor(Timer *self, Reactor *parent, instant_t offset, interval_t period, Reaction **effects, - size_t effects_size); + size_t effects_size, Reaction **observers, size_t observers_size); #endif diff --git a/include/reactor-uc/trigger.h b/include/reactor-uc/trigger.h index eacf375d..e306a7b2 100644 --- a/include/reactor-uc/trigger.h +++ b/include/reactor-uc/trigger.h @@ -47,6 +47,16 @@ typedef struct { size_t num_registered; } TriggerSources; +/** + * @brief TriggerObserver wrap the fields needed to track the reactions registered + * as observers for a certain trigger. + */ +typedef struct { + Reaction **reactions; + size_t size; + size_t num_registered; +} TriggerObservers; + /** * @brief An abstract trigger type. Other trigger types inherit from this. * diff --git a/lfc/core/src/main/java/org/lflang/target/Target.java b/lfc/core/src/main/java/org/lflang/target/Target.java index 3c4438ef..85fee8fa 100644 --- a/lfc/core/src/main/java/org/lflang/target/Target.java +++ b/lfc/core/src/main/java/org/lflang/target/Target.java @@ -438,7 +438,7 @@ public boolean supportsInheritance() { /** Return true if the target supports multiports and banks of reactors. */ public boolean supportsMultiports() { - return false; + return true; } /** diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt index 67dd8ce3..2c32f775 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt @@ -3,113 +3,78 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.generator.orZero -import org.lflang.generator.uc.UcTimerGenerator.Companion.codeType +import org.lflang.generator.uc.UcReactorGenerator.Companion.hasStartup +import org.lflang.generator.uc.UcReactorGenerator.Companion.hasShutdown +import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType +import org.lflang.generator.uc.UcReactorGenerator.Companion.getEffects +import org.lflang.generator.uc.UcReactorGenerator.Companion.getObservers +import org.lflang.generator.uc.UcReactorGenerator.Companion.getSources import org.lflang.lf.* class UcActionGenerator(private val reactor: Reactor) { - val BuiltinTrigger.codeType - get(): String = - if (this == BuiltinTrigger.STARTUP) { - "${reactor.name}_Startup" - } else if (this == BuiltinTrigger.SHUTDOWN) { - "${reactor.name}_Shutdown" - } else { - assert(false) - "" - } - val BuiltinTrigger.codeName - get(): String = - if (this == BuiltinTrigger.STARTUP) { - "startup" - } else if (this == BuiltinTrigger.SHUTDOWN) { - "shutdown" - } else { - unreachable() - } + private val Action.bufSize + get(): Int = 12 // FIXME: This should be annotated in the LF code - val Action.codeType - get(): String = "${reactor.name}_Action_$name" - - val Action.bufSize - get(): Int = 12 // FIXME: This is a parameter/annotation - - val Action.actionType + /** Returns the C Enum representing the type of action.*/ + private val Action.actionType get(): String = if (isPhysical) "PHYSICAL_ACTION" else "LOGICAL_ACTION" - private val hasStartup = reactor.reactions.filter { - it.triggers.filter { it is BuiltinTriggerRef && it.type == BuiltinTrigger.STARTUP }.isNotEmpty() - }.isNotEmpty() - private val hasShutdown = reactor.reactions.filter { - it.triggers.filter { it is BuiltinTriggerRef && it.type == BuiltinTrigger.SHUTDOWN }.isNotEmpty() - }.isNotEmpty() - - fun getEffects(action: Action) = reactor.reactions.filter { it.triggers.filter { it.name == action.name }.isNotEmpty() } - fun getSources(action: Action) = reactor.reactions.filter { it.effects.filter { it.name == action.name }.isNotEmpty() } - - fun getEffects(builtinTrigger: BuiltinTrigger) = - reactor.reactions.filter { it.triggers.filter { it.name == builtinTrigger.literal}.isNotEmpty() } - - fun generateSelfStruct(action: Action) = "DEFINE_ACTION_STRUCT(${action.codeType}, ${action.actionType}, ${getEffects(action).size}, ${getSources(action).size}, ${action.type.toText()}, ${action.bufSize})\n" - fun generateCtor(action: Action) = "DEFINE_ACTION_CTOR(${action.codeType}, ${action.actionType}, ${getEffects(action).size}, ${getSources(action).size}, ${action.type.toText()}, ${action.bufSize})\n" + private fun generateSelfStruct(action: Action) = with(PrependOperator) { + """ + |DEFINE_ACTION_STRUCT${if (action.type == null) "_VOID" else ""}(${reactor.codeType}, ${action.name}, ${action.actionType}, ${reactor.getEffects(action).size}, ${reactor.getSources(action).size}, ${reactor.getObservers(action).size}, ${action.bufSize} ${if (action.type != null) ", ${action.type.toText()}" else ""}); + | + """.trimMargin() + } - fun generateCtor(builtin: BuiltinTrigger) = with(PrependOperator) { + private fun generateCtor(action: Action) = with(PrependOperator) { """ - |${if (builtin == BuiltinTrigger.STARTUP) "DEFINE_STARTUP_CTOR" else "DEFINE_SHUTDOWN_CTOR"}(${builtin.codeType}, ${getEffects(builtin).size}) + |DEFINE_ACTION_CTOR${if (action.type == null) "_VOID" else ""}(${reactor.codeType}, ${action.name}, ${action.actionType}, ${reactor.getEffects(action).size}, ${reactor.getSources(action).size}, ${reactor.getObservers(action).size}, ${action.bufSize} ${if (action.type != null) ", ${action.type.toText()}" else ""}); + | """.trimMargin() } + private fun generateCtor(builtin: BuiltinTrigger) = + (if (builtin == BuiltinTrigger.STARTUP) "DEFINE_STARTUP_CTOR" else "DEFINE_SHUTDOWN_CTOR") + + "(${reactor.codeType});\n" + + fun generateCtors(): String { var code = reactor.actions.joinToString(separator = "\n") { generateCtor(it) } - if (hasStartup) code += generateCtor(BuiltinTrigger.STARTUP); - if (hasShutdown) code += generateCtor(BuiltinTrigger.SHUTDOWN); + if (reactor.hasStartup) code += generateCtor(BuiltinTrigger.STARTUP); + if (reactor.hasShutdown) code += generateCtor(BuiltinTrigger.SHUTDOWN); return code; } - fun generateSelfStruct(builtinTrigger: BuiltinTrigger) = with(PrependOperator) { - """ - |${if (builtinTrigger == BuiltinTrigger.STARTUP) "DEFINE_STARTUP_STRUCT" else "DEFINE_SHUTDOWN_STRUCT"}(${builtinTrigger.codeType}, ${getEffects(builtinTrigger).size}) - """.trimMargin() - }; + private fun generateSelfStruct(builtin: BuiltinTrigger) = + (if (builtin == BuiltinTrigger.STARTUP) "DEFINE_STARTUP_STRUCT" else "DEFINE_SHUTDOWN_STRUCT") + + "(${reactor.codeType}, ${reactor.getEffects(builtin).size}, ${reactor.getObservers(builtin).size});\n" fun generateSelfStructs(): String { var code = reactor.actions.joinToString(separator = "\n") { generateSelfStruct(it) } - - if (hasStartup) { + if (reactor.hasStartup) { code += generateSelfStruct(BuiltinTrigger.STARTUP) ; } - if (hasShutdown) { + if (reactor.hasShutdown) { code += generateSelfStruct(BuiltinTrigger.SHUTDOWN) ; } return code; } fun generateReactorStructFields(): String { - var code = reactor.actions.joinToString(prefix = "// Actions and builtin triggers\n", separator = "\n", postfix = "\n") { "${it.codeType} ${it.name};" } - if (hasStartup) code += "${BuiltinTrigger.STARTUP.codeType} startup;" - if (hasShutdown) code += "${BuiltinTrigger.SHUTDOWN.codeType} shutdown;" + var code = reactor.actions.joinToString(prefix = "// Actions and builtin triggers\n", separator = "\n", postfix = "\n") { "ACTION_INSTANCE(${reactor.codeType}, ${it.name});" } + if (reactor.hasStartup) code += "STARTUP_INSTANCE(${reactor.codeType});" + if (reactor.hasShutdown) code += "SHUTDOWN_INSTANCE(${reactor.codeType});" return code; } - fun generateReactorCtorCode(action: Action) = with(PrependOperator) { - """ - |self->_triggers[trigger_idx++] = (Trigger *) &self->${action.name}; - |${action.codeType}_ctor(&self->${action.name}, &self->super, ${action.minDelay.orZero().toCCode()}); - | - """.trimMargin() - }; + private fun generateReactorCtorCode(action: Action) = "INITIALIZE_ACTION(${reactor.codeType}, ${action.name}, ${action.minDelay.orZero().toCCode()});" + private fun generateReactorCtorCodeStartup() = "INITIALIZE_STARTUP(${reactor.codeType});" + private fun generateReactorCtorCodeShutdown() = "INITIALIZE_SHUTDOWN(${reactor.codeType});" - fun generateReactorCtorCode(builtin: BuiltinTrigger) = with(PrependOperator) { - """ - |self->_triggers[trigger_idx++] = (Trigger *) &self->${builtin.codeName}; - |${builtin.codeType}_ctor(&self->${builtin.codeName}, &self->super); - | - """.trimMargin() - }; fun generateReactorCtorCodes(): String { var code = reactor.actions.joinToString(prefix = "// Initialize actions and builtin triggers\n", separator = "\n", postfix = "\n") { generateReactorCtorCode(it)} - if(hasStartup) code += generateReactorCtorCode(BuiltinTrigger.STARTUP); - if(hasShutdown) code += generateReactorCtorCode(BuiltinTrigger.SHUTDOWN); + if(reactor.hasStartup) code += "${generateReactorCtorCodeStartup()}\n" + if(reactor.hasShutdown) code += "${generateReactorCtorCodeShutdown()}\n" return code; } - } \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index 5581f2dc..5f43c4f5 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -36,8 +36,16 @@ class UcCmakeGenerator(private val targetConfig: TargetConfig, private val fileC |zephyr_library_sources($S{SOURCES}) |zephyr_library_link_libraries(kernel) """.trimMargin() + } else if (platform == PlatformType.Platform.FLEXPRET){ + """ + |add_library($S{LF_MAIN_TARGET} $S{SOURCES}) + |target_link_libraries($S{LF_MAIN_TARGET} PUBLIC fp-sdk) + """.trimMargin() } else { - unreachable() + """ + |add_library($S{LF_MAIN_TARGET} $S{SOURCES}) + """.trimMargin() + } } fun generateCmake(sources: List) = with(PrependOperator) { @@ -57,7 +65,7 @@ class UcCmakeGenerator(private val targetConfig: TargetConfig, private val fileC |) ${" |"..generatePlatformSpecific()} | - |add_subdirectory(${S}ENV{REACTOR_UC_PATH} reactor-uc) + |add_subdirectory(reactor-uc) | |target_include_directories($S{LF_MAIN_TARGET} PUBLIC $S{CMAKE_CURRENT_SOURCE_DIR}) |target_link_libraries($S{LF_MAIN_TARGET} PUBLIC reactor-uc) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 74a426e5..2dd66433 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -2,20 +2,23 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcTimerGenerator.Companion.codeType +import org.lflang.generator.orNever +import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.lf.* -// This class is an abstraction over LF connections. Basically we take -// the LF connections and split them up into UcConnections, each of which has +// This class is an abstraction over LF connections. We take +// the LF connections and split them up into UcGroupedConnections, each of which has // a single upstream port and possibly several downstream ports. We can then // generate Connection objects as expected by reactor-uc -class UcConnection(val src: VarRef, val conn: Connection) { +class UcGroupedConnection(val srcVar: VarRef, val srcPort: Port, val conn: Connection, val connId: Int) { private val dests = mutableListOf(); + private val srcParentName: String = if (srcVar.container != null) srcVar.container.name else "" + private val srcPortName: String = srcPort.name - val codeType = "Conn_${if (src.container != null) src.container.name else ""}_${src.name}" - val codeName= "conn_${if (src.container != null) src.container.name else ""}_${src.name}" val bufSize = 12 // FIXME: Set this somehow, probably an annotation? + val uniqueName = "conn_${srcParentName}_${srcPortName}_${connId}" + val isLogical = !conn.isPhysical && conn.delay == null fun addDst(port: VarRef) { dests.add(port) @@ -26,16 +29,24 @@ class UcConnection(val src: VarRef, val conn: Connection) { } companion object { - fun findConnectionFromPort(conns: List, port: VarRef, connInfo: Connection): UcConnection? { - return conns.find {c -> c.src == port && c.conn.isPhysical== connInfo.isPhysical && c.conn.delay == connInfo.delay} + /** From a list of UcGroupedConnections, find one that has the same upstream port and delay/isPhysical setting*/ + fun findIdenticalConnectionFromPort(conns: List, port: Port, connInfo: Connection): UcGroupedConnection? { + return conns.find {c -> c.srcPort == port && c.conn.isPhysical== connInfo.isPhysical && c.conn.delay == connInfo.delay} + } + + /** From a list of UcGroupedConnection, find all that start from the same port.*/ + fun findAllConnectionFromPort(conns: List, port: Port): List? { + return conns.filter{c -> c.srcPort == port} } } } class UcConnectionGenerator(private val reactor: Reactor) { - fun getUcConnections(): List { - val res = mutableListOf() + private val ucGroupedConnections: List; + + init { + val res = mutableListOf() for (conn: Connection in reactor.connections) { for ((index, rhs) in conn.rightPorts.withIndex()) { var lhs: VarRef; @@ -46,17 +57,20 @@ class UcConnectionGenerator(private val reactor: Reactor) { assert(conn.leftPorts.size == conn.rightPorts.size) lhs = conn.leftPorts.get(index) } + val lhsPort = getPort(lhs) + // We need to assign a unique ID to each UcGroupedConnection with the same srcPort. + // we do this by figuring out how many such connections we already have. + val connsFromPort = UcGroupedConnection.findAllConnectionFromPort(res, lhsPort)?.size - var ucConn = UcConnection.findConnectionFromPort(res, lhs, conn) + var ucConn = UcGroupedConnection.findIdenticalConnectionFromPort(res, lhsPort, conn) if (ucConn == null) { - ucConn = UcConnection(lhs, conn) + ucConn = UcGroupedConnection(lhs, getPort(lhs), conn, connsFromPort ?: 0) res.add(ucConn) } ucConn.addDst(rhs) - } } - return res + ucGroupedConnections = res; } fun getPort(p: VarRef): Port { @@ -81,36 +95,46 @@ class UcConnectionGenerator(private val reactor: Reactor) { return res; } - fun generateLogicalSelfStruct(conn: UcConnection) = "DEFINE_LOGICAL_CONNECTION_STRUCT(${conn.codeType}, ${conn.getDests().size})"; - fun generateLogicalCtor(conn: UcConnection) = "DEFINE_LOGICAL_CONNECTION_CTOR(${conn.codeType}, ${conn.getDests().size})"; + fun getNumConnectionsFromPort(p: Port): Int { + val num = UcGroupedConnection.findAllConnectionFromPort(ucGroupedConnections, p)?.size + return num?:0 + } - fun generateDelayedSelfStruct(conn: UcConnection) = "DEFINE_DELAYED_CONNECTION_STRUCT(${conn.codeType}, ${conn.getDests().size}, ${getPort(conn.src).type.toText()}, ${conn.bufSize}, ${conn.conn.delay.toCCode()})"; - fun generateDelayedCtor(conn: UcConnection) = "DEFINE_DELAYED_CONNECTION_CTOR(${conn.codeType}, ${conn.getDests().size}, ${getPort(conn.src).type.toText()}, ${conn.bufSize}, ${conn.conn.delay.toCCode()}, ${conn.conn.isPhysical})"; + private fun generateLogicalSelfStruct(conn: UcGroupedConnection) = "DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.getDests().size})"; + private fun generateLogicalCtor(conn: UcGroupedConnection) = "DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.getDests().size})"; - fun generateSelfStructs() = getUcConnections().joinToString(prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { - if (it.conn.isPhysical || it.conn.delay != null) generateDelayedSelfStruct(it) - else generateLogicalSelfStruct(it) - } - fun generateReactorStructFields() = - getUcConnections().joinToString(prefix = "// Connections \n", separator = "\n", postfix = "\n") { "${it.codeType} ${it.codeName};" } + private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = "DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.getDests().size}, ${conn.srcPort.type.toText()}, ${conn.bufSize}, ${conn.conn.delay.orNever().toCCode()})"; + private fun generateDelayedCtor(conn: UcGroupedConnection) = "DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.getDests().size}, ${conn.srcPort.type.toText()}, ${conn.bufSize}, ${conn.conn.delay.orNever().toCCode()}, ${conn.conn.isPhysical})"; - fun generateReactorCtorCode(conn: UcConnection) = with(PrependOperator) { + + private fun generateReactorCtorCode(conn: UcGroupedConnection) = with(PrependOperator) { """ - |${conn.codeType}_ctor(&self->${conn.codeName}, &self->super); - ${" | "..generateConnectionStatements(conn)} + |${if (conn.isLogical) "INITIALIZE_LOGICAL_CONNECTION(" else "INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.uniqueName}); + ${" | "..generateConnectionStatements(conn)} | """.trimMargin() }; - fun generateConnectionStatements(conn: UcConnection) = - "CONN_REGISTER_UPSTREAM(self->${conn.codeName}, self->${getPortCodeName(conn.src)});\n" + + private fun generateConnectionStatements(conn: UcGroupedConnection) = + "CONN_REGISTER_UPSTREAM(self->${conn.uniqueName}, self->${getPortCodeName(conn.srcVar)});\n" + conn.getDests().joinToString(separator = "\n") { - "CONN_REGISTER_DOWNSTREAM(self->${conn.codeName}, self->${getPortCodeName(it)});"} + "CONN_REGISTER_DOWNSTREAM(self->${conn.uniqueName}, self->${getPortCodeName(it)});"} - fun generateReactorCtorCodes() = getUcConnections().joinToString(prefix = "// Initialize connections\n", separator = "\n", postfix = "\n") { generateReactorCtorCode(it)} + fun generateReactorCtorCodes() = ucGroupedConnections.joinToString(prefix = "// Initialize connections\n", separator = "\n", postfix = "\n") { generateReactorCtorCode(it)} - fun generateCtors() = getUcConnections().joinToString(prefix = "// Connection constructors\n", separator = "\n", postfix = "\n"){ + fun generateCtors() = ucGroupedConnections.joinToString(prefix = "// Connection constructors\n", separator = "\n", postfix = "\n"){ if(it.conn.isPhysical || it.conn.delay != null) generateDelayedCtor(it) else generateLogicalCtor(it) } + + fun generateSelfStructs() = ucGroupedConnections.joinToString(prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { + if (it.isLogical) generateLogicalSelfStruct(it) + else generateDelayedSelfStruct(it) + } + fun generateReactorStructFields() = + ucGroupedConnections.joinToString(prefix = "// Connections \n", separator = "\n", postfix = "\n") { + if (it.isLogical) "LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName});" + else "DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName});" + } + } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt index 5c2cc59d..8214fd67 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt @@ -1,33 +1,10 @@ package org.lflang.generator.uc -import org.eclipse.emf.ecore.resource.Resource import org.lflang.* import org.lflang.lf.BuiltinTriggerRef import org.lflang.lf.Expression -import org.lflang.lf.Port -import org.lflang.lf.Preamble -import org.lflang.lf.Reaction -import org.lflang.lf.Reactor import org.lflang.lf.TriggerRef import org.lflang.lf.VarRef -import org.lflang.lf.Visibility -import org.lflang.lf.WidthSpec -import org.lflang.lf.impl.InputImpl -import org.lflang.lf.impl.OutputImpl -import org.lflang.target.property.type.LoggingType.LogLevel - -/* ******************************************************************************************* - * - * The following definition provide extension that are likely useful across targets - * - * TODO Move these definitions to a common place and check if they are already implemented elsewhere - */ - -/* ********************************************************************************************** - * C++ specific extensions shared across classes - */ -// TODO: Most of the extensions defined here should be moved to companion objects of their -// corresponding generator classes. See for instance the CppParameterGenerator fun TimeValue.toCCode() = UcTypes.getTargetTimeExpr(this) fun Expression.toCCode(inferredType: InferredType? = null): String = diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt index 2cc5846d..7d390eec 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt @@ -11,28 +11,19 @@ import java.nio.file.Path class UcFileConfig(resource: Resource, srcGenBasePath: Path, useHierarchicalBin: Boolean) : FileConfig(resource, srcGenBasePath, useHierarchicalBin) { - /** - * Clean any artifacts produced by the C++ code generator. - */ @Throws(IOException::class) override fun doClean() { super.doClean() - this.cppBuildDirectories.forEach { FileUtil.deleteDirectory(it) } + this.ucBuildDirectories.forEach { FileUtil.deleteDirectory(it) } } - val cppBuildDirectories = listOf( + val ucBuildDirectories = listOf( this.outPath.resolve("build"), ) /** Relative path to the directory where all source files for this resource should be generated in. */ private fun getGenDir(r: Resource): Path = srcGenPath.resolve(r.name) - /** Path to the preamble header file corresponding to this resource */ - fun getPreambleHeaderPath(r: Resource): Path = getGenDir(r).resolve("_lf_preamble.hh") - - /** Path to the preamble source file corresponding to this resource */ - fun getPreambleSourcePath(r: Resource): Path = getGenDir(r).resolve("_lf_preamble.cc") - /** Path to the header file corresponding to this reactor */ fun getReactorHeaderPath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.h") diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index fedb826e..fe18c49e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -116,7 +116,7 @@ class UcGenerator( Files.createDirectories(libPath) commandFactory.createCommand( "git", - listOf("clone", "-n", "https://github.com/erlingrj/reactor-uc.git", "reactor-uc-$version"), + listOf("clone", "-n", "https://github.com/lf-lang/reactor-uc.git", "reactor-uc-$version"), fileConfig.srcGenBasePath ).run() commandFactory.createCommand("git", listOf("checkout", version), libPath).run() @@ -146,22 +146,6 @@ class UcGenerator( FileUtil.writeToFile(headerCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) FileUtil.writeToFile(reactorCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) } - - - // generate file level preambles for all resources -// for (r in resources) { -//// val generator = UcPreambleGenerator(r, fileConfig, scopeProvider) -// val sourceFile = fileConfig.getPreambleSourcePath(r) -// val headerFile = fileConfig.getPreambleHeaderPath(r) -// val preambleCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) -// ucSources.add(sourceFile) -// codeMaps[srcGenPath.resolve(sourceFile)] = preambleCodeMap -// val headerCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) -// codeMaps[srcGenPath.resolve(headerFile)] = headerCodeMap -// -// FileUtil.writeToFile(headerCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) -// FileUtil.writeToFile(preambleCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) -// } } private fun getPlatformGenerator() = UcStandaloneGenerator(this) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt index d7e170a0..c054d633 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt @@ -1,42 +1,16 @@ -/************* - * Copyright (c) 2021, TU Dresden. - - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ***************/ - package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcPortGenerator.Companion.codeType import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType -import org.lflang.lf.Instantiation -import org.lflang.lf.Port -import org.lflang.lf.Reactor -import org.lflang.validation.AttributeSpec +import org.lflang.lf.* -/** A code generator for reactor instances */ class UcInstanceGenerator( private val reactor: Reactor, private val parameters: UcParameterGenerator, + private val ports: UcPortGenerator, + private val connections: UcConnectionGenerator, + private val reactions: UcReactionGenerator, private val fileConfig: UcFileConfig, private val messageReporter: MessageReporter ) { @@ -45,15 +19,36 @@ class UcInstanceGenerator( .distinct() .joinToString(separator = "\n") { """#include "${it.toUnixString()}" """ } - fun generateReactorStructFields() = reactor.instantiations.joinToString(prefix = "// Child reactor fields\n", separator = "\n", postfix = "\n") { - "${it.reactor.codeType} ${it.name};" - } + fun generateReactorStructContainedOutputFields(inst: Instantiation) = inst.reactor.outputs.joinToString(separator = "\n") { with (PrependOperator) { + """| + |CHILD_OUTPUT_CONNECTIONS(${inst.name}, ${it.name}, ${connections.getNumConnectionsFromPort(it as Port)}); + |CHILD_OUTPUT_EFFECTS(${inst.name}, ${it.name}, ${reactions.getParentReactionEffectsOfOutput(inst, it).size}); + |CHILD_OUTPUT_OBSERVERS(${inst.name}, ${it.name}, ${reactions.getParentReactionObserversOfOutput(inst, it).size}); + """.trimMargin() + }} - fun generateReactorCtorCodes() = reactor.instantiations.joinToString(separator = "\n") { + fun generateReactorStructContainedInputFields(inst: Instantiation) = inst.reactor.inputs.joinToString(separator = "\n") { with (PrependOperator) { """| - |${it.reactor.codeType}_ctor(&self->${it.name}, self->super.env, &self->super ${parameters.generateReactorCtorDeclArguments(it)}); - |self->_children[child_idx++] = &self->${it.name}.super; - | + |CHILD_INPUT_SOURCES(${inst.name}, ${it.name}, ${reactions.getParentReactionSourcesOfInput(inst, it).size}); """.trimMargin() + }} + + fun generateReactorStructFields() = reactor.instantiations.joinToString(prefix = "// Child reactor fields\n", separator = "\n", postfix = "\n") { + """| + |CHILD_REACTOR_INSTANCE(${it.reactor.codeType}, ${it.name}); + |${generateReactorStructContainedOutputFields(it)} + |${generateReactorStructContainedInputFields(it)} + """.trimMargin() } -} + + fun generateReactorCtorCodes() = reactor.instantiations. joinToString(separator = "\n") { with(PrependOperator) { + """| + ${" |"..ports.generateDefineContainedOutputArgs(it)} + ${" |"..ports.generateDefineContainedInputArgs(it)} + |${ if (parameters.generateReactorCtorDeclArguments(it).isNotEmpty() || ports.generateReactorCtorDeclArguments(it).isNotEmpty()) + "INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(${it.reactor.codeType}, ${it.name} ${ports.generateReactorCtorDeclArguments(it)} ${parameters.generateReactorCtorDeclArguments(it)});" + else "INITIALIZE_CHILD_REACTOR(${it.reactor.codeType}, ${it.name});" + } + """.trimMargin() + }} +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index d7104e77..531761b0 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -27,7 +27,7 @@ class UcMainGenerator( |// targeting POSIX. For programs targeting embedded platforms a |// main function is not generated. |int main(int argc, char **argv) { - | lf_main(); + | lf_start(); |} """.trimMargin() } else { @@ -35,19 +35,15 @@ class UcMainGenerator( } } + fun getDuration() = if (targetConfig.isSet(TimeOutProperty.INSTANCE)) targetConfig.get(TimeOutProperty.INSTANCE).toCCode() else "FOREVER" + + fun keepAlive() = "false" + fun generateMainSource() = with(PrependOperator) { """ |#include "reactor-uc/reactor-uc.h" |#include "${fileConfig.getReactorHeaderPath(main).toUnixString()}" - |static Environment env; - |static ${main.codeType} main_reactor; - |void lf_main(void) { - | Environment_ctor(&env, &main_reactor.super); - | ${if (targetConfig.isSet(TimeOutProperty.INSTANCE)) "env.scheduler.set_duration(&env.scheduler, ${targetConfig.get(TimeOutProperty.INSTANCE).toCCode()});" else ""} - | ${main.codeType}_ctor(&main_reactor, &env, NULL); - | env.assemble(&env); - | env.start(&env); - |} + |ENTRY_POINT(${main.codeType}, ${getDuration()}, ${keepAlive()}); ${" |"..generateMainFunction()} """.trimMargin() } @@ -57,7 +53,7 @@ class UcMainGenerator( |#ifndef REACTOR_UC_LF_MAIN_H |#define REACTOR_UC_LF_MAIN_H | - |void lf_main(void); + |void lf_start(void); | |#endif | diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt index ec044c65..be064399 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt @@ -12,13 +12,11 @@ class UcParameterGenerator(private val reactor: Reactor) { companion object { - /** Type of the parameter in C++ code */ val Parameter.targetType get(): String = this.inferredType.CType val Parameter.isPresentName get(): String = "__${this.name}" } - /** Generate all parameter declarations as used in the parameter struct */ fun generateReactorStructFields() = reactor.parameters.joinToString(prefix = "// Reactor parameters\n", separator = "\n", postfix = "\n") {"${it.inferredType.CType} ${it.name};"} @@ -30,10 +28,10 @@ class UcParameterGenerator(private val reactor: Reactor) { } fun generateReactorCtorDefArguments() = - reactor.parameters.joinToString() {", ${it.inferredType.CType} ${it.name}"} + reactor.parameters.joinToString(separator = "") {", ${it.inferredType.CType} ${it.name}"} fun generateReactorCtorDeclArguments(r: Instantiation) = - r.reactor.parameters.joinToString() { + r.reactor.parameters.joinToString(separator = "") { if (r.parameters.filter{ p -> p.lhs.name == it.name}.isEmpty()) { ", ${it.init.expr.toCCode()}" } else { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt index 13a41dab..3168d098 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt @@ -26,25 +26,20 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType +import org.lflang.generator.uc.UcReactorGenerator.Companion.getEffects +import org.lflang.generator.uc.UcReactorGenerator.Companion.getObservers +import org.lflang.generator.uc.UcReactorGenerator.Companion.getSources import org.lflang.lf.* -class UcPortGenerator(private val reactor: Reactor, private val connectionGenerator: UcConnectionGenerator) { - companion object { /** Get the "name" a reaction is represented with in target code.*/ - val Input.codeType - get(): String = "${(eContainer() as Reactor).name}_Input_$name" - val Output.codeType - get(): String = "${(eContainer() as Reactor).name}_Output_$name" - val Port.codeType - get(): String = if (this.isInput) (this as Input).codeType else (this as Output).codeType - } - - fun getEffects(port: Input) = reactor.reactions.filter { it.triggers.filter { it.name == port.name }.isNotEmpty() } - fun getSources(port: Output) = reactor.reactions.filter { it.effects.filter { it.name == port.name }.isNotEmpty() } +class UcPortGenerator(private val reactor: Reactor, private val connections: UcConnectionGenerator) { + val Port.external_args + get(): String = "_${name}_external" - fun generateSelfStruct(input: Input) = "DEFINE_INPUT_PORT_STRUCT(${input.codeType}, ${getEffects(input).size}, ${input.type.toText()})" - fun generateInputCtor(input: Input) = "DEFINE_INPUT_PORT_CTOR(${input.codeType}, ${getEffects(input).size}, ${input.type.toText()})" - fun generateSelfStruct(output: Output) = "DEFINE_OUTPUT_PORT_STRUCT(${output.codeType}, ${getSources(output).size})" - fun generateOutputCtor(output: Output) = "DEFINE_OUTPUT_PORT_CTOR(${output.codeType}, ${getSources(output).size})" + private fun generateSelfStruct(input: Input) = "DEFINE_INPUT_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.toText()}, ${connections.getNumConnectionsFromPort(input as Port)});" + private fun generateInputCtor(input: Input) = "DEFINE_INPUT_CTOR(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.toText()}, ${connections.getNumConnectionsFromPort(input as Port)});" + private fun generateSelfStruct(output: Output) = "DEFINE_OUTPUT_STRUCT(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size}, ${output.type.toText()});" + private fun generateOutputCtor(output: Output) = "DEFINE_OUTPUT_CTOR(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size});" fun generateSelfStructs() = reactor.inputs.plus(reactor.outputs).joinToString(prefix = "// Port structs\n", separator = "\n", postfix = "\n") { when (it) { @@ -55,24 +50,43 @@ class UcPortGenerator(private val reactor: Reactor, private val connectionGenera } fun generateReactorStructFields() = reactor.inputs.plus(reactor.outputs).joinToString(prefix = "// Ports \n", separator = "\n", postfix = "\n") { - "${it.codeType} ${it.name};" + "PORT_INSTANCE(${reactor.codeType}, ${it.name});" } fun generateCtors() = reactor.inputs.plus(reactor.outputs).joinToString(prefix = "// Port constructors\n", separator = "\n", postfix = "\n") { when (it) { is Input -> generateInputCtor(it) is Output -> generateOutputCtor(it) - else -> "" + else -> "" // FIXME: Runtime exception } } - fun generateReactorCtorCode(port: Port) = with(PrependOperator) { - """ - |self->_triggers[trigger_idx++] = (Trigger *) &self->${port.name}; - |${port.codeType}_ctor(&self->${port.name}, &self->super); - | - """.trimMargin() - }; + private fun generateReactorCtorCode(input: Input) = "INITIALIZE_INPUT(${reactor.codeType}, ${input.name}, ${input.external_args});" + private fun generateReactorCtorCode(output: Output) = "INITIALIZE_OUTPUT(${reactor.codeType}, ${output.name}, ${output.external_args});" + + private fun generateReactorCtorCode(port: Port) = + when(port) { + is Input -> generateReactorCtorCode(port) + is Output -> generateReactorCtorCode(port) + else -> "" // FIXME: Runtime exception + } + fun generateReactorCtorCodes() = reactor.inputs.plus(reactor.outputs).joinToString(prefix = "// Initialize ports\n", separator = "\n", postfix = "\n") { generateReactorCtorCode(it)} + + fun generateDefineContainedOutputArgs(r: Instantiation) = + r.reactor.outputs.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { + "DEFINE_CHILD_OUTPUT_ARGS(${r.name}, ${it.name});" + } + fun generateDefineContainedInputArgs(r: Instantiation) = + r.reactor.inputs.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { + "DEFINE_CHILD_INPUT_ARGS(${r.name}, ${it.name});" + } + + fun generateReactorCtorDefArguments() = + reactor.outputs.joinToString(separator = "") {", OutputExternalCtorArgs ${it.external_args}"} + + reactor.inputs.joinToString(separator = "") {", InputExternalCtorArgs ${it.external_args}"} + + fun generateReactorCtorDeclArguments(r: Instantiation) = + r.reactor.outputs.plus(r.reactor.inputs).joinToString(separator = "") {", _${r.name}_${it.name}_args"} } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPreambleGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPreambleGenerator.kt index 0f43125e..8aade9e4 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPreambleGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPreambleGenerator.kt @@ -1,13 +1,7 @@ package org.lflang.generator.uc import org.lflang.* -import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcPortGenerator.Companion.codeType -import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType -import org.lflang.lf.Instantiation -import org.lflang.lf.Port import org.lflang.lf.Reactor -import org.lflang.validation.AttributeSpec class UcPreambleGenerator( private val reactor: Reactor, diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt index f68c073b..941fd4c6 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt @@ -5,160 +5,251 @@ import org.lflang.generator.PrependOperator import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.lf.* -class UcReactionGenerator( - private val reactor: Reactor, - private val portGenerator: UcPortGenerator -) { - val Reaction.codeType - get(): String = name ?: "${reactor.codeType}_Reaction$index" // Must match what is generated by the macro reactor-uc - val Reaction.codeName - get(): String = name ?: "${reactor.name}_reaction$index" - val Reaction.bodyFuncName - get(): String = name ?: "${reactor.codeType}_Reaction${index}_body" +class UcReactionGenerator(private val reactor: Reactor) { + private val Reaction.codeName + get(): String = name ?: "reaction$index" val Reaction.index - get(): Int = priority-1 - - private val VarRef.codeType - get() = - when (val variable = this.variable) { - is Timer -> "${reactor.name}_Timer_${name}" - is Action -> "${reactor.name}_Action_${name}" - is Output -> "${reactor.name}_Output_${name}" - is Input -> "${reactor.name}_Input_${name}" - else -> AssertionError("Unexpected variable type") + get(): Int = priority - 1 + + private val Reaction.allUncontainedTriggers + get() = triggers.filterNot { it.isEffectOf(this) || it.isContainedRef } + private val Reaction.allUncontainedEffects + get() = effects.filterNot { it.isContainedRef } + private val Reaction.allUncontainedSources + get() = sources.filterNot { it.isContainedRef } + + private val Reaction.allContainedEffects + get() = effects.filter { it.isContainedRef } + private val Reaction.allContainedTriggers + get() = triggers.filter { !it.isEffectOf(this) && it.isContainedRef } + private val Reaction.allContainedSources + get() = sources.filter { !it.isEffectOf(this) && it.isContainedRef } + + private val Reaction.allContainedEffectsTriggersAndSources + get() = run { + val res = mutableMapOf>() + for (effect in allContainedEffects) { + res.getOrPut(effect.container!!) { mutableListOf(effect) }.plus(effect) + } + for (trigger in allContainedTriggers) { + res.getOrPut((trigger as VarRef).container!!) { mutableListOf(trigger) }.plus(trigger) } + for (source in allContainedSources) { + res.getOrPut((source as VarRef).container!!) { mutableListOf(source) }.plus(source) + } + res + } - private val VarRef.typeMacro + private fun TriggerRef.isEffectOf(reaction: Reaction): Boolean = this is VarRef && isEffectOf(reaction) + + private val TriggerRef.scope + get() = when { + this is BuiltinTriggerRef && this.type == BuiltinTrigger.STARTUP -> "SCOPE_STARTUP(${reactor.codeType});" + this is BuiltinTriggerRef && this.type == BuiltinTrigger.SHUTDOWN -> "SCOPE_SHUTDOWN(${reactor.codeType});" + this is VarRef -> scope + else -> AssertionError("Unexpected trigger type") + } + + private val VarRef.scope get() = when (val variable = this.variable) { - is Timer -> "TIMER" - is Action -> "ACTION" - is Output -> "OUTPUT" - is Input -> "INPUT" - else -> AssertionError("Unexpected variable type") + is Timer -> "SCOPE_TIMER(${reactor.codeType}, ${name});" + is Action -> "SCOPE_ACTION(${reactor.codeType}, ${name});" + is Output -> "SCOPE_PORT(${reactor.codeType}, ${name});" + is Input -> "SCOPE_PORT(${reactor.codeType}, ${name});" + else -> throw AssertionError("Unexpected variable type") } + private val VarRef.fullName: String get() = if (container != null) "${container.name}.${name}" else name - private val TriggerRef.codeType - get() = when { - this is BuiltinTriggerRef && this.type == BuiltinTrigger.STARTUP -> "${reactor.name}_Startup" - this is BuiltinTriggerRef && this.type == BuiltinTrigger.SHUTDOWN -> "${reactor.name}_Shutdown" - this is VarRef -> codeType - else -> AssertionError("Unexpected trigger type") + private val VarRef.isContainedRef: Boolean get() = container != null + private val TriggerRef.isContainedRef: Boolean get() = this is VarRef && isContainedRef + + private fun VarRef.isEffectOf(reaction: Reaction): Boolean = + reaction.effects.any { name == it.name && container?.name == it.container?.name } + + + private fun registerSource(varRef: VarRef, reaction: Reaction) = + when (val variable = varRef.variable) { + is Action -> "ACTION_REGISTER_SOURCE(${varRef.name}, ${reaction.codeName});" + is Output -> "PORT_REGISTER_SOURCE(${varRef.fullName}, ${reaction.codeName});" + is Input -> "PORT_REGISTER_SOURCE(${varRef.fullName}, ${reaction.codeName});" + else -> throw AssertionError("Unexpected variable type ${varRef}") } - private val TriggerRef.typeMacro - get() = when { - this is BuiltinTriggerRef && this.type == BuiltinTrigger.STARTUP -> "BUILTIN" - this is BuiltinTriggerRef && this.type == BuiltinTrigger.SHUTDOWN -> "BUILTIN" - this is VarRef -> typeMacro - else -> AssertionError("Unexpected trigger type") + private fun registerEffect(triggerRef: TriggerRef, reaction: Reaction) = + when { + triggerRef is BuiltinTriggerRef && triggerRef.type == BuiltinTrigger.STARTUP -> "STARTUP_REGISTER_EFFECT(${reaction.codeName});" + triggerRef is BuiltinTriggerRef && triggerRef.type == BuiltinTrigger.SHUTDOWN -> "SHUTDOWN_REGISTER_EFFECT(${reaction.codeName});" + triggerRef is VarRef -> registerEffect(triggerRef, reaction) + else -> throw AssertionError("Unexpected variable type") } - fun generateEffectsField(reaction: Reaction) = - if (reaction.allUncontainedEffects.size > 0) "Trigger *_effects[${reaction.allUncontainedEffects.size}];" else "\n" + private fun registerEffect(varRef: VarRef, reaction: Reaction) = + when (val variable = varRef.variable) { + is Timer -> "TIMER_REGISTER_EFFECT(${varRef.name}, ${reaction.codeName});" + is Action -> "ACTION_REGISTER_EFFECT(${varRef.name}, ${reaction.codeName});" + is Output -> "PORT_REGISTER_EFFECT(${varRef.fullName}, ${reaction.codeName});" + is Input -> "PORT_REGISTER_EFFECT(${varRef.fullName}, ${reaction.codeName});" + else -> throw AssertionError("Unexpected variable type") + } - fun generateEffectsFieldPtr(reaction: Reaction) = if (reaction.allUncontainedEffects.size > 0) "self->_effects" else "NULL" + private fun registerObserver(varRef: VarRef, reaction: Reaction) = + when (val variable = varRef.variable) { + is Action -> "ACTION_REGISTER_OBSERVER(${varRef.name}, ${reaction.codeName});" + is Output -> "PORT_REGISTER_OBSERVER(${varRef.fullName}, ${reaction.codeName});" + is Input -> "PORT_REGISTER_OBSERVER(${varRef.fullName}, ${reaction.codeName});" + else -> throw AssertionError("Unexpected variable type") + } + + private fun generateReactionCtor(reaction: Reaction) = + "DEFINE_REACTION_CTOR(${reactor.codeType}, ${reaction.codeName}, ${reaction.index});" - fun generateReactionCtor(reaction: Reaction) = "DEFINE_REACTION_CTOR(${reactor.codeType}, ${reaction.index})" - fun generateSelfStruct(reaction: Reaction) = "DEFINE_REACTION_STRUCT(${reactor.codeType}, ${reaction.index}, ${reaction.allUncontainedEffects.size})" + private fun generateSelfStruct(reaction: Reaction) = + "DEFINE_REACTION_STRUCT(${reactor.codeType}, ${reaction.codeName}, ${reaction.effects.size});" fun generateSelfStructs() = reactor.reactions.joinToString( separator = "\n", prefix = "// Reaction structs\n", postfix = "\n" - ) { generateSelfStruct(it) }; + ) { generateSelfStruct(it) } fun generateReactorStructFields() = reactor.reactions.joinToString( separator = "\n", postfix = "\n" ) { - with(PrependOperator) { - """ - |${it.codeType} ${it.codeName}; - """.trimMargin() - } - }; + "REACTION_INSTANCE(${reactor.codeType}, ${it.codeName});" + } fun generateReactionCtors() = reactor.reactions.joinToString( separator = "\n", prefix = "// Reaction constructors\n", postfix = "\n" - ) { generateReactionCtor(it) }; + ) { generateReactionCtor(it) } fun generateReactionBodies() = reactor.reactions.joinToString( separator = "\n", prefix = "// Reaction bodies\n", postfix = "\n" - ) { generateReactionBody(it) }; + ) { generateReactionBody(it) } - fun generateReactionBody(reaction: Reaction) = with(PrependOperator) { + private fun generateReactionBody(reaction: Reaction) = with(PrependOperator) { """ - | DEFINE_REACTION_BODY(${reactor.codeType}, ${reaction.index}) { - | // Bring expected variable names into scope - | ${reactor.codeType} *self = (${reactor.codeType} *) _self->parent; - | (void)self; - | Environment *env = (Environment *) self->super.env; - | (void)env; - | ${generateTriggersInScope(reaction)} - | // Start of user-witten reaction body - | ${reaction.code.toText()} - | // End of user-written reaction body - | } + |DEFINE_REACTION_BODY(${reactor.codeType}, ${reaction.codeName}) { + | // Bring self struct, environment, triggers, effects and sources into scope. + | SCOPE_SELF(${reactor.codeType}); + | SCOPE_ENV(); + ${"| "..generateTriggersEffectsAndSourcesInScope(reaction)} + ${"| "..generateContainedTriggersAndSourcesInScope(reaction)} + | // Start of user-witten reaction body + ${"| "..reaction.code.toText()} + |} """.trimMargin() } - fun generateTriggersInScope(reaction: Reaction) = - reaction.allUncontainedTriggers.plus(reaction.allUncontainedEffects).joinToString(separator = "\n"){ - """ - |${it.codeType} *${it.name} = &self->${it.name}; - |(void) ${it.name}; - """.trimMargin() - }; + private fun generateContainedTriggerInScope(trigger: VarRef) = + "PORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name});" + + private fun generateContainedTriggerFieldInit(trigger: VarRef) = + ".${trigger.name} = &self->${trigger.container.name}.${trigger.name}" + + private fun generateContainedReactorScope(triggers: List, inst: Instantiation) = with(PrependOperator) { + """| + |// Generated struct providing access to ports of child reactor `${inst.name}` + |struct _${inst.reactor.codeType}_${inst.name} { + ${"| "..triggers.joinToString { generateContainedTriggerInScope(it) }} + |}; + |struct _${inst.reactor.codeType}_${inst.name} ${inst.name} = {${ + triggers.joinToString(separator = ", ") { + generateContainedTriggerFieldInit( + it + ) + } + }}; + """.trimMargin() + } + + private fun generateTriggersEffectsAndSourcesInScope(reaction: Reaction) = + reaction.allUncontainedTriggers.plus(reaction.allUncontainedEffects).plus(reaction.allUncontainedSources) + .joinToString(separator = "\n") { with(PrependOperator) { it.scope.toString() } } + + private fun generateContainedTriggersAndSourcesInScope(reaction: Reaction) = + reaction.allContainedEffectsTriggersAndSources.toList() + .joinToString(separator = "\n") { generateContainedReactorScope(it.second, it.first) } + + private fun generateTriggerRegisterEffect(reaction: Reaction) = + reaction.triggers.joinToString( + separator = "\n", + ) { registerEffect(it, reaction) } - fun generateTriggerRegisterEffect(reaction: Reaction) = - reaction.allUncontainedTriggers.joinToString( + private fun generateTriggerRegisterObserver(reaction: Reaction) = + reaction.sources.joinToString( separator = "\n", - ) {"${it.typeMacro}_REGISTER_EFFECT(self->${it.name}, self->${reaction.codeName});"}; + ) { registerObserver(it, reaction) } - fun generateTriggerRegisterSource(reaction: Reaction) = - reaction.allUncontainedEffects.joinToString( + private fun generateTriggerRegisterSource(reaction: Reaction) = + reaction.effects.joinToString( separator = "\n", - ) {"${it.typeMacro}_REGISTER_SOURCE(self->${it.name}, self->${reaction.codeName});"}; + ) { registerSource(it, reaction) } - fun generateRegisterEffects(reaction: Reaction) = - reaction.allUncontainedEffects.joinToString( + private fun generateRegisterEffects(reaction: Reaction) = + reaction.effects.joinToString( separator = "\n", ) { - "REACTION_REGISTER_EFFECT(self->${reaction.codeName}, self->${it.name});" + "REACTION_REGISTER_EFFECT(${reaction.codeName}, ${it.fullName});" }; - fun generateReactorCtorCode(reaction: Reaction) = with(PrependOperator) { + private fun generateReactorCtorCode(reaction: Reaction) = with(PrependOperator) { """ - |self->_reactions[${reaction.index}] = &self->${reaction.codeName}.super; - |${reaction.codeType}_ctor(&self->${reaction.codeName}, &self->super); - |// Register all triggers of this reaction. - ${" |"..generateTriggerRegisterEffect(reaction)} - ${" |"..generateTriggerRegisterSource(reaction)} - ${" |"..generateRegisterEffects(reaction)} + |INITIALIZE_REACTION(${reactor.codeType}, ${reaction.codeName}); + ${" | "..generateTriggerRegisterEffect(reaction)} + ${" | "..generateTriggerRegisterSource(reaction)} + ${" | "..generateTriggerRegisterObserver(reaction)} + ${" | "..generateRegisterEffects(reaction)} """.trimMargin() - }; + } fun generateReactorCtorCodes() = - reactor.reactions.joinToString(separator = "\n", prefix = "// Initialize Reactions \n") { generateReactorCtorCode(it) } - - private val VarRef.isContainedRef: Boolean get() = container != null - private val TriggerRef.isContainedRef: Boolean get() = this is VarRef && isContainedRef - - private fun VarRef.isEffectOf(reaction: Reaction): Boolean = - reaction.effects.any { name == it.name && container?.name == it.container?.name } + reactor.reactions.joinToString( + separator = "\n", + prefix = "// Initialize Reactions \n" + ) { generateReactorCtorCode(it) } + + /** + * Returns all the reactions triggered by the Output port which are contained in the parent reactor. + * This is used for reactions triggered by contained output ports. + */ + fun getParentReactionEffectsOfOutput(inst: Instantiation, port: Output): List { + val res = mutableListOf(); + for (reaction in reactor.reactions) { + if (reaction.allContainedTriggers.filter { it is VarRef && it.variable == port }.isNotEmpty()) { + res.add(reaction) + } + } + return res + } - private fun TriggerRef.isEffectOf(reaction: Reaction): Boolean = this is VarRef && isEffectOf(reaction) + fun getParentReactionObserversOfOutput(inst: Instantiation, port: Output): List { + val res = mutableListOf(); + for (reaction in reactor.reactions) { + if (reaction.allContainedSources.filter { it is VarRef && it.variable == port }.isNotEmpty()) { + res.add(reaction) + } + } + return res + } - private val Reaction.allUncontainedTriggers get() = triggers.filterNot { it.isEffectOf(this) || it.isContainedRef } - private val Reaction.allUncontainedEffects get() = effects.filterNot { it.isContainedRef }; + fun getParentReactionSourcesOfInput(inst: Instantiation, port: Input): List { + val res = mutableListOf(); + for (reaction in reactor.reactions) { + if (reaction.allContainedEffects.filter { it is VarRef && it.variable == port }.isNotEmpty()) { + res.add(reaction) + } + } + return res + } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index 8d11e1b3..7c4da9d6 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -2,47 +2,72 @@ package org.lflang.generator.uc import org.lflang.MessageReporter import org.lflang.generator.PrependOperator -import org.lflang.lf.BuiltinTrigger -import org.lflang.lf.BuiltinTriggerRef -import org.lflang.lf.Reaction -import org.lflang.lf.Reactor -import org.lflang.priority +import org.lflang.lf.* import org.lflang.toUnixString class UcReactorGenerator(private val reactor: Reactor, fileConfig: UcFileConfig, messageReporter: MessageReporter) { private val headerFile = fileConfig.getReactorHeaderPath(reactor).toUnixString() - private val hasStartup = reactor.reactions.filter {it.triggers.filter {it is BuiltinTriggerRef && it.type == BuiltinTrigger.STARTUP}.isNotEmpty()}.isNotEmpty() - private val hasShutdown = reactor.reactions.filter {it.triggers.filter {it is BuiltinTriggerRef && it.type == BuiltinTrigger.SHUTDOWN}.isNotEmpty()}.isNotEmpty() + private val hasStartup = reactor.reactions.filter { + it.triggers.filter { it is BuiltinTriggerRef && it.type == BuiltinTrigger.STARTUP }.isNotEmpty() + }.isNotEmpty() + private val hasShutdown = reactor.reactions.filter { + it.triggers.filter { it is BuiltinTriggerRef && it.type == BuiltinTrigger.SHUTDOWN }.isNotEmpty() + }.isNotEmpty() - // FIXME: We might not need to put all of these in the triggers field of the reactor... - // I think it is only used for causality cycle check. private fun numTriggers(): Int { - var res = reactor.actions.size + reactor.timers.size + reactor.inputs.size + reactor.outputs.size; + var res = reactor.actions.size + reactor.timers.size + reactor.inputs.size; if (hasShutdown) res++; if (hasStartup) res++; return res; } + private val numChildren = reactor.instantiations.size; private val parameters = UcParameterGenerator(reactor) + private val connections = UcConnectionGenerator(reactor) private val state = UcStateGenerator(reactor) -// private val methods = CppMethodGenerator(reactor) - private val instances = UcInstanceGenerator(reactor, parameters, fileConfig, messageReporter) + private val ports = UcPortGenerator(reactor, connections) private val timers = UcTimerGenerator(reactor) private val actions = UcActionGenerator(reactor) - private val ports = UcPortGenerator(reactor) - private val connections = UcConnectionGenerator(reactor) - private val reactions = UcReactionGenerator(reactor, ports) + private val reactions = UcReactionGenerator(reactor) private val preambles = UcPreambleGenerator(reactor) + private val instances = + UcInstanceGenerator(reactor, parameters, ports, connections, reactions, fileConfig, messageReporter) + + + private fun takesExtraParameters(): Boolean = + parameters.generateReactorCtorDefArguments().isNotEmpty() || ports.generateReactorCtorDefArguments() + .isNotEmpty() + + private fun generateReactorCtorSignature(): String = + if (takesExtraParameters()) "REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(${reactor.codeType} ${parameters.generateReactorCtorDefArguments()} ${ports.generateReactorCtorDefArguments()})" + else "REACTOR_CTOR_SIGNATURE(${reactor.codeType})" + companion object { val Reactor.codeType get(): String = "Reactor_$name" + + val Reactor.hasStartup + get(): Boolean = reactions.filter { + it.triggers.filter { it is BuiltinTriggerRef && it.type == BuiltinTrigger.STARTUP }.isNotEmpty() + }.isNotEmpty() + + val Reactor.hasShutdown + get(): Boolean = reactions.filter { + it.triggers.filter { it is BuiltinTriggerRef && it.type == BuiltinTrigger.SHUTDOWN }.isNotEmpty() + }.isNotEmpty() + fun Reactor.getEffects(v: Variable) = reactions.filter { it.triggers.filter { it.name == v.name }.isNotEmpty() } + fun Reactor.getObservers(v: Variable) = reactions.filter { it.sources.filter { it.name == v.name }.isNotEmpty() } + fun Reactor.getSources(v: Variable) = reactions.filter { it.effects.filter { it.name == v.name }.isNotEmpty() } + fun Reactor.getEffects(v: BuiltinTrigger) = reactions.filter { it.triggers.filter { it.name == v.literal}.isNotEmpty() } + fun Reactor.getObservers(v: BuiltinTrigger) = reactions.filter { it.sources.filter { it.name == v.literal}.isNotEmpty() } } - fun generateReactorStruct() = with(PrependOperator) { + + private fun generateReactorStruct() = with(PrependOperator) { """ |typedef struct { | Reactor super; @@ -54,14 +79,29 @@ class UcReactorGenerator(private val reactor: Reactor, fileConfig: UcFileConfig, ${" | "..ports.generateReactorStructFields()} ${" | "..state.generateReactorStructFields()} ${" | "..parameters.generateReactorStructFields()} - | // Pointer arrays used by runtime system. - | Reaction *_reactions[${reactor.reactions.size}]; - | Trigger *_triggers[${numTriggers()}]; - | Reactor *_children[${reactor.instantiations.size}]; + | REACTOR_BOOKKEEPING_INSTANCES(${reactor.reactions.size}, ${numTriggers()}, ${numChildren}); |} ${reactor.codeType}; + | """.trimMargin() } + private fun generateCtorDefinition() = with(PrependOperator) { + """ + |${generateReactorCtorSignature()} { + | REACTOR_CTOR_PREAMBLE(); + | REACTOR_CTOR(${reactor.codeType}); + ${" | "..parameters.generateReactorCtorCodes()} + ${" | "..instances.generateReactorCtorCodes()} + ${" | "..timers.generateReactorCtorCodes()} + ${" | "..actions.generateReactorCtorCodes()} + ${" | "..ports.generateReactorCtorCodes()} + ${" | "..connections.generateReactorCtorCodes()} + ${" | "..reactions.generateReactorCtorCodes()} + |} + | + """.trimMargin() + } + fun generateHeader() = with(PrependOperator) { """ |#include "reactor-uc/reactor-uc.h" @@ -73,14 +113,14 @@ class UcReactorGenerator(private val reactor: Reactor, fileConfig: UcFileConfig, ${" |"..actions.generateSelfStructs()} ${" |"..ports.generateSelfStructs()} ${" |"..connections.generateSelfStructs()} - | // The reactor self struct + |//The reactor self struct ${" |"..generateReactorStruct()} - | // The constructor for the self struct - |void ${reactor.codeType}_ctor(${reactor.codeType} *self, Environment *env, Reactor *parent${parameters.generateReactorCtorDefArguments()}); | + |${generateReactorCtorSignature()}; | """.trimMargin() } + fun generateSource() = with(PrependOperator) { """ |#include "${headerFile}" @@ -92,24 +132,9 @@ class UcReactorGenerator(private val reactor: Reactor, fileConfig: UcFileConfig, ${" |"..ports.generateCtors()} ${" |"..connections.generateCtors()} ${" |"..generateCtorDefinition()} + | """.trimMargin() } - fun generateCtorDefinition() = with(PrependOperator) { - """ - |void ${reactor.codeType}_ctor(${reactor.codeType} *self, Environment *env, Reactor *parent${parameters.generateReactorCtorDefArguments()}) { - | ${if (numTriggers() > 0) "size_t trigger_idx = 0;" else ""} - | ${if (numChildren> 0) "size_t child_idx = 0;" else ""} - | Reactor_ctor(&self->super, "${reactor.name}", env, parent, ${if (numChildren > 0) "self->_children" else "NULL"}, $numChildren, ${if (reactor.reactions.size > 0) "self->_reactions" else "NULL"}, ${reactor.reactions.size}, ${if (numTriggers() > 0) "self->_triggers" else "NULL"}, ${numTriggers()}); - ${" | "..parameters.generateReactorCtorCodes()} - ${" | "..instances.generateReactorCtorCodes()} - ${" | "..timers.generateReactorCtorCodes()} - ${" | "..actions.generateReactorCtorCodes()} - ${" | "..ports.generateReactorCtorCodes()} - ${" | "..connections.generateReactorCtorCodes()} - ${" | "..reactions.generateReactorCtorCodes()} - |} - """.trimMargin() - } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt index 8fe83a9e..0023e58a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt @@ -1,17 +1,17 @@ package org.lflang.generator.uc -import org.apache.commons.text.StringEscapeUtils import org.lflang.generator.CodeMap import org.lflang.generator.LFGeneratorContext import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.type.BuildTypeType.BuildType -import org.lflang.target.property.type.PlatformType import org.lflang.toUnixString import org.lflang.util.FileUtil import org.lflang.util.LFCommand import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths +import kotlin.io.path.createSymbolicLinkPointingTo +import kotlin.io.path.Path as KPath class UcStandaloneGenerator(generator: UcGenerator) : UcPlatformGenerator(generator) { @@ -21,13 +21,12 @@ class UcStandaloneGenerator(generator: UcGenerator) : BuildType.TEST -> "Debug" else -> type.toString() } - - const val DEFAULT_BASE_IMAGE: String = "alpine:latest" } override fun generatePlatformFiles() { val srcGenRoot = fileConfig.srcGenBasePath + val runtimePath: Path = Paths.get(System.getenv("REACTOR_UC_PATH"))!! // FIXME: Generate error if not there // generate the main source file (containing main()) val mainGenerator = UcMainGenerator(mainReactor, generator.targetConfig, generator.fileConfig) @@ -44,20 +43,18 @@ class UcStandaloneGenerator(generator: UcGenerator) : FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) FileUtil.writeToFile(mainGenerator.generateMainHeader(), srcGenPath.resolve(mainHeaderFile), true) - // generate the cmake scripts val cmakeGenerator = UcCmakeGenerator(targetConfig, generator.fileConfig) val makeGenerator = UcMakeGenerator(targetConfig, generator.fileConfig) val pkgName = fileConfig.srcGenPkgPath.fileName.toString() -// FileUtil.writeToFile(cmakeGenerator.generateRootCmake(pkgName), srcGenRoot.resolve("CMakeLists.txt"), true) FileUtil.writeToFile(cmakeGenerator.generateCmake(cppSources), srcGenPath.resolve("CMakeLists.txt"), true) + val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); + try { + runtimeSymlinkPath.createSymbolicLinkPointingTo(runtimePath); + } catch (e: Exception) { + // Do nothing + } + FileUtil.writeToFile(makeGenerator.generateMake(cppSources), srcGenPath.resolve("Makefile"), true) -// FileUtil.writeToFile("", srcGenPath.resolve(".lf-cpp-marker"), true) -// var subdir = srcGenPath.parent -// while (subdir != srcGenRoot) { -// FileUtil.writeToFile(cmakeGenerator.generateSubdirCmake(), subdir.resolve("CMakeLists.txt"), true) -// FileUtil.writeToFile("", subdir.resolve(".lf-cpp-marker"), true) -// subdir = subdir.parent -// } } override fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean): Boolean { @@ -112,7 +109,7 @@ class UcStandaloneGenerator(generator: UcGenerator) : if (version == null || version.compareVersion("3.5.0") < 0) { messageReporter.nowhere( ).error( - "The C++ target requires CMAKE >= 3.5.0 to compile the generated code. " + + "The uC target requires CMAKE >= 3.5.0 to compile the generated code. " + "Auto-compiling can be disabled using the \"no-compile: true\" target property." ) return null diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt index 41400798..33366b4a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt @@ -1,39 +1,9 @@ -/************* - * Copyright (c) 2021, TU Dresden. - - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ***************/ - package org.lflang.generator.uc -import org.lflang.generator.uc.UcTimerGenerator.Companion.codeType -import org.lflang.inferredType -import org.lflang.isInitialized -import org.lflang.joinWithLn import org.lflang.lf.Reactor import org.lflang.toText class UcStateGenerator(private val reactor: Reactor) { - fun generateReactorStructFields() = reactor.stateVars.joinToString(prefix = "// State variables \n", separator = "\n") { "${it.type.toText()} ${it.name};" } - } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTimerGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTimerGenerator.kt index f69720ee..91421d65 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTimerGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTimerGenerator.kt @@ -1,49 +1,15 @@ -/************* - * Copyright (c) 2021, TU Dresden. - - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ***************/ - package org.lflang.generator.uc -import org.lflang.generator.PrependOperator import org.lflang.generator.orNever -import org.lflang.generator.orZero -import org.lflang.isGeneric -import org.lflang.lf.Reaction +import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType +import org.lflang.generator.uc.UcReactorGenerator.Companion.getEffects +import org.lflang.generator.uc.UcReactorGenerator.Companion.getObservers import org.lflang.lf.Reactor import org.lflang.lf.Timer -import org.lflang.priority class UcTimerGenerator(private val reactor: Reactor) { - companion object { - // FIXME: Make nicer and also merge with the VarRef stuff in UcReactionGenerator - val Timer.codeType - get(): String = "${(eContainer() as Reactor).name}_Timer_${name}" - } - - fun getEffects(timer: Timer) = reactor.reactions.filter {it.triggers.filter{ it.name== timer.name}.isNotEmpty()} - - fun generateSelfStructs(timer: Timer) = "DEFINE_TIMER_STRUCT(${timer.codeType}, ${getEffects(timer).size})" - fun generateCtor(timer: Timer) = "DEFINE_TIMER_CTOR(${timer.codeType}, ${getEffects(timer).size})" + private fun generateSelfStructs(timer: Timer) = "DEFINE_TIMER_STRUCT(${reactor.codeType}, ${timer.name}, ${reactor.getEffects(timer).size}, ${reactor.getObservers(timer).size});" + private fun generateCtor(timer: Timer) = "DEFINE_TIMER_CTOR(${reactor.codeType}, ${timer.name}, ${reactor.getEffects(timer).size}, ${reactor.getObservers(timer).size});" fun generateCtors() = reactor.timers.joinToString( separator = "\n", @@ -51,7 +17,6 @@ class UcTimerGenerator(private val reactor: Reactor) { postfix = "\n" ) { generateCtor(it) }; - fun generateSelfStructs() = reactor.timers.joinToString( separator = "\n", @@ -60,14 +25,8 @@ class UcTimerGenerator(private val reactor: Reactor) { ) { generateSelfStructs(it) }; fun generateReactorStructFields() = - reactor.timers.joinToString(separator = "\n", prefix = "// Timers\n", postfix = "\n") { "${it.codeType} ${it.name};" } + reactor.timers.joinToString(separator = "\n", prefix = "// Timers\n", postfix = "\n") { "TIMER_INSTANCE(${reactor.codeType}, ${it.name});" } - fun generateReactorCtorCode(timer: Timer) = with(PrependOperator) { - """ - |self->_triggers[trigger_idx++] = &self->${timer.name}.super.super; - |${timer.codeType}_ctor(&self->${timer.name}, &self->super, ${timer.offset.toCCode()}, ${timer.period.orNever().toCCode()}); - | - """.trimMargin() - }; + private fun generateReactorCtorCode(timer: Timer) = "INITIALIZE_TIMER(${reactor.codeType}, ${timer.name}, ${timer.offset.toCCode()}, ${timer.period.orNever().toCCode()});" fun generateReactorCtorCodes() = reactor.timers.joinToString(separator = "\n", prefix = "// Initialize Timers\n") { generateReactorCtorCode(it)} } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTypes.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTypes.kt index 42ac248d..d298aa5f 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTypes.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTypes.kt @@ -23,14 +23,9 @@ */ package org.lflang.generator.uc - -import org.lflang.InferredType import org.lflang.TimeUnit import org.lflang.TimeValue import org.lflang.generator.TargetTypes -import org.lflang.lf.Initializer -import org.lflang.lf.ParameterReference -import org.lflang.lf.ParenthesisListExpression object UcTypes : TargetTypes { @@ -45,8 +40,6 @@ object UcTypes : TargetTypes { if (magnitude == 0L) "0" else "${unit.cUnit}(${magnitude.toString()})" } -// override fun getTargetParamRef(expr: ParameterReference, typeOrNull: InferredType?): String = -// "self->${escapeIdentifier(expr.getParameter().getName())}" } val TimeUnit?.cUnit diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcValidator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcValidator.kt index cdf89ae8..766cbbba 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcValidator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcValidator.kt @@ -3,14 +3,10 @@ package org.lflang.generator.uc import org.lflang.MessageReporter import org.lflang.generator.CodeMap import org.lflang.generator.DiagnosticReporting -import org.lflang.generator.HumanReadableReportingStrategy import org.lflang.generator.ValidationStrategy import org.lflang.generator.Validator import org.lflang.util.LFCommand -import java.io.File import java.nio.file.Path -import java.nio.file.Paths -import java.util.regex.Pattern class UcValidator( private val fileConfig: UcFileConfig, diff --git a/src/action.c b/src/action.c index e071c5ca..b4dbd5e9 100644 --- a/src/action.c +++ b/src/action.c @@ -92,8 +92,9 @@ lf_ret_t Action_schedule(Action *self, interval_t offset, const void *value) { } void Action_ctor(Action *self, ActionType type, interval_t min_offset, Reactor *parent, Reaction **sources, - size_t sources_size, Reaction **effects, size_t effects_size, void *value_ptr, size_t value_size, - void *payload_buf, bool *payload_used_buf, size_t event_bound) { + size_t sources_size, Reaction **effects, size_t effects_size, Reaction **observers, + size_t observers_size, void *value_ptr, size_t value_size, void *payload_buf, bool *payload_used_buf, + size_t event_bound) { int capacity = 0; if (payload_buf != NULL) { capacity = event_bound; @@ -112,6 +113,9 @@ void Action_ctor(Action *self, ActionType type, interval_t min_offset, Reactor * self->effects.reactions = effects; self->effects.size = effects_size; self->effects.num_registered = 0; + self->observers.reactions = observers; + self->observers.size = observers_size; + self->observers.num_registered = 0; if (type == PHYSICAL_ACTION) { self->super.parent->env->has_async_events = true; diff --git a/src/builtin_triggers.c b/src/builtin_triggers.c index 87dc5445..55d41d17 100644 --- a/src/builtin_triggers.c +++ b/src/builtin_triggers.c @@ -25,11 +25,14 @@ void Builtin_cleanup(Trigger *self) { } void BuiltinTrigger_ctor(BuiltinTrigger *self, TriggerType type, Reactor *parent, Reaction **effects, - size_t effects_size) { + size_t effects_size, Reaction **observers, size_t observers_size) { Trigger_ctor(&self->super, type, parent, NULL, Builtin_prepare, Builtin_cleanup); self->effects.reactions = effects; self->effects.num_registered = 0; self->effects.size = effects_size; + self->observers.reactions = observers; + self->observers.size = observers_size; + self->observers.num_registered = 0; self->next = NULL; if (type == TRIG_STARTUP) { diff --git a/src/connection.c b/src/connection.c index 6b5b6a52..0b1234d2 100644 --- a/src/connection.c +++ b/src/connection.c @@ -5,7 +5,7 @@ #include #include -Output *Connection_get_final_upstream(Connection *self) { +Port *Connection_get_final_upstream(Connection *self) { if (!self->upstream) { return NULL; @@ -13,7 +13,11 @@ Output *Connection_get_final_upstream(Connection *self) { switch (self->upstream->super.type) { case TRIG_OUTPUT: - return (Output *)self->upstream; + if (self->upstream->conn_in) { + return Connection_get_final_upstream(self->upstream->conn_in); + } else { + return self->upstream; + } case TRIG_INPUT: if (self->upstream->conn_in) { return Connection_get_final_upstream(self->upstream->conn_in); @@ -41,16 +45,15 @@ void LogicalConnection_trigger_downstreams(Connection *self, const void *value, for (size_t i = 0; i < self->downstreams_size; i++) { Port *down = self->downstreams[i]; - if (down->super.type == TRIG_INPUT) { - LF_DEBUG(CONN, "Found downstream input port %p to trigger.", down); - Input *inp = (Input *)down; - validate(value_size == inp->value_size); - memcpy(inp->value_ptr, value, value_size); // NOLINT + if (down->effects.size > 0 || down->observers.size > 0) { + validate(value_size == down->value_size); + memcpy(down->value_ptr, value, value_size); // NOLINT + // Only call `prepare` and thus trigger downstream reactions once per // tag. This is to support multiple writes to the same port with // "last write wins" - if (!inp->super.super.is_present) { - inp->super.super.prepare(&inp->super.super, NULL); + if (!down->super.is_present) { + down->super.prepare(&down->super, NULL); } } diff --git a/src/federated.c b/src/federated.c index ebaea0c5..d86b967a 100644 --- a/src/federated.c +++ b/src/federated.c @@ -3,6 +3,8 @@ #include "reactor-uc/logging.h" #include "reactor-uc/platform.h" +#pragma GCC diagnostic ignored "-Wstack-usage=" + // TODO: Refactor so this function is available void LogicalConnection_trigger_downstreams(Connection *self, const void *value, size_t value_size); diff --git a/src/network_channel.c b/src/network_channel.c index e5cf390b..447f387e 100644 --- a/src/network_channel.c +++ b/src/network_channel.c @@ -18,6 +18,11 @@ #error "NETWORK_POSIC_TCP not supported on PICO" #endif +#elif defined(PLATFORM_FLEXPRET) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#error "NETWORK_POSIC_TCP not supported on FlexPRET" +#endif + #else #error "Platform not supported" #endif diff --git a/src/platform.c b/src/platform.c index b756113c..b81736cb 100644 --- a/src/platform.c +++ b/src/platform.c @@ -4,6 +4,8 @@ #include "platform/riot/riot.c" #elif defined(PLATFORM_ZEPHYR) #include "platform/zephyr/zephyr.c" +#elif defined(PLATFORM_FLEXPRET) +#include "platform/flexpret/flexpret.c" #elif defined(PLATFORM_PICO) #include "platform/pico/pico.c" #else diff --git a/src/platform/flexpret/flexpret.c b/src/platform/flexpret/flexpret.c new file mode 100644 index 00000000..2a783045 --- /dev/null +++ b/src/platform/flexpret/flexpret.c @@ -0,0 +1,109 @@ +#include "reactor-uc/platform/flexpret/flexpret.h" +#include "reactor-uc/error.h" + +static PlatformFlexpret platform; + + +void Platform_vprintf(const char *fmt, va_list args) { vprintf(fmt, args); } + +lf_ret_t PlatformFlexpret_initialize(Platform *self) { + PlatformFlexpret *p = (PlatformFlexpret *) self; + p->async_event_occurred = false; + p->in_critical_section = false; + p->lock = (fp_lock_t) FP_LOCK_INITIALIZER; + return LF_OK; +} + +instant_t PlatformFlexpret_get_physical_time(Platform *self) { + (void)self; + return (instant_t) rdtime64(); +} + +lf_ret_t PlatformFlexpret_wait_until_interruptible(Platform *self, instant_t wakeup_time) { + PlatformFlexpret *p = (PlatformFlexpret *) self; + + p->async_event_occurred = false; + self->leave_critical_section(self); + // For FlexPRET specifically this functionality is directly available as + // an instruction + // It cannot fail - if it does, there is a bug in the processor and not much + // software can do about it + fp_wait_until(wakeup_time); + self->enter_critical_section(self); + + if (p->async_event_occurred) { + return LF_SLEEP_INTERRUPTED; + } else { + return LF_OK; + } +} + +lf_ret_t PlatformFlexpret_wait_until(Platform *self, instant_t wakeup_time) { + (void)self; + + // Interrupts should be disabled here so it does not matter whether we + // use wait until or delay until, but delay until is more accurate here + fp_delay_until(wakeup_time); + return LF_OK; +} + +lf_ret_t PlatformFlexpret_wait_for(Platform *self, interval_t wait_time) { + (void)self; + + // Interrupts should be disabled here so it does not matter whether we + // use wait until or delay until, but delay until is more accurate here + fp_delay_for(wait_time); + return LF_OK; +} + +// Note: Code is directly copied from FlexPRET's reactor-c implementation; +// beware of code duplication +void PlatformFlexpret_leave_critical_section(Platform *self) { + PlatformFlexpret *p = (PlatformFlexpret *) self; + + // In the special case where this function is called during an interrupt + // subroutine (isr) it should have no effect + if ((read_csr(CSR_STATUS) & 0x04) == 0x04) + return; + + validate(p->in_critical_section == true); + + fp_interrupt_enable(); + fp_lock_release(&p->lock); + p->in_critical_section = false; +} + +// Note: Code is directly copied from FlexPRET's reactor-c implementation; +// beware of code duplication +void PlatformFlexpret_enter_critical_section(Platform *self) { + PlatformFlexpret *p = (PlatformFlexpret *) self; + + // In the special case where this function is called during an interrupt + // subroutine (isr) it should have no effect + if ((read_csr(CSR_STATUS) & 0x04) == 0x04) + return; + + validate(p->in_critical_section == false); + + fp_interrupt_disable(); + fp_lock_acquire(&p->lock); + p->in_critical_section = true; +} + +void PlatformFlexpret_new_async_event(Platform *self) { + PlatformFlexpret *p = (PlatformFlexpret *) self; + p->async_event_occurred = true; +} + +void Platform_ctor(Platform *self) { + self->enter_critical_section = PlatformFlexpret_enter_critical_section; + self->leave_critical_section = PlatformFlexpret_leave_critical_section; + self->get_physical_time = PlatformFlexpret_get_physical_time; + self->wait_until = PlatformFlexpret_wait_until; + self->wait_until = PlatformFlexpret_wait_for; + self->initialize = PlatformFlexpret_initialize; + self->wait_until_interruptible = PlatformFlexpret_wait_until_interruptible; + self->new_async_event = PlatformFlexpret_new_async_event; +} + +Platform *Platform_new(void) { return (Platform *)&platform; } diff --git a/src/port.c b/src/port.c index 932bcc11..aaa0920f 100644 --- a/src/port.c +++ b/src/port.c @@ -5,12 +5,13 @@ #include #include -void Input_prepare(Trigger *_self, Event *event) { +void Port_prepare(Trigger *_self, Event *event) { (void)event; - assert(_self->type == TRIG_INPUT); - Input *self = (Input *)_self; - LF_DEBUG(TRIG, "Preparing input %p with %d effects", self, self->effects.size); - Scheduler *sched = &self->super.super.parent->env->scheduler; + assert(_self->type == TRIG_INPUT || _self->type == TRIG_OUTPUT); + + Port *self = (Port *)_self; + LF_DEBUG(TRIG, "Preparing port %p with %d effects", self, self->effects.size); + Scheduler *sched = &self->super.parent->env->scheduler; _self->is_present = true; assert(!_self->is_registered_for_cleanup); sched->register_for_cleanup(sched, _self); @@ -20,37 +21,44 @@ void Input_prepare(Trigger *_self, Event *event) { } } -void Input_cleanup(Trigger *_self) { - assert(_self->type == TRIG_INPUT); - assert(_self->is_registered_for_cleanup); - LF_DEBUG(TRIG, "Cleaning up input %p", _self); - _self->is_present = false; -} +void Port_set(Port *self, const void *value) { -void Input_ctor(Input *self, Reactor *parent, Reaction **effects, size_t effects_size, Connection **conns_out, - size_t conns_out_size, void *value_ptr, size_t value_size) { - Port_ctor(&self->super, TRIG_INPUT, parent, conns_out, conns_out_size, Input_prepare, Input_cleanup); - self->effects.reactions = effects; - self->effects.num_registered = 0; - self->effects.size = effects_size; - self->value_ptr = value_ptr; - self->value_size = value_size; -} + if (self->effects.size > 0 || self->observers.size > 0) { + memcpy(self->value_ptr, value, self->value_size); + Port_prepare(&self->super, NULL); + } -void Output_ctor(Output *self, Reactor *parent, Reaction **sources, size_t sources_size, Connection **conns_out, - size_t conns_out_size) { + for (size_t i = 0; i < self->conns_out_registered; i++) { + Connection *conn = self->conns_out[i]; + conn->trigger_downstreams(conn, value, self->value_size); + } +} - Port_ctor(&self->super, TRIG_OUTPUT, parent, conns_out, conns_out_size, NULL, NULL); - self->sources.reactions = sources; - self->sources.size = sources_size; - self->sources.num_registered = 0; +void Port_cleanup(Trigger *_self) { + assert(_self->type == TRIG_INPUT || _self->type == TRIG_OUTPUT); + assert(_self->is_registered_for_cleanup); + LF_DEBUG(TRIG, "Cleaning up port %p", _self); + _self->is_present = false; } -void Port_ctor(Port *self, TriggerType type, Reactor *parent, Connection **conns_out, size_t conns_out_size, - void (*prepare)(Trigger *, Event *), void (*cleanup)(Trigger *)) { - Trigger_ctor(&self->super, type, parent, NULL, prepare, cleanup); +void Port_ctor(Port *self, TriggerType type, Reactor *parent, void *value_ptr, size_t value_size, Reaction **effects, + size_t effects_size, Reaction **sources, size_t sources_size, Reaction **observers, + size_t observers_size, Connection **conns_out, size_t conns_out_size) { + Trigger_ctor(&self->super, type, parent, NULL, Port_prepare, Port_cleanup); + self->set = Port_set; self->conn_in = NULL; self->conns_out = conns_out; self->conns_out_size = conns_out_size; self->conns_out_registered = 0; + self->sources.reactions = sources; + self->sources.size = sources_size; + self->sources.num_registered = 0; + self->effects.reactions = effects; + self->effects.num_registered = 0; + self->effects.size = effects_size; + self->observers.reactions = observers; + self->observers.size = observers_size; + self->observers.num_registered = 0; + self->value_ptr = value_ptr; + self->value_size = value_size; } diff --git a/src/reaction.c b/src/reaction.c index 61e9e09e..7cc84c18 100644 --- a/src/reaction.c +++ b/src/reaction.c @@ -3,8 +3,6 @@ #include "reactor-uc/port.h" #include "reactor-uc/trigger.h" -static size_t calculate_input_port_level(Input *port); - size_t Reaction_get_level(Reaction *self) { if (self->level < 0) { self->level = (int)self->calculate_level(self); @@ -12,10 +10,10 @@ size_t Reaction_get_level(Reaction *self) { return self->level; } -static size_t calculate_input_port_level(Input *port) { +static size_t calculate_port_level(Port *port) { size_t current = 0; - if (port->super.conn_in) { - Output *final_upstream_port = port->super.conn_in->get_final_upstream(port->super.conn_in); + if (port->conn_in) { + Port *final_upstream_port = port->conn_in->get_final_upstream(port->conn_in); if (final_upstream_port) { for (size_t k = 0; k < final_upstream_port->sources.size; k++) { Reaction *upstream = final_upstream_port->sources.reactions[k]; @@ -26,10 +24,43 @@ static size_t calculate_input_port_level(Input *port) { } } } + + for (size_t i = 0; i < port->sources.size; i++) { + Reaction *source = port->sources.reactions[i]; + size_t source_level = source->get_level(source) + 1; + if (source_level > current) { + current = source_level; + } + } + LF_INFO(ENV, "Input port %p has level %d", port, current); return current; } +size_t Reaction_calculate_trigger_level(Reaction *self, Trigger *trigger) { + size_t max_level = 0; + if (trigger->type == TRIG_INPUT || trigger->type == TRIG_OUTPUT) { + Port *port = (Port *)trigger; + for (size_t j = 0; j < port->effects.size; j++) { + if (port->effects.reactions[j] == self) { + size_t level_from_port = calculate_port_level(port) + 1; + if (level_from_port > max_level) { + max_level = level_from_port; + } + } + } + for (size_t j = 0; j < port->observers.size; j++) { + if (port->observers.reactions[j] == self) { + size_t level_from_port = calculate_port_level(port) + 1; + if (level_from_port > max_level) { + max_level = level_from_port; + } + } + } + } + return max_level; +} + // TODO: Do casuality cycle detection here. A causality cycle will currently lead to infinite recursion and stack // overflow. size_t Reaction_calculate_level(Reaction *self) { @@ -47,15 +78,21 @@ size_t Reaction_calculate_level(Reaction *self) { // Find all Input ports with the current reaction as an effect for (size_t i = 0; i < self->parent->triggers_size; i++) { Trigger *trigger = self->parent->triggers[i]; - if (trigger->type == TRIG_INPUT) { - Input *port = (Input *)trigger; - for (size_t j = 0; j < port->effects.size; j++) { - if (port->effects.reactions[j] == self) { - size_t level_from_input = calculate_input_port_level(port) + 1; - if (level_from_input > max_level) { - max_level = level_from_input; - } - } + size_t trigger_from_level = Reaction_calculate_trigger_level(self, trigger); + if (trigger_from_level > max_level) { + max_level = trigger_from_level; + } + } + + // Find all output ports within contained reactors which has marked our reaction + // as an effect or observer. + for (size_t i = 0; i < self->parent->children_size; i++) { + Reactor *child = self->parent->children[i]; + for (size_t j = 0; j < child->triggers_size; j++) { + Trigger *trigger = self->parent->triggers[j]; + size_t trigger_from_level = Reaction_calculate_trigger_level(self, trigger); + if (trigger_from_level > max_level) { + max_level = trigger_from_level; } } } diff --git a/src/reactor.c b/src/reactor.c index 97ae43a1..7251fbc7 100644 --- a/src/reactor.c +++ b/src/reactor.c @@ -20,31 +20,17 @@ void Reactor_validate(Reactor *self) { for (size_t i = 0; i < reaction->effects_size; i++) { Trigger *effect = reaction->effects[i]; validate(effect); - validate(effect->parent == self); } prev_level = reaction->level; } for (size_t i = 0; i < self->triggers_size; i++) { Trigger *trigger = self->triggers[i]; validate(trigger); - validate(trigger->parent == self); - if (trigger->type == TRIG_INPUT) { + if (trigger->type == TRIG_INPUT || trigger->type == TRIG_OUTPUT) { Port *port = (Port *)trigger; - Input *input = (Input *)trigger; - validate(input->effects.num_registered == input->effects.size); - validate(port->conns_out_size == port->conns_out_registered); - for (size_t i = 0; i < port->conns_out_registered; i++) { - Connection *conn = port->conns_out[i]; - validate(conn); - validate(conn->upstream == port); - } - } - - if (trigger->type == TRIG_OUTPUT) { - Port *port = (Port *)trigger; - Output *output = (Output *)trigger; - validate(output->sources.num_registered == output->sources.size); + validate(port->effects.num_registered == port->effects.size); + validate(port->sources.num_registered == port->sources.size); validate(port->conns_out_size == port->conns_out_registered); for (size_t i = 0; i < port->conns_out_registered; i++) { Connection *conn = port->conns_out[i]; diff --git a/src/timer.c b/src/timer.c index 1ce5b983..50fec272 100644 --- a/src/timer.c +++ b/src/timer.c @@ -30,12 +30,16 @@ void Timer_cleanup(Trigger *_self) { } void Timer_ctor(Timer *self, Reactor *parent, instant_t offset, interval_t period, Reaction **effects, - size_t effects_size) { + size_t effects_size, Reaction **observers, size_t observers_size) { + self->offset = offset; self->period = period; self->effects.reactions = effects; self->effects.size = effects_size; self->effects.num_registered = 0; + self->observers.reactions = observers; + self->observers.size = observers_size; + self->observers.num_registered = 0; Trigger_ctor(&self->super, TRIG_TIMER, parent, NULL, Timer_prepare, Timer_cleanup); } diff --git a/test/lf/src/Action.lf b/test/lf/src/Action.lf index 2a3d6d33..df904de1 100644 --- a/test/lf/src/Action.lf +++ b/test/lf/src/Action.lf @@ -13,6 +13,7 @@ main reactor { reaction(a) {= validate(a->value == self->cnt2++); + printf("Hello from Action.lf\n"); =} reaction(shutdown) {= diff --git a/test/lf/src/InwardConnection.lf b/test/lf/src/InwardConnection.lf new file mode 100644 index 00000000..4874c166 --- /dev/null +++ b/test/lf/src/InwardConnection.lf @@ -0,0 +1,41 @@ +target uC; + +reactor Inner { + input in: int + state check: bool = false + + reaction(in) {= + validate(in->value == 42); + self->check = true; + =} + + reaction(shutdown) {= + validate(self->check); + =} +} + +reactor R { + input in:int + + inner = new Inner() + + in -> inner.in +} + +reactor Src { + output out: int + + reaction(startup) -> out {= + lf_set(out, 42); + =} +} + + +main reactor { + src = new Src() + r = new R() + + src.out -> r.in + + +} \ No newline at end of file diff --git a/test/lf/src/InwardConnection2.lf b/test/lf/src/InwardConnection2.lf new file mode 100644 index 00000000..a43e3b34 --- /dev/null +++ b/test/lf/src/InwardConnection2.lf @@ -0,0 +1,58 @@ +target uC; + +reactor Inner { + input in: int + state check: bool = false + + reaction(in) {= + validate(in->value == 42); + self->check = true; + =} + + reaction(shutdown) {= + validate(self->check); + =} +} + +reactor R { + input in:int + + state check:int = 0 + inner = new Inner() + + in -> inner.in + reaction(in) {= + validate(lf_is_present(in)); + validate(in->value == 42); + self->check++; + =} + + reaction(startup) in {= + validate(self->check == 1); + validate(lf_is_present(in)); + validate(in->value == 42); + self->check++; + =} + + reaction(shutdown) {= + validate(self->check == 2); + =} +} + +reactor Src { + output out: int + + reaction(startup) -> out {= + lf_set(out, 42); + =} +} + + +main reactor { + src = new Src() + r = new R() + + src.out -> r.in + + +} \ No newline at end of file diff --git a/test/lf/src/Levels.lf b/test/lf/src/Levels.lf new file mode 100644 index 00000000..59540beb --- /dev/null +++ b/test/lf/src/Levels.lf @@ -0,0 +1,7 @@ +target uC; + +main reactor { + reaction(startup) {= + printf("TODO implement this test"); + =} +} \ No newline at end of file diff --git a/test/lf/src/ManyConnections.lf b/test/lf/src/ManyConnections.lf new file mode 100644 index 00000000..819d503c --- /dev/null +++ b/test/lf/src/ManyConnections.lf @@ -0,0 +1,55 @@ + +target uC; + +reactor R1 { + output out: interval_t + state cnt: int = 0; + + reaction(startup) -> out {= + lf_set(out, env->get_elapsed_logical_time(env)); + =} + +} + + +reactor R2 { + input in:interval_t + input in2:interval_t + input in3:interval_t + input in4:interval_t + state cnt: int = 0; + + + reaction(in) {= + validate(in->value == env->get_elapsed_logical_time(env)); + self->cnt++; + =} + + reaction(in2) {= + validate(in2->value == (env->get_elapsed_logical_time(env) - MSEC(1))); + self->cnt++; + =} + reaction(in3) {= + validate(in3->value < env->get_elapsed_logical_time(env)); + self->cnt++; + =} + + reaction(in4) {= + validate(in4->value == env->get_elapsed_logical_time(env)); + validate(self->cnt==1); + self->cnt++; + =} + + reaction(shutdown) {= + validate(self->cnt == 4); + =} +} + +main reactor { + r1 = new R1() + r2 = new R2() + r1.out -> r2.in + r1.out -> r2.in2 after 1 msec + r1.out ~> r2.in3 + r1.out -> r2.in4 +} diff --git a/test/lf/src/OutwardConnection.lf b/test/lf/src/OutwardConnection.lf new file mode 100644 index 00000000..fbf0c1a7 --- /dev/null +++ b/test/lf/src/OutwardConnection.lf @@ -0,0 +1,36 @@ +target uC; + +reactor Inner { + output out: int + + reaction(startup) -> out {= + lf_set(out, 42); + =} +} + +reactor R { + output out: int + + inner = new Inner() + inner.out -> out +} + +reactor Sink { + input in: int + state check: bool = false + + reaction(in) {= + validate(in->value == 42); + self->check = true; + =} + + reaction(shutdown) {= + validate(self->check); + =} +} + +main reactor { + sink = new Sink() + r = new R() + r.out -> sink.in +} \ No newline at end of file diff --git a/test/lf/src/OutwardConnection2.lf b/test/lf/src/OutwardConnection2.lf new file mode 100644 index 00000000..770635fa --- /dev/null +++ b/test/lf/src/OutwardConnection2.lf @@ -0,0 +1,52 @@ +target uC; + +reactor Inner { + output out: int + + reaction(startup) -> out {= + lf_set(out, 42); + =} +} + +reactor R { + output out: int + state check:int = 0 + + inner = new Inner() + inner.out -> out + + reaction(inner.out) {= + validate(inner.out->value == 42); + self->check++; + =} + + reaction(startup) inner.out {= + validate(inner.out->value == 42); + validate(self->check == 1); + self->check++; + =} + + reaction(shutdown) {= + validate(self->check == 2); + =} +} + +reactor Sink { + input in: int + state check: bool = false + + reaction(in) {= + validate(in->value == 42); + self->check = true; + =} + + reaction(shutdown) {= + validate(self->check); + =} +} + +main reactor { + sink = new Sink() + r = new R() + r.out -> sink.in +} \ No newline at end of file diff --git a/test/lf/src/Parameters.lf b/test/lf/src/Parameters.lf index 29e59dfd..9843395e 100644 --- a/test/lf/src/Parameters.lf +++ b/test/lf/src/Parameters.lf @@ -1,12 +1,11 @@ target uC - reactor Inner(offset: time = 15 msec) { timer t(offset) reaction(t) {= printf("Tag=%d\n", env->get_elapsed_logical_time(env)); - assert(env->get_elapsed_logical_time(env) == MSEC(5)); + validate(env->get_elapsed_logical_time(env) == MSEC(5)); =} } reactor R1(offset: time = 20 msec) { @@ -15,14 +14,15 @@ reactor R1(offset: time = 20 msec) { reactor R2(value: int = 0) { reaction(startup) {= - assert(self->value == 42); + printf("value=%d\n", self->value); + validate(self->value == 42); =} } reactor R3(value: int = 32) { reaction(startup) {= - - assert(self->value == 32); + printf("value=%d\n", self->value); + validate(self->value == 32); =} } diff --git a/test/lf/src/Preamble.lf b/test/lf/src/Preamble.lf index e56fc6f1..ef8da7bc 100644 --- a/test/lf/src/Preamble.lf +++ b/test/lf/src/Preamble.lf @@ -6,6 +6,6 @@ preamble {= static int test = 42; =} reaction(startup) {= - assert(test == 42); + validate(test == 42); =} } diff --git a/test/lf/src/ReactionSources.lf b/test/lf/src/ReactionSources.lf new file mode 100644 index 00000000..3ef102ca --- /dev/null +++ b/test/lf/src/ReactionSources.lf @@ -0,0 +1,20 @@ +target uC; + +reactor R { + input in:int + output out:int + + reaction(startup) in -> out {= + validate(lf_is_present(in)); + validate(in->value == 42); + =} +} + + +main reactor { + r = new R() + reaction(startup) -> r.in {= + lf_set(r.in, 42); + =} + +} \ No newline at end of file diff --git a/test/lf/src/Shutdown.lf b/test/lf/src/Shutdown.lf index 416135c4..f40b04a8 100644 --- a/test/lf/src/Shutdown.lf +++ b/test/lf/src/Shutdown.lf @@ -10,14 +10,17 @@ main reactor { reaction(startup) {= validate(self->cnt == 0); self->cnt++; + printf("startup cnt=%d\n", self->cnt); =} reaction(t) {= validate(self->cnt == 1); self->cnt++; + printf("t cnt=%d\n", self->cnt); =} reaction(shutdown) {= + printf("shutdown cnt=%d\n", self->cnt); validate(self->cnt == 2); =} } \ No newline at end of file diff --git a/test/lf/src/TriggeredByContainedOutput.lf b/test/lf/src/TriggeredByContainedOutput.lf new file mode 100644 index 00000000..26bd2e7b --- /dev/null +++ b/test/lf/src/TriggeredByContainedOutput.lf @@ -0,0 +1,33 @@ +target uC; + +reactor Inner { + output out: int + + reaction(startup) -> out {= + lf_set(out, 42); + =} +} + +reactor R { + output out: int + + inner = new Inner() + inner.out -> out +} + + +main reactor { + r = new R() + state check: bool = false + + reaction(r.out) {= + validate(r.out->value == 42); + r.out->value = + self->check = true; + =} + + reaction(shutdown) {= + validate(self->check); + =} + +} \ No newline at end of file diff --git a/test/lf/src/WritingToContainedInput.lf b/test/lf/src/WritingToContainedInput.lf new file mode 100644 index 00000000..5f1faa20 --- /dev/null +++ b/test/lf/src/WritingToContainedInput.lf @@ -0,0 +1,32 @@ +target uC; + +reactor Inner { + input in: int + state check: bool = false + + reaction(in) {= + printf("Hello\n"); + validate(in->value == 42); + self->check = true; + =} + + reaction(shutdown) {= + validate(self->check); + =} +} + +reactor R { + input in:int + + inner = new Inner() + + in -> inner.in +} + +main reactor { + r = new R() + + reaction(startup) -> r.in {= + lf_set(r.in, 42); + =} +} \ No newline at end of file diff --git a/test/unit/action_lib.h b/test/unit/action_lib.h index 4e5d6339..70acd1b8 100644 --- a/test/unit/action_lib.h +++ b/test/unit/action_lib.h @@ -3,19 +3,19 @@ #include "reactor-uc/reactor-uc.h" #ifdef ACTION_LIB_VOID_TYPE -DEFINE_ACTION_STRUCT_VOID(ActionLib, act, LOGICAL_ACTION, 1, 1, 2); -DEFINE_ACTION_CTOR_VOID(ActionLib, act, LOGICAL_ACTION, 1, 1, 2); +DEFINE_ACTION_STRUCT_VOID(ActionLib, act, LOGICAL_ACTION, 1, 1,0, 2); +DEFINE_ACTION_CTOR_VOID(ActionLib, act, LOGICAL_ACTION, 1, 1, 0, 2); #else -DEFINE_ACTION_STRUCT(ActionLib, act, LOGICAL_ACTION, 1, 1, 10, int); -DEFINE_ACTION_CTOR(ActionLib, act, LOGICAL_ACTION, 1, 1, 10, int); +DEFINE_ACTION_STRUCT(ActionLib, act, LOGICAL_ACTION, 1, 1, 0, 10, int); +DEFINE_ACTION_CTOR(ActionLib, act, LOGICAL_ACTION, 1, 1, 0, 10, int); #endif -DEFINE_STARTUP_STRUCT(ActionLib, 1); +DEFINE_STARTUP_STRUCT(ActionLib, 1, 0); DEFINE_STARTUP_CTOR(ActionLib); DEFINE_REACTION_STRUCT(ActionLib, reaction, 1); DEFINE_REACTION_CTOR(ActionLib, reaction, 0); -DEFINE_SHUTDOWN_STRUCT(ActionLib, 1); +DEFINE_SHUTDOWN_STRUCT(ActionLib, 1, 0); DEFINE_SHUTDOWN_CTOR(ActionLib); DEFINE_REACTION_STRUCT(ActionLib, r_shutdown, 0) DEFINE_REACTION_CTOR(ActionLib, r_shutdown, 1) diff --git a/test/unit/delayed_conn_test.c b/test/unit/delayed_conn_test.c index 94dd6496..f95df50a 100644 --- a/test/unit/delayed_conn_test.c +++ b/test/unit/delayed_conn_test.c @@ -2,11 +2,11 @@ #include "unity.h" // Components of Reactor Sender -DEFINE_TIMER_STRUCT(Sender, t, 1); -DEFINE_TIMER_CTOR(Sender, t, 1); +DEFINE_TIMER_STRUCT(Sender, t, 1, 0); +DEFINE_TIMER_CTOR(Sender, t, 1, 0); DEFINE_REACTION_STRUCT(Sender, r_sender, 1); DEFINE_REACTION_CTOR(Sender, r_sender, 0); -DEFINE_OUTPUT_STRUCT(Sender, out, 1); +DEFINE_OUTPUT_STRUCT(Sender, out, 1, interval_t); DEFINE_OUTPUT_CTOR(Sender, out, 1); typedef struct { @@ -25,15 +25,15 @@ DEFINE_REACTION_BODY(Sender, r_sender) { lf_set(out, env->get_elapsed_logical_time(env)); } -REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, Connection **conn_out, size_t conn_num) { +REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs out_external) { REACTOR_CTOR_PREAMBLE(); REACTOR_CTOR(Sender); INITIALIZE_REACTION(Sender, r_sender); INITIALIZE_TIMER(Sender, t, MSEC(0), MSEC(10)); - INITIALIZE_OUTPUT(Sender, out, conn_out, conn_num); + INITIALIZE_OUTPUT(Sender, out, out_external); TIMER_REGISTER_EFFECT(t, r_sender); - OUTPUT_REGISTER_SOURCE(out, r_sender); + PORT_REGISTER_SOURCE(out, r_sender); REACTION_REGISTER_EFFECT(r_sender, out); } @@ -41,8 +41,8 @@ REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, Connection **conn_out, size_t con DEFINE_REACTION_STRUCT(Receiver, r_recv, 0) DEFINE_REACTION_CTOR(Receiver, r_recv, 0) -DEFINE_INPUT_STRUCT(Receiver, in, 1, instant_t, 0) -DEFINE_INPUT_CTOR(Receiver, in, 1, instant_t, 0) +DEFINE_INPUT_STRUCT(Receiver, in, 1, 0, instant_t, 0) +DEFINE_INPUT_CTOR(Receiver, in, 1, 0, instant_t, 0) typedef struct { Reactor super; @@ -60,27 +60,30 @@ DEFINE_REACTION_BODY(Receiver, r_recv) { TEST_ASSERT_EQUAL(in->value + MSEC(15), env->get_elapsed_logical_time(env)); } -REACTOR_CTOR_SIGNATURE(Receiver) { +REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs sources_in) { REACTOR_CTOR_PREAMBLE(); REACTOR_CTOR(Receiver); INITIALIZE_REACTION(Receiver, r_recv); - INITIALIZE_INPUT(Receiver, in); + INITIALIZE_INPUT(Receiver, in, sources_in); // Register reaction as an effect of in - INPUT_REGISTER_EFFECT(in, r_recv); + PORT_REGISTER_EFFECT(in, r_recv); } // Reactor main -DEFINE_DELAYED_CONNECTION_STRUCT(Main, sender, out, 1, interval_t, 2, MSEC(15)) -DEFINE_DELAYED_CONNECTION_CTOR(Main, sender, out, 1, interval_t, 2, MSEC(15), false) +DEFINE_DELAYED_CONNECTION_STRUCT(Main, sender_out, 1, interval_t, 2, MSEC(15)) +DEFINE_DELAYED_CONNECTION_CTOR(Main, sender_out, 1, interval_t, 2, MSEC(15), false) typedef struct { Reactor super; CHILD_REACTOR_INSTANCE(Sender, sender); CHILD_REACTOR_INSTANCE(Receiver, receiver); - DELAYED_CONNECTION_INSTANCE(Main, sender, out); + DELAYED_CONNECTION_INSTANCE(Main, sender_out); - CONTAINED_OUTPUT_CONNECTIONS(sender, out, 1); + CHILD_OUTPUT_CONNECTIONS(sender, out, 1); + CHILD_OUTPUT_EFFECTS(sender, out, 0); + CHILD_OUTPUT_OBSERVERS(sender, out, 0); + CHILD_INPUT_SOURCES(receiver, in, 0); REACTOR_BOOKKEEPING_INSTANCES(0,0,2); } Main; @@ -88,11 +91,14 @@ REACTOR_CTOR_SIGNATURE(Main) { REACTOR_CTOR_PREAMBLE(); REACTOR_CTOR(Main); - INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, self->_conns_sender_out_out, 1); - INITIALIZE_CHILD_REACTOR(Receiver, receiver); + DEFINE_CHILD_OUTPUT_ARGS(sender, out); + INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, _sender_out_args); + DEFINE_CHILD_INPUT_ARGS(receiver, in); + INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, _receiver_in_args); - INITIALIZE_DELAYED_CONNECTION(Main, sender, out); - DELAYED_CONNECT(sender, out, receiver, in); + INITIALIZE_DELAYED_CONNECTION(Main, sender_out); + CONN_REGISTER_UPSTREAM(self->sender_out, self->sender.out); + CONN_REGISTER_DOWNSTREAM(self->sender_out, self->receiver.in); } void test_simple() { diff --git a/test/unit/multiple_startup_shutdown_test.c b/test/unit/multiple_startup_shutdown_test.c index 731dfa82..9807fc6c 100644 --- a/test/unit/multiple_startup_shutdown_test.c +++ b/test/unit/multiple_startup_shutdown_test.c @@ -1,12 +1,12 @@ #include "reactor-uc/reactor-uc.h" #include "unity.h" -DEFINE_STARTUP_STRUCT(ShutdownTest, 1) +DEFINE_STARTUP_STRUCT(ShutdownTest, 1, 0) DEFINE_STARTUP_CTOR(ShutdownTest) DEFINE_REACTION_STRUCT(ShutdownTest, r_startup, 0) DEFINE_REACTION_CTOR(ShutdownTest, r_startup, 0) -DEFINE_SHUTDOWN_STRUCT(ShutdownTest, 1) +DEFINE_SHUTDOWN_STRUCT(ShutdownTest, 1, 0) DEFINE_SHUTDOWN_CTOR(ShutdownTest) DEFINE_REACTION_STRUCT(ShutdownTest, r_shutdown, 0) DEFINE_REACTION_CTOR(ShutdownTest, r_shutdown, 1) diff --git a/test/unit/physical_action_test.c b/test/unit/physical_action_test.c index 2a8f1b32..eaf33ff0 100644 --- a/test/unit/physical_action_test.c +++ b/test/unit/physical_action_test.c @@ -4,11 +4,11 @@ Environment env; -DEFINE_ACTION_STRUCT(PhyActionTest, act, PHYSICAL_ACTION, 1, 1, 1, int); -DEFINE_ACTION_CTOR(PhyActionTest, act, PHYSICAL_ACTION, 1, 1, 1, int); -DEFINE_STARTUP_STRUCT(PhyActionTest, 1); +DEFINE_ACTION_STRUCT(PhyActionTest, act, PHYSICAL_ACTION, 1, 1, 0, 1, int); +DEFINE_ACTION_CTOR(PhyActionTest, act, PHYSICAL_ACTION, 1, 1, 0, 1, int); +DEFINE_STARTUP_STRUCT(PhyActionTest, 1, 0); DEFINE_STARTUP_CTOR(PhyActionTest); -DEFINE_SHUTDOWN_STRUCT(PhyActionTest, 1); +DEFINE_SHUTDOWN_STRUCT(PhyActionTest, 1, 0); DEFINE_SHUTDOWN_CTOR(PhyActionTest); DEFINE_REACTION_STRUCT(PhyActionTest, r_startup, 1); diff --git a/test/unit/port_test.c b/test/unit/port_test.c index 837a6bee..54896e23 100644 --- a/test/unit/port_test.c +++ b/test/unit/port_test.c @@ -2,11 +2,11 @@ #include "unity.h" // Components of Reactor Sender -DEFINE_TIMER_STRUCT(Sender, t, 1); -DEFINE_TIMER_CTOR(Sender, t, 1); +DEFINE_TIMER_STRUCT(Sender, t, 1, 0); +DEFINE_TIMER_CTOR(Sender, t, 1, 0); DEFINE_REACTION_STRUCT(Sender, r_sender, 1); DEFINE_REACTION_CTOR(Sender, r_sender, 0); -DEFINE_OUTPUT_STRUCT(Sender, out, 1); +DEFINE_OUTPUT_STRUCT(Sender, out, 1, interval_t); DEFINE_OUTPUT_CTOR(Sender, out, 1); typedef struct { @@ -24,15 +24,15 @@ DEFINE_REACTION_BODY(Sender, r_sender) { // printf("Timer triggered @ %ld\n", env->get_elapsed_logical_time(env)); lf_set(out, env->get_elapsed_logical_time(env)); } -REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, Connection **conn_out, size_t conn_num) { +REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs out_external) { REACTOR_CTOR_PREAMBLE(); REACTOR_CTOR(Sender); INITIALIZE_REACTION(Sender, r_sender); INITIALIZE_TIMER(Sender, t, MSEC(0), MSEC(5)); - INITIALIZE_OUTPUT(Sender, out, conn_out, conn_num); + INITIALIZE_OUTPUT(Sender, out, out_external); TIMER_REGISTER_EFFECT(t, r_sender); - OUTPUT_REGISTER_SOURCE(out, r_sender); + PORT_REGISTER_SOURCE(out, r_sender); REACTION_REGISTER_EFFECT(r_sender, out); } @@ -40,8 +40,8 @@ REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, Connection **conn_out, size_t con DEFINE_REACTION_STRUCT(Receiver, r_recv, 0) DEFINE_REACTION_CTOR(Receiver, r_recv, 0) -DEFINE_INPUT_STRUCT(Receiver, in, 1, instant_t, 0) -DEFINE_INPUT_CTOR(Receiver, in, 1, instant_t, 0) +DEFINE_INPUT_STRUCT(Receiver, in, 1, 0, instant_t, 0) +DEFINE_INPUT_CTOR(Receiver, in, 1, 0, instant_t, 0) typedef struct { Reactor super; @@ -60,38 +60,44 @@ DEFINE_REACTION_BODY(Receiver, r_recv) { } -REACTOR_CTOR_SIGNATURE(Receiver) { +REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs in_external) { REACTOR_CTOR(Receiver); REACTOR_CTOR_PREAMBLE(); INITIALIZE_REACTION(Receiver, r_recv); - INITIALIZE_INPUT(Receiver, in); + INITIALIZE_INPUT(Receiver, in, in_external); // Register reaction as an effect of in - INPUT_REGISTER_EFFECT(in, r_recv); + PORT_REGISTER_EFFECT(in, r_recv); } // Reactor main -DEFINE_LOGICAL_CONNECTION_STRUCT(Main, sender, out, 1) -DEFINE_LOGICAL_CONNECTION_CTOR(Main, sender, out, 1) +DEFINE_LOGICAL_CONNECTION_STRUCT(Main, sender_out, 1) +DEFINE_LOGICAL_CONNECTION_CTOR(Main, sender_out, 1) typedef struct { Reactor super; CHILD_REACTOR_INSTANCE(Sender, sender); CHILD_REACTOR_INSTANCE(Receiver, receiver); - LOGICAL_CONNECTION_INSTANCE(Main, sender, out); + LOGICAL_CONNECTION_INSTANCE(Main, sender_out); REACTOR_BOOKKEEPING_INSTANCES(0,0,2) - CONTAINED_OUTPUT_CONNECTIONS(sender, out, 1); + CHILD_OUTPUT_CONNECTIONS(sender, out, 1); + CHILD_OUTPUT_EFFECTS(sender, out, 0); + CHILD_OUTPUT_OBSERVERS(sender, out, 0); + CHILD_INPUT_SOURCES(receiver, in, 0); } Main; REACTOR_CTOR_SIGNATURE(Main) { REACTOR_CTOR_PREAMBLE(); REACTOR_CTOR(Main); - INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, self->_conns_sender_out_out, 1); - INITIALIZE_CHILD_REACTOR(Receiver, receiver); - - INITIALIZE_LOGICAL_CONNECTION(Main, sender, out); - LOGICAL_CONNECT(sender, out, receiver, in); + DEFINE_CHILD_OUTPUT_ARGS(sender, out); + INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, _sender_out_args); + DEFINE_CHILD_INPUT_ARGS(receiver, in); + INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, _receiver_in_args); + + INITIALIZE_LOGICAL_CONNECTION(Main, sender_out); + CONN_REGISTER_UPSTREAM(self->sender_out, self->sender.out); + CONN_REGISTER_DOWNSTREAM(self->sender_out, self->receiver.in); } void test_simple() { diff --git a/test/unit/shutdown_test.c b/test/unit/shutdown_test.c index 12e8a52f..ca4524aa 100644 --- a/test/unit/shutdown_test.c +++ b/test/unit/shutdown_test.c @@ -2,12 +2,12 @@ #include "unity.h" -DEFINE_STARTUP_STRUCT(ShutdownTest, 1) +DEFINE_STARTUP_STRUCT(ShutdownTest, 1, 0) DEFINE_STARTUP_CTOR(ShutdownTest) DEFINE_REACTION_STRUCT(ShutdownTest, r_startup, 0) DEFINE_REACTION_CTOR(ShutdownTest, r_startup, 0) -DEFINE_SHUTDOWN_STRUCT(ShutdownTest, 1) +DEFINE_SHUTDOWN_STRUCT(ShutdownTest, 1, 0) DEFINE_SHUTDOWN_CTOR(ShutdownTest) DEFINE_REACTION_STRUCT(ShutdownTest, r_shutdown, 0) DEFINE_REACTION_CTOR(ShutdownTest, r_shutdown, 1) diff --git a/test/unit/startup_test.c b/test/unit/startup_test.c index 7b123279..317ce706 100644 --- a/test/unit/startup_test.c +++ b/test/unit/startup_test.c @@ -2,7 +2,7 @@ #include "unity.h" -DEFINE_STARTUP_STRUCT(StartupTest, 1) +DEFINE_STARTUP_STRUCT(StartupTest, 1, 0) DEFINE_STARTUP_CTOR(StartupTest) DEFINE_REACTION_STRUCT(StartupTest, r_startup, 0) DEFINE_REACTION_CTOR(StartupTest, r_startup, 0) diff --git a/test/unit/timer_test.c b/test/unit/timer_test.c index be9db86e..9f2fa238 100644 --- a/test/unit/timer_test.c +++ b/test/unit/timer_test.c @@ -1,8 +1,8 @@ #include "reactor-uc/reactor-uc.h" #include "unity.h" -DEFINE_TIMER_STRUCT(TimerTest, t, 1) -DEFINE_TIMER_CTOR(TimerTest, t, 1) +DEFINE_TIMER_STRUCT(TimerTest, t, 1, 0) +DEFINE_TIMER_CTOR(TimerTest, t, 1, 0) DEFINE_REACTION_STRUCT(TimerTest, reaction, 0) DEFINE_REACTION_CTOR(TimerTest, reaction, 0)