diff --git a/.github/workflows/c-zephyr-tests.yml b/.github/workflows/c-zephyr-tests.yml new file mode 100644 index 0000000000..8ad846c999 --- /dev/null +++ b/.github/workflows/c-zephyr-tests.yml @@ -0,0 +1,60 @@ +name: C Zephyr tests + +on: + workflow_call: + inputs: + compiler-ref: + required: false + type: string + runtime-ref: + required: false + type: string + use-cpp: + required: false + type: boolean + default: false + scheduler: + required: false + type: string + +jobs: + run: + runs-on: ubuntu-latest + container: + image: zephyrprojectrtos/zephyr-build:latest + options: -u root --entrypoint /bin/sh + steps: + - name: Install Java 17, Maven and set JAVA_HOME + run: | + sudo apt-get update + sudo apt-get install -y openjdk-17-jdk + sudo apt-get install -y maven + echo "JAVA_HOME_17_X64=/usr/lib/jvm/java-17-openjdk-amd64" >> $GITHUB_ENV + - name: Initialize zephyr + run: | + west init /workdir/zephyrproject --mr v3.2.0 + cd /workdir/zephyrproject + west update + echo "ZEPHYR_BASE=/workdir/zephyrproject/zephyr" >> $GITHUB_ENV + echo "ZEPHYR_SDK_INSTALL_DIR=/opt/toolchains/zephyr-sdk-0.15.2/" >> $GITHUB_ENV + - name: Check out lingua-franca repository + uses: actions/checkout@v3 + with: + repository: lf-lang/lingua-franca + submodules: true + ref: ${{ inputs.compiler-ref }} + fetch-depth: 0 + - name: Prepare build environment + uses: ./.github/actions/prepare-build-env + - name: Check out specific ref of reactor-c + uses: actions/checkout@v3 + with: + repository: lf-lang/reactor-c + path: org.lflang/src/lib/c/reactor-c + ref: ${{ inputs.runtime-ref }} + if: ${{ inputs.runtime-ref }} + - name: Try to get around git safe issues + run: chown root:root . + + - name: Perform Zephyr tests for C target with default scheduler + run: ./gradlew test --tests org.lflang.tests.runtime.CZephyrTest.runZephyrTests diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 094cb0f1db..3ab25a330c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,6 +62,11 @@ jobs: c-tests: uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master needs: cancel + + # Run the C Zephyr integration tests. + c-zephyr-tests: + uses: lf-lang/lingua-franca/.github/workflows/c-zephyr-tests.yml@c-zephyr-support + needs: cancel # Run the CCpp integration tests. ccpp-tests: diff --git a/org.lflang.tests/src/org/lflang/tests/Configurators.java b/org.lflang.tests/src/org/lflang/tests/Configurators.java index 37cad5910f..81e015dfbf 100644 --- a/org.lflang.tests/src/org/lflang/tests/Configurators.java +++ b/org.lflang.tests/src/org/lflang/tests/Configurators.java @@ -62,6 +62,17 @@ public static boolean disableThreading(LFTest test) { return true; } + // TODO: In the future we want to execute to the test with QEMU + // but it requires parsing QEMU output until either: + // 1) A timeout + // 2) "exit" is printed to stdout. Then look if there is a FATAL ERROR printed somewhere + // So it would requre continously parsing the stdout and waiting for exit keyword + public static boolean platformZephyrQemuNoFlash(LFTest test) { + test.getContext().getArgs().setProperty("threading", "false"); + // TODO: How can I set platform as a dictionary (platform.board platform.flash etc) + // test.getContext().getArgs().setProperty("platform", "Zephyr"); + return true; + } /** * Make no changes to the configuration. * @@ -83,7 +94,8 @@ public static boolean compatibleWithThreadingOff(TestCategory category) { || category == TestCategory.SERIALIZATION || category == TestCategory.FEDERATED || category == TestCategory.DOCKER_FEDERATED - || category == TestCategory.DOCKER; + || category == TestCategory.DOCKER + || category == TestCategory.ZEPHYR; // SERIALIZATION, TARGET, and ARDUINO tests are excluded on Windows. excluded |= TestBase.isWindows() && (category == TestCategory.TARGET || category == TestCategory.ARDUINO); diff --git a/org.lflang.tests/src/org/lflang/tests/TestBase.java b/org.lflang.tests/src/org/lflang/tests/TestBase.java index 783b7899cd..80e3e02ac8 100644 --- a/org.lflang.tests/src/org/lflang/tests/TestBase.java +++ b/org.lflang.tests/src/org/lflang/tests/TestBase.java @@ -153,6 +153,7 @@ public static class Message { public static final String DESC_CONCURRENT = "Run concurrent tests."; public static final String DESC_TARGET_SPECIFIC = "Run target-specific tests"; public static final String DESC_ARDUINO = "Running Arduino tests."; + public static final String DESC_ZEPHYR = "Running Zephyr tests."; public static final String DESC_AS_CCPP = "Running C tests as CCpp."; public static final String DESC_SINGLE_THREADED = "Run non-concurrent and non-federated tests with threading = off."; public static final String DESC_SCHED_SWAPPING = "Running with non-default runtime scheduler "; diff --git a/org.lflang.tests/src/org/lflang/tests/TestRegistry.java b/org.lflang.tests/src/org/lflang/tests/TestRegistry.java index 474a29574e..cf40ccc9f8 100644 --- a/org.lflang.tests/src/org/lflang/tests/TestRegistry.java +++ b/org.lflang.tests/src/org/lflang/tests/TestRegistry.java @@ -150,8 +150,9 @@ public enum TestCategory { DOCKER_FEDERATED(true, "docker" + File.separator + "federated"), SERIALIZATION(false), ARDUINO(false, TestLevel.BUILD), + ZEPHYR(false, TestLevel.BUILD), TARGET(false); - + /** * Whether or not we should compare coverage against other targets. */ diff --git a/org.lflang.tests/src/org/lflang/tests/runtime/CCppTest.java b/org.lflang.tests/src/org/lflang/tests/runtime/CCppTest.java index b584b7646e..98dbd1b37e 100644 --- a/org.lflang.tests/src/org/lflang/tests/runtime/CCppTest.java +++ b/org.lflang.tests/src/org/lflang/tests/runtime/CCppTest.java @@ -43,6 +43,7 @@ private static boolean isExcludedFromCCpp(TestCategory category) { boolean excluded = category == TestCategory.SERIALIZATION; excluded |= isWindows() && (category == TestCategory.DOCKER_FEDERATED || category == TestCategory.ARDUINO) ; excluded |= isMac() && (category == TestCategory.DOCKER_FEDERATED || category == TestCategory.DOCKER); + excluded |= category == TestCategory.ZEPHYR; return !excluded; } } diff --git a/org.lflang.tests/src/org/lflang/tests/runtime/CZephyrTest.java b/org.lflang.tests/src/org/lflang/tests/runtime/CZephyrTest.java new file mode 100644 index 0000000000..b63765c0f0 --- /dev/null +++ b/org.lflang.tests/src/org/lflang/tests/runtime/CZephyrTest.java @@ -0,0 +1,58 @@ +/************* +Copyright (c) 2023, The University of California at Berkeley. + +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.tests.runtime; + +import java.util.List; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import org.lflang.Target; +import org.lflang.tests.Configurators; +import org.lflang.tests.RuntimeTest; +import org.lflang.tests.TestRegistry.TestCategory; + +/** + * Collection of Zephyr tests for the C target. + * + * @author Erling Rennemo Jellum + */ +public class CZephyrTest extends RuntimeTest { + + public CZephyrTest() { + super(Target.C); + } + + @Test + public void runZephyrTests() { + Assumptions.assumeTrue(isLinux(), "Zephyr tests only run on Linux"); + super.runTestsFor(List.of(Target.C), + Message.DESC_ZEPHYR, + TestCategory.ZEPHYR::equals, Configurators::platformZephyrQemuNoFlash, + false); + } +} + diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index a03131a1e8..d42631aa69 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit a03131a1e846fdd42632a1db94a7915362a3a58d +Subproject commit d42631aa6902e98f0808a55bf6cef72b50bb7619 diff --git a/org.lflang/src/lib/platform/zephyr/Kconfig b/org.lflang/src/lib/platform/zephyr/Kconfig new file mode 100644 index 0000000000..5ba198ca54 --- /dev/null +++ b/org.lflang/src/lib/platform/zephyr/Kconfig @@ -0,0 +1 @@ +source "Kconfig.zephyr" diff --git a/org.lflang/src/lib/platform/zephyr/boards/esp32.overlay b/org.lflang/src/lib/platform/zephyr/boards/esp32.overlay new file mode 100755 index 0000000000..241947b064 --- /dev/null +++ b/org.lflang/src/lib/platform/zephyr/boards/esp32.overlay @@ -0,0 +1,3 @@ +&timer0 { + status = "okay"; +}; diff --git a/org.lflang/src/lib/platform/zephyr/boards/nrf52dk_nrf52832_lf.conf b/org.lflang/src/lib/platform/zephyr/boards/nrf52dk_nrf52832_lf.conf new file mode 100644 index 0000000000..8bc7cfdbc5 --- /dev/null +++ b/org.lflang/src/lib/platform/zephyr/boards/nrf52dk_nrf52832_lf.conf @@ -0,0 +1 @@ +CONFIG_COUNTER_TIMER1=y diff --git a/org.lflang/src/lib/platform/zephyr/prj_lf.conf b/org.lflang/src/lib/platform/zephyr/prj_lf.conf new file mode 100644 index 0000000000..88613edc08 --- /dev/null +++ b/org.lflang/src/lib/platform/zephyr/prj_lf.conf @@ -0,0 +1,14 @@ +# Lingua Franca Zephyr configuration file + +# This is a generated file, do not edit. +# To supply additional configuration parameters add an additional +# configuration file to the build with +# west build -b qemu_cortex_m3 -- -DOVERLAY_CONFIG=my_conf.prj + +# Enable printf +CONFIG_PRINTK=y +# Use the newlib C library +CONFIG_NEWLIB_LIBC=y +CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y +# Enable the counter API used for hi-res clock +CONFIG_COUNTER=y diff --git a/org.lflang/src/org/lflang/TargetConfig.java b/org.lflang/src/org/lflang/TargetConfig.java index 6e68ce3042..d3566eb5b3 100644 --- a/org.lflang/src/org/lflang/TargetConfig.java +++ b/org.lflang/src/org/lflang/TargetConfig.java @@ -418,12 +418,17 @@ public static class PlatformOptions { /** * The base board we target when building LF on Arduino/Embedded Boards. For OS development and generic embedded systems, this value is unused. */ - public Board board = Board.UNO; + public String board = null; /** * The baud rate used as a parameter to certain embedded platforms. 9600 is a standard rate amongst systems like Arduino, so it's the default value. */ public int baudRate = 9600; + +/** + * Should LFC invoke external tools to flash the resulting binary onto the target board + */ + public boolean flash = false; } /** diff --git a/org.lflang/src/org/lflang/TargetProperty.java b/org.lflang/src/org/lflang/TargetProperty.java index 404a6c44a6..3099aed2cc 100644 --- a/org.lflang/src/org/lflang/TargetProperty.java +++ b/org.lflang/src/org/lflang/TargetProperty.java @@ -327,15 +327,10 @@ public enum TargetProperty { config.platformOptions.baudRate = ASTUtils.toInteger(entry.getValue()); break; case BOARD: - Board b = (Board) UnionType.BOARD_UNION - .forName(ASTUtils.elementToSingleString(entry.getValue())); - if(b == null){ - String s = "Unidentified Board Type, LF supports the following board types: " + Arrays.asList(Board.values()).toString(); - err.reportError(s); - throw new AssertionError(s); - } - - config.platformOptions.board = b; + config.platformOptions.board = ASTUtils.elementToSingleString(entry.getValue()); + break; + case FLASH: + config.platformOptions.flash = ASTUtils.toBoolean(entry.getValue()); break; default: break; @@ -1327,8 +1322,9 @@ public TargetPropertyType getType() { public enum PlatformOption implements DictionaryElement { NAME("name", PrimitiveType.STRING), BAUDRATE("baud-rate", PrimitiveType.NON_NEGATIVE_INTEGER), - BOARD("board", PrimitiveType.STRING); - + BOARD("board", PrimitiveType.STRING), + FLASH("flash", PrimitiveType.BOOLEAN); + public final PrimitiveType type; private final String description; @@ -1413,6 +1409,7 @@ public enum Platform { NRF52("Nrf52"), LINUX("Linux"), MAC("Darwin"), + ZEPHYR("Zephyr"), WINDOWS("Windows"); String cMakeName; diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java index b36c64892e..35a4a8e3ad 100644 --- a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java @@ -118,6 +118,23 @@ CodeBuilder generateCMakeCode( cMakeCode.newLine(); cMakeCode.pr("cmake_minimum_required(VERSION " + MIN_CMAKE_VERSION + ")"); + + if (targetConfig.platformOptions.platform == Platform.ZEPHYR) { + cMakeCode.pr("# Set default configuration file. To add custom configurations,"); + cMakeCode.pr("# pass -- -DOVERLAY_CONFIG=my_config.prj to either cmake or west"); + cMakeCode.pr("set(CONF_FILE prj_lf.conf)"); + if (targetConfig.platformOptions.board != null) { + cMakeCode.pr("# Selecting board specified in target property"); + cMakeCode.pr("set(BOARD "+targetConfig.platformOptions.board+")"); + } else { + cMakeCode.pr("# Selecting default board"); + cMakeCode.pr("set(BOARD qemu_cortex_m3)"); + } + cMakeCode.pr("# We require Zephyr version 3.2.0"); + cMakeCode.pr("find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE} EXACT 3.2.0)"); + cMakeCode.newLine(); + } + cMakeCode.pr("project("+executableName+" LANGUAGES C)"); cMakeCode.newLine(); @@ -178,11 +195,25 @@ CodeBuilder generateCMakeCode( cMakeCode.pr("set(CMAKE_SYSTEM_NAME "+targetConfig.platformOptions.platform.getcMakeName()+")"); } - cMakeCode.pr(setUpMainTarget.getCmakeCode( - hasMain, - executableName, - Stream.concat(additionalSources.stream(), sources.stream()) + cMakeCode.pr("# Target definitions\n"); + targetConfig.compileDefinitions.forEach((key, value) -> cMakeCode.pr( + "add_compile_definitions("+key+"="+value+")\n" )); + cMakeCode.newLine(); + + if (targetConfig.platformOptions.platform == Platform.ZEPHYR) { + cMakeCode.pr(setUpMainTargetZephyr( + hasMain, + executableName, + Stream.concat(additionalSources.stream(), sources.stream()) + )); + } else { + cMakeCode.pr(setUpMainTarget.getCmakeCode( + hasMain, + executableName, + Stream.concat(additionalSources.stream(), sources.stream()) + )); + } cMakeCode.pr("target_link_libraries(${LF_MAIN_TARGET} PRIVATE core)"); @@ -222,7 +253,7 @@ CodeBuilder generateCMakeCode( cMakeCode.pr("target_compile_definitions(${LF_MAIN_TARGET} PUBLIC NUMBER_OF_WORKERS="+targetConfig.workers+")"); cMakeCode.newLine(); } - + // Add additional flags so runtime can distinguish between multi-threaded and single-threaded mode if (targetConfig.threading) { cMakeCode.pr("# Set flag to indicate a multi-threaded runtime"); @@ -233,11 +264,6 @@ CodeBuilder generateCMakeCode( } cMakeCode.newLine(); - cMakeCode.pr("# Target definitions\n"); - targetConfig.compileDefinitions.forEach((key, value) -> cMakeCode.pr( - "target_compile_definitions(${LF_MAIN_TARGET} PUBLIC "+key+"="+value+")\n" - )); - cMakeCode.newLine(); if (CppMode) cMakeCode.pr("enable_language(CXX)"); @@ -341,4 +367,37 @@ private static String setUpMainTarget( code.newLine(); return code.toString(); } + + private static String setUpMainTargetZephyr( + boolean hasMain, + String executableName, + Stream cSources + ) { + var code = new CodeBuilder(); + code.pr("add_subdirectory(core)"); + code.pr("target_link_libraries(core PUBLIC zephyr_interface)"); + code.newLine(); + + if (hasMain) { + code.pr("# Declare a new executable target and list all its sources"); + code.pr("set(LF_MAIN_TARGET app)"); + code.pr("target_sources("); + } else { + code.pr("# Declare a new library target and list all its sources"); + code.pr("set(LF_MAIN_TARGET"+executableName+")"); + code.pr("add_library("); + } + code.indent(); + code.pr("${LF_MAIN_TARGET}"); + + if (hasMain) { + code.pr("PRIVATE"); + } + + cSources.forEach(code::pr); + code.unindent(); + code.pr(")"); + code.newLine(); + return code.toString(); + } } diff --git a/org.lflang/src/org/lflang/generator/c/CCompiler.java b/org.lflang/src/org/lflang/generator/c/CCompiler.java index 99a96dcc3c..7502456d20 100644 --- a/org.lflang/src/org/lflang/generator/c/CCompiler.java +++ b/org.lflang/src/org/lflang/generator/c/CCompiler.java @@ -178,6 +178,17 @@ public boolean runCCompiler( System.out.println("SUCCESS: Compiling generated code for " + fileConfig.name + " finished with no errors."); } + if (targetConfig.platformOptions.platform == Platform.ZEPHYR && targetConfig.platformOptions.flash) { + System.out.println("Invoking flash command for Zephyr"); + LFCommand flash = buildWestFlashCommand(); + int flashRet = flash.run(); + if (flashRet != 0) { + errorReporter.reportError("West flash command failed with error code " + flashRet); + } else { + System.out.println("SUCCESS: Flashed application with west"); + } + } + } return cMakeReturnCode == 0 && makeReturnCode == 0; } @@ -275,6 +286,33 @@ public LFCommand buildCmakeCommand() { return command; } + /** + * Return a flash/emulate command using west. + * If board is null (defaults to qemu_cortex_m3) or qemu_* + * Return a flash command which runs the target as an emulation + * If ordinary target, return `west flash` + */ + public LFCommand buildWestFlashCommand() { + // Set the build directory to be "build" + Path buildPath = fileConfig.getSrcGenPath().resolve("build"); + String board = targetConfig.platformOptions.board; + LFCommand cmd; + if (board == null || board.startsWith("qemu")) { + cmd = commandFactory.createCommand( + "west", List.of("build", "-t", "run"), buildPath); + } else { + cmd = commandFactory.createCommand( + "west", List.of("flash"), buildPath); + } + if (cmd == null) { + errorReporter.reportError( + "Could not create west flash command." + ); + } + + return cmd; + } + /** * Check if the output produced by CMake has any known and common errors. * If a known error is detected, a specialized, more informative message diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index 718672858a..ebf5789b83 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -590,12 +590,32 @@ public void doGenerate(Resource resource, LFGeneratorContext context) { StringBuilder s = new StringBuilder(); s.append("set(ARDUINO_BOARD \""); - s.append(targetConfig.platformOptions.board.getBoardName()); s.append("\")"); FileUtil.writeToFile(s.toString(), fileConfig.getSrcGenPath().resolve("toolchain/BoardOptions.cmake")); } + // If Zephyr target then copy default config and board files + // for Zephyr support + if (targetConfig.platformOptions.platform == Platform.ZEPHYR) { + FileUtil.copyDirectoryFromClassPath( + "/lib/platform/zephyr/boards", + fileConfig.getSrcGenPath().resolve("boards"), + false + ); + FileUtil.copyFileFromClassPath( + "/lib/platform/zephyr/prj_lf.conf", + fileConfig.getSrcGenPath().resolve("prj_lf.conf"), + true + ); + + FileUtil.copyFileFromClassPath( + "/lib/platform/zephyr/Kconfig", + fileConfig.getSrcGenPath().resolve("Kconfig"), + true + ); + } + // Write the generated code code.writeToFile(targetFile); } catch (IOException e) { diff --git a/org.lflang/src/org/lflang/generator/c/CMainFunctionGenerator.java b/org.lflang/src/org/lflang/generator/c/CMainFunctionGenerator.java index 7b05a52923..f34f46345c 100644 --- a/org.lflang/src/org/lflang/generator/c/CMainFunctionGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CMainFunctionGenerator.java @@ -51,12 +51,22 @@ private String generateMainFunction() { "}", "void loop() {}" ); + } else if (targetConfig.platformOptions.platform == Platform.ZEPHYR) { + // The Zephyr "runtime" does not terminate when main returns. + // Rather, `exit` should be called explicitly. + return String.join("\n", + "void main(void) {", + " int res = lf_reactor_c_main(0, NULL);", + " exit(res);", + "}" + ); + } else { + return String.join("\n", + "int main(int argc, const char* argv[]) {", + " return lf_reactor_c_main(argc, argv);", + "}" + ); } - return String.join("\n", - "int main(int argc, const char* argv[]) {", - " return lf_reactor_c_main(argc, argv);", - "}" - ); } /** diff --git a/test/C/src/zephyr/Blinky2.lf b/test/C/src/zephyr/Blinky2.lf new file mode 100644 index 0000000000..039bb0172e --- /dev/null +++ b/test/C/src/zephyr/Blinky2.lf @@ -0,0 +1,13 @@ +target C { + platform: { + name: Zephyr, + board: nrf52dk_nrf52832 + }, + threading: false +} + +import NrfLeds from "lib/Led.lf" + +main reactor { + led0 = new NrfLeds() +} diff --git a/test/C/src/zephyr/HelloZephyr.lf b/test/C/src/zephyr/HelloZephyr.lf new file mode 100644 index 0000000000..2c4a98890f --- /dev/null +++ b/test/C/src/zephyr/HelloZephyr.lf @@ -0,0 +1,11 @@ +target C { + threading: false, + platform: { + name: Zephyr, + board: qemu_cortex_m3 + } +} + +main reactor { + reaction(startup) {= printf("Hello World!\n"); =} +} diff --git a/test/C/src/zephyr/Timer.lf b/test/C/src/zephyr/Timer.lf new file mode 100644 index 0000000000..cac30ae9b1 --- /dev/null +++ b/test/C/src/zephyr/Timer.lf @@ -0,0 +1,13 @@ +target C { + platform: { + name: Zephyr, + board: qemu_cortex_m3 + }, + threading: false +} + +main reactor { + timer t(0, 1 sec) + + reaction(t) {= printf("Hello\n"); =} +} diff --git a/test/C/src/zephyr/lib/Led.lf b/test/C/src/zephyr/lib/Led.lf new file mode 100644 index 0000000000..04999cf14b --- /dev/null +++ b/test/C/src/zephyr/lib/Led.lf @@ -0,0 +1,32 @@ +target C + +reactor NrfLeds { + preamble {= + #include + #include + #define LED0_NODE DT_ALIAS(led0) + #define LED1_NODE DT_ALIAS(led1) + #define LED2_NODE DT_ALIAS(led2) + #define LED3_NODE DT_ALIAS(led3) + static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios); + static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios); + static const struct gpio_dt_spec led2 = GPIO_DT_SPEC_GET(LED2_NODE, gpios); + static const struct gpio_dt_spec led3 = GPIO_DT_SPEC_GET(LED3_NODE, gpios); + =} + timer t(0, 100 msec) + + reaction(startup) {= + gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE); + gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE); + gpio_pin_configure_dt(&led2, GPIO_OUTPUT_ACTIVE); + gpio_pin_configure_dt(&led3, GPIO_OUTPUT_ACTIVE); + =} + + reaction(t) {= + printf("Hello\n"); + gpio_pin_toggle_dt(&led0); + gpio_pin_toggle_dt(&led1); + gpio_pin_toggle_dt(&led2); + gpio_pin_toggle_dt(&led3); + =} +}