diff --git a/.github/actions/setup-flexpret/action.yml b/.github/actions/setup-flexpret/action.yml index 9866c15c6b..e97521d66a 100644 --- a/.github/actions/setup-flexpret/action.yml +++ b/.github/actions/setup-flexpret/action.yml @@ -9,23 +9,12 @@ runs: git clone --recurse-submodules https://github.com/pretis/flexpret # This rest is copied directly from FlexPRET's `azure-pipelines.yml` - - # Ubuntu 20.04 only has Verilator 4.028 but we neeed a more modern version - # so we do not use 'sudo apt-get install -y -qq verilator' here. - wget -q https://github.com/sifive/verilator/releases/download/4.036-0sifive2/verilator_4.036-0sifive2_amd64.deb -O verilator.deb - sudo dpkg -i verilator.deb + sudo apt install verilator # Install riscv compiler - wget https://github.com/stnolting/riscv-gcc-prebuilt/releases/download/rv32i-4.0.0/riscv32-unknown-elf.gcc-12.1.0.tar.gz - sudo mkdir /opt/riscv - sudo tar -xzf riscv32-unknown-elf.gcc-12.1.0.tar.gz -C /opt/riscv/ - rm riscv32-unknown-elf.gcc-12.1.0.tar.gz - - # Update submodules - git submodule update --init --recursive - - # Save location of RISC-V compiler to reuse later - echo "FP_RISCV_COMPILER=/opt/riscv" >> "$GITHUB_ENV" + wget -q --show-progress https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-2/xpack-riscv-none-elf-gcc-14.2.0-2-linux-x64.tar.gz -O gcc.tar.gz + tar xvf gcc.tar.gz --directory=/opt + echo "RISCV_TOOL_PATH_PREFIX=/opt/xpack-riscv-none-elf-gcc-14.2.0-2" >> $GITHUB_ENV shell: bash - name: Build FlexPRET and install to SDK run: | diff --git a/.github/actions/setup-zephyr/action.yml b/.github/actions/setup-zephyr/action.yml index 06baf10907..e0c55059c4 100644 --- a/.github/actions/setup-zephyr/action.yml +++ b/.github/actions/setup-zephyr/action.yml @@ -5,7 +5,7 @@ runs: steps: - name: Setup environment variables run: | - echo "SDK_VERSION=0.16.3" >> $GITHUB_ENV + echo "SDK_VERSION=0.16.8" >> $GITHUB_ENV shell: bash - name: Dependencies run: | diff --git a/.github/scripts/run-zephyr-tests.sh b/.github/scripts/run-zephyr-tests.sh index d1c1d1c4e9..2f1982d80f 100755 --- a/.github/scripts/run-zephyr-tests.sh +++ b/.github/scripts/run-zephyr-tests.sh @@ -1,7 +1,7 @@ #!/bin/bash # Skip -skip=("FileReader" "FilePkgReader") +skip=("FileReader" "FilePkgReader" "AsyncCallback" "AsyncCallbackDrop" "AsyncCallbackReplace") find_kconfig_folders() { if [ -f "$folder/CMakeLists.txt" ]; then @@ -45,7 +45,7 @@ run_qemu_zephyr_test() { pid=$! return_val=2 wait_count=0 - timeout=60 + timeout=120 # Parse the file and match on known outputs. With timeout. while [ "$wait_count" -le "$timeout" ]; do sleep 1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da1990ca3f..e84f9fce42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,10 +30,3 @@ jobs: uses: ./.github/actions/report-code-coverage with: files: core/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/base/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/lfc/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/lfd/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/lff/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml - - epoch: - uses: lf-lang/epoch/.github/workflows/build.yml@main - with: - lingua-franca-ref: ${{ github.head_ref || github.ref_name }} - lingua-franca-repo: ${{ github.event.pull_request.head.repo.full_name }} - upload-artifacts: false diff --git a/.github/workflows/c-embedded.yml b/.github/workflows/c-embedded.yml index 5b49223589..ea9b2acf25 100644 --- a/.github/workflows/c-embedded.yml +++ b/.github/workflows/c-embedded.yml @@ -20,9 +20,9 @@ jobs: zephyr: uses: ./.github/workflows/c-zephyr-tests.yml - # Run the C FlexPRET integration tests. - flexpret: - uses: ./.github/workflows/c-flexpret-tests.yml +# # Run the C FlexPRET integration tests. +# flexpret: +# uses: ./.github/workflows/c-flexpret-tests.yml # Run the C Patmos integration tests. patmos: diff --git a/.github/workflows/c-flexpret-tests.yml b/.github/workflows/c-flexpret-tests.yml index 0df6abe62f..d6c4eb9ad4 100644 --- a/.github/workflows/c-flexpret-tests.yml +++ b/.github/workflows/c-flexpret-tests.yml @@ -46,7 +46,6 @@ jobs: - name: Run FlexPRET smoke tests run: | cd "$FP_DIR" && source env.bash && cd - - export RISCV_TOOL_PATH_PREFIX=$FP_RISCV_COMPILER ./gradlew core:integrationTest \ --tests org.lflang.tests.runtime.CFlexPRETTest.* \ core:integrationTestCodeCoverageReport diff --git a/.github/workflows/c-zephyr-tests.yml b/.github/workflows/c-zephyr-tests.yml index 53ce259cda..2a69452506 100644 --- a/.github/workflows/c-zephyr-tests.yml +++ b/.github/workflows/c-zephyr-tests.yml @@ -39,27 +39,27 @@ jobs: path: core/src/main/resources/lib/c/reactor-c ref: ${{ inputs.runtime-ref }} if: ${{ inputs.runtime-ref }} - # - name: Run Zephyr smoke tests - # run: | - # ./gradlew core:integrationTest \ - # --tests org.lflang.tests.runtime.CZephyrTest.buildZephyrUnthreaded* \ - # --tests org.lflang.tests.runtime.CZephyrTest.buildZephyrThreaded* core:integrationTestCodeCoverageReport - # ./.github/scripts/run-zephyr-tests.sh test/C/src-gen - # rm -rf test/C/src-gen - # - name: Run basic tests - # run: | - # ./gradlew core:integrationTest --tests org.lflang.tests.runtime.CZephyrTest.buildBasic* core:integrationTestCodeCoverageReport - # ./.github/scripts/run-zephyr-tests.sh test/C/src-gen - # rm -rf test/C/src-gen - # - name: Run concurrent tests - # run: | - # ./gradlew core:integrationTest --tests org.lflang.tests.runtime.CZephyrTest.buildConcurrent* core:integrationTestCodeCoverageReport - # ./.github/scripts/run-zephyr-tests.sh test/C/src-gen - # rm -rf test/C/src-gen - # - name: Run Zephyr board tests - # run: | - # ./gradlew core:integrationTest --tests org.lflang.tests.runtime.CZephyrTest.buildZephyrBoards* core:integrationTestCodeCoverageReport - # rm -rf test/C/src-gen + - name: Run Zephyr smoke tests + run: | + ./gradlew core:integrationTest \ + --tests org.lflang.tests.runtime.CZephyrTest.buildZephyrUnthreaded* \ + --tests org.lflang.tests.runtime.CZephyrTest.buildZephyrThreaded* core:integrationTestCodeCoverageReport + ./.github/scripts/run-zephyr-tests.sh test/C/src-gen + rm -rf test/C/src-gen + - name: Run basic tests + run: | + ./gradlew core:integrationTest --tests org.lflang.tests.runtime.CZephyrTest.buildBasic* core:integrationTestCodeCoverageReport + ./.github/scripts/run-zephyr-tests.sh test/C/src-gen + rm -rf test/C/src-gen + - name: Run concurrent tests + run: | + ./gradlew core:integrationTest --tests org.lflang.tests.runtime.CZephyrTest.buildConcurrent* core:integrationTestCodeCoverageReport + ./.github/scripts/run-zephyr-tests.sh test/C/src-gen + rm -rf test/C/src-gen + - name: Run Zephyr board tests + run: | + ./gradlew core:integrationTest --tests org.lflang.tests.runtime.CZephyrTest.buildZephyrBoards* core:integrationTestCodeCoverageReport + rm -rf test/C/src-gen - name: Smoke test of lf-west-template run: | export LFC=$(pwd)/bin/lfc-dev @@ -68,8 +68,8 @@ jobs: west lfc apps/NrfBlinky/src/NrfBlinky.lf --lfc $LFC --build "-p always" west lfc apps/NrfBlinky/src/NrfToggleGPIO.lf --lfc $LFC --build "-p always" west build -b qemu_cortex_m3 -p always apps/HelloZephyr - # - name: Report to CodeCov - # uses: ./.github/actions/report-code-coverage - # with: - # files: core/build/reports/jacoco/integrationTestCodeCoverageReport/integrationTestCodeCoverageReport.xml - # if: ${{ github.repository == 'lf-lang/lingua-franca' }} + - name: Report to CodeCov + uses: ./.github/actions/report-code-coverage + with: + files: core/build/reports/jacoco/integrationTestCodeCoverageReport/integrationTestCodeCoverageReport.xml + if: ${{ github.repository == 'lf-lang/lingua-franca' }} diff --git a/README.md b/README.md index 261636f015..5af0226c8d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,10 @@ [Contributing](CONTRIBUTING.md) | [Changelog](CHANGELOG.md) -[![Build Status](https://github.com/lf-lang/lingua-franca/workflows/CI/badge.svg)](https://github.com/lf-lang/lingua-franca/actions/) +[![CI (targets)](https://github.com/lf-lang/lingua-franca/actions/workflows/all-targets.yml/badge.svg)](https://github.com/lf-lang/lingua-franca/actions/workflows/all-targets.yml?query=branch%3Amaster) +[![CI (misc)](https://github.com/lf-lang/lingua-franca/actions/workflows/all-misc.yml/badge.svg)](https://github.com/lf-lang/lingua-franca/actions/workflows/all-misc.yml?query=branch%3Amaster) +[![CI (misc)](https://github.com/lf-lang/lingua-franca/actions/workflows/all-embedded.yml/badge.svg)]([https://github.com/lf-lang/lingua-franca/actions/](https://github.com/lf-lang/lingua-franca/actions/workflows/all-embedded.yml?query=branch%3Amaster)) + [![Nightly Build](https://github.com/lf-lang/lingua-franca/actions/workflows/nightly-build.yml/badge.svg)](https://github.com/lf-lang/lingua-franca/actions/workflows/nightly-build.yml) [![CodeCov](https://codecov.io/gh/lf-lang/lingua-franca/branch/master/graph/badge.svg?token=b7LrpihI5a)](https://codecov.io/gh/lf-lang/lingua-franca) [![GitHub Contributors](https://img.shields.io/github/contributors/lf-lang/lingua-franca)](https://github.com/lf-lang/lingua-franca/graphs/contributors) diff --git a/core/src/main/java/org/lflang/federated/generator/FedGenerator.java b/core/src/main/java/org/lflang/federated/generator/FedGenerator.java index e1f36a9ad1..e9379dd108 100644 --- a/core/src/main/java/org/lflang/federated/generator/FedGenerator.java +++ b/core/src/main/java/org/lflang/federated/generator/FedGenerator.java @@ -1,6 +1,7 @@ package org.lflang.federated.generator; import static org.lflang.generator.docker.DockerGenerator.dockerGeneratorFactory; +import static org.lflang.target.property.type.PlatformType.Platform.supportsFederated; import com.google.inject.Injector; import java.io.IOException; @@ -63,6 +64,7 @@ import org.lflang.target.property.DockerProperty.DockerOptions; import org.lflang.target.property.KeepaliveProperty; import org.lflang.target.property.NoCompileProperty; +import org.lflang.target.property.PlatformProperty; import org.lflang.target.property.type.CoordinationModeType.CoordinationMode; import org.lflang.util.Averager; import org.lflang.util.FileUtil; @@ -121,7 +123,7 @@ public FedGenerator(LFGeneratorContext context) { * @return False if no errors have occurred, true otherwise. */ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws IOException { - if (!federatedExecutionIsSupported(resource)) return true; + if (!federatedExecutionIsSupported(resource, context)) return true; cleanIfNeeded(context); // In a federated execution, we need keepalive to be true, @@ -174,12 +176,6 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws federates.stream().map(fed -> fed.name).collect(Collectors.toList()))); } - // Do not invoke target code generators if --no-compile flag is used. - if (context.getTargetConfig().get(NoCompileProperty.INSTANCE)) { - context.finish(Status.GENERATED, lf2lfCodeMapMap); - return false; - } - // If the RTI is to be built locally, set up a build environment for it. prepareRtiBuildEnvironment(context); @@ -300,7 +296,7 @@ private void cleanIfNeeded(LFGeneratorContext context) { } /** Return whether federated execution is supported for {@code resource}. */ - private boolean federatedExecutionIsSupported(Resource resource) { + private boolean federatedExecutionIsSupported(Resource resource, LFGeneratorContext context) { TargetDecl targetDecl = GeneratorUtils.findTargetDecl(resource); var target = Target.fromDecl(targetDecl); var targetOK = @@ -316,6 +312,17 @@ private boolean federatedExecutionIsSupported(Resource resource) { .error("Federated LF programs with a C target are currently not supported on Windows."); targetOK = false; } + if (target.equals(Target.C) || target.equals(Target.CCPP)) { + // Currently, only the C runtime has a platform abstraction. + var platform = context.getTargetConfig().get(PlatformProperty.INSTANCE).platform(); + if (!supportsFederated(platform)) { + messageReporter + .at(targetDecl) + .error( + "Federations are not supported by the " + platform.getcMakeName() + " platform."); + targetOK = false; + } + } return targetOK; } diff --git a/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java b/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java index adeb178f75..5931399052 100644 --- a/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java @@ -158,8 +158,8 @@ CodeBuilder generateCMakeCode( cMakeCode.pr("# Selecting default board"); cMakeCode.pr("set(BOARD qemu_cortex_m3)"); } - cMakeCode.pr("# We recommend Zephyr v3.4.0 but we are compatible with older versions also"); - cMakeCode.pr("find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE} 3.4.0)"); + cMakeCode.pr("# We recommend Zephyr v3.7.0 but we are compatible with older versions also"); + cMakeCode.pr("find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE} 3.7.0)"); cMakeCode.newLine(); cMakeCode.pr("project(" + executableName + " LANGUAGES C)"); cMakeCode.newLine(); diff --git a/core/src/main/java/org/lflang/generator/c/CGenerator.java b/core/src/main/java/org/lflang/generator/c/CGenerator.java index 789b6dbef5..f6ee943085 100644 --- a/core/src/main/java/org/lflang/generator/c/CGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CGenerator.java @@ -1375,10 +1375,6 @@ private void recordBuiltinTriggers(ReactorInstance instance) { } } - /** - * Generate code to set up the tables used in _lf_start_time_step to decrement reference counts - * and mark outputs absent between time steps. This function puts the code into startTimeStep. - */ /** * Generate code to set up the tables used in _lf_start_time_step to decrement reference counts * and mark outputs absent between time steps. This function puts the code into startTimeStep. @@ -1410,6 +1406,9 @@ private void generateStartTimeStep(ReactorInstance instance) { temp.pr("// Add port " + port.getFullName() + " to array of is_present fields."); + // We will be iterating over instance (if a bank) and the parent of the port (if a bank). + var width = instance.getWidth() * port.getParent().getWidth(); + if (!Objects.equal(port.getParent(), instance)) { // The port belongs to contained reactor, so we also have // iterate over the instance bank members. @@ -1418,16 +1417,29 @@ private void generateStartTimeStep(ReactorInstance instance) { temp.startScopedBlock(instance); temp.startScopedBankChannelIteration(port, null); } else { + // This branch should not occur because if the port's parent is instance and the port + // is an effect of a reaction of instance, then the port must be an output, not an + // input. + // Nevertheless, leave this here in case we missed something. temp.startScopedBankChannelIteration(port, "count"); + width = port.getParent().getWidth(); } var portRef = CUtil.portRefNested(port); var con = (port.isMultiport()) ? "->" : "."; + var indexString = + String.valueOf(enclaveInfo.numIsPresentFields) + + " + (" + + CUtil.runtimeIndex(instance.getParent()) + + ") * " + + width * port.getWidth() + + " + count"; + temp.pr( enclaveStruct + ".is_present_fields[" - + enclaveInfo.numIsPresentFields - + " + count] = &" + + indexString + + "] = &" + portRef + con + "is_present;"); @@ -1436,19 +1448,19 @@ private void generateStartTimeStep(ReactorInstance instance) { CExtensionUtils.surroundWithIfFederatedDecentralized( enclaveStruct + "._lf_intended_tag_fields[" - + enclaveInfo.numIsPresentFields - + " + count] = &" + + indexString + + "] = &" + portRef + con + "intended_tag;")); - enclaveInfo.numIsPresentFields += port.getWidth() * port.getParent().getTotalWidth(); + enclaveInfo.numIsPresentFields += port.getParent().getTotalWidth() * port.getWidth(); if (!Objects.equal(port.getParent(), instance)) { temp.pr("count++;"); + temp.endScopedBankChannelIteration(port, null); temp.endScopedBlock(); temp.endScopedBlock(); - temp.endScopedBankChannelIteration(port, null); } else { temp.endScopedBankChannelIteration(port, "count"); } @@ -1461,18 +1473,29 @@ private void generateStartTimeStep(ReactorInstance instance) { for (ActionInstance action : instance.actions) { foundOne = true; - temp.startScopedBlock(instance); + + // Build the index into `is_present_fields` for this action. + var indexString = + String.valueOf(enclaveInfo.numIsPresentFields) + + " + (" + + CUtil.runtimeIndex(instance.getParent()) + + ") * " + + action.getParent().getWidth(); + + if (instance.isBank()) { + indexString += " + " + CUtil.bankIndexName(instance); + } temp.pr( String.join( "\n", "// Add action " + action.getFullName() + " to array of is_present fields.", - enclaveStruct + ".is_present_fields[" + enclaveInfo.numIsPresentFields + "] ", - " = &" + enclaveStruct + ".is_present_fields[" + indexString + "]", + " = (bool *) &" + containerSelfStructName - + "->_lf_" + + "->_lf__" + action.getName() - + ".is_present;")); + + ".status;")); // Intended_tag is only applicable to actions in federated execution with decentralized // coordination. @@ -1481,10 +1504,7 @@ private void generateStartTimeStep(ReactorInstance instance) { String.join( "\n", "// Add action " + action.getFullName() + " to array of intended_tag fields.", - enclaveStruct - + "._lf_intended_tag_fields[" - + enclaveInfo.numIsPresentFields - + "] ", + enclaveStruct + "._lf_intended_tag_fields[" + indexString + "]", " = &" + containerSelfStructName + "->_lf_" @@ -1492,7 +1512,6 @@ private void generateStartTimeStep(ReactorInstance instance) { + ".intended_tag;"))); enclaveInfo.numIsPresentFields += action.getParent().getTotalWidth(); - temp.endScopedBlock(); } if (foundOne) startTimeStep.pr(temp.toString()); temp = new CodeBuilder(); @@ -1506,17 +1525,35 @@ private void generateStartTimeStep(ReactorInstance instance) { temp.pr("int count = 0; SUPPRESS_UNUSED_WARNING(count);"); temp.startScopedBlock(child); - var channelCount = 0; + // Need to find the total number of channels over all output ports of the child before + // generating the + // iteration. + var totalChannelCount = 0; + for (PortInstance output : child.outputs) { + if (!output.getDependsOnReactions().isEmpty()) { + totalChannelCount += output.getWidth(); + } + } for (PortInstance output : child.outputs) { if (!output.getDependsOnReactions().isEmpty()) { foundOne = true; - temp.pr("// Add port " + output.getFullName() + " to array of is_present fields."); + temp.pr( + "// Add output port " + output.getFullName() + " to array of is_present fields."); temp.startChannelIteration(output); + var indexString = + "(" + + CUtil.runtimeIndex(instance) + + ") * " + + totalChannelCount * child.getWidth() + + " + " + + enclaveInfo.numIsPresentFields + + " + count"; + temp.pr( enclaveStruct + ".is_present_fields[" - + enclaveInfo.numIsPresentFields - + " + count] = &" + + indexString + + "] = &" + CUtil.portRef(output) + ".is_present;"); @@ -1526,22 +1563,23 @@ private void generateStartTimeStep(ReactorInstance instance) { CExtensionUtils.surroundWithIfFederatedDecentralized( String.join( "\n", - "// Add port " + output.getFullName() + " to array of intended_tag fields.", + "// Add output port " + + output.getFullName() + + " to array of intended_tag fields.", enclaveStruct + "._lf_intended_tag_fields[" - + enclaveInfo.numIsPresentFields - + " + count] = &" + + indexString + + "] = &" + CUtil.portRef(output) + ".intended_tag;"))); temp.pr("count++;"); - channelCount += output.getWidth(); temp.endChannelIteration(output); } } - enclaveInfo.numIsPresentFields += channelCount * child.getTotalWidth(); temp.endScopedBlock(); temp.endScopedBlock(); + enclaveInfo.numIsPresentFields += totalChannelCount * child.getTotalWidth(); } } if (foundOne) startTimeStep.pr(temp.toString()); @@ -2027,7 +2065,7 @@ protected boolean targetLanguageIsCpp() { * if there is none), and the message (or an empty string if there is none). */ @Override - public GeneratorBase.ErrorFileAndLine parseCommandOutput(String line) { + public ErrorFileAndLine parseCommandOutput(String line) { var matcher = compileErrorPattern.matcher(line); if (matcher.find()) { var result = new ErrorFileAndLine(); diff --git a/core/src/main/java/org/lflang/generator/c/CUtil.java b/core/src/main/java/org/lflang/generator/c/CUtil.java index 4dc2ff9c91..64e63bfe95 100644 --- a/core/src/main/java/org/lflang/generator/c/CUtil.java +++ b/core/src/main/java/org/lflang/generator/c/CUtil.java @@ -93,9 +93,9 @@ public static String actionRef(ActionInstance instance, String runtimeIndex) { } /** - * Return a default name of a variable to refer to the bank index of a reactor in a bank. This is - * has the form uniqueID_i where uniqueID is an identifier for the instance that is guaranteed to - * be different from the ID of any other instance in the program. If the instance is not a bank, + * Return a default name of a variable to refer to the bank index of a reactor in a bank. This has + * the form uniqueID_i, where uniqueID is an identifier for the instance that is guaranteed to be + * different from the ID of any other instance in the program. If the instance is not a bank, * return "0". * * @param instance A reactor instance. @@ -106,9 +106,9 @@ public static String bankIndex(ReactorInstance instance) { } /** - * Return a default name of a variable to refer to the bank index of a reactor in a bank. This is - * has the form uniqueID_i where uniqueID is an identifier for the instance that is guaranteed to - * be different from the ID of any other instance in the program. + * Return a default name of a variable to refer to the bank index of a reactor in a bank. This has + * the form uniqueID_i, where uniqueID is an identifier for the instance that is guaranteed to be + * different from the ID of any other instance in the program. * * @param instance A reactor instance. */ diff --git a/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java index 6a55669752..de287b346e 100644 --- a/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java @@ -9,7 +9,7 @@ * @author Hou Seng Wong */ public class PythonDockerGenerator extends CDockerGenerator { - public static final String DEFAULT_BASE_IMAGE = "python:3.10-slim"; + public static final String DEFAULT_BASE_IMAGE = "python:3.10-alpine"; public PythonDockerGenerator(LFGeneratorContext context) { super(context); @@ -20,15 +20,6 @@ public String defaultImage() { return DEFAULT_BASE_IMAGE; } - @Override - protected String generateRunForInstallingDeps() { - if (builderBase().equals(defaultImage())) { - return "RUN set -ex && apt-get update && apt-get install -y python3-pip && pip install cmake"; - } else { - return "# (Skipping installation of build dependencies; custom base image.)"; - } - } - @Override protected String generateCopyOfExecutable() { var lfModuleName = context.getFileConfig().name; diff --git a/core/src/main/java/org/lflang/generator/python/PythonReactorGenerator.java b/core/src/main/java/org/lflang/generator/python/PythonReactorGenerator.java index 99dbae7349..73b4651b18 100644 --- a/core/src/main/java/org/lflang/generator/python/PythonReactorGenerator.java +++ b/core/src/main/java/org/lflang/generator/python/PythonReactorGenerator.java @@ -167,17 +167,22 @@ private static String generatePythonClassInstantiation( CodeBuilder code = new CodeBuilder(); code.pr(PyUtil.reactorRef(instance) + " = _" + className + "("); code.indent(); - // Always add the bank_index - code.pr("_bank_index = " + PyUtil.bankIndex(instance) + ","); + boolean hasBankIndexParameter = false; for (ParameterInstance param : instance.parameters) { - if (!param.getName().equals("bank_index")) { - code.pr( - "_" - + param.getName() - + "=" - + PythonParameterGenerator.generatePythonInitializer(param) - + ","); + if (param.getName().equals("bank_index")) { + if (param.getOverride() != null) hasBankIndexParameter = true; + else continue; // Skip bank_index if it is not explicitly set } + code.pr( + "_" + + param.getName() + + "=" + + PythonParameterGenerator.generatePythonInitializer(param) + + ","); + } + if (!hasBankIndexParameter) { + // Only add bank_index if not explicitly set + code.pr("_bank_index = " + PyUtil.bankIndex(instance) + ","); } code.unindent(); code.pr(")"); diff --git a/core/src/main/java/org/lflang/target/property/PlatformProperty.java b/core/src/main/java/org/lflang/target/property/PlatformProperty.java index 12399319f7..ecd6574b8b 100644 --- a/core/src/main/java/org/lflang/target/property/PlatformProperty.java +++ b/core/src/main/java/org/lflang/target/property/PlatformProperty.java @@ -166,7 +166,6 @@ private void validateFlexPRET(TargetConfig config, MessageReporter reporter) { private void validateZephyr(TargetConfig config, MessageReporter reporter) { var platform = config.get(PlatformProperty.INSTANCE); var singleThreaded = config.get(SingleThreadedProperty.INSTANCE); - if (singleThreaded) { if (platform.userThreads().value() > 0) { reporter diff --git a/core/src/main/java/org/lflang/target/property/type/PlatformType.java b/core/src/main/java/org/lflang/target/property/type/PlatformType.java index 29f4fc9937..650a033b4c 100644 --- a/core/src/main/java/org/lflang/target/property/type/PlatformType.java +++ b/core/src/main/java/org/lflang/target/property/type/PlatformType.java @@ -12,28 +12,24 @@ protected Class enumClass() { public enum Platform { AUTO, - ARDUINO, // FIXME: not multithreaded - NRF52("nRF52", false), - RP2040("Rp2040", true), - LINUX("Linux", true), - MAC("Darwin", true), - ZEPHYR("Zephyr", true), - FLEXPRET("FlexPRET", true), - PATMOS("Patmos", false), - WINDOWS("Windows", true); + ARDUINO, + NRF52("nRF52"), + RP2040("Rp2040"), + LINUX("Linux"), + MAC("Darwin"), + ZEPHYR("Zephyr"), + FLEXPRET("FlexPRET"), + PATMOS("Patmos"), + WINDOWS("Windows"); final String cMakeName; - private final boolean multiThreaded; - Platform() { this.cMakeName = this.toString(); - this.multiThreaded = true; } - Platform(String cMakeName, boolean isMultiThreaded) { + Platform(String cMakeName) { this.cMakeName = cMakeName; - this.multiThreaded = isMultiThreaded; } /** Return the name in lower case. */ @@ -47,12 +43,16 @@ public String getcMakeName() { return this.cMakeName; } - public boolean isMultiThreaded() { - return this.multiThreaded; - } - public Platform getDefault() { return Platform.AUTO; } + + /** Return {@code true} if the given platform supports federated. */ + public static boolean supportsFederated(Platform platform) { + return switch (platform) { + case AUTO, LINUX, MAC -> true; + default -> false; + }; + } } } diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 8d7cfa7394..815696c5d5 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 8d7cfa73949d3e1d1f39a0c10eb3b2d1d866ce94 +Subproject commit 815696c5d53775755e55dc8da4c6fb99f218efe1 diff --git a/core/src/main/resources/lib/platform/zephyr/prj_lf.conf b/core/src/main/resources/lib/platform/zephyr/prj_lf.conf index 5b026c5035..20ed364571 100644 --- a/core/src/main/resources/lib/platform/zephyr/prj_lf.conf +++ b/core/src/main/resources/lib/platform/zephyr/prj_lf.conf @@ -7,3 +7,4 @@ CONFIG_PRINTK=y CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y CONFIG_THREAD_CUSTOM_DATA=y +CONFIG_MAIN_STACK_SIZE=2048 diff --git a/test/C/src/ActionIsPresentReset.lf b/test/C/src/ActionIsPresentReset.lf new file mode 100644 index 0000000000..84388f3d0b --- /dev/null +++ b/test/C/src/ActionIsPresentReset.lf @@ -0,0 +1,30 @@ +target C { + timeout: 7 msecs, + fast: true +} + +main reactor { + logical action a + logical action b + + reaction(startup) -> a {= + lf_schedule(a, MSEC(1)); + =} + + reaction(a, b) -> a, b {= + if (a->is_present) { + printf("A"); + lf_schedule(b, MSEC(2)); + } + if (b->is_present) { + printf("B"); + lf_schedule(a, MSEC(1)); + } + + lf_print(" at %d msecs with triggers (%d,%d)", lf_time_logical_elapsed() / MSEC(1), a->is_present, b->is_present); + + if (a->is_present && b->is_present) { + lf_print_error_and_exit("Both triggers should not be present"); + } + =} +} diff --git a/test/C/src/concurrent/ScheduleAt.lf b/test/C/src/concurrent/ScheduleAt.lf index a32cb47cbe..5997dfdc37 100644 --- a/test/C/src/concurrent/ScheduleAt.lf +++ b/test/C/src/concurrent/ScheduleAt.lf @@ -65,7 +65,7 @@ reactor Scheduler { reaction(act) {= microstep_t microstep = lf_tag().microstep; instant_t elapsed_time = lf_time_logical_elapsed(); - if (elapsed_time == self->action_hit_list_times[self->action_hit_list_index] && + if (self->action_hit_list_index < 9 && elapsed_time == self->action_hit_list_times[self->action_hit_list_index] && microstep == self->action_hit_list_microstep[self->action_hit_list_index]) { self->action_hit_list_index++; } diff --git a/test/C/src/multiport/BankMultiportIsPresentSetup.lf b/test/C/src/multiport/BankMultiportIsPresentSetup.lf new file mode 100644 index 0000000000..66adf0f3c3 --- /dev/null +++ b/test/C/src/multiport/BankMultiportIsPresentSetup.lf @@ -0,0 +1,71 @@ +// Smoke test for checking that the `is_present_fields` is setup correctly +// for a complicated hierarchy of banks with multiports and actions. +target C + +reactor R1 { + logical action a1 + logical action a2 + output[2] out: int + + reaction(startup) -> out {= + lf_set(out[0], 42); + =} + + reaction(startup) -> out {= + lf_set(out[1], 43); + =} +} + +reactor R4 { + logical action a1 + logical action a2 + input[3] in: int + input[3] in2: int + input[2] in3: int + + reaction(in) {= + lf_print("in = [%d, %d, %d]", in[0]->value, in[1]->value, in[2]->value); + =} + + reaction(in2) {= + lf_print("in2 = [%d, %d, %d]", in2[0]->value, in2[1]->value, in2[2]->value); + =} + + reaction(in3) {= + lf_print("in3 = [%d, %d]", in3[0]->value, in3[1]->value); + =} +} + +reactor R2 { + logical action a1 + logical action a2 + r1 = new[3] R1() + r4 = new[2] R4() + r1.out -> r4.in + + reaction(startup) -> r4.in2, r4.in3 {= + lf_set(r4[0].in2[0], 44); + lf_set(r4[1].in2[0], 45); + lf_set(r4[0].in2[1], 46); + lf_set(r4[1].in2[1], 47); + lf_set(r4[0].in2[2], 48); + lf_set(r4[1].in2[2], 49); + + lf_set(r4[0].in3[0], 50); + lf_set(r4[1].in3[0], 51); + lf_set(r4[0].in3[1], 52); + lf_set(r4[1].in3[1], 53); + =} +} + +reactor R3 { + r2 = new[4] R2() + logical action a1 + logical action a2 +} + +main reactor { + r = new[2] R3() + logical action a1 + logical action a2 +} diff --git a/test/C/src/multiport/BankOfActionsIsPresentSetup.lf b/test/C/src/multiport/BankOfActionsIsPresentSetup.lf new file mode 100644 index 0000000000..fe2ce8e4bc --- /dev/null +++ b/test/C/src/multiport/BankOfActionsIsPresentSetup.lf @@ -0,0 +1,39 @@ +// Smoke test for checking that the `is_present_fields` is setup correctly +// for a complicated hierarchy of nested banks with actions. +target C + +reactor R1 { + logical action a1 + logical action a2 + + reaction(startup) -> a1, a2 {= + lf_schedule(a1, MSEC(10)); + lf_schedule(a2, MSEC(10)); + =} + + reaction(a1) {= + lf_print("a1"); + =} + + reaction(a2) {= + lf_print("a2"); + =} +} + +reactor R2 { + r1 = new[3] R1() + logical action a1 + logical action a2 +} + +reactor R3 { + r2 = new[4] R2() + logical action a1 + logical action a2 +} + +main reactor { + r = new[2] R3() + logical action a1 + logical action a2 +} diff --git a/test/Python/src/federated/BankIndexFed.lf b/test/Python/src/federated/BankIndexFed.lf new file mode 100644 index 0000000000..fe914ac116 --- /dev/null +++ b/test/Python/src/federated/BankIndexFed.lf @@ -0,0 +1,28 @@ +target Python + +reactor FirstReactor(bank_index=0) { + output out + + reaction(startup) -> out {= + print("bank_index: {:d}".format(self.bank_index)) + out.set(self.bank_index) + =} +} + +reactor SecondReactor { + input[4] inp + + reaction(inp) {= + for i, port in enumerate(inp): + assert port.is_present + print("in[{:d}]: {:d}".format(i, port.value)) + assert port.value == i + request_stop() + =} +} + +federated reactor { + a = new[4] FirstReactor() + b = new SecondReactor() + a.out -> b.inp +} diff --git a/test/Python/src/federated/BankIndexOverride.lf b/test/Python/src/federated/BankIndexOverride.lf new file mode 100644 index 0000000000..70aa800519 --- /dev/null +++ b/test/Python/src/federated/BankIndexOverride.lf @@ -0,0 +1,12 @@ +target Python + +reactor SingleReactor(bank_index=0) { + reaction(startup) {= + print("bank_index: {:d}".format(self.bank_index)) + assert(self.bank_index == 2) + =} +} + +main reactor { + a = new SingleReactor(bank_index=2) +}