Skip to content

Commit

Permalink
Merge pull request #1536 from lf-lang/c-zephyr-support
Browse files Browse the repository at this point in the history
Add Zephyr support to the C-target
  • Loading branch information
erlingrj authored Jan 16, 2023
2 parents 06fa27b + 0ef8365 commit 788ba74
Show file tree
Hide file tree
Showing 22 changed files with 386 additions and 31 deletions.
60 changes: 60 additions & 0 deletions .github/workflows/c-zephyr-tests.yml
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
14 changes: 13 additions & 1 deletion org.lflang.tests/src/org/lflang/tests/Configurators.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions org.lflang.tests/src/org/lflang/tests/TestBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 ";
Expand Down
3 changes: 2 additions & 1 deletion org.lflang.tests/src/org/lflang/tests/TestRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
58 changes: 58 additions & 0 deletions org.lflang.tests/src/org/lflang/tests/runtime/CZephyrTest.java
Original file line number Diff line number Diff line change
@@ -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 <erling.r.jellum@ntnu.no>
*/
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);
}
}

1 change: 1 addition & 0 deletions org.lflang/src/lib/platform/zephyr/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
source "Kconfig.zephyr"
3 changes: 3 additions & 0 deletions org.lflang/src/lib/platform/zephyr/boards/esp32.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
&timer0 {
status = "okay";
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_COUNTER_TIMER1=y
14 changes: 14 additions & 0 deletions org.lflang/src/lib/platform/zephyr/prj_lf.conf
Original file line number Diff line number Diff line change
@@ -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
7 changes: 6 additions & 1 deletion org.lflang/src/org/lflang/TargetConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
19 changes: 8 additions & 11 deletions org.lflang/src/org/lflang/TargetProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1413,6 +1409,7 @@ public enum Platform {
NRF52("Nrf52"),
LINUX("Linux"),
MAC("Darwin"),
ZEPHYR("Zephyr"),
WINDOWS("Windows");

String cMakeName;
Expand Down
79 changes: 69 additions & 10 deletions org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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)");

Expand Down Expand Up @@ -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");
Expand All @@ -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)");

Expand Down Expand Up @@ -341,4 +367,37 @@ private static String setUpMainTarget(
code.newLine();
return code.toString();
}

private static String setUpMainTargetZephyr(
boolean hasMain,
String executableName,
Stream<String> 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();
}
}
Loading

0 comments on commit 788ba74

Please # to comment.