diff --git a/README.md b/README.md index d0254594..0977fcf3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # reactor-uc + `reactor-uc` is a task scheduling runtime implementing the reactor model-of-computation target at embedded and resource-constrained 32 bit systems. @@ -8,21 +9,25 @@ NB: reactor-uc is still work-in-progress and many (most?) features are not suppo yet. This only documents how to run a few example programs. Setup the environment + ``` source env.bash ``` Initialize submodules + ``` git submodule update --init ``` Compile and run unit tests: + ``` make test ``` Compile and run a simple timer test on Posix + ``` cd examples/posix cmake -Bbuild @@ -45,10 +50,20 @@ west build -b frdm_k64f -p always west flash ``` -For more information on running LF programs using the reactor-uc runtime on -Zephyr take a look at this template: https://github.com/lf-lang/lf-west-template/tree/reactor-uc +Compile and run a simple blinky example on RIOT. +This requires a correctly configured RIOT environment. +Make sure that the environment variable `RIOTBASE` points to a `RIOT` codebase. + +``` +cd examples/riot/blinky +make BOARD=native all term +``` + +For more information on running LF programs using the reactor-uc runtime on +Zephyr take a look at this template: ## Lingua Franca + We have copied a very limited version of the Lingua Franca Compiler (lfc) into `~/lfc` of this repo. In the future, the `reactor-uc` specific code-generation will be merged back upstream. By sourcing `env.bash` or `env.fish` the Lingua @@ -69,6 +84,7 @@ bin/HelloUc ``` ## Goals + - Incorporate unit testing and test-driven development from the start - Optimized for single-core 32-bit systems - Backend for LF @@ -80,13 +96,16 @@ which enable distributed embedded systems. - Avoid malloc as much as possible (or entirely?) ## References + `reactor-uc` draws inspiration from the following existing open-source projects: + - [reactor-cpp](https://github.com/lf-lang/reactor-cpp) - [reactor-c](https://github.com/lf-lang/reactor-c) - [qpc](https://github.com/QuantumLeaps/qpc) - [ssm-runtime](https://github.com/QuantumLeaps/qpc) -## TODO for the MVP: +## TODO for the MVP + - [x] Timers - [x] Input/Output Ports - [x] Logical Actions @@ -99,8 +118,8 @@ which enable distributed embedded systems. - [x] Posix Platform abstractions - [x] Basic code-generation - ## More advanced topics + - [x] More platform abstractions (Riot, Zephyr and FlexPRET/InterPRET) - [x] Reconsider where to buffer data (outputs vs inputs) - [x] Consider if we should have FIFOs of pending events, not just a single for a trigger. @@ -111,20 +130,20 @@ which enable distributed embedded systems. - [ ] Multiports and banks - [ ] Modal reactors - ## Troubleshooting ### Formatting + We are using `clang-format` version 18.1.3 which is default with Ubuntu 24.04 for formatting in CI. ### LFC If you get the following CMake error when calling `lfc/bin/lfc-dev` + ``` CMake Error at CMakeLists.txt:7 (message): Environment variable REACTOR_UC_PATH is not set. Please source the env.bash in reactor-uc. ``` -Please source `env.bash` or `env.fish`. - +Please source `env.bash` or `env.fish`. diff --git a/examples/riot/blinky/Makefile b/examples/riot/blinky/Makefile new file mode 100755 index 00000000..a444960e --- /dev/null +++ b/examples/riot/blinky/Makefile @@ -0,0 +1,18 @@ +# name of your application +APPLICATION = lf-test + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +# Enable reactor-uc features +# CFLAGS += -DNETWORK_POSIX_TCP + +include $(CURDIR)/../../../make/riot/riot.mk diff --git a/examples/riot/blinky/main.c b/examples/riot/blinky/main.c new file mode 100755 index 00000000..90e8e4db --- /dev/null +++ b/examples/riot/blinky/main.c @@ -0,0 +1,37 @@ +#include "board.h" +#include "reactor-uc/reactor-uc.h" +#include + +DEFINE_TIMER(MyTimer, 1, MSEC(0), MSEC(100)) +DEFINE_REACTION(MyReactor, 0, 0) + +typedef struct { + Reactor super; + MyReactor_0 my_reaction; + MyTimer timer; + Reaction *_reactions[1]; + Trigger *_triggers[1]; +} MyReactor; + +REACTION_BODY(MyReactor, 0, { + LED0_TOGGLE; + printf("Hello World @ %" PRId64 "\n", env->get_elapsed_physical_time(env)); +}) + +void MyReactor_ctor(MyReactor *self, Environment *env) { + self->_reactions[0] = (Reaction *)&self->my_reaction; + self->_triggers[0] = (Trigger *)&self->timer; + + Reactor_ctor(&self->super, "MyReactor", env, NULL, NULL, 0, self->_reactions, 1, self->_triggers, 1); + MyReactor_0_ctor(&self->my_reaction, &self->super); + MyTimer_ctor(&self->timer, &self->super); + + TIMER_REGISTER_EFFECT(self->timer, self->my_reaction); +} + +ENTRY_POINT(MyReactor, FOREVER, true) + +int main(void) { + lf_start(); + return 0; +} diff --git a/examples/riot/hello/Makefile b/examples/riot/hello/Makefile new file mode 100755 index 00000000..a444960e --- /dev/null +++ b/examples/riot/hello/Makefile @@ -0,0 +1,18 @@ +# name of your application +APPLICATION = lf-test + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +# Enable reactor-uc features +# CFLAGS += -DNETWORK_POSIX_TCP + +include $(CURDIR)/../../../make/riot/riot.mk diff --git a/examples/riot/hello/main.c b/examples/riot/hello/main.c new file mode 100755 index 00000000..87223565 --- /dev/null +++ b/examples/riot/hello/main.c @@ -0,0 +1,31 @@ +#include "reactor-uc/reactor-uc.h" +#include + +DEFINE_TIMER(MyTimer, 1, 0, MSEC(100)) +DEFINE_REACTION(MyReactor, 0, 0) + +typedef struct { + Reactor super; + MyReactor_0 my_reaction; + MyTimer timer; + Reaction *_reactions[1]; + Trigger *_triggers[1]; +} MyReactor; + +REACTION_BODY(MyReactor, 0, { printf("Hello World @ %lld\n", env->get_elapsed_logical_time(env)); }) + +void MyReactor_ctor(MyReactor *self, Environment *env) { + self->_reactions[0] = (Reaction *)&self->my_reaction; + self->_triggers[0] = (Trigger *)&self->timer; + Reactor_ctor(&self->super, "MyReactor", env, NULL, NULL, 0, self->_reactions, 1, self->_triggers, 1); + MyReactor_0_ctor(&self->my_reaction, &self->super); + Timer_ctor(&self->timer.super, &self->super, MSEC(0), MSEC(100), self->timer.effects, 1); + TIMER_REGISTER_EFFECT(self->timer, self->my_reaction); +} + +ENTRY_POINT(MyReactor, FOREVER, true) + +int main() { + lf_start(); + return 0; +} diff --git a/make/riot/external_modules/nanopb/Makefile b/make/riot/external_modules/nanopb/Makefile new file mode 100755 index 00000000..42a4ae04 --- /dev/null +++ b/make/riot/external_modules/nanopb/Makefile @@ -0,0 +1,6 @@ +include $(RIOTBASE)/Makefile.base + +SRC_DIR := $(realpath $(CURDIR)/../../../../external/nanopb) + +all: + $(QQ)"$(MAKE)" -C $(SRC_DIR) -f $(CURDIR)/nanopb.mk diff --git a/make/riot/external_modules/nanopb/Makefile.include b/make/riot/external_modules/nanopb/Makefile.include new file mode 100755 index 00000000..17cf91d4 --- /dev/null +++ b/make/riot/external_modules/nanopb/Makefile.include @@ -0,0 +1,5 @@ +CFLAGS += -DPLATFORM_RIOT=1 + +# Use an immediate variable to evaluate `MAKEFILE_LIST` now +USEMODULE_INCLUDES_nanopb := $(LAST_MAKEFILEDIR)/../../../../external +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_nanopb) \ No newline at end of file diff --git a/make/riot/external_modules/nanopb/nanopb.mk b/make/riot/external_modules/nanopb/nanopb.mk new file mode 100644 index 00000000..b362e0af --- /dev/null +++ b/make/riot/external_modules/nanopb/nanopb.mk @@ -0,0 +1,3 @@ +MODULE = nanopb + +include $(RIOTBASE)/Makefile.base \ No newline at end of file diff --git a/make/riot/external_modules/proto/Makefile b/make/riot/external_modules/proto/Makefile new file mode 100755 index 00000000..180bd658 --- /dev/null +++ b/make/riot/external_modules/proto/Makefile @@ -0,0 +1,6 @@ +include $(RIOTBASE)/Makefile.base + +SRC_DIR := $(realpath $(CURDIR)/../../../../external/proto) + +all: + $(QQ)"$(MAKE)" -C $(SRC_DIR) -f $(CURDIR)/proto.mk diff --git a/make/riot/external_modules/proto/Makefile.include b/make/riot/external_modules/proto/Makefile.include new file mode 100755 index 00000000..5865a62c --- /dev/null +++ b/make/riot/external_modules/proto/Makefile.include @@ -0,0 +1,5 @@ +CFLAGS += -DPLATFORM_RIOT=1 + +# Use an immediate variable to evaluate `MAKEFILE_LIST` now +USEMODULE_INCLUDES_proto := $(LAST_MAKEFILEDIR)/../../../../external +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_proto) \ No newline at end of file diff --git a/make/riot/external_modules/proto/proto.mk b/make/riot/external_modules/proto/proto.mk new file mode 100644 index 00000000..f29066a5 --- /dev/null +++ b/make/riot/external_modules/proto/proto.mk @@ -0,0 +1,3 @@ +MODULE = proto + +include $(RIOTBASE)/Makefile.base \ No newline at end of file diff --git a/make/riot/external_modules/reactor-uc/Makefile b/make/riot/external_modules/reactor-uc/Makefile new file mode 100755 index 00000000..70ce4c24 --- /dev/null +++ b/make/riot/external_modules/reactor-uc/Makefile @@ -0,0 +1,6 @@ +include $(RIOTBASE)/Makefile.base + +SRC_DIR := $(realpath $(CURDIR)/../../../../src) + +all: + $(QQ)"$(MAKE)" -C $(SRC_DIR) -f $(CURDIR)/reactor-uc.mk diff --git a/make/riot/external_modules/reactor-uc/Makefile.dep b/make/riot/external_modules/reactor-uc/Makefile.dep new file mode 100644 index 00000000..c3ceaedc --- /dev/null +++ b/make/riot/external_modules/reactor-uc/Makefile.dep @@ -0,0 +1,24 @@ +USEMODULE += ztimer64 +USEMODULE += ztimer64_usec +USEMODULE += nanopb +USEMODULE += proto + +# If Feature NETWORK_POSIX_TCP is enabled +ifeq ($(filter -DNETWORK_POSIX_TCP, $(CFLAGS)), -DNETWORK_POSIX_TCP) + # Enable networking + USEMODULE += netdev_default + USEMODULE += auto_init_gnrc_netif + + # Enable sockets + USEMODULE += gnrc_ipv6_default + USEMODULE += sock_tcp + USEMODULE += posix_sockets + USEMODULE += posix_sleep + USEMODULE += posix_inet + + # Enable posix threads + ifneq ($(BOARD),native) + USEMODULE += pthread + endif +endif + diff --git a/make/riot/external_modules/reactor-uc/Makefile.include b/make/riot/external_modules/reactor-uc/Makefile.include new file mode 100755 index 00000000..40dbb05f --- /dev/null +++ b/make/riot/external_modules/reactor-uc/Makefile.include @@ -0,0 +1,15 @@ + +CFLAGS += -DPLATFORM_RIOT=1 + +# Remove the -Wcast-align flag for this module +CFLAGS := $(filter-out -Wcast-align,$(CFLAGS)) + +# Remove the -Wstrict-prototypes flag for this module +CFLAGS := $(filter-out -Wstrict-prototypes,$(CFLAGS)) + +# Remove the -Wold-style-definition flag for this module +CFLAGS := $(filter-out -Wold-style-definition,$(CFLAGS)) + +# Use an immediate variable to evaluate `MAKEFILE_LIST` now +USEMODULE_INCLUDES_reactor-uc := $(LAST_MAKEFILEDIR)/../../../../include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_reactor-uc) \ No newline at end of file diff --git a/make/riot/external_modules/reactor-uc/reactor-uc.mk b/make/riot/external_modules/reactor-uc/reactor-uc.mk new file mode 100644 index 00000000..f5652af7 --- /dev/null +++ b/make/riot/external_modules/reactor-uc/reactor-uc.mk @@ -0,0 +1,3 @@ +MODULE = reactor-uc + +include $(RIOTBASE)/Makefile.base \ No newline at end of file diff --git a/make/riot/riot.mk b/make/riot/riot.mk new file mode 100644 index 00000000..4a3234e8 --- /dev/null +++ b/make/riot/riot.mk @@ -0,0 +1,23 @@ +RIOT_MK_DIR := $(dir $(lastword $(MAKEFILE_LIST))) + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(RIOT_MK_DIR)/../../../RIOT + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +# Use a peripheral timer for the delay, if available +FEATURES_OPTIONAL += periph_timer + +# External modules +EXTERNAL_MODULE_DIRS += $(RIOT_MK_DIR)/external_modules +USEMODULE += reactor-uc + + + +include $(RIOTBASE)/Makefile.include \ No newline at end of file diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 7709cfa4..409903a7 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -217,10 +217,21 @@ void TcpIpChannel_register_callback(NetworkChannel *untyped_self, if (pthread_attr_init(&self->receive_thread_attr) < 0) { throw("pthread_attr_init failed"); } +/* TODO: RIOT posix-wrappers don't have pthread_attr_setstack yet */ +#ifdef __USE_XOPEN2K if (pthread_attr_setstack(&self->receive_thread_attr, &self->receive_thread_stack, TCP_IP_CHANNEL_RECV_THREAD_STACK_SIZE - TCP_IP_CHANNEL_RECV_THREAD_STACK_GUARD_SIZE) < 0) { throw("pthread_attr_setstack failed"); } +#else + if (pthread_attr_setstackaddr(&self->receive_thread_attr, self->receive_thread_stack) != 0) { + throw("pthread_attr_setstackaddr failed"); + } + if (pthread_attr_setstacksize(&self->receive_thread_attr, TCP_IP_CHANNEL_RECV_THREAD_STACK_SIZE - + TCP_IP_CHANNEL_RECV_THREAD_STACK_GUARD_SIZE) != 0) { + throw("pthread_attr_setstacksize failed"); + } +#endif res = pthread_create(&self->receive_thread, &self->receive_thread_attr, TcpIpChannel_receive_thread, self); if (res < 0) { throw("pthread_create failed");